Passed
Push — master ( bbeaaa...9ed6c0 )
by Doug
25:29
created

Angle   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Test Coverage

Coverage 96.15%

Importance

Changes 0
Metric Value
eloc 95
dl 0
loc 264
ccs 50
cts 52
cp 0.9615
rs 10
c 0
b 0
f 0
wmc 15

10 Methods

Rating   Name   Duplication   Size   Complexity  
A convert() 0 5 1
A __toString() 0 3 1
A asDegrees() 0 3 1
A multiply() 0 3 1
A registerCustomUnit() 0 5 1
A subtract() 0 9 2
A divide() 0 3 1
A makeUnit() 0 24 2
A add() 0 9 2
A getSupportedSRIDs() 0 9 3
1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord\UnitOfMeasure\Angle;
10
11
use PHPCoord\Exception\UnknownUnitOfMeasureException;
12
use PHPCoord\UnitOfMeasure\UnitOfMeasure;
13
14
use const M_PI;
15
16
abstract class Angle implements UnitOfMeasure
17
{
18
    /**
19
     * Arc-second
20
     * 1/60th arc-minute = ((pi/180) / 3600) radians.
21
     */
22
    public const EPSG_ARC_SECOND = 'urn:ogc:def:uom:EPSG::9104';
23
24
    /**
25
     * Centesimal second
26
     * 1/100 of a centesimal minute or 1/10,000th of a grad and gon = ((pi/200) / 10000) radians.
27
     */
28
    public const EPSG_CENTESIMAL_SECOND = 'urn:ogc:def:uom:EPSG::9113';
29
30
    /**
31
     * Degree
32
     * = pi/180 radians.
33
     */
34
    public const EPSG_DEGREE = 'urn:ogc:def:uom:EPSG::9102';
35
36
    /**
37
     * Degree hemisphere
38
     * Degree representation. Format: degrees (real, any precision) - hemisphere abbreviation (single character N S E
39
     * or W). Convert to degrees using algorithm.
40
     */
41
    public const EPSG_DEGREE_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9116';
42
43
    /**
44
     * Degree minute
45
     * Degree representation. Format: signed degrees (integer)  - arc-minutes (real, any precision). Different symbol
46
     * sets are in use as field separators, for example º '. Convert to degrees using algorithm.
47
     */
48
    public const EPSG_DEGREE_MINUTE = 'urn:ogc:def:uom:EPSG::9115';
49
50
    /**
51
     * Degree minute hemisphere
52
     * Degree representation. Format: degrees (integer) - arc-minutes (real, any precision) - hemisphere abbreviation
53
     * (single character N S E or W). Different symbol sets are in use as field separators, for example º '. Convert
54
     * to degrees using algorithm.
55
     */
56
    public const EPSG_DEGREE_MINUTE_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9118';
57
58
    /**
59
     * Degree minute second
60
     * Degree representation. Format: signed degrees (integer) - arc-minutes (integer) - arc-seconds (real, any
61
     * precision). Different symbol sets are in use as field separators, for example º ' ". Convert to degrees using
62
     * algorithm.
63
     */
64
    public const EPSG_DEGREE_MINUTE_SECOND = 'urn:ogc:def:uom:EPSG::9107';
65
66
    /**
67
     * Degree minute second hemisphere
68
     * Degree representation. Format: degrees (integer) - arc-minutes (integer) - arc-seconds (real) - hemisphere
69
     * abbreviation (single character N S E or W). Different symbol sets are in use as field separators for example º
70
     * ' ". Convert to deg using algorithm.
71
     */
72
    public const EPSG_DEGREE_MINUTE_SECOND_HEMISPHERE = 'urn:ogc:def:uom:EPSG::9108';
73
74
    /**
75
     * Grad
76
     * =pi/200 radians.
77
     */
78
    public const EPSG_GRAD = 'urn:ogc:def:uom:EPSG::9105';
79
80
    /**
81
     * Hemisphere degree
82
     * Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (real, any
83
     * precision). Convert to degrees using algorithm.
84
     */
85
    public const EPSG_HEMISPHERE_DEGREE = 'urn:ogc:def:uom:EPSG::9117';
86
87
    /**
88
     * Hemisphere degree minute
89
     * Degree representation. Format:  hemisphere abbreviation (single character N S E or W) - degrees (integer) -
90
     * arc-minutes (real, any precision). Different symbol sets are in use as field separators, for example º '.
91
     * Convert to degrees using algorithm.
92
     */
93
    public const EPSG_HEMISPHERE_DEGREE_MINUTE = 'urn:ogc:def:uom:EPSG::9119';
94
95
    /**
96
     * Hemisphere degree minute second
97
     * Degree representation. Format: hemisphere abbreviation (single character N S E or W) - degrees (integer) -
98
     * arc-minutes (integer) - arc-seconds (real). Different symbol sets are in use as field separators for example º
99
     * ' ". Convert to deg using algorithm.
100
     */
101
    public const EPSG_HEMISPHERE_DEGREE_MINUTE_SECOND = 'urn:ogc:def:uom:EPSG::9120';
102
103
    /**
104
     * Microradian
105
     * rad * 10E-6.
106
     */
107
    public const EPSG_MICRORADIAN = 'urn:ogc:def:uom:EPSG::9109';
108
109
    /**
110
     * Milliarc-second
111
     * = ((pi/180) / 3600 / 1000) radians.
112
     */
113
    public const EPSG_MILLIARC_SECOND = 'urn:ogc:def:uom:EPSG::1031';
114
115
    /**
116
     * Radian
117
     * SI coherent derived unit (standard unit) for plane angle.
118
     */
119
    public const EPSG_RADIAN = 'urn:ogc:def:uom:EPSG::9101';
120
121
    /**
122
     * Sexagesimal DMS
123
     * Pseudo unit. Format: signed degrees - period - minutes (2 digits) - integer seconds (2 digits) - fraction of
124
     * seconds (any precision). Must include leading zero in minutes and seconds and exclude decimal point for seconds.
125
     * Convert to deg using algorithm.
126
     */
127
    public const EPSG_SEXAGESIMAL_DMS = 'urn:ogc:def:uom:EPSG::9110';
128
129
    protected static array $sridData = [
130
        'urn:ogc:def:uom:EPSG::1031' => [
131
            'name' => 'milliarc-second',
132
        ],
133
        'urn:ogc:def:uom:EPSG::9101' => [
134
            'name' => 'radian',
135
        ],
136
        'urn:ogc:def:uom:EPSG::9102' => [
137
            'name' => 'degree',
138
        ],
139
        'urn:ogc:def:uom:EPSG::9104' => [
140
            'name' => 'arc-second',
141
        ],
142
        'urn:ogc:def:uom:EPSG::9105' => [
143
            'name' => 'grad',
144
        ],
145
        'urn:ogc:def:uom:EPSG::9107' => [
146
            'name' => 'degree minute second',
147
        ],
148
        'urn:ogc:def:uom:EPSG::9108' => [
149
            'name' => 'degree minute second hemisphere',
150
        ],
151
        'urn:ogc:def:uom:EPSG::9109' => [
152
            'name' => 'microradian',
153
        ],
154
        'urn:ogc:def:uom:EPSG::9110' => [
155
            'name' => 'sexagesimal DMS',
156
        ],
157
        'urn:ogc:def:uom:EPSG::9113' => [
158
            'name' => 'centesimal second',
159
        ],
160
        'urn:ogc:def:uom:EPSG::9115' => [
161
            'name' => 'degree minute',
162
        ],
163
        'urn:ogc:def:uom:EPSG::9116' => [
164
            'name' => 'degree hemisphere',
165
        ],
166
        'urn:ogc:def:uom:EPSG::9117' => [
167
            'name' => 'hemisphere degree',
168
        ],
169
        'urn:ogc:def:uom:EPSG::9118' => [
170
            'name' => 'degree minute hemisphere',
171
        ],
172
        'urn:ogc:def:uom:EPSG::9119' => [
173
            'name' => 'hemisphere degree minute',
174
        ],
175
        'urn:ogc:def:uom:EPSG::9120' => [
176
            'name' => 'hemisphere degree minute second',
177
        ],
178
    ];
179
180
    protected static array $customSridData = [];
181
182
    private static array $supportedCache = [];
183
184
    abstract public function __construct(float $angle);
185
186
    abstract public function asRadians(): Radian;
187
188 6122
    public function asDegrees(): Degree
189
    {
190 6122
        return new Degree($this->asRadians()->getValue() * 180 / M_PI);
191
    }
192
193 296
    public function add(self $unit): self
194
    {
195 296
        if ($this::class === $unit::class) {
196 225
            return new static($this->getValue() + $unit->getValue());
197
        }
198 81
        $resultAsRadians = new Radian($this->asRadians()->getValue() + $unit->asRadians()->getValue());
199 81
        $conversionRatio = (new static(1))->asRadians()->getValue();
200
201 81
        return new static($resultAsRadians->getValue() / $conversionRatio);
202
    }
203
204 2593
    public function subtract(self $unit): self
205
    {
206 2593
        if ($this::class === $unit::class) {
207 2458
            return new static($this->getValue() - $unit->getValue());
208
        }
209 141
        $resultAsRadians = new Radian($this->asRadians()->getValue() - $unit->asRadians()->getValue());
210 141
        $conversionRatio = (new static(1))->asRadians()->getValue();
211
212 141
        return new static($resultAsRadians->getValue() / $conversionRatio);
213
    }
214
215 54
    public function multiply(float $multiplicand): self
216
    {
217 54
        return new static($this->getValue() * $multiplicand);
218
    }
219
220 54
    public function divide(float $divisor): self
221
    {
222 54
        return new static($this->getValue() / $divisor);
223
    }
224
225 6704
    public static function makeUnit(float|string $measurement, string $srid): self
226
    {
227 6704
        if (isset(self::$customSridData[$srid])) {
228 54
            return new self::$customSridData[$srid]['fqcn']($measurement);
229
        }
230
231
        return match ($srid) {
232 6704
            self::EPSG_RADIAN => new Radian($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\Angle\Radian::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

232
            self::EPSG_RADIAN => new Radian(/** @scrutinizer ignore-type */ $measurement),
Loading history...
233 6668
            self::EPSG_MICRORADIAN => new MicroRadian($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\A...roRadian::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

233
            self::EPSG_MICRORADIAN => new MicroRadian(/** @scrutinizer ignore-type */ $measurement),
Loading history...
234 6632
            self::EPSG_DEGREE => new Degree($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\Angle\Degree::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

234
            self::EPSG_DEGREE => new Degree(/** @scrutinizer ignore-type */ $measurement),
Loading history...
235 3339
            self::EPSG_ARC_SECOND => new ArcSecond($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\A...rcSecond::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

235
            self::EPSG_ARC_SECOND => new ArcSecond(/** @scrutinizer ignore-type */ $measurement),
Loading history...
236 2386
            self::EPSG_MILLIARC_SECOND => new ArcSecond($measurement / 1000),
237 2341
            self::EPSG_GRAD => new Grad($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\Angle\Grad::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

237
            self::EPSG_GRAD => new Grad(/** @scrutinizer ignore-type */ $measurement),
Loading history...
238 2098
            self::EPSG_CENTESIMAL_SECOND => new CentesimalSecond($measurement),
0 ignored issues
show
Bug introduced by
It seems like $measurement can also be of type string; however, parameter $angle of PHPCoord\UnitOfMeasure\A...alSecond::__construct() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

238
            self::EPSG_CENTESIMAL_SECOND => new CentesimalSecond(/** @scrutinizer ignore-type */ $measurement),
Loading history...
239 2062
            self::EPSG_DEGREE_MINUTE_SECOND => Degree::fromDegreeMinuteSecond((string) $measurement),
240 2044
            self::EPSG_DEGREE_MINUTE_SECOND_HEMISPHERE => Degree::fromDegreeMinuteSecondHemisphere((string) $measurement),
241 2026
            self::EPSG_HEMISPHERE_DEGREE_MINUTE_SECOND => Degree::fromHemisphereDegreeMinuteSecond((string) $measurement),
242 2008
            self::EPSG_DEGREE_MINUTE => Degree::fromDegreeMinute((string) $measurement),
243 1990
            self::EPSG_DEGREE_MINUTE_HEMISPHERE => Degree::fromDegreeMinuteHemisphere((string) $measurement),
244 1972
            self::EPSG_HEMISPHERE_DEGREE_MINUTE => Degree::fromHemisphereDegreeMinute((string) $measurement),
245 1954
            self::EPSG_DEGREE_HEMISPHERE => Degree::fromDegreeHemisphere((string) $measurement),
246 1936
            self::EPSG_HEMISPHERE_DEGREE => Degree::fromHemisphereDegree((string) $measurement),
247 1918
            self::EPSG_SEXAGESIMAL_DMS => Degree::fromSexagesimalDMS((string) $measurement),
248 6704
            default => throw new UnknownUnitOfMeasureException($srid),
249
        };
250
    }
251
252 117
    public static function getSupportedSRIDs(): array
253
    {
254 117
        if (!self::$supportedCache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::supportedCache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
255
            foreach (static::$sridData as $srid => $data) {
256
                self::$supportedCache[$srid] = $data['name'];
257
            }
258
        }
259
260 117
        return self::$supportedCache;
261
    }
262
263 54
    public static function registerCustomUnit(string $srid, string $name, string $implementingClassFQCN): void
264
    {
265 54
        self::$customSridData[$srid] = ['name' => $name, 'fqcn' => $implementingClassFQCN];
266 54
        self::getSupportedSRIDs(); // init cache if not already
267 54
        self::$supportedCache[$srid] = $name; // update cache
268
    }
269
270 4328
    public static function convert(self $angle, string $targetSRID): self
271
    {
272 4328
        $conversionRatio = static::makeUnit(1, $targetSRID)->asRadians()->getValue();
273
274 4328
        return self::makeUnit($angle->asRadians()->getValue() / $conversionRatio, $targetSRID);
275
    }
276
277 36
    public function __toString(): string
278
    {
279 36
        return (string) $this->getValue();
280
    }
281
}
282