Passed
Push — master ( 1617fd...dd9083 )
by Andreas
05:31
created

GIS::to_ewkt()   C

Complexity

Conditions 12
Paths 193

Size

Total Lines 47
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12

Importance

Changes 0
Metric Value
cc 12
eloc 36
nc 193
nop 1
dl 0
loc 47
ccs 21
cts 21
cp 1
crap 12
rs 6.1916
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Smindel\GIS;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Core\Config\Configurable;
7
use SilverStripe\Core\Injector\Injectable;
8
use SilverStripe\ORM\DB;
9
use proj4php\Proj4php;
10
use proj4php\Proj;
11
use proj4php\Point;
12
use Exception;
13
14
class GIS
15
{
16
    use Configurable;
17
18
    use Injectable;
19
20
    private static $default_srid = 4326;
0 ignored issues
show
introduced by
The private property $default_srid is not used, and could be removed.
Loading history...
21
22
    const EWKT_PATTERN = '/^SRID=(\d+);(([A-Z]+)\s*(\(.+\)))$/i';
23
24
    const TYPES = [
25
        'point' => 'Point',
26
        'linestring' => 'LineString',
27
        'polygon' => 'Polygon',
28
        'multipolygon' => 'MultiPolygon',
29
    ];
30
31 8
    public static function of($dataObjectClass)
32
    {
33 8
        if ($field = $dataObjectClass::config()->get('default_geo_field')) {
34
            return $field;
35
        }
36
37 8
        foreach ($dataObjectClass::config()->get('db') ?: [] as $field => $type) {
38 8
            if ($type == 'Geography' || $type == 'Geometry') {
39 8
                return $field;
40
            }
41
        }
42
    }
43
44 12
    public static function to_ewkt($array)
45
    {
46 12
        if (is_string($array)) return $array;
47
48 12
        if ($array instanceof Geography) $array = $array->getValue();
0 ignored issues
show
Bug introduced by
The type Smindel\GIS\Geography 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...
49
50 12
        $type = isset($array['type']) ? strtoupper($array['type']) : null;
51 12
        $srid = isset($array['srid']) ? $array['srid'] : Config::inst()->get(self::class, 'default_srid');
52 12
        $array = isset($array['coordinates']) ? $array['coordinates'] : $array;
53
54 12
        if (!$type) {
55 5
            if (is_numeric($array[0])) {
56 5
                $type = 'POINT';
57 1
            } elseif (is_numeric($array[0][0])) {
58 1
                $type = 'LINESTRING';
59 1
            } elseif (is_numeric($array[0][0][0])) {
60 1
                $type = 'POLYGON';
61 1
            } elseif (is_numeric($array[0][0][0][0])) {
62 1
                $type = 'MULTIPOLYGON';
63
            }
64
        }
65
66 12
        $coords2 = preg_replace([
67 12
            '/(?<=\d),(?=-|\d)/',
68
            '/\[\[\[\[/',
69
            '/\]\]\]\]/',
70
            '/\[\[\[/',
71
            '/\]\]\]/',
72
            '/\[\[/',
73
            '/\]\]/',
74
            '/\[/',
75
            '/\]/',
76
        ], [
77 12
            ' ',
78
            '(((',
79
            ')))',
80
            '((',
81
            '))',
82
            '(',
83
            ')',
84
            '',
85
            '',
86 12
        ], json_encode($array));
87
88 12
        if ($type == 'POINT') $coords2 = "($coords2)";
89
90 12
        return sprintf('SRID=%d;%s%s', $srid, $type, $coords2);
91
    }
92
93 11
    public static function ewkt_to_array($ewkt)
94
    {
95 11
        if (preg_match(self::EWKT_PATTERN, $ewkt, $matches)) {
96 11
            $coords = str_replace(['(', ')'], ['[', ']'], preg_replace('/([\d\.-]+)\s+([\d\.-]+)/', "[$1,$2]", $matches[4]));
97 11
            if (strtolower($matches[3]) != 'point') {
98 6
                $coords = "[$coords]";
99
            }
100
101
            return [
102 11
                'srid' => $matches[1],
103 11
                'type' => self::TYPES[strtolower($matches[3])],
104 11
                'coordinates' => json_decode($coords, true)[0],
105
            ];
106
        }
107
    }
108
109 14
    public static function split_ewkt($ewkt, $fallbackSrid = null)
110
    {
111 14
        $fallbackSrid = $fallbackSrid ?: Config::inst()->get(self::class, 'default_srid');
112
113 14
        if (preg_match(GIS::EWKT_PATTERN, $ewkt, $matches)) {
114 14
            $wkt = $matches[2];
115 14
            $srid = (int)$matches[1];
116
        } else {
117
            $wkt = $ewkt;
118
            $srid = (int)$fallbackSrid;
119
        }
120 14
        return [$wkt, $srid];
121
    }
122
123 5
    public static function get_type($geometry, $useBestGuess = false)
124
    {
125 5
        if (is_array($geometry) && isset($geometry['type'])) {
126
            return self::TYPES[strtolower($geometry['type'])];
127 5
        } elseif (is_array($geometry)) {
128 1
            $geometry = self::to_ewkt($geometry, null, $useBestGuess);
0 ignored issues
show
Unused Code introduced by
The call to Smindel\GIS\GIS::to_ewkt() has too many arguments starting with null. ( Ignorable by Annotation )

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

128
            /** @scrutinizer ignore-call */ 
129
            $geometry = self::to_ewkt($geometry, null, $useBestGuess);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
129
        }
130 5
        if (preg_match(
131 5
            '/;(' . implode('|', array_keys(self::TYPES)) . ')\(/i',
132 5
            strtolower(substr($geometry, 8, 30)),
133 5
            $matches
134
        )) {
135 5
            return self::TYPES[$matches[1]];
136
        }
137
    }
138
139
    /**
140
     * reproject an array representation of a geometry to the given srid
141
     */
142 7
    public static function reproject_array($array, $toSrid = 4326)
143
    {
144 7
        if (isset($array['srid'])) {
145 7
            $fromSrid = $array['srid'];
146 7
            $fromCoordinates = $array['coordinates'];
147 7
            $type = $array['type'];
148
        } else {
149
            $fromSrid = Config::inst()->get(self::class, 'default_srid') ?: 4326;
150
            $fromCoordinates = $array;
151
            $type = is_array($array[0]) ? (is_array($array[0][0]) ? 'Polygon' : 'LineString') : 'Point';
152
        }
153
154 7
        if ($fromSrid != $toSrid) {
155 1
            $fromProj = self::get_proj4($fromSrid);
156 1
            $toProj = self::get_proj4($toSrid);
157 1
            $toCoordinates = self::reproject($fromCoordinates, $fromProj, $toProj);
158
        } else {
159 6
            $toCoordinates = $fromCoordinates;
160
        }
161
162
        return [
163 7
            'srid' => $toSrid,
164 7
            'type' => $type,
165 7
            'coordinates' => $toCoordinates,
166
        ];
167
    }
168
169
    /**
170
     * @var proj4php instance
171
     */
172
    protected static $proj4;
173
174 1
    protected static function get_proj4($srid)
175
    {
176 1
        self::$proj4 = self::$proj4 ?: new Proj4php();
177
178 1
        if (!self::$proj4->hasDef('EPSG:' . $srid)) {
179 1
            $projDefs = Config::inst()->get(self::class, 'projections');
180 1
            if (!isset($projDefs[$srid])) {
181
                throw new Exception("Cannot use unregistered SRID $srid. Register it's <a href=\"http://spatialreference.org/ref/epsg/$srid/proj4/\">PROJ.4 definition</a> in GIS::projections.");
182
            }
183 1
            self::$proj4->addDef('EPSG:' . $srid, $projDefs[$srid]);
184
        }
185
186 1
        return new Proj('EPSG:' . $srid, self::$proj4);
187
    }
188
189
    protected static function reproject($coordinates, $fromProj, $toProj)
190
    {
191 1
        return self::each($coordinates, function($coordinate) use ($fromProj, $toProj) {
192 1
            return array_slice(self::$proj4->transform($toProj, new Point($coordinate[0], $coordinate[1], $fromProj))->toArray(), 0, 2);
193 1
        });
194
    }
195
196 5
    public static function each($coordinates, $callback)
197
    {
198 5
        if (isset($coordinates['coordinates'])) {
199 4
            $coordinates = $coordinates['coordinates'];
200
        }
201
202 5
        if (is_array($coordinates[0])) {
203 2
            foreach ($coordinates as &$coordinate) {
204 2
                $coordinate = self::each($coordinate, $callback);
205
            }
206 2
            return $coordinates;
207
        }
208
209 5
        return $callback($coordinates);
210
    }
211
212 1
    public static function distance($geo1, $geo2)
213
    {
214 1
        return DB::query('select ' . DB::get_schema()->translateDistanceQuery($geo1, $geo2))->value();
0 ignored issues
show
Bug introduced by
The method translateDistanceQuery() does not exist on SilverStripe\ORM\Connect\DBSchemaManager. It seems like you code against a sub-type of SilverStripe\ORM\Connect\DBSchemaManager such as Smindel\GIS\ORM\MySQLGISSchemaManager. ( Ignorable by Annotation )

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

214
        return DB::query('select ' . DB::get_schema()->/** @scrutinizer ignore-call */ translateDistanceQuery($geo1, $geo2))->value();
Loading history...
215
    }
216
}
217