<?php
namespace League\Geotools\Coordinate;
use Geocoder\Location;
use League\Geotools\Exception\InvalidArgumentException;
class Coordinate implements CoordinateInterface, \JsonSerializable
{
protected $latitude;
protected $longitude;
protected $ellipsoid;
private $precision = 8;
public function __construct($coordinates, ?Ellipsoid $ellipsoid = null)
{
if ($coordinates instanceof Location) {
if (null !== $locationCoordinates = $coordinates->getCoordinates()) {
$this->setLatitude($locationCoordinates->getLatitude());
$this->setLongitude($locationCoordinates->getLongitude());
}
} elseif (is_array($coordinates) && 2 === count($coordinates)) {
$this->setLatitude($coordinates[0]);
$this->setLongitude($coordinates[1]);
} elseif (is_string($coordinates)) {
$this->setFromString($coordinates);
} else {
throw new InvalidArgumentException(
'It should be a string, an array or a class which implements Geocoder\Model\Address !'
);
}
$this->ellipsoid = $ellipsoid ?: Ellipsoid::createFromName(Ellipsoid::WGS84);
}
public function normalizeLatitude($latitude)
{
$latitude = rtrim(sprintf('%.13F', max(-90, min(90, $latitude))), 0);
return '.' === substr($latitude, -1) ? $latitude . '0' : $latitude;
}
public function normalizeLongitude($longitude)
{
if (180 === floor($longitude) % 360) {
return '180.0';
}
$mod = fmod($longitude, 360);
$longitude = $mod < -180 ? $mod + 360 : ($mod > 180 ? $mod - 360 : $mod);
$longitude = rtrim(sprintf('%.13F', $longitude), 0);
return '.' === substr($longitude, -1) ? $longitude . '0' : $longitude;
}
public function setLatitude($latitude)
{
$this->latitude = $this->normalizeLatitude($latitude);
}
public function getLatitude()
{
return $this->latitude;
}
public function setLongitude($longitude)
{
$this->longitude = $this->normalizeLongitude($longitude);
}
public function getLongitude()
{
return $this->longitude;
}
public function getEllipsoid()
{
return $this->ellipsoid;
}
public function setFromString($coordinates)
{
if (!is_string($coordinates)) {
throw new InvalidArgumentException('The given coordinates should be a string !');
}
try {
$inDecimalDegree = $this->toDecimalDegrees($coordinates);
$this->setLatitude($inDecimalDegree[0]);
$this->setLongitude($inDecimalDegree[1]);
} catch (InvalidArgumentException $e) {
throw $e;
}
}
public function getPrecision()
{
return $this->precision;
}
public function setPrecision($precision)
{
$this->precision = $precision;
return $this;
}
private function toDecimalDegrees($coordinates)
{
if (preg_match('/([ns]{1})\s?([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*[, ]?([we]{1})\s? ?([0-9]{1,3})\D+([0-9]{1,2}\.?\d*)\D*$/i', $coordinates, $match)) {
$latitude = $match[2] + $match[3] / 60;
$longitude = $match[5] + $match[6] / 60;
return [
'N' === strtoupper($match[1]) ? $latitude : -$latitude,
'E' === strtoupper($match[4]) ? $longitude : -$longitude,
];
}
if (preg_match('/(\-?[0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3}\.?\d*)$/', $coordinates, $match)) {
return array($match[1], $match[2]);
}
if (preg_match('/(\-?[0-9]{1,2})\D+([0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3})\D+([0-9]{1,2}\.?\d*)$/i',
$coordinates, $match)) {
return array(
$match[1] < 0
? $match[1] - $match[2] / 60
: $match[1] + $match[2] / 60,
$match[3] < 0
? $match[3] - $match[4] / 60
: $match[3] + $match[4] / 60
);
}
if (preg_match('/([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3}\.?\d*)\D*([we]{1})$/i', $coordinates, $match)) {
return array(
'N' === strtoupper($match[2]) ? $match[1] : -$match[1],
'E' === strtoupper($match[4]) ? $match[3] : -$match[3]
);
}
if (preg_match('/([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3})\D+([0-9]{1,2}\.?\d*)\D*([we]{1})$/i',
$coordinates, $match)) {
$latitude = $match[1] + $match[2] / 60;
$longitude = $match[4] + $match[5] / 60;
return array(
'N' === strtoupper($match[3]) ? $latitude : -$latitude,
'E' === strtoupper($match[6]) ? $longitude : -$longitude
);
}
if (preg_match('/([0-9]{1,2})\D+([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3})\D+([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([we]{1})$/i',
$coordinates, $match)) {
$latitude = $match[1] + ($match[2] * 60 + $match[3]) / 3600;
$longitude = $match[5] + ($match[6] * 60 + $match[7]) / 3600;
return array(
'N' === strtoupper($match[4]) ? $latitude : -$latitude,
'E' === strtoupper($match[8]) ? $longitude : -$longitude
);
}
throw new InvalidArgumentException(
'It should be a valid and acceptable ways to write geographic coordinates !'
);
}
public function jsonSerialize()
{
return [$this->latitude, $this->longitude];
}
public function isEqual(Coordinate $coordinate) {
return bccomp($this->latitude, $coordinate->getLatitude(), $this->getPrecision()) === 0 && bccomp($this->longitude, $coordinate->getLongitude(), $this->getPrecision()) === 0;
}
}