mapboxgl.accessToken = 'pk.eyJ1Ijoib3V0ZG9vcm1hcHBpbmdjb21wYW55IiwiYSI6ImNqYmh3cDdjYzNsMnozNGxsYzlvMmk2bTYifQ.QqcZ4LVoLWnXafXdjZxnZg'; const map = new mapboxgl.Map({ container: 'map', center: [20, 80], zoom: 4 }); import * as turf from "@turf/turf"; import { getArrowPolygon } from "./Arrow.js"; import { ARROW_BODY_STYLE_CONSTANT, ARROW_BODY_STYLE_LINEAR, ARROW_BODY_STYLE_EXPONENTIAL } from "./Arrow.js"; import { getCirclePolygon } from "./BasicShapes.js"; import { getRectanglePolygon } from "./BasicShapes.js"; import { getFrontline } from "./Frontline.js"; import { LEFT_SIDE, RIGHT_SIDE, BOTH_SIDES } from "./Frontline.js"; // Polygon merge using Turf library import {mergeTurfPolygons} from "./Polygon.js"; import {addTurfPolygonToMerge} from "./Polygon.js"; import {toTurfPolygon} from "./Polygon.js"; const circleCenter = {x:320, y:180}; const circleRadius = 70; const circleDensity = 15; const circleCenterB = {x:400, y:280}; const circleRadiusB = 70; const circleDensityB = 15; const rectangleCenter= {x:100, y:300}; const rectangleSideA = 70; const rectangleSideB = 200; const rectangleRotation = 40; const frontlinePointsA = [ { x: 120, y: 400 }, { x: 200, y: 100 }, { x: 350, y: 200 }, { x: 350, y: 400 }, { x: 450, y: 480 }, { x: 550, y: 440 }, { x: 600, y: 300 }, ]; const frontlinePointsB = [ { x: 420, y: 280 }, { x: 430, y: 380 }, { x: 500, y: 400 }, { x: 520, y: 300 }, ]; const frontlinePointsC = [ { x: 450, y: 200 }, { x: 500, y: 250 }, { x: 550, y: 250 }, { x: 550, y: 200 } ]; const frontlineDataA = { points: frontlinePointsA, splineStep: 0.08, spacing: 10, offsetDistance: 10, protrusionLength: 15, protrusionStartSize: 5, protrusionEndSize: 2, protrusionGap: 20, style: LEFT_SIDE, }; const frontlineDataB = { points: frontlinePointsB, splineStep: 0.02, spacing: 10, offsetDistance: 10, protrusionLength: 15, protrusionStartSize: 5, protrusionEndSize: 5, protrusionGap: 20, style: RIGHT_SIDE, }; const frontlineDataC = { points: frontlinePointsC, splineStep: 0.02, spacing: 10, offsetDistance: 10, protrusionLength: 15, protrusionStartSize: 5, protrusionEndSize: 0, protrusionGap: 20, style: BOTH_SIDES, }; /* const arrowPolygonA = getArrowPolygon(arrowDataA, styleA, arrowHeadDataA); const arrowPolygonB = getArrowPolygon(arrowDataB, styleB, arrowHeadDataB); const arrowPolygonC = getArrowPolygon(arrowDataC, styleC); const circlePolygon = getCirclePolygon(circleCenter, circleRadius, circleDensity); const circlePolygonB = getCirclePolygon(circleCenterB, circleRadiusB, circleDensityB); const rectanglePolygon = getRectanglePolygon(rectangleCenter, rectangleSideA, rectangleSideB, rectangleRotation); const mergedTurfPoly = mergeTurfPolygons(arrowPolygonA, arrowPolygonC); const mergedTurfPolyAll = addTurfPolygonToMerge(mergedTurfPoly, arrowPolygonB); const mergedTurfPolyRectangle = addTurfPolygonToMerge(mergedTurfPolyAll, rectanglePolygon); const mergedRectangle = mergeTurfPolygons(circlePolygon, circlePolygonB); const rectanglePoly = getRectanglePolygon(circleCenter, rectangleSideA, rectangleSideB, rectangleRotation*-1); const rectangleToTurfPoly = toTurfPolygon(rectanglePoly); const frontlinePolygonA = getFrontline(frontlineDataA); let frontlinePolygonMergedA = mergeTurfPolygons(frontlinePolygonA.body,frontlinePolygonA.protrusions[0]); for (let i = 1; i < frontlinePolygonA.protrusions.length; i++) { frontlinePolygonMergedA = addTurfPolygonToMerge(frontlinePolygonMergedA, frontlinePolygonA.protrusions[i]); } const frontlinePolygonB = getFrontline(frontlineDataB); let frontlinePolygonMergedB = mergeTurfPolygons(frontlinePolygonB.body,frontlinePolygonB.protrusions[0]); for (let i = 1; i < frontlinePolygonB.protrusions.length; i++) { frontlinePolygonMergedB = addTurfPolygonToMerge(frontlinePolygonMergedB, frontlinePolygonB.protrusions[i]); } const frontlinePolygonC = getFrontline(frontlineDataC); let frontlinePolygonMergedLeft = mergeTurfPolygons(frontlinePolygonC.bodyLeft, frontlinePolygonC.protrusionsLeft[0]); for (let i = 1; i < frontlinePolygonC.protrusionsLeft.length; i++) { frontlinePolygonMergedLeft = addTurfPolygonToMerge(frontlinePolygonMergedLeft, frontlinePolygonC.protrusionsLeft[i]); } let frontlinePolygonMergedRight = mergeTurfPolygons(frontlinePolygonC.bodyRight, frontlinePolygonC.protrusionsRight[0]); for (let i = 1; i < frontlinePolygonC.protrusionsRight.length; i++) { frontlinePolygonMergedRight = addTurfPolygonToMerge(frontlinePolygonMergedRight, frontlinePolygonC.protrusionsRight[i]); }*/ //const circleGeoJSON = getCircleGeoJSON({x: 20, y: 80}, 2, 50, map); const pointsB = [ { x: 70, y: 38 }, { x: 71, y: 45}, { x: 65, y: 50 }, { x: 70, y: 53} ]; const arrowDataB = { points: pointsB, splineStep: 0.01, spacing: 0.01, offsetDistance: 1 }; const styleB = { calculation: ARROW_BODY_STYLE_LINEAR, range: 1, minValue: 0.1 }; const arrowHeadDataB = { widthArrow: 1, lengthArrow: 1 }; const arrowPolygonB = getArrowPolygon(arrowDataB, styleB, arrowHeadDataB); const pointsC= [ { x: 50, y: 38 }, { x: 51, y: 45}, { x: 45, y: 50 }, { x: 48, y: 55} ]; const arrowDataC = { points: pointsC, splineStep: 0.02, spacing: 1, offsetDistance: 1 }; const styleC = { calculation: ARROW_BODY_STYLE_LINEAR, range: 1, minValue: 0.1 }; const arrowHeadDataC = { widthArrow: 1, lengthArrow: 1 }; const arrowPolygonC = getArrowPolygon(arrowDataC, styleC, arrowHeadDataC); const points = [ { x: 80, y: 20 }, { x: 81, y: 22}, { x: 82, y: 28 }, { x: 81, y: 30}, { x: 80, y: 20} ]; // Turf polygon const turfPolygon = turf.polygon([[ [20, 80], [22, 81], [28, 82], [30, 81], [20, 80] ]]); const pointsA= [ { x: 80, y: 38 }, { x: 81, y: 45}, { x: 75, y: 50 }, { x: 78, y: 55} ]; const arrowDataA = { points: pointsA, splineStep: 0.2, spacing: 3, offsetDistance: 1 }; const styleA = { calculation: ARROW_BODY_STYLE_LINEAR, range: 1, minValue: 0.1 }; const arrowHeadDataA = { widthArrow: 1, lengthArrow: 1 }; const arrowPolygonA = getArrowPolygon(arrowDataA, styleA, arrowHeadDataA); const latLonGrid = generateLatLonGrid(5); function createIsoscelesTriangle(center, baseLengthMeters, heightMeters, bearing = 0) { const halfBase = baseLengthMeters / 2; // Výpočet bodů základny (levý a pravý bod) const leftBase = turf.destination(center, halfBase, bearing - 90, { units: 'meters' }); const rightBase = turf.destination(center, halfBase, bearing + 90, { units: 'meters' }); // Výpočet vrcholu trojúhelníku – směr daný bearing (nahoru např. 0°) const apex = turf.destination(center, heightMeters, bearing, { units: 'meters' }); const triangle = turf.polygon([[ leftBase.geometry.coordinates, rightBase.geometry.coordinates, apex.geometry.coordinates, leftBase.geometry.coordinates // Uzavření polygonu ]]); return triangle; } function cubicInterpolate(p, x) { return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0]))); } function interpolatePointsCatmullRom(points, segments = 10) { if (points.length < 2) return points; const extended = [points[0], ...points, points[points.length - 1]]; const interpolated = []; for (let i = 1; i < extended.length - 2; i++) { for (let j = 0; j < segments; j++) { const t = j / segments; const lon = cubicInterpolate([extended[i - 1][0], extended[i][0], extended[i + 1][0], extended[i + 2][0]], t); const lat = cubicInterpolate([extended[i - 1][1], extended[i][1], extended[i + 1][1], extended[i + 2][1]], t); interpolated.push([lon, lat]); } } interpolated.push(points[points.length - 1]); return interpolated; } function computeSideOffsets(points, offsetMeters) { const left = []; const right = []; for (let i = 1; i < points.length; i++) { const prev = points[i - 1]; const curr = points[i]; const bearing = turf.bearing(turf.point(prev), turf.point(curr)); const leftOffset = turf.destination(turf.point(curr), offsetMeters, bearing - 90, { units: 'meters' }); const rightOffset = turf.destination(turf.point(curr), offsetMeters, bearing + 90, { units: 'meters' }); left.push(leftOffset.geometry.coordinates); right.push(rightOffset.geometry.coordinates); } return { left, right }; } function createIsoscelesTriangleCoords(center, baseLengthMeters, heightMeters, bearing) { const halfBase = baseLengthMeters / 2; const left = turf.destination(center, halfBase, bearing - 90, { units: 'meters' }).geometry.coordinates; const right = turf.destination(center, halfBase, bearing + 90, { units: 'meters' }).geometry.coordinates; const tip = turf.destination(center, heightMeters, bearing, { units: 'meters' }).geometry.coordinates; console.log("Aktuální bod:", left); console.log("Aktuální bod:", right); console.log("Aktuální bod:", tip); return [left, right, tip]; } function drawArrowPolygon(map, basePoints, offset = 10000) { const smooth = interpolatePointsCatmullRom(basePoints, 20); const { leftSidePoints, rightSidePoints } = computeSidesWGS84(smooth, offset); console.log("Aktuální bod:", tip); const end = smooth[smooth.length - 1]; const prev = smooth[smooth.length - 2]; const bearing = turf.bearing(turf.point(prev), turf.point(end)); const triangleCoords = createIsoscelesTriangleCoords(turf.point(end), offset * 2, offset * 3, bearing); const polygonCoords = [ ...leftSidePoints, ...triangleCoords, ...rightSidePoints.reverse(), leftSidePoints[0] ]; const fullPolygon = turf.polygon([[...polygonCoords]]); map.addSource("arrow-shape", { type: "geojson", data: fullPolygon }); map.addLayer({ id: "arrow-shape", type: "fill", source: "arrow-shape", paint: { "fill-color": "#ff0000", "fill-opacity": 0.7 } }); } function computeSidesWGS84(points, offsetDistanceMeters = 1000) { const leftSidePoints = []; const rightSidePoints = []; for (let i = 1; i < points.length; i++) { const prev = points[i - 1]; const curr = points[i]; const bearing = turf.bearing(turf.point(prev), turf.point(curr)); const leftBearing = bearing - 90; const rightBearing = bearing + 90; const leftPoint = turf.destination(turf.point(curr), offsetDistanceMeters, leftBearing, { units: 'meters' }); const rightPoint = turf.destination(turf.point(curr), offsetDistanceMeters, rightBearing, { units: 'meters' }); leftSidePoints.push(leftPoint.geometry.coordinates); rightSidePoints.push(rightPoint.geometry.coordinates); } // První bod const first = points[0]; const second = points[1]; const initialBearing = turf.bearing(turf.point(first), turf.point(second)); const leftInitial = turf.destination(turf.point(first), offsetDistanceMeters, initialBearing - 90, { units: 'meters' }); const rightInitial = turf.destination(turf.point(first), offsetDistanceMeters, initialBearing + 90, { units: 'meters' }); leftSidePoints.unshift(leftInitial.geometry.coordinates); rightSidePoints.unshift(rightInitial.geometry.coordinates); console.log("Aktuální bod:", leftSidePoints); console.log("Aktuální bod:", rightSidePoints); return { leftSidePoints, rightSidePoints }; } function drawSideLines(map, sides, prefix = "arrow-side") { map.addSource(`${prefix}-left`, { type: "geojson", data: turf.lineString(sides.leftSidePoints) }); map.addLayer({ id: `${prefix}-left`, type: "line", source: `${prefix}-left`, paint: { "line-color": "blue", "line-width": 2 } }); map.addSource(`${prefix}-right`, { type: "geojson", data: turf.lineString(sides.rightSidePoints) }); map.addLayer({ id: `${prefix}-right`, type: "line", source: `${prefix}-right`, paint: { "line-color": "green", "line-width": 2 } }); } function drawSmoothLineThroughPoints(map, points, lineId = "smooth-line") { const smoothPoints = interpolatePointsCatmullRom(points, 20); const line = turf.lineString(smoothPoints); map.addSource(lineId, { type: "geojson", data: line }); map.addLayer({ id: lineId, type: "line", source: lineId, paint: { "line-color": "red", "line-width": 3 } }); // Boční linie const sides = computeSidesWGS84(smoothPoints, 10000); // 10 km offset drawSideLines(map, sides, lineId + "-sides"); // Šipka na konci const lastPoint = smoothPoints[smoothPoints.length - 1]; const secondLastPoint = smoothPoints[smoothPoints.length - 2]; const bearing = turf.bearing(turf.point(secondLastPoint), turf.point(lastPoint)); const triangle = createIsoscelesTriangle(turf.point(lastPoint), 60000, 80000, bearing); // 20km základna, 30km výška map.addSource(lineId + "-arrow", { type: "geojson", data: triangle }); map.addLayer({ id: lineId + "-arrow", type: "fill", source: lineId + "-arrow", paint: { "fill-color": "#ff0000", "fill-opacity": 0.7 } }); } map.on('load', () => { const points = [ [14.42076, 50.08804], // Praha [15.0, 50.0], [16.3725, 48.2082], // Vídeň [17.0, 49.0], [13.4050, 52.52] // Berlín ]; function createIsoscelesTriangle(center, baseLengthMeters, heightMeters, bearing = 0) { const halfBase = baseLengthMeters / 2; const leftBase = turf.destination(center, halfBase, bearing - 90, { units: 'meters' }); const rightBase = turf.destination(center, halfBase, bearing + 90, { units: 'meters' }); const apex = turf.destination(center, heightMeters, bearing, { units: 'meters' }); return turf.polygon([[ leftBase.geometry.coordinates, rightBase.geometry.coordinates, apex.geometry.coordinates, leftBase.geometry.coordinates ]]); } const center = turf.point([52.95, 69.95]); // výchozí střed základny // Vytvoř polygon trojúhelníku const triangle = createIsoscelesTriangle(center, 40000, 100000, 10); // 2 km základna, 1 km výška, směr 0° (na sever) map.addSource("triangle", { type: "geojson", data: triangle }); map.addLayer({ id: "triangle", type: "fill", source: "triangle", paint: { "fill-color": "purple", "fill-opacity": 0.5 } }); map.addLayer({ id: "triangle-outline", type: "line", source: "triangle", paint: { "line-color": "#000", "line-width": 2 } }); map.addSource("latLonGrid", { type: "geojson", data: latLonGrid }); map.addLayer({ id: "latLonGrid", type: "line", source: "latLonGrid", layout: {}, paint: { "line-color": "#888", "line-width": 1, "line-opacity": 0.5 } }); // Generuj GeoJSON pro kruh const circleGeoJSON = getCirclePolygon([20, 80], 120, 20); const circle = turf.circle([20, 80], 120000, { units: "meters", steps: 64 }); const rectangleGeoJSON = getRectanglePolygon([20, 80], 2200, 2200); const fsdafds = toTurfPolygon(points); const arrowBGeoJSON = toTurfPolygon(arrowPolygonB); const arrowCGeoJSON = toTurfPolygon(arrowPolygonC); const arrowAGeoJSON = toTurfPolygon(arrowPolygonA); //console.log(JSON.stringify(arrowAGeoJSON, null, 2)); // Arrow // Přidání GeoJSON jako zdroj map.addSource("arrowPolygonA", { type: "geojson", data: arrowAGeoJSON }); // Vrstva pro výplň polygonu map.addLayer({ id: "arrowPolygonA", type: "fill", source: "arrowPolygonA", layout: {}, paint: { "fill-color": "pink", "fill-opacity": 0.6 } }); // Vrstva pro obrys polygonu map.addLayer({ id: "arrowPolygonA-outline", type: "line", source: "arrowPolygonA", paint: { "line-color": "#000", "line-width": 3 } }); // Arrow // Přidání GeoJSON jako zdroj map.addSource("arrowPolygonB", { type: "geojson", data: arrowBGeoJSON }); // Vrstva pro výplň polygonu map.addLayer({ id: "arrowPolygonB", type: "fill", source: "arrowPolygonB", layout: {}, paint: { "fill-color": "yellow", "fill-opacity": 0.6 } }); // Vrstva pro obrys polygonu map.addLayer({ id: "arrowPolygonB-outline", type: "line", source: "arrowPolygonB", paint: { "line-color": "#000", "line-width": 3 } }); // Arrow // Přidání GeoJSON jako zdroj map.addSource("arrowPolygonC", { type: "geojson", data: arrowCGeoJSON }); // Vrstva pro výplň polygonu map.addLayer({ id: "arrowPolygonC", type: "fill", source: "arrowPolygonC", layout: {}, paint: { "fill-color": "yellow", "fill-opacity": 0.6 } }); // Vrstva pro obrys polygonu map.addLayer({ id: "arrowPolygonC-outline", type: "line", source: "arrowPolygonC", paint: { "line-color": "#000", "line-width": 3 } }); // CIRCLE // Přidání GeoJSON jako zdroj map.addSource("circlePolygon", { "type": "geojson", "data": circleGeoJSON }); // Přidání vrstvy pro vykreslení polygonu map.addLayer({ "id": "circlePolygon", "type": "fill", "source": "circlePolygon", "layout": {}, "paint": { "fill-color": "blue", "fill-opacity": 0.6 } }); // Přidání outline pro polygon map.addLayer({ "id": "circlePolygon-outline", "type": "line", "source": "circlePolygon", "paint": { "line-color": "#000", "line-width": 3 } }); // RECTANGLE map.addSource("rectanglePolygon", { "type": "geojson", "data": rectangleGeoJSON }); // Přidání vrstvy pro vykreslení polygonu map.addLayer({ "id": "rectanglePolygon", "type": "fill", "source": "rectanglePolygon", "layout": {}, "paint": { "fill-color": "red", "fill-opacity": 0.6 } }); // Přidání outline pro polygon map.addLayer({ "id": "rectanglePolygon-outline", "type": "line", "source": "rectanglePolygon", "paint": { "line-color": "#000", "line-width": 3 } }); }); function generateLatLonGrid(step = 10) { const features = []; // Rovnoběžky (latitudes) for (let lat = -80; lat <= 80; lat += step) { features.push({ type: "Feature", geometry: { type: "LineString", coordinates: Array.from({ length: 37 }, (_, i) => [-180 + i * 10, lat]) }, properties: { type: "latitude", value: lat } }); } // Poledníky (longitudes) for (let lon = -180; lon <= 180; lon += step) { features.push({ type: "Feature", geometry: { type: "LineString", coordinates: Array.from({ length: 17 }, (_, i) => [lon, -80 + i * 10]) }, properties: { type: "longitude", value: lon } }); } return { type: "FeatureCollection", features }; }