diff --git a/src/libs/utils-map.ts b/src/libs/utils-map.ts index fb83df8fc..ae8c61864 100644 --- a/src/libs/utils-map.ts +++ b/src/libs/utils-map.ts @@ -1,3 +1,7 @@ +import * as turf from '@turf/turf' +import type { Feature, Polygon } from 'geojson' +import * as L from 'leaflet' + import type { WaypointCoordinates } from '@/types/mission' /** @@ -158,3 +162,124 @@ export class TargetFollower { this.onTargetChange(this.target) } } + +/** + * Generates a survey path based on the given polygon and parameters. + * @param {L.LatLng[]} polygonPoints - The points of the polygon. + * @param {number} distanceBetweenLines - The distance between survey lines in meters. + * @param {number} linesAngle - The angle of the survey lines in degrees. + * @returns {L.LatLng[]} The generated survey path. + */ +export const generateSurveyPath = ( + polygonPoints: L.LatLng[], + distanceBetweenLines: number, + linesAngle: number +): L.LatLng[] => { + if (polygonPoints.length < 4) return [] + + const polygonCoords = polygonPoints.map((p) => [p.lng, p.lat]) + if ( + polygonCoords[0][0] !== polygonCoords[polygonCoords.length - 1][0] || + polygonCoords[0][1] !== polygonCoords[polygonCoords.length - 1][1] + ) { + polygonCoords.push(polygonCoords[0]) + } + + try { + const poly = turf.polygon([polygonCoords]) + const bbox = turf.bbox(poly) + const [minX, minY, maxX, maxY] = bbox + const diagonal = Math.sqrt(Math.pow(maxX - minX, 2) + Math.pow(maxY - minY, 2)) + + const adjustedAngle = linesAngle + 90 + const angleRad = (adjustedAngle * Math.PI) / 180 + + const continuousPath: L.LatLng[] = [] + let d = -diagonal + let isReverse = false + + while (d <= diagonal * 2) { + const lineStart = [ + minX + d * Math.cos(angleRad) - diagonal * Math.sin(angleRad), + minY + d * Math.sin(angleRad) + diagonal * Math.cos(angleRad), + ] + const lineEnd = [ + minX + d * Math.cos(angleRad) + diagonal * Math.sin(angleRad), + minY + d * Math.sin(angleRad) - diagonal * Math.cos(angleRad), + ] + + const line = turf.lineString([lineStart, lineEnd]) + const clipped = turf.lineIntersect(poly, line) + + if (clipped.features.length >= 2) { + const coords = clipped.features.map((f) => f.geometry.coordinates) + if (isReverse) coords.reverse() + + const linePoints = coords.map((c) => L.latLng(c[1], c[0])) + + if (continuousPath.length > 0) { + const lastPoint = continuousPath[continuousPath.length - 1] + const edgePath = moveAlongEdge(poly, lastPoint, linePoints[0], distanceBetweenLines / 111000) + continuousPath.push(...edgePath) + } + + continuousPath.push(...linePoints) + isReverse = !isReverse + } + + d += distanceBetweenLines / 111000 + } + + return continuousPath + } catch (error) { + console.error('Error in generateSurveyPath:', error) + return [] + } +} + +/** + * Moves along the edge of a polygon from start to end point. + * @param {Feature} polygon - The polygon to move along. + * @param {L.LatLng} start - The starting point. + * @param {L.LatLng} end - The ending point. + * @param {number} maxDistance - The maximum distance to move. + * @returns {L.LatLng[]} The path along the edge. + */ +export const moveAlongEdge = ( + polygon: Feature, + start: L.LatLng, + end: L.LatLng, + maxDistance: number +): L.LatLng[] => { + const coords = polygon.geometry.coordinates[0] + const path: L.LatLng[] = [] + let remainingDistance = maxDistance + let currentPoint = turf.point([start.lng, start.lat]) + + for (let i = 0; i < coords.length; i++) { + const nextPoint = turf.point(coords[(i + 1) % coords.length]) + const edgeLine = turf.lineString([coords[i], coords[(i + 1) % coords.length]]) + + if (turf.booleanPointOnLine(currentPoint, edgeLine)) { + while (remainingDistance > 0) { + const distance = turf.distance(currentPoint, nextPoint) + if (distance <= remainingDistance) { + path.push(L.latLng(nextPoint.geometry.coordinates[1], nextPoint.geometry.coordinates[0])) + remainingDistance -= distance + currentPoint = nextPoint + break + } else { + const move = turf.along(edgeLine, remainingDistance, { units: 'kilometers' }) + path.push(L.latLng(move.geometry.coordinates[1], move.geometry.coordinates[0])) + break + } + } + } + + if (turf.booleanPointOnLine(turf.point([end.lng, end.lat]), edgeLine)) { + break + } + } + + return path +} diff --git a/src/views/MissionPlanningView.vue b/src/views/MissionPlanningView.vue index 300251898..2c12f36a6 100644 --- a/src/views/MissionPlanningView.vue +++ b/src/views/MissionPlanningView.vue @@ -3,6 +3,43 @@
+ +
+

Distance between lines (m)

+ +

Lines angle (degrees)

+ + + +
+

Waypoint type