feat: initialize drainage network frontend
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user