라우팅 끄고 포인트 이동 시 고도값을 주변 앵커에서 보간하도록 개선
기존엔 직선 구간의 고도를 매번 gpx.studio 서버(CORS로 막혀있음)에서 조회해서, 실패하면 0으로 떨어지고 포인트 이동 자체가 실패하기도 했음. 양쪽 앵커에 이미 실제 고도값이 있는데 버려지고 있던 것을 발견하고, 거리 비례로 보간하도록 변경: - 양쪽 다 알면 보간, 한쪽만 알면 그 값으로 확장, 둘 다 모르면 기존처럼 네트워크 조회로 폴백 - 대부분의 실사용 시나리오(기존 트랙 위 포인트 이동)에서 네트워크 요청 자체가 불필요해짐 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
42b875ec95
commit
443b85880c
@ -703,7 +703,10 @@ export class RoutingControls {
|
||||
|
||||
let response: TrackPoint[];
|
||||
try {
|
||||
response = await route(targetTrackPoints.map((trkpt) => trkpt.getCoordinates()));
|
||||
response = await route(
|
||||
targetTrackPoints.map((trkpt) => trkpt.getCoordinates()),
|
||||
targetTrackPoints.map((trkpt) => trkpt.ele)
|
||||
);
|
||||
} catch (e: any) {
|
||||
toast.error(i18n._(e.message, e.message));
|
||||
return false;
|
||||
|
||||
@ -23,7 +23,10 @@ export const routingProfiles: { [key: string]: RoutingProfile } = {
|
||||
railway: { engine: 'brouter', profile: 'rail' },
|
||||
};
|
||||
|
||||
export function route(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
export function route(
|
||||
points: Coordinates[],
|
||||
elevations?: (number | undefined)[]
|
||||
): Promise<TrackPoint[]> {
|
||||
if (get(routing)) {
|
||||
const profile = routingProfiles[get(routingProfile)];
|
||||
if (profile.engine === 'graphhopper') {
|
||||
@ -32,7 +35,7 @@ export function route(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
return getBRouterRoute(points, profile.profile);
|
||||
}
|
||||
} else {
|
||||
return getIntermediatePoints(points);
|
||||
return getIntermediatePoints(points, elevations);
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,22 +270,83 @@ function getTags(message: string): { [key: string]: string } {
|
||||
return tags;
|
||||
}
|
||||
|
||||
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
// Fills in anchors with unknown elevation (e.g. a point just dragged to a new location) by
|
||||
// interpolating between the nearest anchors on either side that do have a known elevation,
|
||||
// weighted by distance along the path. Falls back to extrapolating the nearest known value
|
||||
// when there is no known anchor on one side. Leaves entries as undefined only when none of the
|
||||
// anchors have a known elevation at all.
|
||||
function fillMissingElevations(
|
||||
points: Coordinates[],
|
||||
elevations?: (number | undefined)[]
|
||||
): (number | undefined)[] {
|
||||
if (!elevations) {
|
||||
return points.map(() => undefined);
|
||||
}
|
||||
|
||||
const known = elevations
|
||||
.map((ele, index) => ({ ele, index }))
|
||||
.filter((item): item is { ele: number; index: number } => item.ele !== undefined);
|
||||
|
||||
if (known.length === 0) {
|
||||
return elevations;
|
||||
}
|
||||
|
||||
const cumulativeDistance = [0];
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
cumulativeDistance.push(cumulativeDistance[i - 1] + distance(points[i - 1], points[i]));
|
||||
}
|
||||
|
||||
return elevations.map((ele, index) => {
|
||||
if (ele !== undefined) {
|
||||
return ele;
|
||||
}
|
||||
|
||||
const before = known.filter((item) => item.index < index).at(-1);
|
||||
const after = known.find((item) => item.index > index);
|
||||
|
||||
if (before && after) {
|
||||
const span = cumulativeDistance[after.index] - cumulativeDistance[before.index];
|
||||
const ratio =
|
||||
span === 0
|
||||
? 0
|
||||
: (cumulativeDistance[index] - cumulativeDistance[before.index]) / span;
|
||||
return before.ele + ratio * (after.ele - before.ele);
|
||||
}
|
||||
|
||||
return (before ?? after)!.ele;
|
||||
});
|
||||
}
|
||||
|
||||
function getIntermediatePoints(
|
||||
points: Coordinates[],
|
||||
elevations?: (number | undefined)[]
|
||||
): Promise<TrackPoint[]> {
|
||||
elevations = fillMissingElevations(points, elevations);
|
||||
|
||||
let route: TrackPoint[] = [];
|
||||
let step = 0.05;
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
// Add intermediate points between each pair of points
|
||||
let dist = distance(points[i], points[i + 1]) / 1000;
|
||||
let eleStart = elevations?.[i];
|
||||
let eleEnd = elevations?.[i + 1];
|
||||
for (let d = 0; d < dist; d += step) {
|
||||
let lat = points[i].lat + (d / dist) * (points[i + 1].lat - points[i].lat);
|
||||
let lon = points[i].lon + (d / dist) * (points[i + 1].lon - points[i].lon);
|
||||
// Interpolate between the known elevations of the surrounding anchor points,
|
||||
// rather than requesting elevation data for every intermediate point
|
||||
let ele =
|
||||
eleStart !== undefined && eleEnd !== undefined
|
||||
? eleStart + (d / dist) * (eleEnd - eleStart)
|
||||
: undefined;
|
||||
route.push(
|
||||
new TrackPoint({
|
||||
attributes: {
|
||||
lat: lat,
|
||||
lon: lon,
|
||||
},
|
||||
ele,
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -294,12 +358,19 @@ function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
lat: points[points.length - 1].lat,
|
||||
lon: points[points.length - 1].lon,
|
||||
},
|
||||
ele: elevations?.[points.length - 1],
|
||||
})
|
||||
);
|
||||
|
||||
if (route.every((point) => point.ele !== undefined)) {
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
return getElevation(route).then((elevations) => {
|
||||
route.forEach((point, i) => {
|
||||
if (point.ele === undefined) {
|
||||
point.ele = elevations[i];
|
||||
}
|
||||
});
|
||||
return route;
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user