StorymapperArrow/BasicShapes.js
2025-06-06 11:02:47 +02:00

173 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as turf from "@turf/turf";
import { toMercator, toWgs84 } from '@turf/projection';
//CIRCLE
const DISTANCE_PER_DEGREE_LONGITUDE = 111.320; // 2π×6378.1km/360
const DISTANCE_PER_DEGREE_LATITUDE = 110.574; // 2π×6356.75km/360
//GEOJSON
/**
* @param {Object} center - The center point of the circle.
* @param {number} center.x
* @param {number} center.y
* @param {number} radius - The radius of the circle.
* @param {number} density - The number of points used to approximate the circle.
*
* @returns {GeoJSON.Feature<GeoJSON.Polygon>} GeoJSON Feature representing the circle polygon.
*/
export function getCirclePolygon(center, radius, density = 64) {
if (!center || !radius ) {
console.warn("getCirclePolygon: Invalid center, radius.");
return [];
}
const points = [];
const coords = {
latitude: center[1],
longitude: center[0]
};
const distanceX = radius / (DISTANCE_PER_DEGREE_LONGITUDE * Math.cos(coords.latitude * Math.PI / 180));
const distanceY = radius / DISTANCE_PER_DEGREE_LATITUDE;
for (let i = 0; i < density; i++) {
const angle = (i / density) * Math.PI * 2;
const x = distanceX * Math.cos(angle);
const y = distanceY * Math.sin(angle);
points.push([coords.longitude + x, coords.latitude + y]);
}
// Close the circle by adding the first point again
points.push(points[0]);
return turf.polygon([[...points]]);
/*
return {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [points]
},
"properties": {}
};*/
}
//GEOJSON
//CANVAS
/**
* @param {Object} center - The center point of the circle.
* @param {number} center.x
* @param {number} center.y
* @param {number} radius - The radius of the circle.
* @param {number} density - The number of points used to approximate the circle.
*
* @returns {{x: number, y: number}[]} An array of points representing the vertices of the circle polygon.
*/
export function getCirclePolygonEuclidean(center, radius, density) {
if (!center || !radius || !density) {
console.warn("getCirclePolygonEuclidean: Invalid center, radius or density.");
return [];
}
const points = [];
for (let i = 0; i < density; i++) {
const angle = (i / density) * Math.PI * 2;
const x = center.x + radius * Math.cos(angle);
const y = center.y + radius * Math.sin(angle);
points.push({ x, y });
}
return points;
}
//CANVAS
//CIRCLE
//RECTANGLE
//GEOJSON
/**
* @param {Object} center - The center point of the rectangle.
* @param {number} center.x
* @param {number} center.y
* @param {number} width - The length of the first side of the rectangle.
* @param {number} height - The length of the second side of the rectangle.
* @param {number} rotation - The angle (in radians) by which to rotate the rectangle.
*
* @returns {GeoJSON.Feature<GeoJSON.Polygon>} GeoJSON Feature representing the rectangle polygon.
*/
export function getRectanglePolygon(center, width, height, rotation = 0) {
if (!center || !width || !height) {
console.warn("getRectanglePolygon: Invalid center, width or height.");
return [];
}
const widthMeters = width * 1000 ;
const heightMeters = height * 1000;
const centerMerc = toMercator(turf.point(center)).geometry.coordinates;
const halfWidth = widthMeters / 2;
const halfHeight = heightMeters / 2;
let corners = [
[centerMerc[0] - halfWidth, centerMerc[1] + halfHeight], // topLeft
[centerMerc[0] + halfWidth, centerMerc[1] + halfHeight], // topRight
[centerMerc[0] + halfWidth, centerMerc[1] - halfHeight], // bottomRight
[centerMerc[0] - halfWidth, centerMerc[1] - halfHeight], // bottomLeft
];
if (rotation !== 0) {
const rad = (rotation * Math.PI) / 180;
corners = corners.map(([x, y]) => rotateXY(x, y, centerMerc[0], centerMerc[1], rad));
}
corners.push(corners[0]);
const wgsCoords = corners.map(([x, y]) => toWgs84([x, y]));
return turf.polygon([wgsCoords]);
}
function rotateXY(x, y, cx, cy, angleRad) {
const dx = x - cx;
const dy = y - cy;
const cos = Math.cos(angleRad);
const sin = Math.sin(angleRad);
const rx = cx + dx * cos - dy * sin;
const ry = cy + dx * sin + dy * cos;
return [rx, ry];
}
//GEOJSON
//CANVAS
/**
* @param {Object} center - The center point of the rectangle.
* @param {number} center.x
* @param {number} center.y
* @param {number} sideA - The length of the first side of the rectangle.
* @param {number} sideB - The length of the second side of the rectangle.
* @param {number} rotation - The angle (in radians) by which to rotate the rectangle.
*
* @returns {{x: number, y: number}[]} An array of points representing the vertices of the rectangle polygon.
*/
export function getRectanglePolygonEuclidean(center, sideA, sideB, rotation = 0) {
if (!center || !sideA || !sideB) {
console.warn("getRectanglePolygonEuclidean: Invalid center, sideA or sideB.");
return [];
}
const halfA = sideA / 2;
const halfB = sideB / 2;
const corners = [
{ x: -halfA, y: -halfB },
{ x: halfA, y: -halfB },
{ x: halfA, y: halfB },
{ x: -halfA, y: halfB }
];
return corners.map(point => {
return {
x: center.x + point.x * Math.cos(rotation) - point.y * Math.sin(rotation),
y: center.y + point.x * Math.sin(rotation) + point.y * Math.cos(rotation)
};
});
}
//CAVNAS
//RECTANGLE