@ingissa/navcore-react-native 1.0.3 → 1.0.7
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/LICENSE +3 -0
- package/dist/core/src/NavCore.d.ts +142 -0
- package/dist/core/src/NavCore.d.ts.map +1 -0
- package/dist/core/src/NavCore.js +390 -0
- package/dist/core/src/bearing/BearingEngine.d.ts +24 -0
- package/dist/core/src/bearing/BearingEngine.d.ts.map +1 -0
- package/dist/core/src/bearing/BearingEngine.js +50 -0
- package/dist/core/src/constants.d.ts +34 -0
- package/dist/core/src/constants.d.ts.map +1 -0
- package/dist/core/src/constants.js +41 -0
- package/dist/core/src/corridor/DeviationDetector.d.ts +33 -0
- package/dist/core/src/corridor/DeviationDetector.d.ts.map +1 -0
- package/dist/core/src/corridor/DeviationDetector.js +83 -0
- package/dist/core/src/corridor/DynamicCorridor.d.ts +13 -0
- package/dist/core/src/corridor/DynamicCorridor.d.ts.map +1 -0
- package/dist/core/src/corridor/DynamicCorridor.js +26 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.d.ts +35 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.d.ts.map +1 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.js +49 -0
- package/dist/core/src/directions/DirectionsProvider.d.ts +70 -0
- package/dist/core/src/directions/DirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/DirectionsProvider.js +7 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.d.ts +26 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.js +90 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.d.ts +30 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.d.ts.map +1 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.js +111 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts +33 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.js +155 -0
- package/dist/core/src/eta/ETAEngine.d.ts +61 -0
- package/dist/core/src/eta/ETAEngine.d.ts.map +1 -0
- package/dist/core/src/eta/ETAEngine.js +74 -0
- package/dist/core/src/features.d.ts +76 -0
- package/dist/core/src/features.d.ts.map +1 -0
- package/dist/core/src/features.js +76 -0
- package/dist/core/src/geo/bearing.d.ts +11 -0
- package/dist/core/src/geo/bearing.d.ts.map +1 -0
- package/dist/core/src/geo/bearing.js +25 -0
- package/dist/core/src/geo/cumulative.d.ts +9 -0
- package/dist/core/src/geo/cumulative.d.ts.map +1 -0
- package/dist/core/src/geo/cumulative.js +20 -0
- package/dist/core/src/geo/haversine.d.ts +13 -0
- package/dist/core/src/geo/haversine.d.ts.map +1 -0
- package/dist/core/src/geo/haversine.js +39 -0
- package/dist/core/src/geo/projection.d.ts +7 -0
- package/dist/core/src/geo/projection.d.ts.map +1 -0
- package/dist/core/src/geo/projection.js +17 -0
- package/dist/core/src/geofencing/GeofencingEngine.d.ts +93 -0
- package/dist/core/src/geofencing/GeofencingEngine.d.ts.map +1 -0
- package/dist/core/src/geofencing/GeofencingEngine.js +154 -0
- package/dist/core/src/index.d.ts +66 -0
- package/dist/core/src/index.d.ts.map +1 -0
- package/dist/core/src/index.js +62 -0
- package/dist/core/src/kalman/KalmanFilter2D.d.ts +46 -0
- package/dist/core/src/kalman/KalmanFilter2D.d.ts.map +1 -0
- package/dist/core/src/kalman/KalmanFilter2D.js +77 -0
- package/dist/core/src/license/LicenseManager.d.ts +40 -0
- package/dist/core/src/license/LicenseManager.d.ts.map +1 -0
- package/dist/core/src/license/LicenseManager.js +119 -0
- package/dist/core/src/license/verifyP256Signature.d.ts +2 -0
- package/dist/core/src/license/verifyP256Signature.d.ts.map +1 -0
- package/dist/core/src/license/verifyP256Signature.js +26 -0
- package/dist/core/src/off-route/NavigationFSM.d.ts +42 -0
- package/dist/core/src/off-route/NavigationFSM.d.ts.map +1 -0
- package/dist/core/src/off-route/NavigationFSM.js +56 -0
- package/dist/core/src/off-route/OffRouteFSM.d.ts +70 -0
- package/dist/core/src/off-route/OffRouteFSM.d.ts.map +1 -0
- package/dist/core/src/off-route/OffRouteFSM.js +151 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.d.ts +24 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.d.ts.map +1 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.js +55 -0
- package/dist/core/src/progress/InstructionEditor.d.ts +77 -0
- package/dist/core/src/progress/InstructionEditor.d.ts.map +1 -0
- package/dist/core/src/progress/InstructionEditor.js +151 -0
- package/dist/core/src/progress/InstructionResolver.d.ts +25 -0
- package/dist/core/src/progress/InstructionResolver.d.ts.map +1 -0
- package/dist/core/src/progress/InstructionResolver.js +78 -0
- package/dist/core/src/progress/ProgressTracker.d.ts +12 -0
- package/dist/core/src/progress/ProgressTracker.d.ts.map +1 -0
- package/dist/core/src/progress/ProgressTracker.js +22 -0
- package/dist/core/src/renderer/MapRendererAdapter.d.ts +72 -0
- package/dist/core/src/renderer/MapRendererAdapter.d.ts.map +1 -0
- package/dist/core/src/renderer/MapRendererAdapter.js +19 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.d.ts +83 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.d.ts.map +1 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.js +160 -0
- package/dist/core/src/route-plan/RoutePlan.d.ts +38 -0
- package/dist/core/src/route-plan/RoutePlan.d.ts.map +1 -0
- package/dist/core/src/route-plan/RoutePlan.js +102 -0
- package/dist/core/src/session/NavigationSession.d.ts +81 -0
- package/dist/core/src/session/NavigationSession.d.ts.map +1 -0
- package/dist/core/src/session/NavigationSession.js +115 -0
- package/dist/core/src/snapper/ParallelRoadResolver.d.ts +86 -0
- package/dist/core/src/snapper/ParallelRoadResolver.d.ts.map +1 -0
- package/dist/core/src/snapper/ParallelRoadResolver.js +83 -0
- package/dist/core/src/snapper/RouteSnapper.d.ts +38 -0
- package/dist/core/src/snapper/RouteSnapper.d.ts.map +1 -0
- package/dist/core/src/snapper/RouteSnapper.js +80 -0
- package/dist/core/src/voice/VoiceTriggerEngine.d.ts +80 -0
- package/dist/core/src/voice/VoiceTriggerEngine.d.ts.map +1 -0
- package/dist/core/src/voice/VoiceTriggerEngine.js +108 -0
- package/dist/react-native/src/NavCoreProvider.d.ts.map +1 -0
- package/dist/react-native/src/index.d.ts +10 -0
- package/dist/react-native/src/index.d.ts.map +1 -0
- package/dist/{index.js → react-native/src/index.js} +1 -0
- package/dist/{useNavCore.d.ts → react-native/src/useNavCore.d.ts} +7 -5
- package/dist/react-native/src/useNavCore.d.ts.map +1 -0
- package/dist/{useNavCore.js → react-native/src/useNavCore.js} +21 -47
- package/dist/react-native/src/useNavSelectors.d.ts +42 -0
- package/dist/react-native/src/useNavSelectors.d.ts.map +1 -0
- package/dist/react-native/src/useNavSelectors.js +103 -0
- package/package.json +17 -9
- package/src/NavCoreProvider.tsx +59 -0
- package/{dist/index.d.ts → src/index.ts} +18 -9
- package/src/useNavCore.ts +208 -0
- package/src/useNavSelectors.ts +143 -0
- package/dist/NavCoreProvider.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/useNavCore.d.ts.map +0 -1
- /package/dist/{NavCoreProvider.d.ts → react-native/src/NavCoreProvider.d.ts} +0 -0
- /package/dist/{NavCoreProvider.js → react-native/src/NavCoreProvider.js} +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValhallaDirectionsProvider
|
|
3
|
+
*
|
|
4
|
+
* Self-hosted Valhalla routing engine adapter.
|
|
5
|
+
* Supports the `/route` and `/optimized_route` endpoints.
|
|
6
|
+
* Zero API key for self-hosted. Uses Mapbox-hosted Valhalla with a token.
|
|
7
|
+
*
|
|
8
|
+
* @see https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/
|
|
9
|
+
*/
|
|
10
|
+
import type { Coordinate } from '../geo/haversine';
|
|
11
|
+
import type { DirectionsProvider, DirectionsOptions, DirectionsResult } from './DirectionsProvider';
|
|
12
|
+
export type ValhallaCostingModel = 'auto' | 'bicycle' | 'pedestrian' | 'bus' | 'motor_scooter';
|
|
13
|
+
export interface ValhallaConfig {
|
|
14
|
+
/** Base URL of your Valhalla server or hosted instance */
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/** Routing costing model (default: 'auto') */
|
|
17
|
+
costing?: ValhallaCostingModel;
|
|
18
|
+
/** Optional API key (required for hosted Valhalla services) */
|
|
19
|
+
apiKey?: string;
|
|
20
|
+
/** Max waypoints per chunk (default: 50) */
|
|
21
|
+
maxWaypointsPerChunk?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class ValhallaDirectionsProvider implements DirectionsProvider {
|
|
24
|
+
private readonly config;
|
|
25
|
+
constructor(config: ValhallaConfig);
|
|
26
|
+
getRoute(waypoints: Coordinate[], options?: DirectionsOptions): Promise<DirectionsResult>;
|
|
27
|
+
private profileToCosting;
|
|
28
|
+
private fetchRoute;
|
|
29
|
+
/** Decode Valhalla's encoded polyline6 format */
|
|
30
|
+
private decodePolyline;
|
|
31
|
+
private fetchChunked;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ValhallaDirectionsProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValhallaDirectionsProvider.d.ts","sourceRoot":"","sources":["../../../../../core/src/directions/ValhallaDirectionsProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEpG,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,GAAG,eAAe,CAAC;AAE/F,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID,qBAAa,0BAA2B,YAAW,kBAAkB;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;gBAEtC,MAAM,EAAE,cAAc;IAS5B,QAAQ,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY/F,OAAO,CAAC,gBAAgB;YAQV,UAAU;IA6ExB,iDAAiD;IACjD,OAAO,CAAC,cAAc;YAmCR,YAAY;CA+B3B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValhallaDirectionsProvider
|
|
3
|
+
*
|
|
4
|
+
* Self-hosted Valhalla routing engine adapter.
|
|
5
|
+
* Supports the `/route` and `/optimized_route` endpoints.
|
|
6
|
+
* Zero API key for self-hosted. Uses Mapbox-hosted Valhalla with a token.
|
|
7
|
+
*
|
|
8
|
+
* @see https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_MAX_CHUNK = 50;
|
|
11
|
+
export class ValhallaDirectionsProvider {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = {
|
|
14
|
+
baseUrl: config.baseUrl.replace(/\/$/, ''),
|
|
15
|
+
costing: config.costing ?? 'auto',
|
|
16
|
+
apiKey: config.apiKey ?? '',
|
|
17
|
+
maxWaypointsPerChunk: config.maxWaypointsPerChunk ?? DEFAULT_MAX_CHUNK,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async getRoute(waypoints, options) {
|
|
21
|
+
if (waypoints.length < 2)
|
|
22
|
+
throw new Error('[Valhalla] At least 2 waypoints required');
|
|
23
|
+
const costing = this.profileToCosting(options?.profile);
|
|
24
|
+
if (waypoints.length <= this.config.maxWaypointsPerChunk) {
|
|
25
|
+
return this.fetchRoute(waypoints, costing, options);
|
|
26
|
+
}
|
|
27
|
+
return this.fetchChunked(waypoints, costing, options);
|
|
28
|
+
}
|
|
29
|
+
profileToCosting(profile) {
|
|
30
|
+
switch (profile) {
|
|
31
|
+
case 'cycling': return 'bicycle';
|
|
32
|
+
case 'walking': return 'pedestrian';
|
|
33
|
+
default: return this.config.costing;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async fetchRoute(waypoints, costing, _options) {
|
|
37
|
+
const locations = waypoints.map(w => ({ lon: w[0], lat: w[1], type: 'break' }));
|
|
38
|
+
const body = JSON.stringify({
|
|
39
|
+
locations,
|
|
40
|
+
costing,
|
|
41
|
+
directions_options: { units: 'kilometers' },
|
|
42
|
+
});
|
|
43
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
44
|
+
if (this.config.apiKey)
|
|
45
|
+
headers['api_key'] = this.config.apiKey;
|
|
46
|
+
const res = await fetch(`${this.config.baseUrl}/route`, { method: 'POST', headers, body });
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
const err = await res.text();
|
|
49
|
+
throw new Error(`[Valhalla] HTTP ${res.status}: ${err}`);
|
|
50
|
+
}
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
const totalSummary = data.trip?.summary ?? {};
|
|
53
|
+
const fullGeometry = [];
|
|
54
|
+
const legs = (data.trip?.legs ?? []).map((leg, legIdx) => {
|
|
55
|
+
const legGeometry = this.decodePolyline(leg.shape ?? '');
|
|
56
|
+
if (legIdx === 0) {
|
|
57
|
+
fullGeometry.push(...legGeometry);
|
|
58
|
+
}
|
|
59
|
+
else if (legGeometry.length > 0) {
|
|
60
|
+
// avoid duplicating the boundary coordinate between consecutive legs
|
|
61
|
+
fullGeometry.push(...legGeometry.slice(1));
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
distance: (leg.summary?.length ?? 0) * 1000, // km → m
|
|
65
|
+
duration: leg.summary?.time ?? 0,
|
|
66
|
+
steps: (leg.maneuvers ?? []).map((m) => {
|
|
67
|
+
const beginIdx = typeof m.begin_shape_index === 'number' ? m.begin_shape_index : 0;
|
|
68
|
+
const endIdx = typeof m.end_shape_index === 'number' ? m.end_shape_index : beginIdx;
|
|
69
|
+
const stepGeometry = legGeometry.slice(beginIdx, endIdx + 1);
|
|
70
|
+
const location = legGeometry[beginIdx] ?? waypoints[0];
|
|
71
|
+
return {
|
|
72
|
+
distance: (m.length ?? 0) * 1000,
|
|
73
|
+
duration: m.time ?? 0,
|
|
74
|
+
geometry: stepGeometry,
|
|
75
|
+
maneuver: {
|
|
76
|
+
type: m.type?.toString() ?? 'unknown',
|
|
77
|
+
modifier: undefined,
|
|
78
|
+
location,
|
|
79
|
+
bearingBefore: m.begin_heading ?? 0,
|
|
80
|
+
bearingAfter: m.end_heading ?? 0,
|
|
81
|
+
instruction: m.instruction,
|
|
82
|
+
},
|
|
83
|
+
name: m.street_names?.[0],
|
|
84
|
+
};
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
const geometry = fullGeometry.length > 0
|
|
89
|
+
? fullGeometry
|
|
90
|
+
: this.decodePolyline(data.trip?.legs?.[0]?.shape ?? '');
|
|
91
|
+
return {
|
|
92
|
+
geometry,
|
|
93
|
+
distance: (totalSummary.length ?? 0) * 1000,
|
|
94
|
+
duration: totalSummary.time ?? 0,
|
|
95
|
+
legs,
|
|
96
|
+
raw: data,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Decode Valhalla's encoded polyline6 format */
|
|
100
|
+
decodePolyline(encoded, precision = 6) {
|
|
101
|
+
const factor = Math.pow(10, precision);
|
|
102
|
+
const coords = [];
|
|
103
|
+
let index = 0;
|
|
104
|
+
let lat = 0;
|
|
105
|
+
let lng = 0;
|
|
106
|
+
while (index < encoded.length) {
|
|
107
|
+
let shift = 0;
|
|
108
|
+
let result = 0;
|
|
109
|
+
let byte;
|
|
110
|
+
do {
|
|
111
|
+
byte = encoded.charCodeAt(index++) - 63;
|
|
112
|
+
result |= (byte & 0x1f) << shift;
|
|
113
|
+
shift += 5;
|
|
114
|
+
} while (byte >= 0x20);
|
|
115
|
+
const dLat = (result & 1) !== 0 ? ~(result >> 1) : result >> 1;
|
|
116
|
+
lat += dLat;
|
|
117
|
+
shift = 0;
|
|
118
|
+
result = 0;
|
|
119
|
+
do {
|
|
120
|
+
byte = encoded.charCodeAt(index++) - 63;
|
|
121
|
+
result |= (byte & 0x1f) << shift;
|
|
122
|
+
shift += 5;
|
|
123
|
+
} while (byte >= 0x20);
|
|
124
|
+
const dLng = (result & 1) !== 0 ? ~(result >> 1) : result >> 1;
|
|
125
|
+
lng += dLng;
|
|
126
|
+
coords.push([lng / factor, lat / factor]);
|
|
127
|
+
}
|
|
128
|
+
return coords;
|
|
129
|
+
}
|
|
130
|
+
async fetchChunked(waypoints, costing, options) {
|
|
131
|
+
const size = this.config.maxWaypointsPerChunk;
|
|
132
|
+
const chunks = [];
|
|
133
|
+
for (let i = 0; i < waypoints.length; i += size - 1) {
|
|
134
|
+
const chunk = waypoints.slice(i, i + size);
|
|
135
|
+
if (chunk.length < 2)
|
|
136
|
+
break;
|
|
137
|
+
chunks.push(chunk);
|
|
138
|
+
if (i + size >= waypoints.length)
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
const results = await Promise.all(chunks.map(c => this.fetchRoute(c, costing, options)));
|
|
142
|
+
const geometry = [];
|
|
143
|
+
let distance = 0;
|
|
144
|
+
let duration = 0;
|
|
145
|
+
const legs = [];
|
|
146
|
+
for (let i = 0; i < results.length; i++) {
|
|
147
|
+
const r = results[i];
|
|
148
|
+
geometry.push(...(i === 0 ? r.geometry : r.geometry.slice(1)));
|
|
149
|
+
distance += r.distance;
|
|
150
|
+
duration += r.duration;
|
|
151
|
+
legs.push(...r.legs);
|
|
152
|
+
}
|
|
153
|
+
return { geometry, distance, duration, legs, raw: results.map(r => r.raw) };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETAEngine — Local ETA computation. Zero API calls.
|
|
3
|
+
*
|
|
4
|
+
* Computes Estimated Time of Arrival from current NavCore state using
|
|
5
|
+
* a rolling speed average. No server required — entirely offline.
|
|
6
|
+
*
|
|
7
|
+
* FEATURE: ETA
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const eta = new ETAEngine({ speedWindowSize: 8 });
|
|
12
|
+
* engine.on('update', (state) => {
|
|
13
|
+
* const result = eta.update(state);
|
|
14
|
+
* console.log(`ETA: ${result.etaSeconds}s, ${result.distanceRemainingM.toFixed(0)}m`);
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { NavCoreState } from '../NavCore';
|
|
19
|
+
export interface ETAResult {
|
|
20
|
+
/** ETA in seconds from now */
|
|
21
|
+
etaSeconds: number;
|
|
22
|
+
/** ETA as absolute timestamp (Date.now() + etaSeconds * 1000) */
|
|
23
|
+
etaMs: number;
|
|
24
|
+
/** Remaining distance in metres */
|
|
25
|
+
distanceRemainingM: number;
|
|
26
|
+
/** Rolling average speed in m/s */
|
|
27
|
+
averageSpeedMs: number;
|
|
28
|
+
/** Whether the estimate is reliable (enough speed samples + non-zero speed) */
|
|
29
|
+
isReliable: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface ETAOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Number of speed samples in the rolling average window.
|
|
34
|
+
* More samples = smoother but slower to react. Default: 6.
|
|
35
|
+
*/
|
|
36
|
+
speedWindowSize?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Minimum speed (m/s) below which we use the last reliable speed
|
|
39
|
+
* rather than 0 (avoids Infinity ETA while stopped). Default: 0.5 m/s (~2 km/h).
|
|
40
|
+
*/
|
|
41
|
+
minimumSpeedMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Speed to assume if no reliable samples yet. Default: 8.3 m/s (30 km/h).
|
|
44
|
+
*/
|
|
45
|
+
fallbackSpeedMs?: number;
|
|
46
|
+
}
|
|
47
|
+
export declare class ETAEngine {
|
|
48
|
+
private readonly options;
|
|
49
|
+
private speedSamples;
|
|
50
|
+
private lastReliableSpeed;
|
|
51
|
+
constructor(options?: ETAOptions);
|
|
52
|
+
/**
|
|
53
|
+
* Feed a NavCore state snapshot and receive an ETA estimate.
|
|
54
|
+
* Call this on every GPS update tick.
|
|
55
|
+
*/
|
|
56
|
+
update(state: NavCoreState): ETAResult;
|
|
57
|
+
/** Reset the speed window (call on new route or significant GPS gap) */
|
|
58
|
+
reset(): void;
|
|
59
|
+
private rollingAverage;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=ETAEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ETAEngine.d.ts","sourceRoot":"","sources":["../../../../../core/src/eta/ETAEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAQD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,CAAC,EAAE,UAAU;IAKhC;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS;IAsCtC,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,cAAc;CAKvB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETAEngine — Local ETA computation. Zero API calls.
|
|
3
|
+
*
|
|
4
|
+
* Computes Estimated Time of Arrival from current NavCore state using
|
|
5
|
+
* a rolling speed average. No server required — entirely offline.
|
|
6
|
+
*
|
|
7
|
+
* FEATURE: ETA
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const eta = new ETAEngine({ speedWindowSize: 8 });
|
|
12
|
+
* engine.on('update', (state) => {
|
|
13
|
+
* const result = eta.update(state);
|
|
14
|
+
* console.log(`ETA: ${result.etaSeconds}s, ${result.distanceRemainingM.toFixed(0)}m`);
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULTS = {
|
|
19
|
+
speedWindowSize: 6,
|
|
20
|
+
minimumSpeedMs: 0.5,
|
|
21
|
+
fallbackSpeedMs: 8.33, // ~30 km/h
|
|
22
|
+
};
|
|
23
|
+
export class ETAEngine {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.speedSamples = [];
|
|
26
|
+
this.options = { ...DEFAULTS, ...options };
|
|
27
|
+
this.lastReliableSpeed = this.options.fallbackSpeedMs;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Feed a NavCore state snapshot and receive an ETA estimate.
|
|
31
|
+
* Call this on every GPS update tick.
|
|
32
|
+
*/
|
|
33
|
+
update(state) {
|
|
34
|
+
const distanceRemainingM = state.distanceToDestination ?? 0;
|
|
35
|
+
// Update rolling speed window
|
|
36
|
+
const speed = state.currentSpeed ?? 0;
|
|
37
|
+
this.speedSamples.push(speed);
|
|
38
|
+
if (this.speedSamples.length > this.options.speedWindowSize) {
|
|
39
|
+
this.speedSamples.shift();
|
|
40
|
+
}
|
|
41
|
+
const avgSpeed = this.rollingAverage();
|
|
42
|
+
// Track last reliable speed (above minimum threshold)
|
|
43
|
+
if (avgSpeed >= this.options.minimumSpeedMs) {
|
|
44
|
+
this.lastReliableSpeed = avgSpeed;
|
|
45
|
+
}
|
|
46
|
+
const effectiveSpeed = avgSpeed >= this.options.minimumSpeedMs
|
|
47
|
+
? avgSpeed
|
|
48
|
+
: this.lastReliableSpeed;
|
|
49
|
+
const isReliable = this.speedSamples.length >= Math.ceil(this.options.speedWindowSize / 2)
|
|
50
|
+
&& effectiveSpeed > 0
|
|
51
|
+
&& distanceRemainingM > 0;
|
|
52
|
+
const etaSeconds = effectiveSpeed > 0
|
|
53
|
+
? distanceRemainingM / effectiveSpeed
|
|
54
|
+
: 0;
|
|
55
|
+
return {
|
|
56
|
+
etaSeconds: Math.round(etaSeconds),
|
|
57
|
+
etaMs: Date.now() + etaSeconds * 1000,
|
|
58
|
+
distanceRemainingM,
|
|
59
|
+
averageSpeedMs: avgSpeed,
|
|
60
|
+
isReliable,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Reset the speed window (call on new route or significant GPS gap) */
|
|
64
|
+
reset() {
|
|
65
|
+
this.speedSamples = [];
|
|
66
|
+
this.lastReliableSpeed = this.options.fallbackSpeedMs;
|
|
67
|
+
}
|
|
68
|
+
rollingAverage() {
|
|
69
|
+
if (this.speedSamples.length === 0)
|
|
70
|
+
return 0;
|
|
71
|
+
const sum = this.speedSamples.reduce((a, b) => a + b, 0);
|
|
72
|
+
return sum / this.speedSamples.length;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature flag constants for SDK consumers.
|
|
3
|
+
*
|
|
4
|
+
* CORE features: available to all users (MIT license).
|
|
5
|
+
* PRO features: require a valid commercial license key.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FEATURES: {
|
|
8
|
+
/**
|
|
9
|
+
* Basic Kalman filter GPS smoothing.
|
|
10
|
+
* Reduces GPS jitter for stable route snapping.
|
|
11
|
+
*/
|
|
12
|
+
readonly GPS_SMOOTHING: "gps_smoothing";
|
|
13
|
+
/**
|
|
14
|
+
* Basic route snapping (nearest-point projection).
|
|
15
|
+
* Snaps GPS position to the closest route segment.
|
|
16
|
+
*/
|
|
17
|
+
readonly BASIC_SNAPPING: "basic_snapping";
|
|
18
|
+
/**
|
|
19
|
+
* Advanced route snapping with bearing penalty + inertia.
|
|
20
|
+
* Prevents incorrect snaps on parallel roads and U-turns.
|
|
21
|
+
*/
|
|
22
|
+
readonly ADVANCED_SNAPPING: "advanced_snapping";
|
|
23
|
+
/**
|
|
24
|
+
* Dead reckoning: continues navigation during GPS signal loss.
|
|
25
|
+
* Predicts position based on last known speed and bearing.
|
|
26
|
+
*/
|
|
27
|
+
readonly DEAD_RECKONING: "dead_reckoning";
|
|
28
|
+
/**
|
|
29
|
+
* Smooth route offset predictor for jitter-free map rendering.
|
|
30
|
+
* Advances render position at frame rate between GPS fixes.
|
|
31
|
+
*/
|
|
32
|
+
readonly ROUTE_OFFSET: "route_offset";
|
|
33
|
+
/**
|
|
34
|
+
* Progress anti-regression: prevents route index from jumping backward.
|
|
35
|
+
* Essential for circuit routes and motorway slip roads.
|
|
36
|
+
*/
|
|
37
|
+
readonly PROGRESS_TRACKING: "progress_tracking";
|
|
38
|
+
/**
|
|
39
|
+
* Advanced bearing engine with look-ahead and speed-adaptive alpha.
|
|
40
|
+
* Produces stable heading for smooth map camera rotation.
|
|
41
|
+
*/
|
|
42
|
+
readonly ADVANCED_BEARING: "advanced_bearing";
|
|
43
|
+
/**
|
|
44
|
+
* Compound deviation detection: combines distance, bearing, and speed signals.
|
|
45
|
+
* Eliminates false off-route triggers in tunnels and urban canyons.
|
|
46
|
+
*/
|
|
47
|
+
readonly COMPOUND_DEVIATION: "compound_deviation";
|
|
48
|
+
/**
|
|
49
|
+
* Parallel road disambiguation + U-turn snap protection.
|
|
50
|
+
* Prevents snapping to the wrong carriageway when bearing mismatches route direction.
|
|
51
|
+
*/
|
|
52
|
+
readonly PARALLEL_ROAD_RESOLUTION: "parallel_road_resolution";
|
|
53
|
+
/**
|
|
54
|
+
* U-turn protection: bearing penalty applied when vehicle heading opposes
|
|
55
|
+
* route segment direction by more than the configured threshold (default 60°).
|
|
56
|
+
*/
|
|
57
|
+
readonly U_TURN_PROTECTION: "u_turn_protection";
|
|
58
|
+
/**
|
|
59
|
+
* Timed voice instruction cues with deduplication and priority levels.
|
|
60
|
+
*/
|
|
61
|
+
readonly VOICE_TRIGGERS: "voice_triggers";
|
|
62
|
+
/**
|
|
63
|
+
* Local ETA computation using rolling speed average — zero API calls.
|
|
64
|
+
*/
|
|
65
|
+
readonly ETA: "eta";
|
|
66
|
+
/**
|
|
67
|
+
* Circular and polygon geofencing with enter/exit/dwell events.
|
|
68
|
+
*/
|
|
69
|
+
readonly GEOFENCING: "geofencing";
|
|
70
|
+
/**
|
|
71
|
+
* AI-powered navigation stability: oscillation suppression + turn anticipation.
|
|
72
|
+
*/
|
|
73
|
+
readonly NAV_AI: "nav_ai";
|
|
74
|
+
};
|
|
75
|
+
export type FeatureKey = typeof FEATURES[keyof typeof FEATURES];
|
|
76
|
+
//# sourceMappingURL=features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../../core/src/features.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,QAAQ;IAGnB;;;OAGG;;IAGH;;;OAGG;;IAKH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;CAEK,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature flag constants for SDK consumers.
|
|
3
|
+
*
|
|
4
|
+
* CORE features: available to all users (MIT license).
|
|
5
|
+
* PRO features: require a valid commercial license key.
|
|
6
|
+
*/
|
|
7
|
+
export const FEATURES = {
|
|
8
|
+
// ── CORE — available without a license key ──────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Basic Kalman filter GPS smoothing.
|
|
11
|
+
* Reduces GPS jitter for stable route snapping.
|
|
12
|
+
*/
|
|
13
|
+
GPS_SMOOTHING: 'gps_smoothing',
|
|
14
|
+
/**
|
|
15
|
+
* Basic route snapping (nearest-point projection).
|
|
16
|
+
* Snaps GPS position to the closest route segment.
|
|
17
|
+
*/
|
|
18
|
+
BASIC_SNAPPING: 'basic_snapping',
|
|
19
|
+
// ── PRO — require a valid commercial license key ────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Advanced route snapping with bearing penalty + inertia.
|
|
22
|
+
* Prevents incorrect snaps on parallel roads and U-turns.
|
|
23
|
+
*/
|
|
24
|
+
ADVANCED_SNAPPING: 'advanced_snapping',
|
|
25
|
+
/**
|
|
26
|
+
* Dead reckoning: continues navigation during GPS signal loss.
|
|
27
|
+
* Predicts position based on last known speed and bearing.
|
|
28
|
+
*/
|
|
29
|
+
DEAD_RECKONING: 'dead_reckoning',
|
|
30
|
+
/**
|
|
31
|
+
* Smooth route offset predictor for jitter-free map rendering.
|
|
32
|
+
* Advances render position at frame rate between GPS fixes.
|
|
33
|
+
*/
|
|
34
|
+
ROUTE_OFFSET: 'route_offset',
|
|
35
|
+
/**
|
|
36
|
+
* Progress anti-regression: prevents route index from jumping backward.
|
|
37
|
+
* Essential for circuit routes and motorway slip roads.
|
|
38
|
+
*/
|
|
39
|
+
PROGRESS_TRACKING: 'progress_tracking',
|
|
40
|
+
/**
|
|
41
|
+
* Advanced bearing engine with look-ahead and speed-adaptive alpha.
|
|
42
|
+
* Produces stable heading for smooth map camera rotation.
|
|
43
|
+
*/
|
|
44
|
+
ADVANCED_BEARING: 'advanced_bearing',
|
|
45
|
+
/**
|
|
46
|
+
* Compound deviation detection: combines distance, bearing, and speed signals.
|
|
47
|
+
* Eliminates false off-route triggers in tunnels and urban canyons.
|
|
48
|
+
*/
|
|
49
|
+
COMPOUND_DEVIATION: 'compound_deviation',
|
|
50
|
+
/**
|
|
51
|
+
* Parallel road disambiguation + U-turn snap protection.
|
|
52
|
+
* Prevents snapping to the wrong carriageway when bearing mismatches route direction.
|
|
53
|
+
*/
|
|
54
|
+
PARALLEL_ROAD_RESOLUTION: 'parallel_road_resolution',
|
|
55
|
+
/**
|
|
56
|
+
* U-turn protection: bearing penalty applied when vehicle heading opposes
|
|
57
|
+
* route segment direction by more than the configured threshold (default 60°).
|
|
58
|
+
*/
|
|
59
|
+
U_TURN_PROTECTION: 'u_turn_protection',
|
|
60
|
+
/**
|
|
61
|
+
* Timed voice instruction cues with deduplication and priority levels.
|
|
62
|
+
*/
|
|
63
|
+
VOICE_TRIGGERS: 'voice_triggers',
|
|
64
|
+
/**
|
|
65
|
+
* Local ETA computation using rolling speed average — zero API calls.
|
|
66
|
+
*/
|
|
67
|
+
ETA: 'eta',
|
|
68
|
+
/**
|
|
69
|
+
* Circular and polygon geofencing with enter/exit/dwell events.
|
|
70
|
+
*/
|
|
71
|
+
GEOFENCING: 'geofencing',
|
|
72
|
+
/**
|
|
73
|
+
* AI-powered navigation stability: oscillation suppression + turn anticipation.
|
|
74
|
+
*/
|
|
75
|
+
NAV_AI: 'nav_ai',
|
|
76
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bearing and angular math utilities.
|
|
3
|
+
*/
|
|
4
|
+
import type { Coordinate } from './haversine';
|
|
5
|
+
/** Normalise an angle to (−180, 180] */
|
|
6
|
+
export declare function normalizeAngle(angle: number): number;
|
|
7
|
+
/** Bearing between two [lng, lat] coordinates in degrees [0, 360) */
|
|
8
|
+
export declare function getBearingBetweenPoints(start: Coordinate, end: Coordinate): number;
|
|
9
|
+
/** Linear interpolation between two coordinates */
|
|
10
|
+
export declare function interpolatePoint(a: Coordinate, b: Coordinate, t: number): Coordinate;
|
|
11
|
+
//# sourceMappingURL=bearing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearing.d.ts","sourceRoot":"","sources":["../../../../../core/src/geo/bearing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,MAAM,CAWlF;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAGpF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bearing and angular math utilities.
|
|
3
|
+
*/
|
|
4
|
+
/** Normalise an angle to (−180, 180] */
|
|
5
|
+
export function normalizeAngle(angle) {
|
|
6
|
+
const a = angle % 360;
|
|
7
|
+
return a > 180 ? a - 360 : a < -180 ? a + 360 : a;
|
|
8
|
+
}
|
|
9
|
+
/** Bearing between two [lng, lat] coordinates in degrees [0, 360) */
|
|
10
|
+
export function getBearingBetweenPoints(start, end) {
|
|
11
|
+
const lat1 = start[1] * (Math.PI / 180);
|
|
12
|
+
const lon1 = start[0] * (Math.PI / 180);
|
|
13
|
+
const lat2 = end[1] * (Math.PI / 180);
|
|
14
|
+
const lon2 = end[0] * (Math.PI / 180);
|
|
15
|
+
const y = Math.sin(lon2 - lon1) * Math.cos(lat2);
|
|
16
|
+
const x = Math.cos(lat1) * Math.sin(lat2) -
|
|
17
|
+
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
|
|
18
|
+
const brng = Math.atan2(y, x);
|
|
19
|
+
return (brng * 180 / Math.PI + 360) % 360;
|
|
20
|
+
}
|
|
21
|
+
/** Linear interpolation between two coordinates */
|
|
22
|
+
export function interpolatePoint(a, b, t) {
|
|
23
|
+
const lerp = (s, e, t) => s + (e - s) * t;
|
|
24
|
+
return [lerp(a[0], b[0], t), lerp(a[1], b[1], t)];
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-calculated cumulative distance caches.
|
|
3
|
+
*/
|
|
4
|
+
import { type Coordinate } from './haversine';
|
|
5
|
+
/** Forward cumulative distances: result[i] = metres from route[0] to route[i]. O(n) build. */
|
|
6
|
+
export declare function buildForwardCumulativeDistances(route: Coordinate[]): Float64Array;
|
|
7
|
+
/** Reverse cumulative distances: result[i] = metres from vertex i to the end. O(1) reads. */
|
|
8
|
+
export declare function buildReverseCumulativeDistances(route: Coordinate[]): Float64Array;
|
|
9
|
+
//# sourceMappingURL=cumulative.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cumulative.d.ts","sourceRoot":"","sources":["../../../../../core/src/geo/cumulative.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,aAAa,CAAC;AAEjE,8FAA8F;AAC9F,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,YAAY,CASjF;AAED,6FAA6F;AAC7F,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,YAAY,CASjF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-calculated cumulative distance caches.
|
|
3
|
+
*/
|
|
4
|
+
import { haversineDistance } from './haversine';
|
|
5
|
+
/** Forward cumulative distances: result[i] = metres from route[0] to route[i]. O(n) build. */
|
|
6
|
+
export function buildForwardCumulativeDistances(route) {
|
|
7
|
+
const cum = new Float64Array(route.length);
|
|
8
|
+
for (let i = 1; i < route.length; i++) {
|
|
9
|
+
cum[i] = cum[i - 1] + haversineDistance(route[i - 1][1], route[i - 1][0], route[i][1], route[i][0]);
|
|
10
|
+
}
|
|
11
|
+
return cum;
|
|
12
|
+
}
|
|
13
|
+
/** Reverse cumulative distances: result[i] = metres from vertex i to the end. O(1) reads. */
|
|
14
|
+
export function buildReverseCumulativeDistances(route) {
|
|
15
|
+
const cum = new Float64Array(route.length);
|
|
16
|
+
for (let i = route.length - 2; i >= 0; i--) {
|
|
17
|
+
cum[i] = cum[i + 1] + haversineDistance(route[i][1], route[i][0], route[i + 1][1], route[i + 1][0]);
|
|
18
|
+
}
|
|
19
|
+
return cum;
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haversine geographic math utilities.
|
|
3
|
+
* ZERO dependencies — pure math.
|
|
4
|
+
*/
|
|
5
|
+
/** [longitude, latitude] coordinate */
|
|
6
|
+
export type Coordinate = [number, number];
|
|
7
|
+
/** Haversine distance between two points in metres */
|
|
8
|
+
export declare function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number;
|
|
9
|
+
/** Forward Haversine: compute destination given start, bearing (°), distance (m) */
|
|
10
|
+
export declare function destinationPoint(lat: number, lon: number, brng: number, dist: number): Coordinate;
|
|
11
|
+
/** Bearing between two [lng, lat] coordinates in degrees [0, 360) */
|
|
12
|
+
export declare function getBearingBetweenPoints(c1: Coordinate, c2: Coordinate): number;
|
|
13
|
+
//# sourceMappingURL=haversine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"haversine.d.ts","sourceRoot":"","sources":["../../../../../core/src/geo/haversine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAI1C,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAShG;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAgBjG;AAED,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,MAAM,CAa9E"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Haversine geographic math utilities.
|
|
3
|
+
* ZERO dependencies — pure math.
|
|
4
|
+
*/
|
|
5
|
+
const R_EARTH = 6371000;
|
|
6
|
+
/** Haversine distance between two points in metres */
|
|
7
|
+
export function haversineDistance(lat1, lon1, lat2, lon2) {
|
|
8
|
+
const dLat = (lat2 - lat1) * (Math.PI / 180);
|
|
9
|
+
const dLon = (lon2 - lon1) * (Math.PI / 180);
|
|
10
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
11
|
+
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
|
|
12
|
+
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
13
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
14
|
+
return R_EARTH * c;
|
|
15
|
+
}
|
|
16
|
+
/** Forward Haversine: compute destination given start, bearing (°), distance (m) */
|
|
17
|
+
export function destinationPoint(lat, lon, brng, dist) {
|
|
18
|
+
const angularDist = dist / R_EARTH;
|
|
19
|
+
const brngRad = brng * (Math.PI / 180);
|
|
20
|
+
const latRad = lat * (Math.PI / 180);
|
|
21
|
+
const lonRad = lon * (Math.PI / 180);
|
|
22
|
+
const destLatRad = Math.asin(Math.sin(latRad) * Math.cos(angularDist) +
|
|
23
|
+
Math.cos(latRad) * Math.sin(angularDist) * Math.cos(brngRad));
|
|
24
|
+
const destLonRad = lonRad + Math.atan2(Math.sin(brngRad) * Math.sin(angularDist) * Math.cos(latRad), Math.cos(angularDist) - Math.sin(latRad) * Math.sin(destLatRad));
|
|
25
|
+
return [((destLonRad * 180 / Math.PI) + 540) % 360 - 180, destLatRad * 180 / Math.PI];
|
|
26
|
+
}
|
|
27
|
+
/** Bearing between two [lng, lat] coordinates in degrees [0, 360) */
|
|
28
|
+
export function getBearingBetweenPoints(c1, c2) {
|
|
29
|
+
const lat1 = c1[1] * (Math.PI / 180);
|
|
30
|
+
const lon1 = c1[0] * (Math.PI / 180);
|
|
31
|
+
const lat2 = c2[1] * (Math.PI / 180);
|
|
32
|
+
const lon2 = c2[0] * (Math.PI / 180);
|
|
33
|
+
const y = Math.sin(lon2 - lon1) * Math.cos(lat2);
|
|
34
|
+
const x = Math.cos(lat1) * Math.sin(lat2) -
|
|
35
|
+
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
|
|
36
|
+
let brng = Math.atan2(y, x) * (180 / Math.PI);
|
|
37
|
+
brng = (brng + 360) % 360;
|
|
38
|
+
return brng;
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector projection utilities for coordinate snapping.
|
|
3
|
+
*/
|
|
4
|
+
import type { Coordinate } from './haversine';
|
|
5
|
+
/** Project point p onto line segment [a, b]. Returns the projected coordinate. */
|
|
6
|
+
export declare function projectPointOnSegment(p: Coordinate, a: Coordinate, b: Coordinate): Coordinate;
|
|
7
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../../../core/src/geo/projection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAe7F"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector projection utilities for coordinate snapping.
|
|
3
|
+
*/
|
|
4
|
+
/** Project point p onto line segment [a, b]. Returns the projected coordinate. */
|
|
5
|
+
export function projectPointOnSegment(p, a, b) {
|
|
6
|
+
const [px, py] = p;
|
|
7
|
+
const [ax, ay] = a;
|
|
8
|
+
const [bx, by] = b;
|
|
9
|
+
const dx = bx - ax;
|
|
10
|
+
const dy = by - ay;
|
|
11
|
+
const lenSq = dx * dx + dy * dy;
|
|
12
|
+
if (lenSq === 0)
|
|
13
|
+
return a;
|
|
14
|
+
let t = ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
15
|
+
t = Math.max(0, Math.min(1, t));
|
|
16
|
+
return [ax + t * dx, ay + t * dy];
|
|
17
|
+
}
|