所以,我最终选择了 PHP 库路线。它的性能可能不如其他选项,但它是最简单的。如果其他人想知道如何实现这一点,这是我使用phpgeo包的解决方案:
namespace App\Services;
use Location\Bearing\BearingEllipsoidal;
use Location\Coordinate;
use Location\Distance\Vincenty;
class Arc
{
protected Coordinate $start;
protected Coordinate $end;
/**
* @param \Location\Coordinate $start
* @param \Location\Coordinate $end
*/
public function __construct(Coordinate $start, Coordinate $end)
{
$this->start = $start;
$this->end = $end;
}
/**
* @param int $points
* @return array
*/
public function line(int $points = 100): array
{
$bearingCalculator = new BearingEllipsoidal();
$distanceCalculator = new Vincenty();
$totalDistance = $distanceCalculator->getDistance($this->start, $this->end);
$intervalDistance = $totalDistance / ($points + 1);
$currentBearing = $bearingCalculator->calculateBearing($this->start, $this->end);
$currentCoords = $this->start;
$polyline = [
[
$this->start->getLng(),
$this->start->getLat(),
],
];
for ($i = 1; $i < ($points + 1); $i++) {
$currentCoords = $bearingCalculator->calculateDestination(
$currentCoords,
$currentBearing,
$intervalDistance
);
$point = [
$currentCoords->getLng(),
$currentCoords->getLat(),
];
$polyline[$i] = $this->forAntiMeridian(
$polyline[$i - 1],
$point
);
$currentBearing = $bearingCalculator->calculateBearing(
$currentCoords,
$this->end
);
}
$polyline[] = $this->forAntiMeridian(
$polyline[$i - 1],
[
$this->end->getLng(),
$this->end->getLat(),
]
);
return array_values($polyline);
}
/**
* @param array $start
* @param array $end
* @return array
*/
protected function forAntiMeridian(array $start, array $end): array
{
$startLng = $start[0];
$endLng = $end[0];
if ($endLng - $startLng > 180) {
$end[0] -= 360;
} elseif ($startLng - $endLng > 180) {
$end[0] += 360;
}
return $end;
}
}
我在line
其他地方找到了完成大部分工作的方法的内容,但似乎无法再次找到它。我只是稍微更新了一下,并添加了一条线何时穿过反子午线的计算。出于这个原因,Mapbox 允许坐标越界。