PHP KML圆形生成器在国际日期变更线附近失效求助
问题描述
我开发的KML圆形生成器,99%的输入坐标都能正常工作,但当位置靠近国际日期变更线时,绘制会出现异常。
测试坐标:19.282494,166.636650,半径1047海里,生成的圆形会跨越国际日期变更线,输出的坐标在经过0度和180度时出现跳变,导致绘制异常。
异常输出
<coordinates>166.63665,36.70122262,30 170.33929602,36.39506383,30 173.86075879,35.49288317,30 177.04308391,34.04103068,30 179.76710126,32.10928643,30 -178.04299159,29.78247231,30 -176.42417943,27.15263215,30 -175.38386586,24.31316399,30 -174.90965799,21.35519771,30 -174.97725208,18.36580524,30 -175.55559428,15.42737555,30 -176.60947581,12.617518,30 -178.10014056,10.00900144,30 -179.98458741,7.66939861,30 177.7857603,5.66025581,30 175.26638245,4.0357509,30 172.52041272,2.84094525,30 169.61836825,2.10986796,30 166.63665,1.86376538,30 163.65493175,2.10986796,30 160.75288728,2.84094525,30 158.00691755,4.0357509,30 155.4875397,5.66025581,30 153.25788741,7.66939861,30 151.37344056,10.00900144,30 149.88277581,12.617518,30 148.82889428,15.42737555,30 148.25055208,18.36580524,30 148.18295799,21.35519771,30 148.65716586,24.31316399,30 149.69747943,27.15263215,30 151.31629159,29.78247231,30 153.50619874,32.10928643,30 156.23021609,34.04103068,30 159.41254121,35.49288317,30 162.93400398,36.39506383,30 166.63665,36.70122262,30 </coordinates>
相关PHP代码
foreach($ap as $name) { $name=trim(strtoupper($name)); $circleLat = $airport[$name][0]; $circleLon = $airport[$name][1]; $circleRadius = $_GET['circleRadius']*1852; $circlePrecision = mostPrecision($circleLat, $circleLon, $circleRadius); $coordinatesList = ""; // Code based on Google Earth community posting by 'ink_polaroid' $num_points = 36; $delta_pts = 360/$num_points; // convert coordinates to radians $lat = deg2rad(floatval($circleLat)); $lon = deg2rad(floatval($circleLon)); $d = $circleRadius; $d_rad = $d/6378137; // convert dist to angular distance in radians // loop around the compass, appending coordinates of each vertex for($i=0; $i<=$num_points; $i++) { $radial = deg2rad($i*$delta_pts); $lat_rad = asin(sin($lat)*cos($d_rad) + cos($lat)*sin($d_rad)*cos($radial)); $dlon_rad = atan2(sin($radial)*sin($d_rad)*cos($lat), cos($d_rad)-sin($lat)*sin($lat_rad)); $lon_rad = fmod(($lon+$dlon_rad + M_PI), 2*M_PI) - M_PI; $coordinatesList .= Round(rad2deg($lon_rad),$circlePrecision)."," . Round(rad2deg($lat_rad),$circlePrecision).",30 "; }
问题原因
当前代码将经度转换为-180°~180°范围,当圆形跨越国际日期变更线(180°/-180°)时,相邻点的经度会出现从179.x°跳变到-178.x°的情况。KML解析时会将这两个点用最短路径连接,也就是跨太平洋的直线,而非沿着圆形边缘的弧线,导致绘制异常。
解决方案
可以通过两种方式修复这个问题:
方式一:统一使用0°~360°经度格式
修改经度的计算逻辑,将所有经度转换为0°~360°范围,避免正负跳变:
foreach($ap as $name) { $name=trim(strtoupper($name)); $circleLat = $airport[$name][0]; $circleLon = $airport[$name][1]; $circleRadius = $_GET['circleRadius']*1852; $circlePrecision = mostPrecision($circleLat, $circleLon, $circleRadius); $coordinatesList = ""; $num_points = 36; $delta_pts = 360/$num_points; // convert coordinates to radians $lat = deg2rad(floatval($circleLat)); $lon = deg2rad(floatval($circleLon)); $d = $circleRadius; $d_rad = $d/6378137; // convert dist to angular distance in radians // loop around the compass, appending coordinates of each vertex for($i=0; $i<=$num_points; $i++) { $radial = deg2rad($i*$delta_pts); $lat_rad = asin(sin($lat)*cos($d_rad) + cos($lat)*sin($d_rad)*cos($radial)); $dlon_rad = atan2(sin($radial)*sin($d_rad)*cos($lat), cos($d_rad)-sin($lat)*sin($lat_rad)); // 修改为0~2π范围的弧度,转度数后为0°~360° $lon_rad = fmod(($lon+$dlon_rad), 2*M_PI); // 处理fmod可能返回负数的情况(当lon+dlon_rad为负时) if ($lon_rad < 0) { $lon_rad += 2*M_PI; } $coordinatesList .= Round(rad2deg($lon_rad),$circlePrecision)."," . Round(rad2deg($lat_rad),$circlePrecision).",30 "; }
方式二:保持-180°~180°但保证相邻点经度连续
记录前一个点的经度,当当前点与前一个点的经度差绝对值超过180°时,调整当前经度(加/减360°),确保相邻点的最短路径是圆形边缘:
foreach($ap as $name) { $name=trim(strtoupper($name)); $circleLat = $airport[$name][0]; $circleLon = $airport[$name][1]; $circleRadius = $_GET['circleRadius']*1852; $circlePrecision = mostPrecision($circleLat, $circleLon, $circleRadius); $coordinatesList = ""; $num_points = 36; $delta_pts = 360/$num_points; // convert coordinates to radians $lat = deg2rad(floatval($circleLat)); $lon = deg2rad(floatval($circleLon)); $d = $circleRadius; $d_rad = $d/6378137; // convert dist to angular distance in radians $prev_lon_deg = null; // 记录前一个点的经度 // loop around the compass, appending coordinates of each vertex for($i=0; $i<=$num_points; $i++) { $radial = deg2rad($i*$delta_pts); $lat_rad = asin(sin($lat)*cos($d_rad) + cos($lat)*sin($d_rad)*cos($radial)); $dlon_rad = atan2(sin($radial)*sin($d_rad)*cos($lat), cos($d_rad)-sin($lat)*sin($lat_rad)); $lon_rad = fmod(($lon+$dlon_rad + M_PI), 2*M_PI) - M_PI; $current_lon_deg = Round(rad2deg($lon_rad),$circlePrecision); // 调整经度保证连续 if ($prev_lon_deg !== null) { $diff = $current_lon_deg - $prev_lon_deg; if ($diff > 180) { $current_lon_deg -= 360; } elseif ($diff < -180) { $current_lon_deg += 360; } } $coordinatesList .= $current_lon_deg."," . Round(rad2deg($lat_rad),$circlePrecision).",30 "; $prev_lon_deg = $current_lon_deg; }
说明
两种方式都能解决跨国际日期变更线的绘制异常问题,方式一实现更简单,方式二更贴合传统的-180°~180°经度习惯,可根据需求选择。
内容的提问来源于stack exchange,提问作者Steve C.
相关产品推荐
相关产品推荐

