Arrow fix
This commit is contained in:
parent
3129ec1fea
commit
a96f4f29a9
92
MapArrow.js
92
MapArrow.js
@ -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";
|
import { ARROW_BODY_STYLE_CONSTANT, ARROW_BODY_STYLE_LINEAR, ARROW_BODY_STYLE_EXPONENTIAL } from "./Arrow.js";
|
||||||
|
|
||||||
map.on('load', () => {
|
map.on('load', () => {
|
||||||
const points = [
|
const fullPolygon = getArrowPolygon(arrowData, style, arrowHeadData);
|
||||||
[1.42076, 40.08804],
|
|
||||||
[358.4050, 50.52]
|
|
||||||
];
|
|
||||||
|
|
||||||
const fullPolygon = getArrowPolygon(points, 20000); // offset 20 km
|
|
||||||
map.addSource("arrow-shape", { type: "geojson", data: fullPolygon });
|
map.addSource("arrow-shape", { type: "geojson", data: fullPolygon });
|
||||||
map.addLayer({
|
map.addLayer({
|
||||||
id: "arrow-shape",
|
id: "arrow-shape",
|
||||||
@ -53,26 +48,27 @@ map.on('load', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const points = [
|
const points = [
|
||||||
{ x: 70, y: 38 },
|
[1.42076, 40.08804],
|
||||||
{ x: 71, y: 45},
|
[15.42076, 80.08804],
|
||||||
{ x: 65, y: 50 },
|
[55.42076, 75.08804],
|
||||||
{ x: 70, y: 53}
|
[120.42076, 40.08804],
|
||||||
];
|
[358.4050, 50.52]
|
||||||
|
];
|
||||||
|
|
||||||
const arrowData = {
|
const arrowData = {
|
||||||
points: points,
|
points: points,
|
||||||
splineStep: 0.01,
|
splineStep: 20,
|
||||||
spacing: 0.01,
|
spacing: 0.01,
|
||||||
offsetDistance: 10000
|
offsetDistance: 20000
|
||||||
};
|
};
|
||||||
const style = {
|
const style = {
|
||||||
calculation: ARROW_BODY_STYLE_LINEAR,
|
calculation: ARROW_BODY_STYLE_CONSTANT,
|
||||||
range: 1,
|
range: 1,
|
||||||
minValue: 0.1
|
minValue: 0.1
|
||||||
};
|
};
|
||||||
const arrowHeadData = {
|
const arrowHeadData = {
|
||||||
widthArrow: 1,
|
widthArrow: 10,
|
||||||
lengthArrow: 1
|
lengthArrow: 5
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cubic interpolation source from https://www.paulinternet.nl/?page=bicubic
|
// 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.
|
* @returns {{x: number, y: number}[]} - An array of points representing the arrow polygon.
|
||||||
*/
|
*/
|
||||||
export function getArrowPolygon(arrowData, style= undefined, arrowHeadData = undefined) {
|
export function getArrowPolygon(arrowData, style, arrowHeadData) {
|
||||||
const splineStep = 20;
|
if (!style)
|
||||||
const smooth = computeSplinePoints(arrowData.points, splineStep);
|
style = {
|
||||||
const { leftSidePoints, rightSidePoints } = computeSideOffsets(smooth, arrowData.offsetDistance);
|
calculation: ARROW_BODY_STYLE_CONSTANT,
|
||||||
|
range: 0,
|
||||||
|
minValue: 0
|
||||||
|
};
|
||||||
|
|
||||||
const end = smooth[smooth.length -1];
|
const splinePoints = computeSplinePoints(arrowData.points, arrowData.splineStep);
|
||||||
const bearing = averageBearing(smooth, 3);
|
const { leftSidePoints, rightSidePoints } = computeSideOffsets(splinePoints, arrowData.offsetDistance, style);
|
||||||
const triangle = createIsoscelesTriangleCoords(turf.point(end), arrowData.offsetDistance * 5, arrowData.offsetDistance * 5, bearing);
|
|
||||||
|
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 = [
|
const polygonCoords = [
|
||||||
...leftSidePoints,
|
...leftSidePoints,
|
||||||
...triangle,
|
...arrowHead,
|
||||||
...rightSidePoints.reverse(),
|
...rightSidePoints.reverse(),
|
||||||
leftSidePoints[0]
|
leftSidePoints[0]
|
||||||
];
|
];
|
||||||
@ -125,13 +131,13 @@ function averageBearing(points, count = 3) {
|
|||||||
bearings.push(b);
|
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 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);
|
const cosSum = bearings.reduce((sum, b) => sum + Math.cos(b * Math.PI / 180), 0);
|
||||||
return Math.atan2(sinSum, cosSum) * 180 / Math.PI;
|
return Math.atan2(sinSum, cosSum) * 180 / Math.PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeSplinePoints(points, segments = 10) {
|
function computeSplinePoints(points, splineStep = 10) {
|
||||||
if (points.length < 2) return points;
|
if (points.length < 2) return points;
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|
||||||
@ -140,8 +146,8 @@ function computeSplinePoints(points, segments = 10) {
|
|||||||
const p1 = points[i];
|
const p1 = points[i];
|
||||||
const p2 = points[i + 1];
|
const p2 = points[i + 1];
|
||||||
const p3 = points[i + 2] || p2;
|
const p3 = points[i + 2] || p2;
|
||||||
for (let j = 0; j < segments; j++) {
|
for (let j = 0; j < splineStep; j++) {
|
||||||
const t = j / segments;
|
const t = j / splineStep;
|
||||||
const lon = cubicInterpolate([p0[0], p1[0], p2[0], p3[0]], t);
|
const lon = cubicInterpolate([p0[0], p1[0], p2[0], p3[0]], t);
|
||||||
const lat = cubicInterpolate([p0[1], p1[1], p2[1], p3[1]], t);
|
const lat = cubicInterpolate([p0[1], p1[1], p2[1], p3[1]], t);
|
||||||
result.push([lon, lat]);
|
result.push([lon, lat]);
|
||||||
@ -152,16 +158,32 @@ function computeSplinePoints(points, segments = 10) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeSideOffsets(points, offsetMeters) {
|
function computeSideOffsets(points, offsetMeters, style) {
|
||||||
let leftSidePoints = [];
|
let leftSidePoints = [];
|
||||||
let rightSidePoints = [];
|
let rightSidePoints = [];
|
||||||
|
const total = points.length - 1;
|
||||||
|
|
||||||
for (let i = 1; i < points.length; i++) {
|
for (let i = 1; i < points.length; i++) {
|
||||||
const previousPoint = points[i - 1];
|
const previousPoint = points[i - 1];
|
||||||
const currentPoint = points[i];
|
const currentPoint = points[i];
|
||||||
const bearing = turf.bearing(turf.point(previousPoint), turf.point(currentPoint));
|
const bearing = turf.bearing(turf.point(previousPoint), turf.point(currentPoint));
|
||||||
leftSidePoints.push(turf.destination(turf.point(currentPoint), offsetMeters, bearing - 90, { units: 'meters' }).geometry.coordinates);
|
const normalizedPosition = i / total;
|
||||||
rightSidePoints.push(turf.destination(turf.point(currentPoint), offsetMeters, bearing + 90, { units: 'meters' }).geometry.coordinates);
|
|
||||||
|
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 };
|
return { leftSidePoints, rightSidePoints };
|
||||||
@ -202,4 +224,12 @@ function generateLatLonGrid(step = 10) {
|
|||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features
|
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 ;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user