Skip to content

Commit

Permalink
Backport UnitOfMeasure tweaks from v5
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Jan 26, 2024
1 parent fed406a commit 031d896
Show file tree
Hide file tree
Showing 61 changed files with 506 additions and 221 deletions.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"license": "(MIT and proprietary)",
"require": {
"php": "^7.4||^8.0",
"symfony/polyfill-php80": "^1.18"
"symfony/polyfill-php80": "^1.18",
"composer-runtime-api": "^2.1",
"composer/pcre": "^3.1"
},
"require-dev": {
"ext-json": "*",
Expand Down
94 changes: 63 additions & 31 deletions src/UnitOfMeasure/Angle/Angle.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* PHPCoord.
*
Expand All @@ -8,180 +9,197 @@

namespace PHPCoord\UnitOfMeasure\Angle;

use const M_PI;
use PHPCoord\Exception\UnknownUnitOfMeasureException;
use PHPCoord\UnitOfMeasure\UnitOfMeasure;

use function array_map;

use const M_PI;

abstract class Angle implements UnitOfMeasure
{
/**
* arc-second
* Arc-second
* 1/60th arc-minute = ((pi/180) / 3600) radians.
*/
public const EPSG_ARC_SECOND = 'urn:ogc:def:uom:EPSG::9104';

/**
* centesimal second
* Centesimal second
* 1/100 of a centesimal minute or 1/10,000th of a grad and gon = ((pi/200) / 10000) radians.
*/
public const EPSG_CENTESIMAL_SECOND = 'urn:ogc:def:uom:EPSG::9113';

/**
* degree
* Degree
* = pi/180 radians.
*/
public const EPSG_DEGREE = 'urn:ogc:def:uom:EPSG::9102';

/**
* degree hemisphere
* Degree hemisphere
* Degree representation. Format: degrees (real, any precision) - hemisphere abbreviation (single character N S E
* or W). Convert to degrees using algorithm.
*/
public const EPSG_DEGREE_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9116';

/**
* degree minute
* Degree minute
* Degree representation. Format: signed degrees (integer) - arc-minutes (real, any precision). Different symbol
* sets are in use as field separators, for example º '. Convert to degrees using algorithm.
*/
public const EPSG_DEGREE_MINUTE = 'urn:ogc:def:uom:EPSG::9115';

/**
* degree minute hemisphere
* Degree minute hemisphere
* Degree representation. Format: degrees (integer) - arc-minutes (real, any precision) - hemisphere abbreviation
* (single character N S E or W). Different symbol sets are in use as field separators, for example º '. Convert
* to degrees using algorithm.
*/
public const EPSG_DEGREE_MINUTE_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9118';

/**
* degree minute second
* Degree minute second
* Degree representation. Format: signed degrees (integer) - arc-minutes (integer) - arc-seconds (real, any
* precision). Different symbol sets are in use as field separators, for example º ' ". Convert to degrees using
* algorithm.
*/
public const EPSG_DEGREE_MINUTE_SECOND = 'urn:ogc:def:uom:EPSG::9107';

/**
* degree minute second hemisphere
* Degree minute second hemisphere
* Degree representation. Format: degrees (integer) - arc-minutes (integer) - arc-seconds (real) - hemisphere
* abbreviation (single character N S E or W). Different symbol sets are in use as field separators for example º
* ' ". Convert to deg using algorithm.
*/
public const EPSG_DEGREE_MINUTE_SECOND_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9108';

/**
* grad
* Grad
* =pi/200 radians.
*/
public const EPSG_GRAD = 'urn:ogc:def:uom:EPSG::9105';

/**
* hemisphere degree
* Hemisphere degree
* Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (real, any
* precision). Convert to degrees using algorithm.
*/
public const EPSG_HEMISPHERE_DEGREE = 'urn:ogc:def:uom:EPSG::9117';

/**
* hemisphere degree minute
* Hemisphere degree minute
* Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (integer) -
* arc-minutes (real, any precision). Different symbol sets are in use as field separators, for example º '.
* Convert to degrees using algorithm.
*/
public const EPSG_HEMISPHERE_DEGREE_MINUTE = 'urn:ogc:def:uom:EPSG::9119';

/**
* hemisphere degree minute second
* Hemisphere degree minute second
* Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (integer) -
* arc-minutes (integer) - arc-seconds (real). Different symbol sets are in use as field separators for example º
* ' ". Convert to deg using algorithm.
*/
public const EPSG_HEMISPHERE_DEGREE_MINUTE_SECOND = 'urn:ogc:def:uom:EPSG::9120';

/**
* microradian
* Microradian
* rad * 10E-6.
*/
public const EPSG_MICRORADIAN = 'urn:ogc:def:uom:EPSG::9109';

/**
* milliarc-second
* Milliarc-second
* = ((pi/180) / 3600 / 1000) radians.
*/
public const EPSG_MILLIARC_SECOND = 'urn:ogc:def:uom:EPSG::1031';

/**
* radian
* Radian
* SI coherent derived unit (standard unit) for plane angle.
*/
public const EPSG_RADIAN = 'urn:ogc:def:uom:EPSG::9101';

/**
* sexagesimal DMS
* Sexagesimal DMS
* Pseudo unit. Format: signed degrees - period - minutes (2 digits) - integer seconds (2 digits) - fraction of
* seconds (any precision). Must include leading zero in minutes and seconds and exclude decimal point for seconds.
* Convert to deg using algorithm.
*/
public const EPSG_SEXAGESIMAL_DMS = 'urn:ogc:def:uom:EPSG::9110';

/**
* @deprecated use EPSG_DEGREE instead
* @var array<string, array{name: string, fqcn?: class-string<self>, help: string}>
*/
public const EPSG_DEGREE_SUPPLIER_TO_DEFINE_REPRESENTATION = 'urn:ogc:def:uom:EPSG::9102';

protected static array $sridData = [
'urn:ogc:def:uom:EPSG::1031' => [
'name' => 'milliarc-second',
'help' => '= ((pi/180) / 3600 / 1000) radians',
],
'urn:ogc:def:uom:EPSG::9101' => [
'name' => 'radian',
'help' => 'SI coherent derived unit (standard unit) for plane angle.',
],
'urn:ogc:def:uom:EPSG::9102' => [
'name' => 'degree',
'help' => '= pi/180 radians',
],
'urn:ogc:def:uom:EPSG::9104' => [
'name' => 'arc-second',
'help' => '1/60th arc-minute = ((pi/180) / 3600) radians',
],
'urn:ogc:def:uom:EPSG::9105' => [
'name' => 'grad',
'help' => '=pi/200 radians.',
],
'urn:ogc:def:uom:EPSG::9107' => [
'name' => 'degree minute second',
'help' => 'Degree representation. Format: signed degrees (integer) - arc-minutes (integer) - arc-seconds (real, any precision). Different symbol sets are in use as field separators, for example º \' ". Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9108' => [
'name' => 'degree minute second hemisphere',
'help' => 'Degree representation. Format: degrees (integer) - arc-minutes (integer) - arc-seconds (real) - hemisphere abbreviation (single character N S E or W). Different symbol sets are in use as field separators for example º \' ". Convert to deg using algorithm.',
],
'urn:ogc:def:uom:EPSG::9109' => [
'name' => 'microradian',
'help' => 'rad * 10E-6',
],
'urn:ogc:def:uom:EPSG::9110' => [
'name' => 'sexagesimal DMS',
'help' => 'Pseudo unit. Format: signed degrees - period - minutes (2 digits) - integer seconds (2 digits) - fraction of seconds (any precision). Must include leading zero in minutes and seconds and exclude decimal point for seconds. Convert to deg using algorithm.',
],
'urn:ogc:def:uom:EPSG::9113' => [
'name' => 'centesimal second',
'help' => '1/100 of a centesimal minute or 1/10,000th of a grad and gon = ((pi/200) / 10000) radians',
],
'urn:ogc:def:uom:EPSG::9115' => [
'name' => 'degree minute',
'help' => 'Degree representation. Format: signed degrees (integer) - arc-minutes (real, any precision). Different symbol sets are in use as field separators, for example º \'. Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9116' => [
'name' => 'degree hemisphere',
'help' => 'Degree representation. Format: degrees (real, any precision) - hemisphere abbreviation (single character N S E or W). Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9117' => [
'name' => 'hemisphere degree',
'help' => 'Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (real, any precision). Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9118' => [
'name' => 'degree minute hemisphere',
'help' => 'Degree representation. Format: degrees (integer) - arc-minutes (real, any precision) - hemisphere abbreviation (single character N S E or W). Different symbol sets are in use as field separators, for example º \'. Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9119' => [
'name' => 'hemisphere degree minute',
'help' => 'Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (integer) - arc-minutes (real, any precision). Different symbol sets are in use as field separators, for example º \'. Convert to degrees using algorithm.',
],
'urn:ogc:def:uom:EPSG::9120' => [
'name' => 'hemisphere degree minute second',
'help' => 'Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (integer) - arc-minutes (integer) - arc-seconds (real). Different symbol sets are in use as field separators for example º \' ". Convert to deg using algorithm.',
],
];

private static array $supportedCache = [];
abstract public function __construct(float $angle);

abstract public function asRadians(): Radian;

Expand All @@ -192,6 +210,9 @@ public function asDegrees(): Degree

public function add(self $unit): self
{
if (get_class($this) === get_class($unit)) {
return new static($this->getValue() + $unit->getValue());
}
$resultAsRadians = new Radian($this->asRadians()->getValue() + $unit->asRadians()->getValue());
$conversionRatio = (new static(1))->asRadians()->getValue();

Expand All @@ -200,6 +221,9 @@ public function add(self $unit): self

public function subtract(self $unit): self
{
if (get_class($this) === get_class($unit)) {
return new static($this->getValue() - $unit->getValue());
}
$resultAsRadians = new Radian($this->asRadians()->getValue() - $unit->asRadians()->getValue());
$conversionRatio = (new static(1))->asRadians()->getValue();

Expand Down Expand Up @@ -259,22 +283,30 @@ public static function makeUnit($measurement, string $srid): self
throw new UnknownUnitOfMeasureException($srid);
}

public static function getSupportedSRIDs(): array
public static function convert(self $angle, string $targetSRID): self
{
if (!self::$supportedCache) {
foreach (static::$sridData as $srid => $data) {
self::$supportedCache[$srid] = $data['name'];
}
}
$conversionRatio = static::makeUnit(1, $targetSRID)->asRadians()->getValue();

return self::$supportedCache;
return self::makeUnit($angle->asRadians()->getValue() / $conversionRatio, $targetSRID);
}

public static function convert(self $angle, string $targetSRID): self
/**
* @return array<string, string>
*/
public static function getSupportedSRIDs(): array
{
$conversionRatio = static::makeUnit(1, $targetSRID)->asRadians()->getValue();
return array_map(fn ($supportedSrid) => $supportedSrid['name'], self::$sridData);
}

return self::makeUnit($angle->asRadians()->getValue() / $conversionRatio, $targetSRID);
/**
* @return array<string, array{name: string, help: string}>
*/
public static function getSupportedSRIDsWithHelp(): array
{
return array_map(fn (array $data) => [
'name' => $data['name'],
'help' => $data['help'],
], static::$sridData);
}

public function __toString(): string
Expand Down
2 changes: 1 addition & 1 deletion src/UnitOfMeasure/Angle/ArcSecond.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public function getValue(): float

public function getUnitName(): string
{
return 'arcsecond';
return 'arc-second';
}
}
36 changes: 36 additions & 0 deletions src/UnitOfMeasure/Angle/CentesimalSecond.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* PHPCoord.
*
* @author Doug Wright
*/
declare(strict_types=1);

namespace PHPCoord\UnitOfMeasure\Angle;

use const M_PI;

class CentesimalSecond extends Angle
{
private float $angle;

public function __construct(float $angle)
{
$this->angle = $angle;
}

public function asRadians(): Radian
{
return new Radian($this->angle * M_PI / 2000000);
}

public function getValue(): float
{
return $this->angle;
}

public function getUnitName(): string
{
return 'centesimal second';
}
}
27 changes: 16 additions & 11 deletions src/UnitOfMeasure/Angle/Degree.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@

namespace PHPCoord\UnitOfMeasure\Angle;

use function in_array;
use Composer\Pcre\Preg;
use InvalidArgumentException;
use const M_PI;
use function preg_match;
use const PREG_UNMATCHED_AS_NULL;

use function in_array;
use function str_pad;
use const STR_PAD_RIGHT;
use function str_replace;
use function strlen;
use function strpos;

use const M_PI;
use const STR_PAD_RIGHT;

class Degree extends Angle
{
private float $angle;
Expand Down Expand Up @@ -137,20 +138,24 @@ public static function fromSexagesimalDM(string $angle): self
return self::fromRegex($angle, $regex);
}

/**
* @param non-empty-string $regex
*/
private static function fromRegex(string $angle, string $regex): self
{
/** @var non-empty-string $angle */
$angle = str_replace(' ', '', $angle);
$foundAngle = preg_match($regex, $angle, $angleParts, PREG_UNMATCHED_AS_NULL);
$foundAngle = Preg::match($regex, $angle, $angleParts);

if (!$foundAngle) {
throw new InvalidArgumentException("Could not find angle in '{$angle}'");
}

$degrees = ($angleParts['degrees'] * 1);
$degrees += (($angleParts['arcminutes'] ?? 0) / 60);
$degrees += isset($angleParts['fractionarcminutes']) ? ($angleParts['fractionarcminutes'] / 60 / 10 ** (strlen($angleParts['fractionarcminutes']))) : 0;
$degrees += (($angleParts['arcseconds'] ?? 0) / 3600);
$degrees += isset($angleParts['fractionarcseconds']) ? ($angleParts['fractionarcseconds'] / 3600 / 10 ** (strlen($angleParts['fractionarcseconds']))) : 0;
$degrees = (float) $angleParts['degrees'];
$degrees += ((float) ($angleParts['arcminutes'] ?? 0) / 60);
$degrees += isset($angleParts['fractionarcminutes']) ? ((float) $angleParts['fractionarcminutes'] / 60 / 10 ** strlen($angleParts['fractionarcminutes'])) : 0;
$degrees += ((float) ($angleParts['arcseconds'] ?? 0) / 3600);
$degrees += isset($angleParts['fractionarcseconds']) ? ((float) $angleParts['fractionarcseconds'] / 3600 / 10 ** strlen($angleParts['fractionarcseconds'])) : 0;

if ($angleParts['negative'] ?? '' || in_array($angleParts['hemisphere'] ?? [], ['S', 'W'], true)) {
$degrees *= -1;
Expand Down
Loading

0 comments on commit 031d896

Please sign in to comment.