expo-gaode-map-navigation 2.0.13 → 2.0.14
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 +23 -15
- package/build/index.d.ts +26 -126
- package/build/index.d.ts.map +1 -1
- package/build/index.js +11 -612
- package/build/index.js.map +1 -1
- package/build/route-geometry.d.ts +13 -0
- package/build/route-geometry.d.ts.map +1 -0
- package/build/route-geometry.js +154 -0
- package/build/route-geometry.js.map +1 -0
- package/build/route-planning.d.ts +21 -0
- package/build/route-planning.d.ts.map +1 -0
- package/build/route-planning.js +67 -0
- package/build/route-planning.js.map +1 -0
- package/build/web-api-fallback.d.ts +5 -0
- package/build/web-api-fallback.d.ts.map +1 -0
- package/build/web-api-fallback.js +160 -0
- package/build/web-api-fallback.js.map +1 -0
- package/build/web-route-following.d.ts +3 -0
- package/build/web-route-following.d.ts.map +1 -0
- package/build/web-route-following.js +178 -0
- package/build/web-route-following.js.map +1 -0
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -1,624 +1,23 @@
|
|
|
1
1
|
import ExpoGaodeMapNavigationModule from './ExpoGaodeMapNavigationModule';
|
|
2
|
-
import { ExpoGaodeMapModule } from './map';
|
|
3
2
|
import { ExpoGaodeMapNaviView, } from './ExpoGaodeMapNaviView';
|
|
4
|
-
// 重新导出地图模块的所有内容
|
|
5
3
|
export * from './map';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.filter(Boolean)
|
|
15
|
-
.map((segment) => {
|
|
16
|
-
const [longitude, latitude] = segment.split(',').map((value) => Number(value.trim()));
|
|
17
|
-
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return {
|
|
21
|
-
latitude,
|
|
22
|
-
longitude,
|
|
23
|
-
};
|
|
24
|
-
})
|
|
25
|
-
.filter((point) => point !== null);
|
|
26
|
-
}
|
|
27
|
-
function dedupeAdjacentPoints(points) {
|
|
28
|
-
return points.filter((point, index) => {
|
|
29
|
-
if (index === 0) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
const previous = points[index - 1];
|
|
33
|
-
return (previous.latitude !== point.latitude ||
|
|
34
|
-
previous.longitude !== point.longitude);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function haversineDistance(pointA, pointB) {
|
|
38
|
-
const toRadians = (degrees) => (degrees * Math.PI) / 180;
|
|
39
|
-
const earthRadiusMeters = 6371000;
|
|
40
|
-
const latitudeDelta = toRadians(pointB.latitude - pointA.latitude);
|
|
41
|
-
const longitudeDelta = toRadians(pointB.longitude - pointA.longitude);
|
|
42
|
-
const latitudeA = toRadians(pointA.latitude);
|
|
43
|
-
const latitudeB = toRadians(pointB.latitude);
|
|
44
|
-
const a = Math.sin(latitudeDelta / 2) ** 2 +
|
|
45
|
-
Math.cos(latitudeA) * Math.cos(latitudeB) * Math.sin(longitudeDelta / 2) ** 2;
|
|
46
|
-
return 2 * earthRadiusMeters * Math.asin(Math.sqrt(a));
|
|
47
|
-
}
|
|
48
|
-
function distanceBetweenCoordinatesSafe(pointA, pointB) {
|
|
49
|
-
try {
|
|
50
|
-
return ExpoGaodeMapModule.distanceBetweenCoordinates(pointA, pointB);
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return haversineDistance(pointA, pointB);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function calculatePathLengthSafe(points) {
|
|
57
|
-
try {
|
|
58
|
-
return ExpoGaodeMapModule.calculatePathLength(points);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
let total = 0;
|
|
62
|
-
for (let index = 1; index < points.length; index += 1) {
|
|
63
|
-
total += distanceBetweenCoordinatesSafe(points[index - 1], points[index]);
|
|
64
|
-
}
|
|
65
|
-
return total;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function simplifyPolylineSafe(points, tolerance) {
|
|
69
|
-
if (points.length <= 2) {
|
|
70
|
-
return points;
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
const simplified = ExpoGaodeMapModule.simplifyPolyline(points, tolerance);
|
|
74
|
-
return simplified.length >= 2 ? simplified : points;
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return points;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function getDistanceToPathSafe(path, target) {
|
|
81
|
-
if (path.length === 0) {
|
|
82
|
-
return Number.POSITIVE_INFINITY;
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
const nearest = ExpoGaodeMapModule.getNearestPointOnPath(path, target);
|
|
86
|
-
if (nearest) {
|
|
87
|
-
return nearest.distanceMeters;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// ignore and fallback to point-to-point scan
|
|
92
|
-
}
|
|
93
|
-
return path.reduce((minimum, point) => {
|
|
94
|
-
const distance = distanceBetweenCoordinatesSafe(point, target);
|
|
95
|
-
return distance < minimum ? distance : minimum;
|
|
96
|
-
}, Number.POSITIVE_INFINITY);
|
|
97
|
-
}
|
|
98
|
-
function samplePolyline(points, targetSamples = 36) {
|
|
99
|
-
if (points.length <= targetSamples) {
|
|
100
|
-
return points;
|
|
101
|
-
}
|
|
102
|
-
const step = Math.max(1, Math.floor(points.length / targetSamples));
|
|
103
|
-
const samples = points.filter((_, index) => index % step === 0);
|
|
104
|
-
const lastPoint = points[points.length - 1];
|
|
105
|
-
const lastSample = samples[samples.length - 1];
|
|
106
|
-
if (!lastSample ||
|
|
107
|
-
lastSample.latitude !== lastPoint.latitude ||
|
|
108
|
-
lastSample.longitude !== lastPoint.longitude) {
|
|
109
|
-
samples.push(lastPoint);
|
|
110
|
-
}
|
|
111
|
-
return samples;
|
|
112
|
-
}
|
|
113
|
-
function selectEvenlySpacedPoints(points, count) {
|
|
114
|
-
if (count <= 0 || points.length <= count) {
|
|
115
|
-
return points;
|
|
116
|
-
}
|
|
117
|
-
return Array.from({ length: count }, (_, index) => {
|
|
118
|
-
const rawIndex = Math.round(((index + 1) * (points.length + 1)) / (count + 1)) - 1;
|
|
119
|
-
const boundedIndex = Math.min(points.length - 1, Math.max(0, rawIndex));
|
|
120
|
-
return points[boundedIndex];
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function normalizeWebRoutePolyline(webRoute) {
|
|
124
|
-
const directPolyline = dedupeAdjacentPoints(webRoute.polyline ?? []);
|
|
125
|
-
if (directPolyline.length > 1) {
|
|
126
|
-
return directPolyline;
|
|
127
|
-
}
|
|
128
|
-
const stepPolyline = dedupeAdjacentPoints((webRoute.steps ?? []).flatMap((step) => step.polyline ?? []));
|
|
129
|
-
return stepPolyline;
|
|
130
|
-
}
|
|
131
|
-
export function buildAnchorWaypointsFromWebRoute(options) {
|
|
132
|
-
const { webRoute, maxViaPoints = 8, simplifyTolerance = 80, minSpacingMeters = 800, } = options;
|
|
133
|
-
const polyline = normalizeWebRoutePolyline(webRoute);
|
|
134
|
-
if (polyline.length <= 2) {
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
137
|
-
const simplified = dedupeAdjacentPoints(simplifyPolylineSafe(polyline, simplifyTolerance));
|
|
138
|
-
const candidatePoints = simplified.length > 2 ? simplified : polyline;
|
|
139
|
-
const interiorPoints = candidatePoints.slice(1, -1);
|
|
140
|
-
const spacedPoints = [];
|
|
141
|
-
let previousPoint = polyline[0];
|
|
142
|
-
for (const point of interiorPoints) {
|
|
143
|
-
if (distanceBetweenCoordinatesSafe(previousPoint, point) < minSpacingMeters) {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
spacedPoints.push(point);
|
|
147
|
-
previousPoint = point;
|
|
148
|
-
}
|
|
149
|
-
const waypoints = spacedPoints.length > 0
|
|
150
|
-
? spacedPoints
|
|
151
|
-
: candidatePoints.length > 2
|
|
152
|
-
? [candidatePoints[Math.floor(candidatePoints.length / 2)]]
|
|
153
|
-
: [];
|
|
154
|
-
return dedupeAdjacentPoints(selectEvenlySpacedPoints(waypoints, maxViaPoints));
|
|
155
|
-
}
|
|
156
|
-
async function loadWebApiFallback(feature) {
|
|
157
|
-
try {
|
|
158
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
159
|
-
const webApi = require('expo-gaode-map-web-api');
|
|
160
|
-
if (typeof webApi?.GaodeWebAPI !== 'function') {
|
|
161
|
-
throw new Error('expo-gaode-map-web-api 未导出 GaodeWebAPI');
|
|
162
|
-
}
|
|
163
|
-
return webApi;
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
throw new Error(`${feature}依赖 expo-gaode-map-web-api。请安装该包,并在 ExpoGaodeMapModule.initSDK 中提供 webKey。`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function normalizeAvoidPolygons(polygons) {
|
|
170
|
-
if (!polygons?.length) {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
const normalized = polygons
|
|
174
|
-
.map((polygon) => polygon
|
|
175
|
-
.map((point) => `${point.longitude},${point.latitude}`)
|
|
176
|
-
.join(';'))
|
|
177
|
-
.filter(Boolean)
|
|
178
|
-
.join('|');
|
|
179
|
-
return normalized || undefined;
|
|
180
|
-
}
|
|
181
|
-
function normalizeDrivingStrategy(strategy) {
|
|
182
|
-
// 导航 SDK 与 Web API 的策略枚举不完全一致。
|
|
183
|
-
// 这里做“尽量等价”的映射,主要用于带规避参数时的路线预览。
|
|
184
|
-
switch (strategy) {
|
|
185
|
-
case DriveStrategy.FASTEST:
|
|
186
|
-
return 38;
|
|
187
|
-
case DriveStrategy.FEE_FIRST:
|
|
188
|
-
return 1;
|
|
189
|
-
case DriveStrategy.SHORTEST:
|
|
190
|
-
return 2;
|
|
191
|
-
case DriveStrategy.NO_EXPRESSWAYS:
|
|
192
|
-
return 37;
|
|
193
|
-
case DriveStrategy.AVOID_CONGESTION:
|
|
194
|
-
return 33;
|
|
195
|
-
case DriveStrategy.NO_HIGHWAY:
|
|
196
|
-
return 35;
|
|
197
|
-
case DriveStrategy.NO_HIGHWAY_AVOID_CONGESTION:
|
|
198
|
-
return 40;
|
|
199
|
-
case DriveStrategy.AVOID_COST_CONGESTION:
|
|
200
|
-
case DriveStrategy.AVOID_CONGESTION_COST:
|
|
201
|
-
return 41;
|
|
202
|
-
case DriveStrategy.NO_HIGHWAY_AVOID_COST_CONGESTION:
|
|
203
|
-
return 43;
|
|
204
|
-
default:
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
function normalizeDrivingRouteResult(options, result) {
|
|
209
|
-
const paths = result?.route?.paths ?? [];
|
|
210
|
-
const routes = paths.map((path, index) => {
|
|
211
|
-
const segments = (path?.steps ?? []).map((step) => ({
|
|
212
|
-
instruction: step?.instruction ?? '',
|
|
213
|
-
orientation: step?.orientation,
|
|
214
|
-
road: step?.road_name,
|
|
215
|
-
distance: Number(step?.step_distance ?? 0),
|
|
216
|
-
duration: Number(step?.cost?.duration ?? 0),
|
|
217
|
-
polyline: parsePolyline(step?.polyline),
|
|
218
|
-
assistantAction: step?.assistant_action,
|
|
219
|
-
tollDistance: step?.cost?.toll_distance ? Number(step.cost.toll_distance) : undefined,
|
|
220
|
-
tollCost: step?.cost?.tolls ? Number(step.cost.tolls) : undefined,
|
|
221
|
-
}));
|
|
222
|
-
const restrictionCode = path?.restriction != null ? Number(path.restriction) : undefined;
|
|
223
|
-
return {
|
|
224
|
-
id: index,
|
|
225
|
-
start: options.from,
|
|
226
|
-
end: options.to,
|
|
227
|
-
distance: Number(path?.distance ?? 0),
|
|
228
|
-
duration: Number(path?.cost?.duration ?? path?.duration ?? 0),
|
|
229
|
-
segments,
|
|
230
|
-
polyline: segments.flatMap((segment) => segment.polyline),
|
|
231
|
-
tollDistance: path?.cost?.toll_distance ? Number(path.cost.toll_distance) : undefined,
|
|
232
|
-
tollCost: path?.cost?.tolls ? Number(path.cost.tolls) : undefined,
|
|
233
|
-
trafficLightCount: path?.cost?.traffic_lights ? Number(path.cost.traffic_lights) : undefined,
|
|
234
|
-
restrictionCode,
|
|
235
|
-
restrictionInfo: restrictionCode === 0
|
|
236
|
-
? '限行已规避或未限行'
|
|
237
|
-
: restrictionCode === 1
|
|
238
|
-
? '限行无法规避'
|
|
239
|
-
: undefined,
|
|
240
|
-
strategy: options.strategy,
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
return {
|
|
244
|
-
count: Number(result?.count ?? routes.length),
|
|
245
|
-
mainPathIndex: 0,
|
|
246
|
-
routeIds: routes.map((route) => route.id),
|
|
247
|
-
routes,
|
|
248
|
-
taxiCost: result?.route?.taxi_cost ? Number(result.route.taxi_cost) : undefined,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
function shouldUseAvoidPreviewFallback(options) {
|
|
252
|
-
return Boolean(options.avoidRoad?.trim() || options.avoidPolygons?.length);
|
|
253
|
-
}
|
|
254
|
-
function extractRoutePolyline(route) {
|
|
255
|
-
if (Array.isArray(route.polyline) && route.polyline.length > 0) {
|
|
256
|
-
return route.polyline;
|
|
257
|
-
}
|
|
258
|
-
const segments = Array.isArray(route.segments) ? route.segments : [];
|
|
259
|
-
if (segments.length > 0) {
|
|
260
|
-
return dedupeAdjacentPoints(segments.flatMap((segment) => segment.polyline ?? []));
|
|
261
|
-
}
|
|
262
|
-
const steps = Array.isArray(route.steps) ? route.steps : [];
|
|
263
|
-
if (steps.length > 0) {
|
|
264
|
-
return dedupeAdjacentPoints(steps.flatMap((step) => step.polyline ?? []));
|
|
265
|
-
}
|
|
266
|
-
return [];
|
|
267
|
-
}
|
|
268
|
-
function resolveIndependentRouteId(result, route, routeIndex) {
|
|
269
|
-
if (typeof route.id === 'number') {
|
|
270
|
-
return route.id;
|
|
271
|
-
}
|
|
272
|
-
if (typeof route.routeId === 'number') {
|
|
273
|
-
return route.routeId;
|
|
274
|
-
}
|
|
275
|
-
return result.routeIds?.[routeIndex];
|
|
276
|
-
}
|
|
277
|
-
function scoreIndependentRouteAgainstWebPolyline(result, route, routeIndex, webPolyline, anchorWaypoints, thresholdMeters) {
|
|
278
|
-
const nativePolyline = extractRoutePolyline(route);
|
|
279
|
-
if (nativePolyline.length === 0 || webPolyline.length === 0) {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
const sampledNativePoints = samplePolyline(nativePolyline);
|
|
283
|
-
const pointDistances = sampledNativePoints.map((point) => getDistanceToPathSafe(webPolyline, point));
|
|
284
|
-
const averageDeviationMeters = pointDistances.reduce((total, distance) => total + distance, 0) / pointDistances.length;
|
|
285
|
-
const maxDeviationMeters = Math.max(...pointDistances);
|
|
286
|
-
const missedAnchorCount = anchorWaypoints.reduce((count, point) => (getDistanceToPathSafe(nativePolyline, point) > thresholdMeters ? count + 1 : count), 0);
|
|
287
|
-
return {
|
|
288
|
-
routeId: resolveIndependentRouteId(result, route, routeIndex),
|
|
289
|
-
routeIndex,
|
|
290
|
-
averageDeviationMeters,
|
|
291
|
-
maxDeviationMeters,
|
|
292
|
-
missedAnchorCount,
|
|
293
|
-
score: averageDeviationMeters +
|
|
294
|
-
maxDeviationMeters * 0.35 +
|
|
295
|
-
missedAnchorCount * thresholdMeters * 0.5,
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
function evaluateIndependentResultAgainstWebRoute(independentResult, webPolyline, anchorWaypoints, maxDeviationMeters) {
|
|
299
|
-
const candidateMatches = independentResult.routes
|
|
300
|
-
.map((route, routeIndex) => scoreIndependentRouteAgainstWebPolyline(independentResult, route, routeIndex, webPolyline, anchorWaypoints, maxDeviationMeters))
|
|
301
|
-
.filter((candidate) => candidate !== null)
|
|
302
|
-
.sort((routeA, routeB) => routeA.score - routeB.score);
|
|
303
|
-
const bestMatch = candidateMatches[0];
|
|
304
|
-
const selectedRoute = bestMatch
|
|
305
|
-
? independentResult.routes[bestMatch.routeIndex]
|
|
306
|
-
: undefined;
|
|
307
|
-
const nativePolyline = selectedRoute ? extractRoutePolyline(selectedRoute) : [];
|
|
308
|
-
let mode = 'preview_only';
|
|
309
|
-
let reason = '未找到足够接近 Web 规划线的原生路线';
|
|
310
|
-
if (bestMatch) {
|
|
311
|
-
if (bestMatch.averageDeviationMeters <= maxDeviationMeters / 2 &&
|
|
312
|
-
bestMatch.maxDeviationMeters <= maxDeviationMeters &&
|
|
313
|
-
bestMatch.missedAnchorCount === 0) {
|
|
314
|
-
mode = 'matched';
|
|
315
|
-
reason = '原生路线与 Web 规划线高度接近,可直接按近似结果导航';
|
|
316
|
-
}
|
|
317
|
-
else if (bestMatch.averageDeviationMeters <= maxDeviationMeters &&
|
|
318
|
-
bestMatch.maxDeviationMeters <= maxDeviationMeters * 2) {
|
|
319
|
-
mode = 'approximate';
|
|
320
|
-
reason = '原生路线与 Web 规划线接近,但仍存在可见偏差';
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
candidateMatches,
|
|
325
|
-
bestMatch,
|
|
326
|
-
selectedRoute,
|
|
327
|
-
nativePolyline,
|
|
328
|
-
mode,
|
|
329
|
-
reason,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
export async function followWebPlannedRoute(options) {
|
|
333
|
-
const { from, to, webRoute, strategy, carNumber, restriction, maxDeviationMeters = 120, startNavigation = false, naviType = 0, } = options;
|
|
334
|
-
const webPolyline = normalizeWebRoutePolyline(webRoute);
|
|
335
|
-
if (webPolyline.length < 2) {
|
|
336
|
-
throw new Error('webRoute.polyline 至少需要 2 个点');
|
|
337
|
-
}
|
|
338
|
-
const anchorWaypoints = buildAnchorWaypointsFromWebRoute(options);
|
|
339
|
-
const anchoredIndependentResult = await independentDriveRoute({
|
|
340
|
-
from,
|
|
341
|
-
to,
|
|
342
|
-
strategy,
|
|
343
|
-
carNumber,
|
|
344
|
-
restriction,
|
|
345
|
-
waypoints: anchorWaypoints,
|
|
346
|
-
});
|
|
347
|
-
let independentResult = anchoredIndependentResult;
|
|
348
|
-
let evaluation = evaluateIndependentResultAgainstWebRoute(anchoredIndependentResult, webPolyline, anchorWaypoints, maxDeviationMeters);
|
|
349
|
-
let navigationUsesAnchorWaypoints = anchorWaypoints.length > 0;
|
|
350
|
-
if (evaluation.bestMatch && evaluation.mode !== 'preview_only' && anchorWaypoints.length > 0) {
|
|
351
|
-
try {
|
|
352
|
-
const directIndependentResult = await independentDriveRoute({
|
|
353
|
-
from,
|
|
354
|
-
to,
|
|
355
|
-
strategy,
|
|
356
|
-
carNumber,
|
|
357
|
-
restriction,
|
|
358
|
-
});
|
|
359
|
-
const directEvaluation = evaluateIndependentResultAgainstWebRoute(directIndependentResult, webPolyline, [], maxDeviationMeters);
|
|
360
|
-
const anchoredBest = evaluation.bestMatch;
|
|
361
|
-
const directBest = directEvaluation.bestMatch;
|
|
362
|
-
const canSwitchToDirectNavigation = Boolean(directBest) &&
|
|
363
|
-
directEvaluation.mode !== 'preview_only' &&
|
|
364
|
-
directBest.averageDeviationMeters <= Math.max(anchoredBest.averageDeviationMeters + 45, anchoredBest.averageDeviationMeters * 1.45) &&
|
|
365
|
-
directBest.maxDeviationMeters <= Math.max(anchoredBest.maxDeviationMeters + 90, anchoredBest.maxDeviationMeters * 1.45);
|
|
366
|
-
if (canSwitchToDirectNavigation) {
|
|
367
|
-
ExpoGaodeMapNavigationModule.clearIndependentRoute({
|
|
368
|
-
token: anchoredIndependentResult.token,
|
|
369
|
-
}).catch(() => { });
|
|
370
|
-
independentResult = directIndependentResult;
|
|
371
|
-
evaluation = directEvaluation;
|
|
372
|
-
navigationUsesAnchorWaypoints = false;
|
|
373
|
-
evaluation.reason =
|
|
374
|
-
directEvaluation.mode === 'matched'
|
|
375
|
-
? '已切换为无途经点导航结果,且与 Web 规划线高度接近'
|
|
376
|
-
: '已切换为无途经点导航结果,但与 Web 规划线仍存在轻微偏差';
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
ExpoGaodeMapNavigationModule.clearIndependentRoute({
|
|
380
|
-
token: directIndependentResult.token,
|
|
381
|
-
}).catch(() => { });
|
|
382
|
-
evaluation.reason = `${evaluation.reason};最终导航仍需依赖锚点途经点逼近 Web 线路`;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
catch {
|
|
386
|
-
evaluation.reason = `${evaluation.reason};无途经点重算失败,最终导航仍需依赖锚点途经点`;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
let navigationStarted = false;
|
|
390
|
-
if (startNavigation && evaluation.bestMatch && evaluation.mode !== 'preview_only') {
|
|
391
|
-
navigationStarted = await startNaviWithIndependentPath({
|
|
392
|
-
token: independentResult.token,
|
|
393
|
-
naviType,
|
|
394
|
-
routeId: evaluation.bestMatch.routeId,
|
|
395
|
-
routeIndex: evaluation.bestMatch.routeId == null ? evaluation.bestMatch.routeIndex : undefined,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
return {
|
|
399
|
-
mode: evaluation.mode,
|
|
400
|
-
token: independentResult.token,
|
|
401
|
-
anchorWaypoints,
|
|
402
|
-
webDistance: calculatePathLengthSafe(webPolyline),
|
|
403
|
-
nativeDistance: evaluation.nativePolyline.length > 1
|
|
404
|
-
? calculatePathLengthSafe(evaluation.nativePolyline)
|
|
405
|
-
: undefined,
|
|
406
|
-
selectedRouteId: evaluation.bestMatch?.routeId,
|
|
407
|
-
selectedRouteIndex: evaluation.bestMatch?.routeIndex,
|
|
408
|
-
averageDeviationMeters: evaluation.bestMatch?.averageDeviationMeters,
|
|
409
|
-
maxDeviationMeters: evaluation.bestMatch?.maxDeviationMeters,
|
|
410
|
-
navigationStarted,
|
|
411
|
-
navigationUsesAnchorWaypoints,
|
|
412
|
-
independentResult,
|
|
413
|
-
candidateMatches: evaluation.candidateMatches,
|
|
414
|
-
reason: evaluation.reason,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
async function calculateDriveRouteWithAvoidPreview(options) {
|
|
418
|
-
const { GaodeWebAPI } = await loadWebApiFallback('规避路线预览');
|
|
419
|
-
const api = new GaodeWebAPI();
|
|
420
|
-
const result = await api.route.driving(`${options.from.longitude},${options.from.latitude}`, `${options.to.longitude},${options.to.latitude}`, {
|
|
421
|
-
strategy: normalizeDrivingStrategy(options.strategy),
|
|
422
|
-
waypoints: options.waypoints?.map((point) => `${point.longitude},${point.latitude}`),
|
|
423
|
-
avoidpolygons: normalizeAvoidPolygons(options.avoidPolygons),
|
|
424
|
-
avoidroad: options.avoidRoad?.trim() || undefined,
|
|
425
|
-
plate: options.carNumber,
|
|
426
|
-
show_fields: 'cost,navi,polyline',
|
|
427
|
-
});
|
|
428
|
-
return normalizeDrivingRouteResult(options, result);
|
|
429
|
-
}
|
|
430
|
-
function normalizeTransitRouteResult(options, result) {
|
|
431
|
-
// 导航包内部仍保持独立实现;
|
|
432
|
-
// 这里只是在“公交无法由导航 SDK 直算”时,把 Web API 结果映射成现有 RouteResult 形状。
|
|
433
|
-
const routes = (result?.route?.transits ?? []).map((transit, index) => {
|
|
434
|
-
const polyline = (transit?.segments ?? []).flatMap((segment) => [
|
|
435
|
-
...(segment.walking?.steps?.flatMap((step) => parsePolyline(step.polyline)) ?? []),
|
|
436
|
-
...(segment.bus?.buslines?.flatMap((line) => parsePolyline(line.polyline)) ?? []),
|
|
437
|
-
...(segment.railway?.buslines?.flatMap((line) => parsePolyline(line.polyline)) ?? []),
|
|
438
|
-
]);
|
|
439
|
-
return {
|
|
440
|
-
id: index,
|
|
441
|
-
start: options.from,
|
|
442
|
-
end: options.to,
|
|
443
|
-
distance: Number(transit?.distance ?? 0),
|
|
444
|
-
duration: Number(transit?.cost?.duration ?? 0),
|
|
445
|
-
segments: [],
|
|
446
|
-
polyline,
|
|
447
|
-
tollDistance: 0,
|
|
448
|
-
tollCost: Number(transit?.cost?.transit_fee ?? 0),
|
|
449
|
-
strategy: options.strategy,
|
|
450
|
-
};
|
|
451
|
-
});
|
|
452
|
-
return {
|
|
453
|
-
count: routes.length,
|
|
454
|
-
mainPathIndex: 0,
|
|
455
|
-
routes,
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function hasStrategyOption(options) {
|
|
459
|
-
return 'strategy' in options;
|
|
460
|
-
}
|
|
461
|
-
function isMotorcycleRouteOptions(options) {
|
|
462
|
-
return 'motorcycleCC' in options;
|
|
463
|
-
}
|
|
4
|
+
import { DriveStrategy, RouteType, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, } from './types';
|
|
5
|
+
import { calculateRoute, calculateDriveRoute, calculateWalkRoute, calculateRideRoute, calculateEBikeRoute, calculateTransitRoute, calculateTruckRoute, calculateMotorcycleRoute, initNavigation, destroyAllCalculators, independentDriveRoute, independentTruckRoute, independentWalkRoute, independentRideRoute, independentMotorcycleRoute, selectIndependentRoute, startNaviWithIndependentPath, openOfficialNaviPage, clearIndependentRoute, } from './route-planning';
|
|
6
|
+
import { buildAnchorWaypointsFromWebRoute } from './route-geometry';
|
|
7
|
+
import { followWebPlannedRoute, } from './web-route-following';
|
|
8
|
+
export { calculateRoute, calculateDriveRoute, calculateWalkRoute, calculateRideRoute, calculateEBikeRoute, calculateTransitRoute, calculateTruckRoute, calculateMotorcycleRoute, initNavigation, destroyAllCalculators, independentDriveRoute, independentTruckRoute, independentWalkRoute, independentRideRoute, independentMotorcycleRoute, selectIndependentRoute, startNaviWithIndependentPath, openOfficialNaviPage, clearIndependentRoute, };
|
|
9
|
+
export { buildAnchorWaypointsFromWebRoute };
|
|
10
|
+
export { followWebPlannedRoute };
|
|
11
|
+
export { ExpoGaodeMapNavigationModule };
|
|
464
12
|
// 导出官方导航界面组件
|
|
465
13
|
export { ExpoGaodeMapNaviView };
|
|
466
14
|
/**
|
|
467
15
|
* @deprecated 请使用 `ExpoGaodeMapNaviView`
|
|
468
16
|
*/
|
|
469
17
|
export const NaviView = ExpoGaodeMapNaviView;
|
|
470
|
-
/**
|
|
471
|
-
* 初始化导航模块(可选)
|
|
472
|
-
*/
|
|
473
|
-
export const initNavigation = () => ExpoGaodeMapNavigationModule.initNavigation();
|
|
474
|
-
/**
|
|
475
|
-
* 销毁所有路径计算器实例
|
|
476
|
-
* 用于页面切换时释放资源,避免"Another route calculation is in progress"错误
|
|
477
|
-
*/
|
|
478
|
-
export const destroyAllCalculators = () => ExpoGaodeMapNavigationModule.destroyAllCalculators();
|
|
479
|
-
/**
|
|
480
|
-
* 路径规划(通用方法)
|
|
481
|
-
*/
|
|
482
|
-
export async function calculateRoute(options) {
|
|
483
|
-
if ('type' in options && options.type === RouteType.TRANSIT) {
|
|
484
|
-
return calculateTransitRoute(options);
|
|
485
|
-
}
|
|
486
|
-
// 1. 货车
|
|
487
|
-
if ('size' in options) {
|
|
488
|
-
return calculateTruckRoute(options);
|
|
489
|
-
}
|
|
490
|
-
// 2. 步行、骑行、电动车
|
|
491
|
-
if ('multiple' in options || 'travelStrategy' in options) {
|
|
492
|
-
if ('usePoi' in options)
|
|
493
|
-
return calculateEBikeRoute(options);
|
|
494
|
-
// 策略判断:0 或 1 通常为骑行策略,其余默认步行
|
|
495
|
-
const strategy = hasStrategyOption(options) ? options.strategy : undefined;
|
|
496
|
-
if (strategy === 0 || strategy === 1) {
|
|
497
|
-
return calculateRideRoute(options);
|
|
498
|
-
}
|
|
499
|
-
return calculateWalkRoute(options);
|
|
500
|
-
}
|
|
501
|
-
// 3. 摩托车 (通过 carType 或 motorcycleCC 判断)
|
|
502
|
-
if (isMotorcycleRouteOptions(options)) {
|
|
503
|
-
return calculateMotorcycleRoute(options);
|
|
504
|
-
}
|
|
505
|
-
// 4. 默认驾车
|
|
506
|
-
return calculateDriveRoute(options);
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* 驾车路径规划
|
|
510
|
-
*/
|
|
511
|
-
export async function calculateDriveRoute(options) {
|
|
512
|
-
if (shouldUseAvoidPreviewFallback(options)) {
|
|
513
|
-
try {
|
|
514
|
-
return await calculateDriveRouteWithAvoidPreview(options);
|
|
515
|
-
}
|
|
516
|
-
catch {
|
|
517
|
-
// 若未安装 Web API 包,则保持现有原生逻辑不变。
|
|
518
|
-
// 这样不会破坏当前依赖 Android 反射重载的项目。
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
return ExpoGaodeMapNavigationModule.calculateDriveRoute(options);
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* 步行路径规划
|
|
525
|
-
*/
|
|
526
|
-
export const calculateWalkRoute = (options) => ExpoGaodeMapNavigationModule.calculateWalkRoute(options);
|
|
527
|
-
/**
|
|
528
|
-
* 骑行路径规划
|
|
529
|
-
*/
|
|
530
|
-
export const calculateRideRoute = (options) => ExpoGaodeMapNavigationModule.calculateRideRoute(options);
|
|
531
|
-
/**
|
|
532
|
-
* 骑行电动车路径规划
|
|
533
|
-
*/
|
|
534
|
-
export const calculateEBikeRoute = (options) => ExpoGaodeMapNavigationModule.calculateEBikeRoute(options);
|
|
535
|
-
/**
|
|
536
|
-
* 货车路径规划
|
|
537
|
-
*/
|
|
538
|
-
export const calculateTruckRoute = (options) => ExpoGaodeMapNavigationModule.calculateTruckRoute(options);
|
|
539
|
-
/**
|
|
540
|
-
* 摩托车路径规划(车类型为 11,支持传入排量)
|
|
541
|
-
*/
|
|
542
|
-
export const calculateMotorcycleRoute = (options) => ExpoGaodeMapNavigationModule.calculateMotorcycleRoute(options);
|
|
543
|
-
/**
|
|
544
|
-
* 公交换乘路径规划(运行时 fallback 到 Web API)
|
|
545
|
-
*/
|
|
546
|
-
export async function calculateTransitRoute(options) {
|
|
547
|
-
// 运行时按需加载,避免把 navigation 包和 web-api 包在构建期强绑定。
|
|
548
|
-
const { GaodeWebAPI, TransitStrategy } = await loadWebApiFallback('公交路径规划');
|
|
549
|
-
const api = new GaodeWebAPI();
|
|
550
|
-
const result = await api.route.transit(`${options.from.longitude},${options.from.latitude}`, `${options.to.longitude},${options.to.latitude}`, options.city1, options.city2, {
|
|
551
|
-
strategy: options.strategy ?? TransitStrategy.RECOMMENDED,
|
|
552
|
-
AlternativeRoute: options.alternativeRoute,
|
|
553
|
-
show_fields: 'cost,polyline',
|
|
554
|
-
});
|
|
555
|
-
return normalizeTransitRouteResult(options, result);
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* 独立驾车路径规划
|
|
559
|
-
*
|
|
560
|
-
* - 只负责生成独立路径组,不会自动开始导航
|
|
561
|
-
* - 适合路线预览、行前选路、自定义路线选择页
|
|
562
|
-
* - 后续可配合 selectIndependentRoute / startNaviWithIndependentPath 使用
|
|
563
|
-
*/
|
|
564
|
-
export const independentDriveRoute = (options) => ExpoGaodeMapNavigationModule.independentDriveRoute(options);
|
|
565
|
-
/**
|
|
566
|
-
* 独立货车路径规划
|
|
567
|
-
*
|
|
568
|
-
* - 只负责生成独立路径组,不会自动开始导航
|
|
569
|
-
* - 适合路线预览、行前选路
|
|
570
|
-
*/
|
|
571
|
-
export const independentTruckRoute = (options) => ExpoGaodeMapNavigationModule.independentTruckRoute(options);
|
|
572
|
-
/**
|
|
573
|
-
* 独立步行路径规划
|
|
574
|
-
*
|
|
575
|
-
* - 只负责生成独立路径组,不会自动开始导航
|
|
576
|
-
* - 适合路线预览、行前选路
|
|
577
|
-
*/
|
|
578
|
-
export const independentWalkRoute = (options) => ExpoGaodeMapNavigationModule.independentWalkRoute(options);
|
|
579
|
-
/**
|
|
580
|
-
* 独立骑行路径规划
|
|
581
|
-
*
|
|
582
|
-
* - 只负责生成独立路径组,不会自动开始导航
|
|
583
|
-
* - 适合路线预览、行前选路
|
|
584
|
-
*/
|
|
585
|
-
export const independentRideRoute = (options) => ExpoGaodeMapNavigationModule.independentRideRoute(options);
|
|
586
|
-
/**
|
|
587
|
-
* 独立摩托车路径规划
|
|
588
|
-
*
|
|
589
|
-
* - 只负责生成独立路径组,不会自动开始导航
|
|
590
|
-
* - 适合路线预览、行前选路
|
|
591
|
-
*/
|
|
592
|
-
export const independentMotorcycleRoute = (options) => ExpoGaodeMapNavigationModule.independentMotorcycleRoute(options);
|
|
593
|
-
/**
|
|
594
|
-
* 独立路径组:选主路线
|
|
595
|
-
*
|
|
596
|
-
* - 仅切换 token 对应路径组里的当前主路线
|
|
597
|
-
* - 本身不会开始导航
|
|
598
|
-
*/
|
|
599
|
-
export const selectIndependentRoute = (options) => ExpoGaodeMapNavigationModule.selectIndependentRoute(options);
|
|
600
|
-
/**
|
|
601
|
-
* 独立路径组:使用指定路线启动导航
|
|
602
|
-
*
|
|
603
|
-
* - 这是模块级启动入口,不依赖 ExpoGaodeMapNaviView ref
|
|
604
|
-
* - 适合工具函数、流程封装、非嵌入式页面场景
|
|
605
|
-
* - 若你已经持有嵌入式导航视图 ref,更适合调用 ref.startNavigationWithIndependentPath(...)
|
|
606
|
-
*/
|
|
607
|
-
export const startNaviWithIndependentPath = (options) => ExpoGaodeMapNavigationModule.startNaviWithIndependentPath(options);
|
|
608
|
-
/**
|
|
609
|
-
* 打开高德官方导航页(Android 原生 AmapNaviPage)
|
|
610
|
-
*/
|
|
611
|
-
export const openOfficialNaviPage = (options) => ExpoGaodeMapNavigationModule.openOfficialNaviPage(options);
|
|
612
|
-
/**
|
|
613
|
-
* 独立路径组:清理
|
|
614
|
-
*
|
|
615
|
-
* - 释放 independentXXXRoute 生成的 token 对应缓存
|
|
616
|
-
* - 当页面只保留路线预览、不再需要后续选路/导航时,建议主动清理
|
|
617
|
-
*/
|
|
618
|
-
export const clearIndependentRoute = (options) => ExpoGaodeMapNavigationModule.clearIndependentRoute(options);
|
|
619
18
|
export { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, };
|
|
620
|
-
//
|
|
621
|
-
|
|
19
|
+
// 默认导出保留为兼容层,方便老代码一次性挂载整个导航 API。
|
|
20
|
+
const ExpoGaodeMapNavigation = {
|
|
622
21
|
// 初始化
|
|
623
22
|
initNavigation,
|
|
624
23
|
destroyAllCalculators,
|
|
@@ -645,5 +44,5 @@ export default {
|
|
|
645
44
|
openOfficialNaviPage,
|
|
646
45
|
clearIndependentRoute,
|
|
647
46
|
};
|
|
648
|
-
export
|
|
47
|
+
export default ExpoGaodeMapNavigation;
|
|
649
48
|
//# sourceMappingURL=index.js.map
|