Arrow fix

This commit is contained in:
Tomas Richtar 2025-05-27 17:33:51 +02:00
parent 3129ec1fea
commit a96f4f29a9

View File

@ -9,12 +9,7 @@ import * as turf from "@turf/turf";
import { ARROW_BODY_STYLE_CONSTANT, ARROW_BODY_STYLE_LINEAR, ARROW_BODY_STYLE_EXPONENTIAL } from "./Arrow.js";
map.on('load', () => {
const points = [
[1.42076, 40.08804],
[358.4050, 50.52]
];
const fullPolygon = getArrowPolygon(points, 20000); // offset 20 km
const fullPolygon = getArrowPolygon(arrowData, style, arrowHeadData);
map.addSource("arrow-shape", { type: "geojson", data: fullPolygon });
map.addLayer({
id: "arrow-shape",
@ -53,26 +48,27 @@ map.on('load', () => {
});
const points = [
{ x: 70, y: 38 },
{ x: 71, y: 45},
{ x: 65, y: 50 },
{ x: 70, y: 53}
[1.42076, 40.08804],
[15.42076, 80.08804],
[55.42076, 75.08804],
[120.42076, 40.08804],
[358.4050, 50.52]
];
const arrowData = {
points: points,
splineStep: 0.01,
splineStep: 20,
spacing: 0.01,
offsetDistance: 10000
offsetDistance: 20000
};
const style = {
calculation: ARROW_BODY_STYLE_LINEAR,
calculation: ARROW_BODY_STYLE_CONSTANT,
range: 1,
minValue: 0.1
};
const arrowHeadData = {
widthArrow: 1,
lengthArrow: 1
widthArrow: 10,
lengthArrow: 5
};
// Cubic interpolation source from https://www.paulinternet.nl/?page=bicubic
@ -98,18 +94,28 @@ function cubicInterpolate(p, x) {
*
* @returns {{x: number, y: number}[]} - An array of points representing the arrow polygon.
*/
export function getArrowPolygon(arrowData, style= undefined, arrowHeadData = undefined) {
const splineStep = 20;
const smooth = computeSplinePoints(arrowData.points, splineStep);
const { leftSidePoints, rightSidePoints } = computeSideOffsets(smooth, arrowData.offsetDistance);
export function getArrowPolygon(arrowData, style, arrowHeadData) {
if (!style)
style = {
calculation: ARROW_BODY_STYLE_CONSTANT,
range: 0,
minValue: 0
};
const end = smooth[smooth.length -1];
const bearing = averageBearing(smooth, 3);
const triangle = createIsoscelesTriangleCoords(turf.point(end), arrowData.offsetDistance * 5, arrowData.offsetDistance * 5, bearing);
const splinePoints = computeSplinePoints(arrowData.points, arrowData.splineStep);
const { leftSidePoints, rightSidePoints } = computeSideOffsets(splinePoints, arrowData.offsetDistance, style);
const end = splinePoints[splinePoints.length -1];
const bearing = averageBearing(splinePoints, 3);
const arrowHead= arrowHeadData
? createIsoscelesTriangleCoords(
turf.point(end),
arrowData.offsetDistance * arrowHeadData.widthArrow, arrowData.offsetDistance * arrowHeadData.lengthArrow, bearing)
: [];
const polygonCoords = [
...leftSidePoints,
...triangle,
...arrowHead,
...rightSidePoints.reverse(),
leftSidePoints[0]
];
@ -125,13 +131,13 @@ function averageBearing(points, count = 3) {
bearings.push(b);
}
}
// Průměr s korekcí kruhového rozsahu
const sinSum = bearings.reduce((sum, b) => sum + Math.sin(b * Math.PI / 180), 0);
const cosSum = bearings.reduce((sum, b) => sum + Math.cos(b * Math.PI / 180), 0);
return Math.atan2(sinSum, cosSum) * 180 / Math.PI;
}
function computeSplinePoints(points, segments = 10) {
function computeSplinePoints(points, splineStep = 10) {
if (points.length < 2) return points;
const result = [];
@ -140,8 +146,8 @@ function computeSplinePoints(points, segments = 10) {
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[i + 2] || p2;
for (let j = 0; j < segments; j++) {
const t = j / segments;
for (let j = 0; j < splineStep; j++) {
const t = j / splineStep;
const lon = cubicInterpolate([p0[0], p1[0], p2[0], p3[0]], t);
const lat = cubicInterpolate([p0[1], p1[1], p2[1], p3[1]], t);
result.push([lon, lat]);
@ -152,16 +158,32 @@ function computeSplinePoints(points, segments = 10) {
return result;
}
function computeSideOffsets(points, offsetMeters) {
function computeSideOffsets(points, offsetMeters, style) {
let leftSidePoints = [];
let rightSidePoints = [];
const total = points.length - 1;
for (let i = 1; i < points.length; i++) {
const previousPoint = points[i - 1];
const currentPoint = points[i];
const bearing = turf.bearing(turf.point(previousPoint), turf.point(currentPoint));
leftSidePoints.push(turf.destination(turf.point(currentPoint), offsetMeters, bearing - 90, { units: 'meters' }).geometry.coordinates);
rightSidePoints.push(turf.destination(turf.point(currentPoint), offsetMeters, bearing + 90, { units: 'meters' }).geometry.coordinates);
const normalizedPosition = i / total;
let localOffsetDistance;
switch (style.calculation) {
case ARROW_BODY_STYLE_LINEAR:
localOffsetDistance = offsetMeters * linearWidthCurve(normalizedPosition, style.range, style.minValue);
break;
case ARROW_BODY_STYLE_EXPONENTIAL:
localOffsetDistance = offsetMeters * exponentialWidthCurve(normalizedPosition, style.range, style.minValue);
break;
case ARROW_BODY_STYLE_CONSTANT:
default:
localOffsetDistance = offsetMeters;
}
leftSidePoints.push(turf.destination(turf.point(currentPoint), localOffsetDistance, bearing - 90, { units: 'meters' }).geometry.coordinates);
rightSidePoints.push(turf.destination(turf.point(currentPoint), localOffsetDistance, bearing + 90, { units: 'meters' }).geometry.coordinates);
}
return { leftSidePoints, rightSidePoints };
@ -203,3 +225,11 @@ function generateLatLonGrid(step = 10) {
features
};
}
function exponentialWidthCurve(normalizedPosition, range = 5, minValue = 0.1) {
return minValue + (1 - minValue) * Math.exp(-range * normalizedPosition);
}
function linearWidthCurve(normalizedPosition, range = 1, minValue = 0.1) {
return 1 + (minValue - 1) * normalizedPosition / range ;
}