test3.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <?php
  2. class OptimizedFlightPathGenerator
  3. {
  4. private $startPoint;
  5. private $points;
  6. private $noFlyZone;
  7. private $highVoltage;
  8. const EARTH_RADIUS = 6371000;
  9. public function __construct($flightInput)
  10. {
  11. $this->startPoint = $flightInput['start_point'];
  12. $this->points = $flightInput['points'];
  13. $this->noFlyZone = $flightInput['no_fly_zone'];
  14. $this->highVoltage = $flightInput['high_voltage'];
  15. }
  16. /**
  17. * 生成优化的航线路径
  18. */
  19. public function generateOptimizedFlightPath()
  20. {
  21. // 使用凸包算法找到外围点,然后填充内部点
  22. $optimizedPath = $this->generateEfficientPath();
  23. // 检查并处理禁飞区相交 - 使用简化的避让算法
  24. $pathAfterNoFly = $this->handleNoFlyZoneIntersectionsSimple($optimizedPath);
  25. // 检查并处理高压线相交
  26. $finalPath = $this->handleHighVoltageIntersections($pathAfterNoFly);
  27. return $finalPath;
  28. }
  29. /**
  30. * 简化的禁飞区相交处理方法 - 减少避让点数量
  31. */
  32. private function handleNoFlyZoneIntersectionsSimple($path)
  33. {
  34. $maxIterations = 10;
  35. $iteration = 0;
  36. $simplifiedPath = $path;
  37. do {
  38. $hasIntersection = false;
  39. $newPath = [];
  40. for ($i = 0; $i < count($simplifiedPath) - 1; $i++) {
  41. $currentPoint = $simplifiedPath[$i];
  42. $nextPoint = $simplifiedPath[$i + 1];
  43. // 检查当前线段是否与禁飞区相交
  44. $intersections = $this->checkLineCircleIntersection(
  45. $currentPoint,
  46. $nextPoint,
  47. $this->noFlyZone['center'],
  48. $this->noFlyZone['radius']
  49. );
  50. if (!empty($intersections)) {
  51. // 发现相交,采用简化避让策略
  52. $hasIntersection = true;
  53. // 只在真正需要的地方添加一个避让点
  54. $detourPoint = $this->generateSingleDetourPoint($currentPoint, $nextPoint);
  55. $newPath[] = $currentPoint;
  56. $newPath[] = $detourPoint;
  57. // 不添加nextPoint,它会在下一次循环中处理
  58. } else {
  59. // 没有相交,保持原路径点
  60. $newPath[] = $currentPoint;
  61. // 如果是最后一个点,也要添加
  62. if ($i == count($simplifiedPath) - 2) {
  63. $newPath[] = $nextPoint;
  64. }
  65. }
  66. }
  67. $simplifiedPath = $newPath;
  68. $iteration++;
  69. } while ($hasIntersection && $iteration < $maxIterations);
  70. // 对最终路径进行简化,移除不必要的点
  71. return $this->simplifyPath($simplifiedPath);
  72. }
  73. /**
  74. * 生成单个避让点(简化版本)
  75. */
  76. private function generateSingleDetourPoint($pointA, $pointB)
  77. {
  78. $center = $this->noFlyZone['center'];
  79. $radius = $this->noFlyZone['radius'];
  80. $safeDistance = 100;
  81. // 计算线段中点
  82. $midPoint = [
  83. 'lng' => ($pointA['lng'] + $pointB['lng']) / 2,
  84. 'lat' => ($pointA['lat'] + $pointB['lat']) / 2
  85. ];
  86. // 计算从禁飞区中心指向中点的方向
  87. $dx = $midPoint['lng'] - $center['lng'];
  88. $dy = $midPoint['lat'] - $center['lat'];
  89. $distanceToCenter = $this->calculateDistance($midPoint, $center);
  90. if ($distanceToCenter <= $radius + $safeDistance) {
  91. // 需要向外移动
  92. $moveDistance = $radius + $safeDistance - $distanceToCenter;
  93. $vectorLength = sqrt($dx * $dx + $dy * $dy);
  94. if ($vectorLength > 0) {
  95. $dx /= $vectorLength;
  96. $dy /= $vectorLength;
  97. return [
  98. 'lng' => $midPoint['lng'] + $dx * $moveDistance * 0.00001,
  99. 'lat' => $midPoint['lat'] + $dy * $moveDistance * 0.00001,
  100. 'name' => '禁飞区避让点',
  101. 'type' => 'no_fly_zone_avoidance'
  102. ];
  103. }
  104. }
  105. // 如果已经在安全距离外,返回中点
  106. return [
  107. 'lng' => $midPoint['lng'],
  108. 'lat' => $midPoint['lat'],
  109. 'name' => '路径中点',
  110. 'type' => 'midpoint'
  111. ];
  112. }
  113. /**
  114. * 路径简化算法 - 移除不必要的中间点
  115. */
  116. private function simplifyPath($path, $tolerance = 0.0001)
  117. {
  118. if (count($path) <= 3) {
  119. return $path;
  120. }
  121. $simplified = [];
  122. $simplified[] = $path[0]; // 总是保留起点
  123. for ($i = 1; $i < count($path) - 1; $i++) {
  124. $current = $path[$i];
  125. $prev = $path[$i - 1];
  126. $next = $path[$i + 1];
  127. // 如果是关键点(避让点、起点、终点等),总是保留
  128. if (isset($current['type']) &&
  129. ($current['type'] === 'no_fly_zone_avoidance' ||
  130. $current['type'] === 'high_voltage_intersection' ||
  131. $current['type'] === 'no_fly_zone_detour')) {
  132. $simplified[] = $current;
  133. continue;
  134. }
  135. // 检查当前点是否可以被移除(与前后点形成的角度是否足够大)
  136. if ($this->isPointNecessary($prev, $current, $next, $tolerance)) {
  137. $simplified[] = $current;
  138. }
  139. }
  140. $simplified[] = $path[count($path) - 1]; // 总是保留终点
  141. return $simplified;
  142. }
  143. /**
  144. * 检查点是否必要(基于角度变化)
  145. */
  146. private function isPointNecessary($prev, $current, $next, $tolerance)
  147. {
  148. // 计算向量
  149. $v1 = [
  150. 'lng' => $current['lng'] - $prev['lng'],
  151. 'lat' => $current['lat'] - $prev['lat']
  152. ];
  153. $v2 = [
  154. 'lng' => $next['lng'] - $current['lng'],
  155. 'lat' => $next['lat'] - $current['lat']
  156. ];
  157. // 计算向量长度
  158. $len1 = sqrt($v1['lng'] * $v1['lng'] + $v1['lat'] * $v1['lat']);
  159. $len2 = sqrt($v2['lng'] * $v2['lng'] + $v2['lat'] * $v2['lat']);
  160. if ($len1 < 0.00001 || $len2 < 0.00001) {
  161. return true; // 避免除零错误
  162. }
  163. // 计算点积和角度
  164. $dotProduct = $v1['lng'] * $v2['lng'] + $v1['lat'] * $v2['lat'];
  165. $cosAngle = $dotProduct / ($len1 * $len2);
  166. // 如果角度接近180度(直线),可以移除该点
  167. // cos(180°) = -1,我们使用容差值来判断
  168. return ($cosAngle < 0.95); // 如果角度变化大于约18度,保留该点
  169. }
  170. /**
  171. * 改进的路径生成 - 减少不必要的航点
  172. */
  173. private function generateEfficientPath()
  174. {
  175. if (count($this->points) <= 2) {
  176. return $this->nearestNeighborPath();
  177. }
  178. // 使用改进的最近邻算法,减少路径复杂度
  179. $path = $this->optimizedNearestNeighbor();
  180. $fullPath = [$this->startPoint];
  181. foreach ($path as $point) {
  182. $fullPath[] = $point;
  183. }
  184. $fullPath[] = $this->startPoint;
  185. return $fullPath;
  186. }
  187. /**
  188. * 优化的最近邻算法
  189. */
  190. private function optimizedNearestNeighbor()
  191. {
  192. $unvisited = $this->points;
  193. $currentPoint = $this->startPoint;
  194. $path = [];
  195. while (!empty($unvisited)) {
  196. $nearestIndex = $this->findNearestPoint($currentPoint, $unvisited);
  197. $nearestPoint = $unvisited[$nearestIndex];
  198. $path[] = $nearestPoint;
  199. $currentPoint = $nearestPoint;
  200. array_splice($unvisited, $nearestIndex, 1);
  201. }
  202. return $path;
  203. }
  204. // 以下方法保持不变,只做必要的最小修改...
  205. private function findNearestPoint($currentPoint, $points)
  206. {
  207. $minDistance = PHP_FLOAT_MAX;
  208. $nearestIndex = 0;
  209. foreach ($points as $index => $point) {
  210. $distance = $this->calculateDistance($currentPoint, $point);
  211. if ($distance < $minDistance) {
  212. $minDistance = $distance;
  213. $nearestIndex = $index;
  214. }
  215. }
  216. return $nearestIndex;
  217. }
  218. private function handleHighVoltageIntersections($path)
  219. {
  220. $finalPath = [];
  221. for ($i = 0; $i < count($path) - 1; $i++) {
  222. $currentPoint = $path[$i];
  223. $nextPoint = $path[$i + 1];
  224. $finalPath[] = $currentPoint;
  225. $intersections = $this->checkHighVoltageIntersections(
  226. $currentPoint,
  227. $nextPoint
  228. );
  229. if (!empty($intersections)) {
  230. // 只添加第一个相交点,避免过多点
  231. $intersection = $intersections[0];
  232. $finalPath[] = [
  233. 'name' => '高压线避让点',
  234. 'lng' => $intersection['lng'],
  235. 'lat' => $intersection['lat'],
  236. 'type' => 'high_voltage_avoidance'
  237. ];
  238. }
  239. }
  240. $finalPath[] = $path[count($path) - 1];
  241. return $finalPath;
  242. }
  243. private function checkHighVoltageIntersections($pointA, $pointB)
  244. {
  245. $intersections = [];
  246. $towers = $this->highVoltage['towers'];
  247. for ($i = 0; $i < count($towers) - 1; $i++) {
  248. $tower1 = $towers[$i];
  249. $tower2 = $towers[$i + 1];
  250. $intersection = $this->checkLineSegmentIntersection(
  251. $pointA, $pointB, $tower1, $tower2
  252. );
  253. if ($intersection !== null) {
  254. $intersections[] = [
  255. 'lng' => $intersection['lng'],
  256. 'lat' => $intersection['lat'],
  257. 'tower1' => $tower1,
  258. 'tower2' => $tower2
  259. ];
  260. break; // 只找一个相交点
  261. }
  262. }
  263. return $intersections;
  264. }
  265. private function checkLineSegmentIntersection($line1A, $line1B, $line2A, $line2B)
  266. {
  267. $p1 = $this->latLngToMeters($line1A['lat'], $line1A['lng']);
  268. $p2 = $this->latLngToMeters($line1B['lat'], $line1B['lng']);
  269. $p3 = $this->latLngToMeters($line2A['lat'], $line2A['lng']);
  270. $p4 = $this->latLngToMeters($line2B['lat'], $line2B['lng']);
  271. $denominator = (($p4[1] - $p3[1]) * ($p2[0] - $p1[0]) - ($p4[0] - $p3[0]) * ($p2[1] - $p1[1]));
  272. if ($denominator == 0) {
  273. return null;
  274. }
  275. $ua = (($p4[0] - $p3[0]) * ($p1[1] - $p3[1]) - ($p4[1] - $p3[1]) * ($p1[0] - $p3[0])) / $denominator;
  276. $ub = (($p2[0] - $p1[0]) * ($p1[1] - $p3[1]) - ($p2[1] - $p1[1]) * ($p1[0] - $p3[0])) / $denominator;
  277. if ($ua >= 0 && $ua <= 1 && $ub >= 0 && $ub <= 1) {
  278. $x = $p1[0] + $ua * ($p2[0] - $p1[0]);
  279. $y = $p1[1] + $ua * ($p2[1] - $p1[1]);
  280. $latLng = $this->metersToLatLng($x, $y);
  281. return [
  282. 'lng' => $latLng['lng'],
  283. 'lat' => $latLng['lat']
  284. ];
  285. }
  286. return null;
  287. }
  288. private function checkLineCircleIntersection($pointA, $pointB, $circleCenter, $radius)
  289. {
  290. $A = $this->latLngToMeters($pointA['lat'], $pointA['lng']);
  291. $B = $this->latLngToMeters($pointB['lat'], $pointB['lng']);
  292. $C = $this->latLngToMeters($circleCenter['lat'], $circleCenter['lng']);
  293. $AB = [$B[0] - $A[0], $B[1] - $A[1]];
  294. $AC = [$C[0] - $A[0], $C[1] - $A[1]];
  295. $ab2 = $AB[0] * $AB[0] + $AB[1] * $AB[1];
  296. $t = ($AC[0] * $AB[0] + $AC[1] * $AB[1]) / $ab2;
  297. $closestPoint = [
  298. $A[0] + $t * $AB[0],
  299. $A[1] + $t * $AB[1]
  300. ];
  301. $distance = sqrt(
  302. pow($closestPoint[0] - $C[0], 2) +
  303. pow($closestPoint[1] - $C[1], 2)
  304. );
  305. $intersections = [];
  306. if ($distance <= $radius) {
  307. $dt = sqrt($radius * $radius - $distance * $distance) / sqrt($ab2);
  308. $t1 = $t - $dt;
  309. $t2 = $t + $dt;
  310. if ($t1 >= 0 && $t1 <= 1) {
  311. $intersection1 = [
  312. $A[0] + $t1 * $AB[0],
  313. $A[1] + $t1 * $AB[1]
  314. ];
  315. $intersectionLatLng1 = $this->metersToLatLng($intersection1[0], $intersection1[1]);
  316. $intersections[] = [
  317. 'lng' => $intersectionLatLng1['lng'],
  318. 'lat' => $intersectionLatLng1['lat']
  319. ];
  320. }
  321. if ($t2 >= 0 && $t2 <= 1) {
  322. $intersection2 = [
  323. $A[0] + $t2 * $AB[0],
  324. $A[1] + $t2 * $AB[1]
  325. ];
  326. $intersectionLatLng2 = $this->metersToLatLng($intersection2[0], $intersection2[1]);
  327. $intersections[] = [
  328. 'lng' => $intersectionLatLng2['lng'],
  329. 'lat' => $intersectionLatLng2['lat']
  330. ];
  331. }
  332. }
  333. return $intersections;
  334. }
  335. private function latLngToMeters($lat, $lng)
  336. {
  337. $x = $lng * (M_PI / 180) * self::EARTH_RADIUS * cos($lat * M_PI / 180);
  338. $y = $lat * (M_PI / 180) * self::EARTH_RADIUS;
  339. return [$x, $y];
  340. }
  341. private function metersToLatLng($x, $y)
  342. {
  343. $lng = $x / (self::EARTH_RADIUS * cos($y / self::EARTH_RADIUS));
  344. $lat = $y / self::EARTH_RADIUS;
  345. return [
  346. 'lng' => $lng * (180 / M_PI),
  347. 'lat' => $lat * (180 / M_PI)
  348. ];
  349. }
  350. private function calculateDistance($point1, $point2)
  351. {
  352. $lat1 = $point1['lat'];
  353. $lng1 = $point1['lng'];
  354. $lat2 = $point2['lat'];
  355. $lng2 = $point2['lng'];
  356. $dLat = deg2rad($lat2 - $lat1);
  357. $dLng = deg2rad($lng2 - $lng1);
  358. $a = sin($dLat/2) * sin($dLat/2) +
  359. cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
  360. sin($dLng/2) * sin($dLng/2);
  361. $c = 2 * atan2(sqrt($a), sqrt(1-$a));
  362. return self::EARTH_RADIUS * $c;
  363. }
  364. public function printPathInfo($path)
  365. {
  366. echo "生成的优化航线路径:\n";
  367. echo "总点数: " . count($path) . "\n\n";
  368. $totalDistance = 0;
  369. for ($i = 0; $i < count($path); $i++) {
  370. $point = $path[$i];
  371. $name = isset($point['name']) ? $point['name'] : (isset($point['type']) ? $point['type'] : '航点');
  372. $type = isset($point['type']) ? " ({$point['type']})" : "";
  373. echo ($i + 1) . ". {$name}{$type}";
  374. echo " 经度: " . number_format($point['lng'], 9) . ", 纬度: " . number_format($point['lat'], 9) . "\n";
  375. if ($i > 0) {
  376. $segmentDistance = $this->calculateDistance($path[$i-1], $point);
  377. $totalDistance += $segmentDistance;
  378. }
  379. }
  380. echo "\n总路径长度: " . number_format($totalDistance, 2) . " 米\n";
  381. }
  382. }
  383. // 使用示例
  384. $flightInput = [
  385. "start_point" => ["lng" => 119.728918245, "lat" => 29.158205373],
  386. "points" => [
  387. ["name" => "航点1", "lng" => 119.731844828, "lat" => 29.155170432],
  388. ["name" => "航点2", "lng" => 119.734061703, "lat" => 29.157567795],
  389. ["name" => "航点3", "lng" => 119.731450602, "lat" => 29.163489386],
  390. ["name" => "航点4", "lng" => 119.727492742, "lat" => 29.165032019],
  391. ["name" => "航点5", "lng" => 119.727926162, "lat" => 29.162911848]
  392. ],
  393. "no_fly_zone" => [
  394. "center" => ["lng" => 119.729029043, "lat" => 29.161302197],
  395. "radius" => 200
  396. ],
  397. "high_voltage" => [
  398. "towers" => [
  399. ["lng" => 119.728687943, "lat" => 29.156864388],
  400. ["lng" => 119.735076462, "lat" => 29.160592962]
  401. ]
  402. ]
  403. ];
  404. // 创建航线生成器实例
  405. $flightGenerator = new OptimizedFlightPathGenerator($flightInput);
  406. // 生成优化航线
  407. $flightPath = $flightGenerator->generateOptimizedFlightPath();
  408. // 输出航线信息
  409. $flightGenerator->printPathInfo($flightPath);
  410. ?>