123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- <?php
- class OptimizedFlightPathGenerator
- {
- private $startPoint;
- private $points;
- private $noFlyZone;
- private $highVoltage;
-
- const EARTH_RADIUS = 6371000;
-
- public function __construct($flightInput)
- {
- $this->startPoint = $flightInput['start_point'];
- $this->points = $flightInput['points'];
- $this->noFlyZone = $flightInput['no_fly_zone'];
- $this->highVoltage = $flightInput['high_voltage'];
- }
-
- /**
- * 生成优化的航线路径
- */
- public function generateOptimizedFlightPath()
- {
- // 使用凸包算法找到外围点,然后填充内部点
- $optimizedPath = $this->generateEfficientPath();
-
- // 检查并处理禁飞区相交 - 使用简化的避让算法
- $pathAfterNoFly = $this->handleNoFlyZoneIntersectionsSimple($optimizedPath);
-
- // 检查并处理高压线相交
- $finalPath = $this->handleHighVoltageIntersections($pathAfterNoFly);
-
- return $finalPath;
- }
-
- /**
- * 简化的禁飞区相交处理方法 - 减少避让点数量
- */
- private function handleNoFlyZoneIntersectionsSimple($path)
- {
- $maxIterations = 10;
- $iteration = 0;
- $simplifiedPath = $path;
-
- do {
- $hasIntersection = false;
- $newPath = [];
-
- for ($i = 0; $i < count($simplifiedPath) - 1; $i++) {
- $currentPoint = $simplifiedPath[$i];
- $nextPoint = $simplifiedPath[$i + 1];
-
- // 检查当前线段是否与禁飞区相交
- $intersections = $this->checkLineCircleIntersection(
- $currentPoint,
- $nextPoint,
- $this->noFlyZone['center'],
- $this->noFlyZone['radius']
- );
-
- if (!empty($intersections)) {
- // 发现相交,采用简化避让策略
- $hasIntersection = true;
-
- // 只在真正需要的地方添加一个避让点
- $detourPoint = $this->generateSingleDetourPoint($currentPoint, $nextPoint);
-
- $newPath[] = $currentPoint;
- $newPath[] = $detourPoint;
- // 不添加nextPoint,它会在下一次循环中处理
- } else {
- // 没有相交,保持原路径点
- $newPath[] = $currentPoint;
-
- // 如果是最后一个点,也要添加
- if ($i == count($simplifiedPath) - 2) {
- $newPath[] = $nextPoint;
- }
- }
- }
-
- $simplifiedPath = $newPath;
- $iteration++;
-
- } while ($hasIntersection && $iteration < $maxIterations);
-
- // 对最终路径进行简化,移除不必要的点
- return $this->simplifyPath($simplifiedPath);
- }
-
- /**
- * 生成单个避让点(简化版本)
- */
- private function generateSingleDetourPoint($pointA, $pointB)
- {
- $center = $this->noFlyZone['center'];
- $radius = $this->noFlyZone['radius'];
- $safeDistance = 100;
-
- // 计算线段中点
- $midPoint = [
- 'lng' => ($pointA['lng'] + $pointB['lng']) / 2,
- 'lat' => ($pointA['lat'] + $pointB['lat']) / 2
- ];
-
- // 计算从禁飞区中心指向中点的方向
- $dx = $midPoint['lng'] - $center['lng'];
- $dy = $midPoint['lat'] - $center['lat'];
-
- $distanceToCenter = $this->calculateDistance($midPoint, $center);
-
- if ($distanceToCenter <= $radius + $safeDistance) {
- // 需要向外移动
- $moveDistance = $radius + $safeDistance - $distanceToCenter;
- $vectorLength = sqrt($dx * $dx + $dy * $dy);
-
- if ($vectorLength > 0) {
- $dx /= $vectorLength;
- $dy /= $vectorLength;
-
- return [
- 'lng' => $midPoint['lng'] + $dx * $moveDistance * 0.00001,
- 'lat' => $midPoint['lat'] + $dy * $moveDistance * 0.00001,
- 'name' => '禁飞区避让点',
- 'type' => 'no_fly_zone_avoidance'
- ];
- }
- }
-
- // 如果已经在安全距离外,返回中点
- return [
- 'lng' => $midPoint['lng'],
- 'lat' => $midPoint['lat'],
- 'name' => '路径中点',
- 'type' => 'midpoint'
- ];
- }
-
- /**
- * 路径简化算法 - 移除不必要的中间点
- */
- private function simplifyPath($path, $tolerance = 0.0001)
- {
- if (count($path) <= 3) {
- return $path;
- }
-
- $simplified = [];
- $simplified[] = $path[0]; // 总是保留起点
-
- for ($i = 1; $i < count($path) - 1; $i++) {
- $current = $path[$i];
- $prev = $path[$i - 1];
- $next = $path[$i + 1];
-
- // 如果是关键点(避让点、起点、终点等),总是保留
- if (isset($current['type']) &&
- ($current['type'] === 'no_fly_zone_avoidance' ||
- $current['type'] === 'high_voltage_intersection' ||
- $current['type'] === 'no_fly_zone_detour')) {
- $simplified[] = $current;
- continue;
- }
-
- // 检查当前点是否可以被移除(与前后点形成的角度是否足够大)
- if ($this->isPointNecessary($prev, $current, $next, $tolerance)) {
- $simplified[] = $current;
- }
- }
-
- $simplified[] = $path[count($path) - 1]; // 总是保留终点
-
- return $simplified;
- }
-
- /**
- * 检查点是否必要(基于角度变化)
- */
- private function isPointNecessary($prev, $current, $next, $tolerance)
- {
- // 计算向量
- $v1 = [
- 'lng' => $current['lng'] - $prev['lng'],
- 'lat' => $current['lat'] - $prev['lat']
- ];
-
- $v2 = [
- 'lng' => $next['lng'] - $current['lng'],
- 'lat' => $next['lat'] - $current['lat']
- ];
-
- // 计算向量长度
- $len1 = sqrt($v1['lng'] * $v1['lng'] + $v1['lat'] * $v1['lat']);
- $len2 = sqrt($v2['lng'] * $v2['lng'] + $v2['lat'] * $v2['lat']);
-
- if ($len1 < 0.00001 || $len2 < 0.00001) {
- return true; // 避免除零错误
- }
-
- // 计算点积和角度
- $dotProduct = $v1['lng'] * $v2['lng'] + $v1['lat'] * $v2['lat'];
- $cosAngle = $dotProduct / ($len1 * $len2);
-
- // 如果角度接近180度(直线),可以移除该点
- // cos(180°) = -1,我们使用容差值来判断
- return ($cosAngle < 0.95); // 如果角度变化大于约18度,保留该点
- }
-
- /**
- * 改进的路径生成 - 减少不必要的航点
- */
- private function generateEfficientPath()
- {
- if (count($this->points) <= 2) {
- return $this->nearestNeighborPath();
- }
-
- // 使用改进的最近邻算法,减少路径复杂度
- $path = $this->optimizedNearestNeighbor();
-
- $fullPath = [$this->startPoint];
- foreach ($path as $point) {
- $fullPath[] = $point;
- }
- $fullPath[] = $this->startPoint;
-
- return $fullPath;
- }
-
- /**
- * 优化的最近邻算法
- */
- private function optimizedNearestNeighbor()
- {
- $unvisited = $this->points;
- $currentPoint = $this->startPoint;
- $path = [];
-
- while (!empty($unvisited)) {
- $nearestIndex = $this->findNearestPoint($currentPoint, $unvisited);
- $nearestPoint = $unvisited[$nearestIndex];
-
- $path[] = $nearestPoint;
- $currentPoint = $nearestPoint;
-
- array_splice($unvisited, $nearestIndex, 1);
- }
-
- return $path;
- }
-
- // 以下方法保持不变,只做必要的最小修改...
- private function findNearestPoint($currentPoint, $points)
- {
- $minDistance = PHP_FLOAT_MAX;
- $nearestIndex = 0;
-
- foreach ($points as $index => $point) {
- $distance = $this->calculateDistance($currentPoint, $point);
- if ($distance < $minDistance) {
- $minDistance = $distance;
- $nearestIndex = $index;
- }
- }
-
- return $nearestIndex;
- }
-
- private function handleHighVoltageIntersections($path)
- {
- $finalPath = [];
-
- for ($i = 0; $i < count($path) - 1; $i++) {
- $currentPoint = $path[$i];
- $nextPoint = $path[$i + 1];
-
- $finalPath[] = $currentPoint;
-
- $intersections = $this->checkHighVoltageIntersections(
- $currentPoint,
- $nextPoint
- );
-
- if (!empty($intersections)) {
- // 只添加第一个相交点,避免过多点
- $intersection = $intersections[0];
- $finalPath[] = [
- 'name' => '高压线避让点',
- 'lng' => $intersection['lng'],
- 'lat' => $intersection['lat'],
- 'type' => 'high_voltage_avoidance'
- ];
- }
- }
-
- $finalPath[] = $path[count($path) - 1];
-
- return $finalPath;
- }
-
- private function checkHighVoltageIntersections($pointA, $pointB)
- {
- $intersections = [];
- $towers = $this->highVoltage['towers'];
-
- for ($i = 0; $i < count($towers) - 1; $i++) {
- $tower1 = $towers[$i];
- $tower2 = $towers[$i + 1];
-
- $intersection = $this->checkLineSegmentIntersection(
- $pointA, $pointB, $tower1, $tower2
- );
-
- if ($intersection !== null) {
- $intersections[] = [
- 'lng' => $intersection['lng'],
- 'lat' => $intersection['lat'],
- 'tower1' => $tower1,
- 'tower2' => $tower2
- ];
- break; // 只找一个相交点
- }
- }
-
- return $intersections;
- }
-
- private function checkLineSegmentIntersection($line1A, $line1B, $line2A, $line2B)
- {
- $p1 = $this->latLngToMeters($line1A['lat'], $line1A['lng']);
- $p2 = $this->latLngToMeters($line1B['lat'], $line1B['lng']);
- $p3 = $this->latLngToMeters($line2A['lat'], $line2A['lng']);
- $p4 = $this->latLngToMeters($line2B['lat'], $line2B['lng']);
-
- $denominator = (($p4[1] - $p3[1]) * ($p2[0] - $p1[0]) - ($p4[0] - $p3[0]) * ($p2[1] - $p1[1]));
-
- if ($denominator == 0) {
- return null;
- }
-
- $ua = (($p4[0] - $p3[0]) * ($p1[1] - $p3[1]) - ($p4[1] - $p3[1]) * ($p1[0] - $p3[0])) / $denominator;
- $ub = (($p2[0] - $p1[0]) * ($p1[1] - $p3[1]) - ($p2[1] - $p1[1]) * ($p1[0] - $p3[0])) / $denominator;
-
- if ($ua >= 0 && $ua <= 1 && $ub >= 0 && $ub <= 1) {
- $x = $p1[0] + $ua * ($p2[0] - $p1[0]);
- $y = $p1[1] + $ua * ($p2[1] - $p1[1]);
-
- $latLng = $this->metersToLatLng($x, $y);
-
- return [
- 'lng' => $latLng['lng'],
- 'lat' => $latLng['lat']
- ];
- }
-
- return null;
- }
-
- private function checkLineCircleIntersection($pointA, $pointB, $circleCenter, $radius)
- {
- $A = $this->latLngToMeters($pointA['lat'], $pointA['lng']);
- $B = $this->latLngToMeters($pointB['lat'], $pointB['lng']);
- $C = $this->latLngToMeters($circleCenter['lat'], $circleCenter['lng']);
-
- $AB = [$B[0] - $A[0], $B[1] - $A[1]];
- $AC = [$C[0] - $A[0], $C[1] - $A[1]];
-
- $ab2 = $AB[0] * $AB[0] + $AB[1] * $AB[1];
- $t = ($AC[0] * $AB[0] + $AC[1] * $AB[1]) / $ab2;
-
- $closestPoint = [
- $A[0] + $t * $AB[0],
- $A[1] + $t * $AB[1]
- ];
-
- $distance = sqrt(
- pow($closestPoint[0] - $C[0], 2) +
- pow($closestPoint[1] - $C[1], 2)
- );
-
- $intersections = [];
-
- if ($distance <= $radius) {
- $dt = sqrt($radius * $radius - $distance * $distance) / sqrt($ab2);
-
- $t1 = $t - $dt;
- $t2 = $t + $dt;
-
- if ($t1 >= 0 && $t1 <= 1) {
- $intersection1 = [
- $A[0] + $t1 * $AB[0],
- $A[1] + $t1 * $AB[1]
- ];
- $intersectionLatLng1 = $this->metersToLatLng($intersection1[0], $intersection1[1]);
- $intersections[] = [
- 'lng' => $intersectionLatLng1['lng'],
- 'lat' => $intersectionLatLng1['lat']
- ];
- }
-
- if ($t2 >= 0 && $t2 <= 1) {
- $intersection2 = [
- $A[0] + $t2 * $AB[0],
- $A[1] + $t2 * $AB[1]
- ];
- $intersectionLatLng2 = $this->metersToLatLng($intersection2[0], $intersection2[1]);
- $intersections[] = [
- 'lng' => $intersectionLatLng2['lng'],
- 'lat' => $intersectionLatLng2['lat']
- ];
- }
- }
-
- return $intersections;
- }
-
- private function latLngToMeters($lat, $lng)
- {
- $x = $lng * (M_PI / 180) * self::EARTH_RADIUS * cos($lat * M_PI / 180);
- $y = $lat * (M_PI / 180) * self::EARTH_RADIUS;
- return [$x, $y];
- }
-
- private function metersToLatLng($x, $y)
- {
- $lng = $x / (self::EARTH_RADIUS * cos($y / self::EARTH_RADIUS));
- $lat = $y / self::EARTH_RADIUS;
-
- return [
- 'lng' => $lng * (180 / M_PI),
- 'lat' => $lat * (180 / M_PI)
- ];
- }
-
- private function calculateDistance($point1, $point2)
- {
- $lat1 = $point1['lat'];
- $lng1 = $point1['lng'];
- $lat2 = $point2['lat'];
- $lng2 = $point2['lng'];
-
- $dLat = deg2rad($lat2 - $lat1);
- $dLng = deg2rad($lng2 - $lng1);
-
- $a = sin($dLat/2) * sin($dLat/2) +
- cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
- sin($dLng/2) * sin($dLng/2);
- $c = 2 * atan2(sqrt($a), sqrt(1-$a));
-
- return self::EARTH_RADIUS * $c;
- }
-
- public function printPathInfo($path)
- {
- echo "生成的优化航线路径:\n";
- echo "总点数: " . count($path) . "\n\n";
-
- $totalDistance = 0;
-
- for ($i = 0; $i < count($path); $i++) {
- $point = $path[$i];
- $name = isset($point['name']) ? $point['name'] : (isset($point['type']) ? $point['type'] : '航点');
- $type = isset($point['type']) ? " ({$point['type']})" : "";
-
- echo ($i + 1) . ". {$name}{$type}";
- echo " 经度: " . number_format($point['lng'], 9) . ", 纬度: " . number_format($point['lat'], 9) . "\n";
-
- if ($i > 0) {
- $segmentDistance = $this->calculateDistance($path[$i-1], $point);
- $totalDistance += $segmentDistance;
- }
- }
-
- echo "\n总路径长度: " . number_format($totalDistance, 2) . " 米\n";
- }
- }
- // 使用示例
- $flightInput = [
- "start_point" => ["lng" => 119.728918245, "lat" => 29.158205373],
- "points" => [
- ["name" => "航点1", "lng" => 119.731844828, "lat" => 29.155170432],
- ["name" => "航点2", "lng" => 119.734061703, "lat" => 29.157567795],
- ["name" => "航点3", "lng" => 119.731450602, "lat" => 29.163489386],
- ["name" => "航点4", "lng" => 119.727492742, "lat" => 29.165032019],
- ["name" => "航点5", "lng" => 119.727926162, "lat" => 29.162911848]
- ],
- "no_fly_zone" => [
- "center" => ["lng" => 119.729029043, "lat" => 29.161302197],
- "radius" => 200
- ],
- "high_voltage" => [
- "towers" => [
- ["lng" => 119.728687943, "lat" => 29.156864388],
- ["lng" => 119.735076462, "lat" => 29.160592962]
- ]
- ]
- ];
- // 创建航线生成器实例
- $flightGenerator = new OptimizedFlightPathGenerator($flightInput);
- // 生成优化航线
- $flightPath = $flightGenerator->generateOptimizedFlightPath();
- // 输出航线信息
- $flightGenerator->printPathInfo($flightPath);
- ?>
|