123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- <?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->handleNoFlyZoneIntersectionsNew($optimizedPath);
-
- // 检查并处理高压线相交
- $finalPath = $this->handleHighVoltageIntersections($pathAfterNoFly);
-
- return $finalPath;
- }
-
- /**
- * 新的禁飞区相交处理方法 - 生成中间点并向外移动
- */
- private function handleNoFlyZoneIntersectionsNew($path)
- {
- $maxIterations = 10; // 最大迭代次数,防止无限循环
- $iteration = 0;
-
- do {
- $hasIntersection = false;
- $newPath = [];
- // echo '--aaaaaaaa-'.PHP_EOL;
- for ($i = 0; $i < count($path) - 1; $i++) {
- $currentPoint = $path[$i];
- $nextPoint = $path[$i + 1];
-
- // 检查当前线段是否与禁飞区相交
- $intersections = $this->checkLineCircleIntersection(
- $currentPoint,
- $nextPoint,
- $this->noFlyZone['center'],
- $this->noFlyZone['radius']+50
- );
- // var_dump($intersections);
-
- if (!empty($intersections)) {
- // 发现相交,生成中间点并向外移动
- $hasIntersection = true;
-
- // 计算两个航点的中间点
- $midPoint = $this->calculateMidpoint($currentPoint, $nextPoint);
- // var_dump($intersections);
- // 将中间点向外移动到禁飞区外100米
- $safePoint = $this->movePointAwayFromNoFlyZone($midPoint, 150);
-
- // 添加安全点到路径
- $newPath[] = $currentPoint;
- $newPath[] = [
- 'name' => '禁飞区避让点',
- 'lng' => $safePoint['lng'],
- 'lat' => $safePoint['lat'],
- 'type' => 'no_fly_zone_avoidance'
- ];
- // 不添加nextPoint,因为它会在下一次循环中作为currentPoint处理
- } else {
- // 没有相交,保持原路径点
- $newPath[] = $currentPoint;
-
- // 如果是最后一个点,也要添加
- if ($i == count($path) - 2) {
- $newPath[] = $nextPoint;
- }
- }
- }
-
- $path = $newPath;
- $iteration++;
-
- } while ($hasIntersection && $iteration < $maxIterations);
-
- // if ($iteration >= $maxIterations) {
- // echo "警告: 达到最大迭代次数,可能仍有禁飞区相交\n";
- // }
-
- return $path;
- }
-
- /**
- * 计算两个点的中间点
- */
- private function calculateMidpoint($point1, $point2)
- {
- return [
- 'lng' => ($point1['lng'] + $point2['lng']) / 2,
- 'lat' => ($point1['lat'] + $point2['lat']) / 2
- ];
- }
-
- /**
- * 将点从禁飞区中心向外移动指定距离
- */
- private function movePointAwayFromNoFlyZone($point, $safeDistance)
- {
- $center = $this->noFlyZone['center'];
- $radius = $this->noFlyZone['radius'];
-
- // 计算点到禁飞区中心的距离
- $distanceToCenter = $this->calculateDistance($point, $center);
-
- if ($distanceToCenter <= $radius + $safeDistance) {
- // 计算从禁飞区中心指向点的方向向量
- $dx = $point['lng'] - $center['lng'];
- $dy = $point['lat'] - $center['lat'];
-
- // 计算方向向量的长度
- $vectorLength = sqrt($dx * $dx + $dy * $dy);
-
- if ($vectorLength > 0) {
- // 归一化方向向量
- $dx /= $vectorLength;
- $dy /= $vectorLength;
-
- // 计算需要移动的总距离(从当前位置移动到禁飞区外safeDistance米)
- $moveDistance = $radius + $safeDistance - $distanceToCenter;
-
- // 计算新的点位置
- $newLng = $point['lng'] + $dx * $moveDistance * 0.00001; // 调整缩放因子
- $newLat = $point['lat'] + $dy * $moveDistance * 0.00001;
-
- return [
- 'lng' => $newLng,
- 'lat' => $newLat
- ];
- }
- }
-
- // 如果已经在安全距离外,返回原點
- return $point;
- }
-
- // 以下是原有的方法,保持不变
- private function generateEfficientPath()
- {
- if (count($this->points) <= 2) {
- return $this->nearestNeighborPath();
- }
-
- $center = $this->calculateCenter($this->points);
- $sortedPoints = $this->sortPointsByAngle($this->points, $center);
- $path = $this->improvedNearestInsertion($sortedPoints);
-
- $fullPath = [$this->startPoint];
- foreach ($path as $point) {
- $fullPath[] = $point;
- }
- $fullPath[] = $this->startPoint;
-
- return $fullPath;
- }
-
- private function calculateCenter($points)
- {
- $totalLng = 0;
- $totalLat = 0;
- $count = count($points);
-
- foreach ($points as $point) {
- $totalLng += $point['lng'];
- $totalLat += $point['lat'];
- }
-
- return [
- 'lng' => $totalLng / $count,
- 'lat' => $totalLat / $count
- ];
- }
-
- private function sortPointsByAngle($points, $center)
- {
- $angles = [];
-
- foreach ($points as $index => $point) {
- $angle = atan2(
- $point['lat'] - $center['lat'],
- $point['lng'] - $center['lng']
- );
- $angles[$index] = $angle;
- }
-
- asort($angles);
-
- $sortedPoints = [];
- foreach ($angles as $index => $angle) {
- $sortedPoints[] = $points[$index];
- }
-
- return $sortedPoints;
- }
-
- private function improvedNearestInsertion($points)
- {
- if (empty($points)) {
- return [];
- }
-
- $firstPointIndex = $this->findNearestPoint($this->startPoint, $points);
- $path = [$points[$firstPointIndex]];
- $remainingPoints = $points;
- unset($remainingPoints[$firstPointIndex]);
- $remainingPoints = array_values($remainingPoints);
-
- while (!empty($remainingPoints)) {
- $bestPointIndex = null;
- $bestPosition = null;
- $minIncrease = PHP_FLOAT_MAX;
-
- foreach ($remainingPoints as $pointIndex => $point) {
- for ($i = 0; $i <= count($path); $i++) {
- $increase = $this->calculateInsertionCost($path, $point, $i);
-
- if ($increase < $minIncrease) {
- $minIncrease = $increase;
- $bestPointIndex = $pointIndex;
- $bestPosition = $i;
- }
- }
- }
-
- array_splice($path, $bestPosition, 0, [$remainingPoints[$bestPointIndex]]);
- unset($remainingPoints[$bestPointIndex]);
- $remainingPoints = array_values($remainingPoints);
- }
-
- return $path;
- }
-
- private function calculateInsertionCost($path, $point, $position)
- {
- if (empty($path)) {
- return 0;
- }
-
- if ($position == 0) {
- return $this->calculateDistance($this->startPoint, $point) +
- $this->calculateDistance($point, $path[0]) -
- $this->calculateDistance($this->startPoint, $path[0]);
- } elseif ($position == count($path)) {
- return $this->calculateDistance($path[count($path)-1], $point) +
- $this->calculateDistance($point, $this->startPoint) -
- $this->calculateDistance($path[count($path)-1], $this->startPoint);
- } else {
- return $this->calculateDistance($path[$position-1], $point) +
- $this->calculateDistance($point, $path[$position]) -
- $this->calculateDistance($path[$position-1], $path[$position]);
- }
- }
-
- private function nearestNeighborPath()
- {
- $unvisited = $this->points;
- $currentPoint = $this->startPoint;
- $path = [$currentPoint];
-
- while (!empty($unvisited)) {
- $nearestIndex = $this->findNearestPoint($currentPoint, $unvisited);
- $nearestPoint = $unvisited[$nearestIndex];
-
- $path[] = $nearestPoint;
- $currentPoint = $nearestPoint;
-
- array_splice($unvisited, $nearestIndex, 1);
- }
-
- $path[] = $this->startPoint;
-
- 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 handleNoFlyZoneIntersections($path)
- {
- $finalPath = [];
-
- for ($i = 0; $i < count($path) - 1; $i++) {
- $currentPoint = $path[$i];
- $nextPoint = $path[$i + 1];
-
- $finalPath[] = $currentPoint;
-
- $intersections = $this->checkLineCircleIntersection(
- $currentPoint,
- $nextPoint,
- $this->noFlyZone['center'],
- $this->noFlyZone['radius']
- );
-
- if (!empty($intersections)) {
- foreach ($intersections as $intersection) {
- $finalPath[] = [
- 'name' => '禁飞区相交点',
- 'lng' => $intersection['lng'],
- 'lat' => $intersection['lat'],
- 'type' => 'no_fly_zone_intersection'
- ];
- }
- }
- }
-
- $finalPath[] = $path[count($path) - 1];
-
- return $finalPath;
- }
-
- 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)) {
- foreach ($intersections as $intersection) {
- $finalPath[] = [
- 'name' => '高压线相交点',
- 'lng' => $intersection['lng'],
- 'lat' => $intersection['lat'],
- 'type' => 'high_voltage_intersection',
- 'tower1' => $intersection['tower1'],
- 'tower2' => $intersection['tower2']
- ];
- }
- }
- }
-
- $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
- ];
- }
- }
-
- 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;
- }
-
-
- /**
- * 检测线段与圆是否相交并返回交点坐标
- * @param array $pointA 线段端点A,包含lat和lng键
- * @param array $pointB 线段端点B,包含lat和lng键
- * @param array $circleCenter 圆心坐标,包含lat和lng键
- * @param float $radius 圆的半径(米)
- * @return array 交点坐标数组,每个元素包含lat和lng键
- */
- function checkLineCircleIntersection($pointA, $pointB, $circleCenter, $radius)
- {
- $intersections = [];
- $earthRadius = 6378137; // 地球半径(米)
- $epsilon = 1e-12; // 容差
-
- // 度转弧度
- function toRadians($deg) {
- return $deg * M_PI / 180;
- }
-
- // 弧度转度
- function toDegrees($rad) {
- return $rad * 180 / M_PI;
- }
-
- // Haversine公式计算两点间距离(米)[1,3,5](@ref)
- function getDistanceFromLatLng($lat1, $lng1, $lat2, $lng2) {
- global $earthRadius;
-
- $φ1 = toRadians($lat1);
- $φ2 = toRadians($lat2);
- $Δφ = toRadians($lat2 - $lat1);
- $Δλ = toRadians($lng2 - $lng1);
-
- $a = sin($Δφ/2) * sin($Δφ/2) +
- cos($φ1) * cos($φ2) *
- sin($Δλ/2) * sin($Δλ/2);
- $c = 2 * atan2(sqrt($a), sqrt(1-$a));
- return $earthRadius * $c;
- }
-
- // 经纬度转墨卡托米坐标
- function latLngToMeters($lat, $lng) {
- global $earthRadius;
-
- $x = $earthRadius * toRadians($lng);
- $y = $earthRadius * log(tan(M_PI/4 + toRadians($lat)/2));
- return [$x, $y];
- }
-
- // 墨卡托米坐标转经纬度
- function metersToLatLng($x, $y) {
- global $earthRadius;
-
- $lng = ($x / $earthRadius) * 180 / M_PI;
- $lat = (2 * atan(exp($y / $earthRadius)) - M_PI/2) * 180 / M_PI;
- return [$lat, $lng];
- }
-
- // 判断点是否在圆内(用实际距离)[1](@ref)
- function isPointInCircle($point) {
- global $circleCenter, $radius, $epsilon;
-
- $dist = getDistanceFromLatLng(
- $point['lat'], $point['lng'],
- $circleCenter['lat'], $circleCenter['lng']
- );
- return $dist <= $radius + $epsilon;
- }
-
- // 转换坐标为墨卡托米
- $A = latLngToMeters($pointA['lat'], $pointA['lng']);
- $B = latLngToMeters($pointB['lat'], $pointB['lng']);
- $C = latLngToMeters($circleCenter['lat'], $circleCenter['lng']);
-
- // 向量计算[2,7](@ref)
- $AB = [$B[0] - $A[0], $B[1] - $A[1]];
- $AC = [$C[0] - $A[0], $C[1] - $A[1]];
- $ab2 = $AB[0]**2 + $AB[1]**2;
-
- // 线段为点的情况
- if ($ab2 < $epsilon) {
- if (isPointInCircle($pointA)) {
- return [[
- 'lng' => $pointA['lng'],
- 'lat' => $pointA['lat']
- ]];
- }
- return [];
- }
-
- // 计算最近点参数t[2](@ref)
- $t = ($AC[0] * $AB[0] + $AC[1] * $AB[1]) / $ab2;
- $closest = [$A[0] + $t * $AB[0], $A[1] + $t * $AB[1]];
- $distToClosest = sqrt(($closest[0]-$C[0])**2 + ($closest[1]-$C[1])**2);
-
- // 判断跨圆场景[7,8](@ref)
- $isPointAIn = isPointInCircle($pointA);
- $isPointBIn = isPointInCircle($pointB);
- $isCrossing = $isPointAIn !== $isPointBIn;
-
- // 关键修复:跨圆场景强制计算交点,即使最近点在圆外
- if ($distToClosest <= $radius + $epsilon || $isCrossing) {
- $discriminant = $radius**2 - $distToClosest**2;
- $dt = $discriminant >= 0 ? sqrt($discriminant / $ab2) : 0;
-
- $t1 = $t - $dt;
- $t2 = $t + $dt;
-
- // 添加交点的辅助函数
- $addIntersection = function($tVal) use ($A, $AB, $pointA, $pointB, $circleCenter, $radius) {
- // 允许tVal在[-0.1, 1.1]范围内(补偿墨卡托误差)
- if ($tVal >= -0.1 && $tVal <= 1.1) {
- $tClamped = max(0, min(1, $tVal)); // 强制夹紧到线段上
- $x = $A[0] + $tClamped * $AB[0];
- $y = $A[1] + $tClamped * $AB[1];
- list($lat, $lng) = metersToLatLng($x, $y);
-
- // 额外验证:确保交点到圆心的实际距离≈半径(容错5米)
- $intersectionDist = getDistanceFromLatLng($lat, $lng, $circleCenter['lat'], $circleCenter['lng']);
- if (abs($intersectionDist - $radius) <= 5) {
- return [
- 'lat' => round($lat, 6),
- 'lng' => round($lng, 6)
- ];
- }
- }
- return null;
- };
-
- $p1 = $addIntersection($t1);
- $p2 = $addIntersection($t2);
-
- if ($p1) {
- $intersections[] = $p1;
- }
- if ($p2 && !in_array($p2, $intersections, true)) {
- $intersections[] = $p2;
- }
-
- // 跨圆场景但无交点时,手动计算线段与圆的交点(最终保障)[6](@ref)
- if ($isCrossing && empty($intersections)) {
- // 用参数方程暴力求解(t从0到1逐步计算)
- for ($tForce = 0; $tForce <= 1; $tForce += 0.001) {
- $x = $A[0] + $tForce * $AB[0];
- $y = $A[1] + $tForce * $AB[1];
- list($lat, $lng) = metersToLatLng($x, $y);
- $dist = getDistanceFromLatLng($lat, $lng, $circleCenter['lat'], $circleCenter['lng']);
-
- if (abs($dist - $radius) <= 1) { // 误差≤1米
- $intersection = [
- 'lat' => round($lat, 6),
- 'lng' => round($lng, 6)
- ];
- if (!in_array($intersection, $intersections, true)) {
- $intersections[] = $intersection;
- }
- break;
- }
- }
- }
- }
-
- 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;
- $pathSequence = [];
- $intersectionCount = 0;
- $avoidanceCount = 0;
-
- for ($i = 0; $i < count($path); $i++) {
- $point = $path[$i];
- $name = isset($point['name']) ? $point['name'] : '起点/终点';
- $type = isset($point['type']) ? " ({$point['type']})" : "";
-
- echo ($i + 1) . ". {$name}{$type}\n";
- echo " 经度: {$point['lng']}, 纬度: {$point['lat']}\n";
-
-
- }
-
- }
-
- private function generateRandomPath()
- {
- $points = $this->points;
- shuffle($points);
-
- $path = [$this->startPoint];
- foreach ($points as $point) {
- $path[] = $point;
- }
- $path[] = $this->startPoint;
-
- return $path;
- }
-
- private function calculatePathDistance($path)
- {
- $totalDistance = 0;
- for ($i = 0; $i < count($path) - 1; $i++) {
- $totalDistance += $this->calculateDistance($path[$i], $path[$i + 1]);
- }
- return $totalDistance;
- }
-
- public function visualizePath($path)
- {
- echo "\n路径可视化:\n";
-
- $lngs = array_map(function($point) { return $point['lng']; }, $path);
- $lats = array_map(function($point) { return $point['lat']; }, $path);
-
- $minLng = min($lngs);
- $maxLng = max($lngs);
- $minLat = min($lats);
- $maxLat = max($lats);
-
- $width = 60;
- $height = 20;
-
- $grid = array_fill(0, $height, array_fill(0, $width, ' '));
-
- foreach ($path as $index => $point) {
- $x = (int)(($point['lng'] - $minLng) / ($maxLng - $minLng) * ($width - 1));
- $y = (int)(($point['lat'] - $minLat) / ($maxLat - $minLat) * ($height - 1));
-
- $char = '•';
- if ($index === 0 || $index === count($path)-1) {
- $char = 'S';
- } elseif (isset($point['type'])) {
- if ($point['type'] === 'no_fly_zone_intersection') {
- $char = 'X';
- } elseif ($point['type'] === 'high_voltage_intersection') {
- $char = 'H';
- } elseif ($point['type'] === 'no_fly_zone_avoidance') {
- $char = 'A'; // 避让点
- }
- }
-
- $grid[$y][$x] = $char;
- }
-
- $cx = (int)(($this->noFlyZone['center']['lng'] - $minLng) / ($maxLng - $minLng) * ($width - 1));
- $cy = (int)(($this->noFlyZone['center']['lat'] - $minLat) / ($maxLat - $minLat) * ($height - 1));
-
- $radius = 3;
- for ($a = 0; $a < 2 * M_PI; $a += 0.1) {
- $dx = (int)($radius * cos($a));
- $dy = (int)($radius * sin($a));
- if ($cx+$dx >= 0 && $cx+$dx < $width && $cy+$dy >= 0 && $cy+$dy < $height) {
- $grid[$cy+$dy][$cx+$dx] = 'O';
- }
- }
-
- $towers = $this->highVoltage['towers'];
- for ($i = 0; $i < count($towers) - 1; $i++) {
- $tower1 = $towers[$i];
- $tower2 = $towers[$i + 1];
-
- $x1 = (int)(($tower1['lng'] - $minLng) / ($maxLng - $minLng) * ($width - 1));
- $y1 = (int)(($tower1['lat'] - $minLat) / ($maxLat - $minLat) * ($height - 1));
- $x2 = (int)(($tower2['lng'] - $minLng) / ($maxLng - $minLng) * ($width - 1));
- $y2 = (int)(($tower2['lat'] - $minLat) / ($maxLat - $minLat) * ($height - 1));
-
- $grid[$y1][$x1] = 'T';
- $grid[$y2][$x2] = 'T';
-
- $this->drawLine($grid, $x1, $y1, $x2, $y2, '=');
- }
-
- for ($y = 0; $y < $height; $y++) {
- for ($x = 0; $x < $width; $x++) {
- echo $grid[$y][$x];
- }
- echo "\n";
- }
-
- echo "\n图例: S=起点/终点, •=航点, A=禁飞区避让点, X=禁飞区相交点, H=高压线相交点, O=禁飞区边界, T=高压线塔, ===高压线\n";
- }
-
- private function drawLine(&$grid, $x1, $y1, $x2, $y2, $char)
- {
- $dx = abs($x2 - $x1);
- $dy = abs($y2 - $y1);
- $sx = ($x1 < $x2) ? 1 : -1;
- $sy = ($y1 < $y2) ? 1 : -1;
- $err = $dx - $dy;
-
- while (true) {
- if ($grid[$y1][$x1] === ' ' || $grid[$y1][$x1] === '=') {
- $grid[$y1][$x1] = $char;
- }
-
- if ($x1 == $x2 && $y1 == $y2) break;
-
- $e2 = 2 * $err;
- if ($e2 > -$dy) {
- $err -= $dy;
- $x1 += $sx;
- }
- if ($e2 < $dx) {
- $err += $dx;
- $y1 += $sy;
- }
- }
- }
- }
- // 使用示例
- $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);
- ?>
|