Files
next-tjwater-drainage-frontend/features/workbench/map/geo.ts
T

97 lines
3.0 KiB
TypeScript

export type LngLatTuple = [number, number];
const EARTH_RADIUS_METERS = 6371008.8;
const SAME_COORDINATE_EPSILON = 0.0000001;
export function getDistanceMeters(from: LngLatTuple, to: LngLatTuple) {
const fromLatitude = toRadians(from[1]);
const toLatitude = toRadians(to[1]);
const deltaLatitude = toRadians(to[1] - from[1]);
const deltaLongitude = toRadians(to[0] - from[0]);
const a =
Math.sin(deltaLatitude / 2) ** 2 +
Math.cos(fromLatitude) * Math.cos(toLatitude) * Math.sin(deltaLongitude / 2) ** 2;
return 2 * EARTH_RADIUS_METERS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
export function getLineDistanceMeters(coordinates: LngLatTuple[]) {
return coordinates.reduce((total, coordinate, index) => {
if (index === 0) {
return total;
}
return total + getDistanceMeters(coordinates[index - 1], coordinate);
}, 0);
}
export function getLastSegmentDistanceMeters(coordinates: LngLatTuple[]) {
if (coordinates.length < 2) {
return 0;
}
return getDistanceMeters(coordinates[coordinates.length - 2], coordinates[coordinates.length - 1]);
}
export function getPolygonAreaSquareMeters(coordinates: LngLatTuple[]) {
if (coordinates.length < 3) {
return 0;
}
const closedCoordinates = [...coordinates, coordinates[0]];
const area = closedCoordinates.slice(0, -1).reduce((total, coordinate, index) => {
const nextCoordinate = closedCoordinates[index + 1];
return (
total +
toRadians(nextCoordinate[0] - coordinate[0]) *
(2 + Math.sin(toRadians(coordinate[1])) + Math.sin(toRadians(nextCoordinate[1])))
);
}, 0);
return Math.abs((area * EARTH_RADIUS_METERS * EARTH_RADIUS_METERS) / 2);
}
export function createCircleCoordinates(center: LngLatTuple, radiusMeters: number, steps = 48) {
const angularDistance = radiusMeters / EARTH_RADIUS_METERS;
const centerLongitude = toRadians(center[0]);
const centerLatitude = toRadians(center[1]);
const coordinates: LngLatTuple[] = [];
for (let index = 0; index <= steps; index += 1) {
const bearing = (2 * Math.PI * index) / steps;
const latitude = Math.asin(
Math.sin(centerLatitude) * Math.cos(angularDistance) +
Math.cos(centerLatitude) * Math.sin(angularDistance) * Math.cos(bearing)
);
const longitude =
centerLongitude +
Math.atan2(
Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(centerLatitude),
Math.cos(angularDistance) - Math.sin(centerLatitude) * Math.sin(latitude)
);
coordinates.push([toDegrees(longitude), toDegrees(latitude)]);
}
return coordinates;
}
export function isSameCoordinate(previous: LngLatTuple | undefined, next: LngLatTuple) {
if (!previous) {
return false;
}
return (
Math.abs(previous[0] - next[0]) < SAME_COORDINATE_EPSILON &&
Math.abs(previous[1] - next[1]) < SAME_COORDINATE_EPSILON
);
}
export function toRadians(value: number) {
return (value * Math.PI) / 180;
}
function toDegrees(value: number) {
return (value * 180) / Math.PI;
}