tongshanglei 15 小时之前
父节点
当前提交
2b22ed945a

文件差异内容过多而无法显示
+ 42 - 184
task_script/LIVESTOCK_MQTT_CLIENT.php


文件差异内容过多而无法显示
+ 16 - 16
task_script/LIVESTOCK_MQTT_FMZL.php


+ 221 - 0
task_script/LIVESTOCK_REPEAT_BAXI.php

@@ -0,0 +1,221 @@
+<?php
+require('../vendor/autoload.php');
+use \PhpMqtt\Client\MqttClient;
+use \PhpMqtt\Client\ConnectionSettings;
+use think\facade\Cache;
+date_default_timezone_set("PRC");
+// date_default_timezone_set("America/Bahia");
+// define('HOST', 'r-bp1eebab79320044pd.redis.rds.aliyuncs.com');
+// define('PORT', '6379');
+// define('PASSWORD', '7e2b5c91e438be3c!');
+// define('DATABASE', 4);
+define('HOST', '127.0.0.1');
+define('PORT', '6379');
+define('PASSWORD', '123456');
+define('DATABASE', 2);
+
+
+function rlog(...$args)
+{
+    if (empty($args[0])) {
+        return;
+    }
+    static $LOG_CONSOLE = false; //是否输出到控制台
+    static $LOG_NAME = "livestock_repeat.log"; //值为空时 不写入文件
+    static $LOG_SIZE = 64 * 1024 * 1024; //文件最大尺寸
+
+    static $LOG_CACHE = false; //是否缓存日志内容 用于批量写入文件
+    static $CACHE_DURATION = 10; //缓存最大时间 秒
+    static $CACHE_SIZE = 1024; //缓存大小
+    static $cacheStartTime = 0;
+    static $cacheBuf = '';
+
+    static $LOG_TIMES = 10; //调用这个函数最大次数 超过次数后判断下文件大小
+    static $logCount = 0;
+
+
+    $buf = '';
+    if (count($args) == 1 && $args[0] == "\n") { //只有换行时 不写入时间戳了
+        $buf = "\n";
+    } else {
+        $pid = ''; //进程id
+        if (function_exists('posix_getpid')) {
+            $pid = ' ' . posix_getpid() . ' ';
+        }
+        $fileLine = ''; //文件名:行号
+        {
+            $debug = debug_backtrace();
+            $fileLine = ($pid == '' ? ' ' : '') . basename($debug[0]['file']) . ':' . $debug[0]['line'] . ' ';
+        }
+        $buf = date("y-m-d H:i:s") . "{$pid}{$fileLine}" . implode(' ', $args) . "\n";
+    }
+
+    $logCount++;
+    if (!empty($LOG_NAME)) {
+        if ($LOG_CACHE) {
+            $cacheBuf .= $buf;
+            //超过缓存尺寸 或者 超过缓存时长 写缓存到文件
+            if (strlen($cacheBuf) > $CACHE_SIZE || time() - $cacheStartTime > $CACHE_DURATION) {
+                $cacheStartTime = time();
+                goto write;
+            } else {
+                goto skipWrite;
+            }
+        } else {
+            $cacheBuf = $buf;
+        }
+        write: {
+            //超过尺寸后 删除旧文件 把新文件重命名为旧文件  多进程同时操作 不加锁问题不大
+            if ($logCount > $LOG_TIMES && filesize($LOG_NAME) > $LOG_SIZE) {
+                $oldLogName = $LOG_NAME . '.old';
+                if (file_exists($oldLogName)) {
+                    if (!unlink($oldLogName)) {
+                        echo "unlink err\n";
+                    }
+                }
+                if (!rename($LOG_NAME, $oldLogName)) {
+                    echo "rename err\n";
+                }
+                $logCount = 0;
+            }
+            if (!file_put_contents($LOG_NAME, $cacheBuf, FILE_APPEND)) {
+                echo "file_put_contents err\n";
+            }
+            $cacheBuf = '';
+        }
+        skipWrite: {
+        }
+    }
+    if ($LOG_CONSOLE) {
+        echo $buf;
+    }
+}
+
+function loop()
+{
+    $server   = '116.62.220.88';
+    $port     = 1883;
+    $clientId = 'repeat_mqtt_livestock_develop_paigou';
+    $username = 'rl517';
+    $password = "rlian2022";
+    $clean_session = false;
+    $connectionSettings  = new ConnectionSettings();
+    $connectionSettings = $connectionSettings
+        ->setUsername($username)
+        ->setPassword($password)
+        ->setKeepAliveInterval(60)
+        // Last Will 设置
+        //        ->setLastWillTopic('emqx/test/last-will')
+        //        ->setLastWillMessage('client disconnect')
+        //        ->setLastWillQualityOfService(1)
+    ;
+
+    //include "RLog.php";
+    //    $mqtt = new MqttClient($server, $port, $clientId, MqttClient::MQTT_3_1, null, new RLog());
+
+    $mqtt = new MqttClient($server, $port, $clientId);
+
+    $mqtt->connect($connectionSettings, $clean_session);
+    rlog('INFO', "connect OK");
+
+    /*
+    消息方向	设备->服务器 
+    设备主动上报当前设备公共信息参数:ScBusTem/DevRegularInfo
+    服务器获取设备系统信息后设备上传信息,即GetDevSysMsg的回应 ScBusTem/GetUpDevSysMsg 
+    服务器设置设备重量信息信息 ScBusTem/RCInfoMsg
+    */
+
+
+    // $mqtt->subscribe('ScBusTem/GetDevSysMsg/*', function ($topic, $message) {
+    //     rlog("INFO", 'recv', $topic, $message);
+    //     getDevSysMsg($topic, $message);
+    // }, 0);
+    //终端上报系统信息数据
+    $mqtt->subscribe('earings/+/reportData', function ($topic, $message) use($mqtt) {
+        rlog("reportData", 'recv', $topic, $message);
+        $topicArr=explode('/',$topic);
+        $deviceId=$topicArr[1];
+
+        mqttRepeat($topic, $message);
+    }, 1);
+    // $mqtt->subscribe('earings/+/cloudResp', function ($topic, $message) use($mqtt) {
+    //     rlog("cloudResp", 'recv', $topic, $message);
+    //     mqttRepeat($topic, $message);
+    // }, 1);
+    // $mqtt->subscribe('earings/+/cloudControl', function ($topic, $message) use($mqtt) {
+    //     rlog("cloudControl", 'recv', $topic, $message);
+    //     mqttRepeat($topic, $message);
+    // }, 1);
+    // $mqtt->subscribe('$SYS/brokers/+/clients/+/connected', function ($topic, $message) use($mqtt) {
+    //     rlog("connected", 'recv', $topic, $message);
+    //     $data=json_decode($message,true);
+    //     $data['deviceId']=$data['clientid'];
+    //     $data['data_type']='connected';
+    // }, 1);
+    // $mqtt->subscribe('$SYS/brokers/+/clients/+/disconnected', function ($topic, $message) use($mqtt) {
+    //     rlog("connected", 'recv', $topic, $message);
+    //     $data=json_decode($message,true);
+    //     $data['deviceId']=$data['clientid'];
+    //     $data['data_type']='disconnected';
+    // }, 1);
+    // 上线,: $SYS/brokers/+/clients/+/connected
+	// 下线,: $SYS/brokers/+/clients/+/disconnected
+    $mqtt->loop(true);
+}
+
+function mqttRepeat($topic,$message){
+    
+
+
+
+    $server   = '47.114.189.154';
+    $port     = 1883;
+    $clientId = 'repeat_mqtt_livestock_ningbo_paigou';
+    $username = 'rl241107';
+    $password = "rlian2024";
+    $clean_session = false;
+
+    $connectionSettings  = new ConnectionSettings();
+    $connectionSettings = $connectionSettings
+        ->setUsername($username)
+        ->setPassword($password)
+        ->setKeepAliveInterval(60)
+        // Last Will 设置
+        //        ->setLastWillTopic('emqx/test/last-will')
+        //        ->setLastWillMessage('client disconnect')
+        //        ->setLastWillQualityOfService(1)
+    ;
+
+    $mqtt2 = new MqttClient($server, $port, $clientId);
+
+    $mqtt2->connect($connectionSettings, $clean_session);
+    echo '['.date('Y-m-d H:i:s').']connect OK'.PHP_EOL;
+    echo '['.date('Y-m-d H:i:s').']topic:'.$topic.PHP_EOL;
+    echo '['.date('Y-m-d H:i:s').']message:'.$message.PHP_EOL;
+    $res=$mqtt2->publish(
+        $topic,
+        $message,
+        1
+    );
+    echo '['.date('Y-m-d H:i:s').']publish end'.PHP_EOL;
+    $mqtt2->loop(true,true);
+    $mqtt2->disconnect();
+    return $res;
+
+    
+} 
+
+
+while (1) {
+    try {
+        rlog('INFO', 'connect start');
+        loop();
+    } catch (\Exception $ex) {
+        rlog("ERR", $ex->getTraceAsString());
+        rlog("ERR", $ex->getMessage());
+    }
+    sleep(3);
+}
+// $text='{"idESim":460046697314223,"stepCount":0,"EnvironmentTemperature":"27.0","earTemperature":"22.2","latitude":0,"longitude":0,"charging":1,"lastCharge":1704328944,"battery-level":99,"measurementTimestamp":1691173083,"agnss-dtime":16170,"agnss-inserttime":28050,"gnss-locatetime":39600,"gnss-satnum":1,"gnss-cn":28,"csq":"0-0","edrxrdp":",,","deviceId":"869154043484299-999202300000012","data_type":"reportData"}';
+// $text='{"clean_start":false,"clientid":"866216066939047-999274877906912","connack":0,"connected_at":1716169665,"expiry_interval":86400,"ipaddress":"39.144.129.107","keepalive":120,"proto_name":"MQTT","proto_ver":4,"sockport":1883,"ts":1716169665478,"username":"rl517","deviceId":"866216066939047-999274877906912","data_type":"connected"}';
+// app_redis()->lpush("mqtt_data_livestock",$text);

+ 10 - 1
task_script/PUMP_MQTT_PUBLISH.php

@@ -363,7 +363,16 @@ class MQTT_TYPE {/*对应从1开始*/
     public static $SET_COMPENS_VALUE = 'v';
     public static $GET_COMPENS_VALUE = 'w';
 }
-
+function countDecimalPlaces($number) {
+    $strNumber = strval($number);
+    var_dump($strNumber);
+    if (strpos($strNumber, '.') !== false) {
+        $decimalPart = substr($strNumber, strpos($strNumber, '.') + 1);
+        return strlen($decimalPart);
+    } else {
+        return 0;
+    }
+}
 define("ALTER_SEP", ",");
 //成功返回二进制, 失败返回false
 function buildMqttData($mqttType, $msgId/*整数 0-4294967295*/, $data) {

+ 1 - 1
task_script/RFID_STATION_CLIENT.php

@@ -279,7 +279,7 @@ function parseData($dataStr) {
     $data=[];
     $data_str=substr($dataStr, 16);
     if(strlen($data_str)!=$result['length']){
-        echo '数据格式不正确';
+        echo date('Y-m-d H:i:s').'数据格式不正确'.PHP_EOL;
     }
     for($i=0;$i<$result['length'];$i+=32){
         $unit = substr($data_str,$i,32);

文件差异内容过多而无法显示
+ 877 - 256
task_script/test.php


+ 501 - 59
task_script/test3.php

@@ -1,66 +1,508 @@
 <?php
-require('../vendor/autoload.php');
-// 设置接口路径
-$api_url = 'http://58.213.156.66/qjpt/f/interactive/dealRfidData';
 
-$data = [
-    "data" => [
-        [
-            "rfid" => "000001004968",
-            "location" => "01393A3173657005",
-            "volt" => "0"
+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]
         ]
     ]
 ];
-//{"data":[{"rfid":"000001004968","location":"01393A3173657005","volt":"0"}]}
-// 1. 将数据转换为 JSON 格式并进行 Base64 编码
-$json_data = json_encode($data);
-var_dump($json_data);
-$base64_data = base64_encode($json_data);
-
-// 2. 设置 POST 请求数据
-$post_data = [
-    'data' => $base64_data
-];
-
-$curl = curl_init();
-
-curl_setopt_array($curl, array(
-  CURLOPT_URL => $api_url,
-  CURLOPT_RETURNTRANSFER => true,
-  CURLOPT_ENCODING => '',
-  CURLOPT_MAXREDIRS => 10,
-  CURLOPT_TIMEOUT => 0,
-  CURLOPT_FOLLOWLOCATION => true,
-  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
-  CURLOPT_CUSTOMREQUEST => 'POST',
-  CURLOPT_POSTFIELDS =>$base64_data,
-  CURLOPT_HTTPHEADER => array(
-    'Content-Type: text/plain'
-  ),
-));
-// 3. 接口返回的数据
-$response = curl_exec($curl);
-
-curl_close($curl);
-var_dump($response);
-
-
-// 4. AES 解密函数
-function aes_decrypt($data, $key) {
-    $decrypted_data = openssl_decrypt(base64_decode($data), 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
-    return $decrypted_data;
-}
-// 5. 返回数据处理 AES 解密
-// 解密密码
-$password = 'njmind.comnjsjly';
-$decrypted_response = aes_decrypt($response, $password);
-
-$base64_decrypted_data = base64_decode($decrypted_response);
-
-$final_response = json_decode($base64_decrypted_data, true);
-
-// 6. 输出解密后的结果
-var_dump($final_response);
 
+// 创建航线生成器实例
+$flightGenerator = new OptimizedFlightPathGenerator($flightInput);
 
+// 生成优化航线
+$flightPath = $flightGenerator->generateOptimizedFlightPath();
+// 输出航线信息
+$flightGenerator->printPathInfo($flightPath);
+?>

+ 825 - 0
task_script/test4.php

@@ -0,0 +1,825 @@
+<?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);
+
+
+
+?>