113 lines
3.8 KiB
PHP
113 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Traits\Models;
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
trait HasCoordinates
|
|
{
|
|
/**
|
|
* Convert decimal coordinate to sexagesimal
|
|
*
|
|
* @param float $value Decimal coordinate (ex : 1.209333).
|
|
* @param boolean $isLatitude Coordinates is a latitude ?.
|
|
* @return string Coordinate in sexagesimal
|
|
*/
|
|
private static function convertDecimalToDegree(float $value, bool $isLatitude): string
|
|
{
|
|
// Add cardinal point according to the axe (latitude or longitude) and the sign of the value.
|
|
$point = $isLatitude ? (($value < 0) ? 'S' : 'N') : (($value < 0) ? 'W' : 'E');
|
|
|
|
// Degrees.
|
|
list($degree, $decimal) = explode('.', \number_format($value, 10));
|
|
$degree = abs($degree);
|
|
|
|
// Minutes.
|
|
list($minute, $decimal) = explode('.', \number_format(\floatval('0.' . $decimal) * 60, 10));
|
|
$minute = \str_pad($minute, 2, "0", STR_PAD_LEFT);
|
|
|
|
// Seconds.
|
|
$second = round(\floatval('0.' . $decimal) * 60, 1, PHP_ROUND_HALF_UP);
|
|
$second = \str_pad(number_format($second, 1), 4, "0", STR_PAD_LEFT);
|
|
|
|
return "{$degree}°{$minute}'{$second}\"{$point}";
|
|
}
|
|
|
|
/**
|
|
* Convert sexagesimal coordinate to decimal
|
|
*
|
|
* @param string $value Sexagesimal coordinate (ex : 44°20'18.1"N).
|
|
* @param boolean $isLatitude Coordinate is a latitude ?.
|
|
* @return float Coordinate in decimal
|
|
* @throws \RuntimeException Not the good DMS format.
|
|
*/
|
|
private static function convertDegreeToDecimal(string $value, bool $isLatitude): float
|
|
{
|
|
// Si c'est une latitude, alors le nombre positif correspond au Nord, sinon l'Est.
|
|
$axe = $isLatitude ? 'N' : 'E';
|
|
$re = $isLatitude ?
|
|
'/^([0-8]?\d)°([0-5]?\d)\'([0-5]?\d\.\d)"(N|S)$/i' :
|
|
'/^([0-1]?[0-8]\d|\d?\d)°([0-5]?\d)\'([0-5]?\d\.\d)"(E|W|O)$/i';
|
|
|
|
preg_match($re, $value, $matches);
|
|
|
|
if (empty($matches)) {
|
|
throw new \RuntimeException("Le format DMS n'est pas conforme '{$value}'");
|
|
}
|
|
|
|
list($value, $degree, $minutes, $seconds, $point) = $matches;
|
|
|
|
// Si l'axe est Nord ou Est, alors le nombre est positif sinon i est négatif.
|
|
$degreeDecimalValue = \floatval(\sprintf(
|
|
'%s%s',
|
|
Str::upper($point) === $axe ? '' : '-',
|
|
round($degree + $minutes / 60 + $seconds / 3600, 6, PHP_ROUND_HALF_UP)
|
|
));
|
|
|
|
return !$degreeDecimalValue ? abs($degreeDecimalValue) : $degreeDecimalValue;
|
|
}
|
|
|
|
/**
|
|
* Get decimal shop's latitude and convert it to sexagesimal coordinate.
|
|
*
|
|
* @param float $value Decimal coordinate.
|
|
* @return string $sexagesimalValue Sexagesimal coordinate
|
|
*/
|
|
public function getLatitudeAttribute(float $value): string
|
|
{
|
|
return static::convertDecimalToDegree($value, true);
|
|
}
|
|
|
|
/**
|
|
* Set decimal shop's latitude from sexagesimal coordinate.
|
|
*
|
|
* @param string $value Sexagesimal coordinate.
|
|
* @return void
|
|
*/
|
|
public function setLatitudeAttribute(string $value): void
|
|
{
|
|
$this->attributes['latitude'] = static::convertDegreeToDecimal($value, true);
|
|
}
|
|
|
|
/**
|
|
* Get decimal shop's longitude and convert it to sexagesimal coordinate.
|
|
*
|
|
* @param float $value Decimal coordinate.
|
|
* @return string $sexagesimalValue Sexagesimal coordinate
|
|
*/
|
|
public function getLongitudeAttribute(float $value)
|
|
{
|
|
return static::convertDecimalToDegree($value, false);
|
|
}
|
|
|
|
/**
|
|
* Set decimal shop's longitude from sexagesimal coordinate.
|
|
*
|
|
* @param string $value Sexagesimal coordinate.
|
|
* @return void
|
|
*/
|
|
public function setLongitudeAttribute(string $value): void
|
|
{
|
|
$this->attributes['longitude'] = static::convertDegreeToDecimal($value, false);
|
|
}
|
|
}
|