Passed
Push — 4.x ( f9780c...a64886 )
by Doug
05:20
created

AutoConversion   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 95
Duplicated Lines 0 %

Test Coverage

Coverage 97.56%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 51
c 1
b 0
f 0
dl 0
loc 95
ccs 40
cts 41
cp 0.9756
rs 10
wmc 29

2 Methods

Rating   Name   Duplication   Size   Complexity  
D findOperationPath() 0 61 23
A convert() 0 21 6
1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord\CoordinateOperation;
10
11
use function abs;
12
use function array_filter;
13
use DateTimeImmutable;
14
use function in_array;
15
use PHPCoord\CoordinateReferenceSystem\CoordinateReferenceSystem;
16
use PHPCoord\Exception\UnknownConversionException;
17
use PHPCoord\Point;
18
use PHPCoord\UnitOfMeasure\Time\Year;
19
use function reset;
20
use function strpos;
21
use function usort;
22
23
trait AutoConversion
24
{
25 14
    public function convert(CoordinateReferenceSystem $to, bool $ignoreBoundaryRestrictions = false): Point
26
    {
27 14
        if ($this->getCRS() == $to) {
28 1
            return $this;
29
        }
30
31 13
        if (strpos($this->getCRS()->getSRID(), CoordinateReferenceSystem::CRS_SRID_PREFIX_EPSG) !== 0 || strpos($to->getSRID(), CoordinateReferenceSystem::CRS_SRID_PREFIX_EPSG) !== 0) {
32
            throw new UnknownConversionException('Automatic conversions are only supported for EPSG CRSs');
33
        }
34
35 13
        $point = $this;
36 13
        $path = $this->findOperationPath($to, $ignoreBoundaryRestrictions);
37
38 11
        $inReverse = reset($path)['source_crs'] !== $this->getCRS()->getSRID();
39
40 11
        foreach ($path as $step) {
41 11
            $target = CoordinateReferenceSystem::fromSRID($inReverse ? $step['source_crs'] : $step['target_crs']);
42 11
            $point = $point->performOperation($step['operation'], $target, $inReverse);
43
        }
44
45 11
        return $point;
46
    }
47
48 13
    protected function findOperationPath(CoordinateReferenceSystem $to, bool $ignoreBoundaryRestrictions): array
49
    {
50 13
        $candidates = [];
51 13
        foreach (CRSTransformations::getSupportedTransformations() as $transformation) {
52 13
            if ($transformation['source_crs'] === $this->getCRS()->getSRID() && $transformation['target_crs'] === $to->getSRID()) {
53 11
                $candidates[] = $transformation;
54 13
            } elseif ($transformation['target_crs'] === $this->getCRS()->getSRID() && $transformation['source_crs'] === $to->getSRID() && $transformation['reversible']) {
55 2
                $candidates[] = $transformation;
56
            }
57
        }
58
59 13
        $asGeog = $this->asGeographicValue();
60 13
        $lat = $asGeog->getLatitude()->asDegrees()->getValue();
61 13
        $long = $asGeog->getLongitude()->asDegrees()->getValue();
62 13
        $candidates = array_filter($candidates, function (array $candidate) use ($lat, $long, $ignoreBoundaryRestrictions) {
63 13
            $operation = CoordinateOperations::getOperationData($candidate['operation']);
0 ignored issues
show
Bug introduced by
The type PHPCoord\CoordinateOperation\CoordinateOperations was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
64 13
            $ok = true;
65
66 13
            if (!$ignoreBoundaryRestrictions) {
67
                //filter out operations that only operate outside this point
68 12
                if ($operation['bounding_box']['east'] < $operation['bounding_box']['west']) {
69 1
                    $ok = $ok && $lat <= $operation['bounding_box']['north'] && $lat >= $operation['bounding_box']['south'] && $long >= $operation['bounding_box']['west'] && $long <= ($operation['bounding_box']['east'] + 360);
70
                } else {
71 11
                    $ok = $ok && $lat <= $operation['bounding_box']['north'] && $lat >= $operation['bounding_box']['south'] && $long >= $operation['bounding_box']['west'] && $long <= $operation['bounding_box']['east'];
72
                }
73
            }
74
75
            //filter out operations that require an epoch if we don't have one
76 13
            if (!$this->getCoordinateEpoch() && in_array($operation['method'], [
77
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_COORDINATE_FRAME_ROTATION_GEOCEN,
78
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_COORDINATE_FRAME_ROTATION_GEOG2D,
79
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_COORDINATE_FRAME_ROTATION_GEOG3D,
80
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_POSITION_VECTOR_TFM_GEOCENTRIC,
81
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_POSITION_VECTOR_TFM_GEOG2D,
82
                    CoordinateOperationMethods::EPSG_TIME_DEPENDENT_POSITION_VECTOR_TFM_GEOG3D,
83
                    CoordinateOperationMethods::EPSG_TIME_SPECIFIC_COORDINATE_FRAME_ROTATION_GEOCEN,
84
                    CoordinateOperationMethods::EPSG_TIME_SPECIFIC_POSITION_VECTOR_TRANSFORM_GEOCEN,
85
                ], true)) {
86 1
                $ok = false;
87
            }
88
89
            //filter out operations that require a specific epoch
90 13
            if ($this->getCoordinateEpoch() && in_array($operation['method'], [
91
                    CoordinateOperationMethods::EPSG_TIME_SPECIFIC_COORDINATE_FRAME_ROTATION_GEOCEN,
92
                    CoordinateOperationMethods::EPSG_TIME_SPECIFIC_POSITION_VECTOR_TRANSFORM_GEOCEN,
93
                ], true)) {
94 1
                $params = CoordinateOperationParams::getParamData($candidate['operation']);
0 ignored issues
show
Bug introduced by
The type PHPCoord\CoordinateOpera...ordinateOperationParams was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
95 1
                $pointEpoch = Year::fromDateTime($this->getCoordinateEpoch());
96 1
                $ok = $ok && (abs($pointEpoch->getValue() - $params['Transformation reference epoch']['value']) <= 0.001);
97
            }
98
99 13
            return $ok;
100 13
        });
101
102 13
        usort($candidates, static function (array $a, array $b) { return $a['accuracy'] <=> $b['accuracy']; });
103
104 13
        if (!$candidates) {
105 2
            throw new UnknownConversionException('Unable to perform conversion, please file a bug if you think this is incorrect');
106
        }
107
108 11
        return [reset($candidates)];
109
    }
110
111
    abstract public function getCRS(): CoordinateReferenceSystem;
112
113
    abstract public function getCoordinateEpoch(): ?DateTimeImmutable;
114
115
    abstract public function asGeographicValue(): GeographicValue;
116
117
    abstract protected function performOperation(string $srid, CoordinateReferenceSystem $to, bool $inReverse): Point;
118
}
119