This repository has been archived by the owner on May 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
208 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { LatLng } from '../models'; | ||
|
||
/** | ||
* Get the coordinates of the point at the given distance along the path | ||
* @param coordinates path from the .rsp file | ||
* @param givenDistance distance in km | ||
* @param debug | ||
* @returns { lat: number; lon: number } the coordinates of the point at the given distance | ||
* | ||
* @author Liwei | ||
*/ | ||
export function getGpsPointAtDistance( | ||
coordinates: LatLng[], | ||
givenDistance: number, | ||
debug: boolean = false, | ||
): LatLng { | ||
function calculateDistance(coord1: LatLng, coord2: LatLng) { | ||
const earthRadius = 6371; //Unit: kilometers | ||
const [lat1, lon1] = [coord1.lat, coord1.lng]; | ||
const [lat2, lon2] = [coord2.lat, coord2.lng]; | ||
const dLat = (lat2 - lat1) * (Math.PI / 180); | ||
const dLon = (lon2 - lon1) * (Math.PI / 180); | ||
const a = | ||
Math.sin(dLat / 2) ** 2 + | ||
Math.cos(lat1 * (Math.PI / 180)) * | ||
Math.cos(lat2 * (Math.PI / 180)) * | ||
Math.sin(dLon / 2) ** 2; | ||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); //Use the Haversine formula | ||
return earthRadius * c; //// Returns the distance between two points in kilometers. | ||
} | ||
|
||
let remainingDistance = givenDistance; | ||
let targetSegmentIndex = -1; | ||
|
||
if (debug) | ||
console.debug( | ||
`before for, remainingDistance: ${remainingDistance}, targetSegmentIndex: ${targetSegmentIndex}`, | ||
); | ||
|
||
for (let i = 0; i < coordinates.length - 1; i++) { | ||
const segmentDistance = calculateDistance( | ||
coordinates[i], | ||
coordinates[i + 1], | ||
); | ||
if (remainingDistance > segmentDistance) { | ||
remainingDistance -= segmentDistance; | ||
} else { | ||
targetSegmentIndex = i; | ||
break; | ||
} | ||
} | ||
|
||
if (targetSegmentIndex === -1) { | ||
console.warn( | ||
'The given distance is longer than the path length. The last point of the path is returned.', | ||
); | ||
return coordinates[coordinates.length - 1]; | ||
} | ||
|
||
if (debug) | ||
console.debug( | ||
`remainingDistance: ${remainingDistance}, targetSegmentIndex: ${targetSegmentIndex}`, | ||
); | ||
|
||
const [startLat, startLon] = [ | ||
coordinates[targetSegmentIndex].lat, | ||
coordinates[targetSegmentIndex].lng, | ||
]; | ||
const [endLat, endLon] = [ | ||
coordinates[targetSegmentIndex + 1].lat, | ||
coordinates[targetSegmentIndex + 1].lng, | ||
]; | ||
|
||
if (debug) console.debug(startLat, startLon, endLat, endLon); | ||
|
||
const ratio = | ||
remainingDistance / | ||
calculateDistance( | ||
coordinates[targetSegmentIndex], | ||
coordinates[targetSegmentIndex + 1], | ||
); // Calculate the proportion of the target point's position in the current segment. | ||
const targetLatitude = startLat + ratio * (endLat - startLat); | ||
const targetLongitude = startLon + ratio * (endLon - startLon); | ||
|
||
return { lat: targetLatitude, lng: targetLongitude }; | ||
} | ||
|
||
/** | ||
* The path of data that needs to be map matched. | ||
* @param data the path of data. | ||
* @result for each point of the data array, the way_id, the distance along the way and the length of the way is returned. | ||
* | ||
* @author Kerbourc'h | ||
*/ | ||
export async function valhalla( | ||
data: LatLng[], | ||
): Promise< | ||
false | { way_id: number; distance_way: number; length_way: number }[] | ||
> { | ||
const request = { | ||
shape: data.map((v) => ({ lat: v.lat, lon: v.lng })), | ||
costing: 'auto', | ||
shape_match: 'map_snap', | ||
filters: { | ||
attributes: [ | ||
'edge.way_id', | ||
'edge.length', | ||
'matched.distance_from_trace_point', | ||
'matched.point', | ||
'matched.type', | ||
'matched.edge_index', | ||
'matched.distance_along_edge', | ||
], | ||
action: 'include', | ||
}, | ||
}; | ||
|
||
return fetch('http://localhost:8002/trace_attributes', { | ||
method: 'POST', | ||
body: JSON.stringify(request), | ||
headers: { 'Content-Type': 'application/json' }, | ||
}).then(async (res) => { | ||
const json = await res.json(); | ||
|
||
if (json.error) return false; | ||
|
||
return json.matched_points.map( | ||
(p: { | ||
edge_index: number; | ||
distance_along_edge: number; | ||
lat: number; | ||
lon: number; | ||
}) => ({ | ||
way_id: json.edges[p.edge_index]?.way_id, | ||
distance_way: | ||
p.distance_along_edge * json.edges[p.edge_index]?.length * 1000, | ||
coordinates: { lat: p.lat, lng: p.lon }, | ||
}), | ||
); | ||
}); | ||
} |