Total Complexity | 71 |
Total Lines | 364 |
Duplicated Lines | 0 % |
Changes | 4 | ||
Bugs | 0 | Features | 1 |
Complex classes like WKT often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WKT, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class WKT implements GeoAdapter |
||
19 | { |
||
20 | |||
21 | /** |
||
22 | * @var bool true if geometry has z-values |
||
23 | */ |
||
24 | protected $hasZ = false; |
||
25 | |||
26 | /** |
||
27 | * @var bool true if geometry has m-values |
||
28 | */ |
||
29 | protected $measured = false; |
||
30 | |||
31 | /** |
||
32 | * Determines if the given typeString is a valid WKT geometry type |
||
33 | * |
||
34 | * @param string $typeString Type to find, eg. "Point", or "LineStringZ" |
||
35 | * @return string|bool The geometry type if found or false |
||
36 | */ |
||
37 | public static function isWktType(string $typeString) |
||
38 | { |
||
39 | foreach (geoPHP::getGeometryList() as $geom => $type) { |
||
40 | if (strtolower((substr($typeString, 0, strlen($geom)))) == $geom) { |
||
41 | return $type; |
||
42 | } |
||
43 | } |
||
44 | return false; |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * Read WKT string into geometry objects |
||
49 | * |
||
50 | * @param string $wkt A WKT string |
||
51 | * @return Geometry |
||
52 | * @throws \Exception |
||
53 | */ |
||
54 | public function read(string $wkt): Geometry |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * @param string $wkt |
||
88 | * @return Geometry|null |
||
89 | * @throws \Exception |
||
90 | */ |
||
91 | private function parseTypeAndGetData(string $wkt) |
||
92 | { |
||
93 | // geometry type is the first word |
||
94 | $m = []; |
||
95 | if (preg_match('#^([a-z]*)#i', $wkt, $m)) { |
||
96 | $geometryType = $this->isWktType($m[1]); |
||
97 | |||
98 | $dataString = 'EMPTY'; |
||
99 | if ($geometryType) { |
||
100 | if (preg_match('#(z{0,1})(m{0,1})\s*\((.*)\)$#i', $wkt, $m)) { |
||
101 | $this->hasZ = $m[1]; |
||
102 | $this->measured = $m[2] ?: null; |
||
103 | $dataString = $m[3] ?: $dataString; |
||
104 | } |
||
105 | $method = 'parse' . $geometryType; |
||
106 | return call_user_func([$this, $method], $dataString); |
||
107 | } |
||
108 | throw new \Exception('Invalid WKT type "' . $m[1] . '"'); |
||
109 | } |
||
110 | throw new \Exception('Cannot parse WKT'); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @param string $dataString |
||
115 | * @return Point |
||
116 | */ |
||
117 | private function parsePoint(string $dataString): Point |
||
118 | { |
||
119 | $dataString = trim($dataString); |
||
120 | |||
121 | // If it's marked as empty, then return an empty point |
||
122 | if ($dataString === 'EMPTY') { |
||
123 | return new Point(); |
||
124 | } |
||
125 | |||
126 | $z = $m = null; |
||
127 | $parts = explode(' ', $dataString); |
||
128 | if (isset($parts[2])) { |
||
129 | if ($this->measured) { |
||
130 | $m = $parts[2]; |
||
131 | } else { |
||
132 | $z = $parts[2]; |
||
133 | } |
||
134 | } |
||
135 | if (isset($parts[3])) { |
||
136 | $m = $parts[3]; |
||
137 | } |
||
138 | return new Point($parts[0], $parts[1], $z, $m); |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * @param string $dataString |
||
143 | * @return LineString |
||
144 | */ |
||
145 | private function parseLineString(string $dataString): LineString |
||
146 | { |
||
147 | // If it's marked as empty, then return an empty line |
||
148 | if ($dataString === 'EMPTY') { |
||
149 | return new LineString(); |
||
150 | } |
||
151 | |||
152 | $points = []; |
||
153 | foreach (explode(',', $dataString) as $part) { |
||
154 | $points[] = $this->parsePoint($part); |
||
155 | } |
||
156 | return new LineString($points); |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * @param string $dataString |
||
161 | * @return Polygon |
||
162 | */ |
||
163 | private function parsePolygon(string $dataString): Polygon |
||
164 | { |
||
165 | // If it's marked as empty, then return an empty polygon |
||
166 | if ($dataString === 'EMPTY') { |
||
167 | return new Polygon(); |
||
168 | } |
||
169 | |||
170 | $lines = []; |
||
171 | $m = []; |
||
172 | if (preg_match_all('/\(([^)(]*)\)/', $dataString, $m)) { |
||
173 | foreach ($m[1] as $part) { |
||
174 | $lines[] = $this->parseLineString($part); |
||
175 | } |
||
176 | } |
||
177 | return new Polygon($lines); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @noinspection PhpUnusedPrivateMethodInspection |
||
182 | * @param string $dataString |
||
183 | * @return MultiPoint |
||
184 | */ |
||
185 | private function parseMultiPoint(string $dataString): MultiPoint |
||
186 | { |
||
187 | // If it's marked as empty, then return an empty MultiPoint |
||
188 | if ($dataString === 'EMPTY') { |
||
189 | return new MultiPoint(); |
||
190 | } |
||
191 | |||
192 | $points = []; |
||
193 | /* Should understand both forms: |
||
194 | * MULTIPOINT ((1 2), (3 4)) |
||
195 | * MULTIPOINT (1 2, 3 4) |
||
196 | */ |
||
197 | foreach (explode(',', $dataString) as $part) { |
||
198 | $points[] = $this->parsePoint(trim($part, ' ()')); |
||
199 | } |
||
200 | return new MultiPoint($points); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @noinspection PhpUnusedPrivateMethodInspection |
||
205 | * @param string $dataString |
||
206 | * @return MultiLineString |
||
207 | */ |
||
208 | private function parseMultiLineString(string $dataString): MultiLineString |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * @noinspection PhpUnusedPrivateMethodInspection |
||
226 | * @param string $dataString |
||
227 | * @return MultiPolygon |
||
228 | */ |
||
229 | private function parseMultiPolygon(string $dataString): MultiPolygon |
||
230 | { |
||
231 | // If it's marked as empty, then return an empty multi-polygon |
||
232 | if ($dataString === 'EMPTY') { |
||
233 | return new MultiPolygon(); |
||
234 | } |
||
235 | |||
236 | $polygons = []; |
||
237 | $m = []; |
||
238 | if (preg_match_all('/(\(\([^(].+?\)\)|EMPTY)/', $dataString, $m)) { |
||
239 | foreach ($m[0] as $part) { |
||
240 | $polygons[] = $this->parsePolygon($part); |
||
241 | } |
||
242 | } |
||
243 | return new MultiPolygon($polygons); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * @noinspection PhpUnusedPrivateMethodInspection |
||
248 | * @param string $dataString |
||
249 | * @return GeometryCollection |
||
250 | */ |
||
251 | private function parseGeometryCollection(string $dataString): GeometryCollection |
||
252 | { |
||
253 | // If it's marked as empty, then return an empty geom-collection |
||
254 | if ($dataString === 'EMPTY') { |
||
255 | return new GeometryCollection(); |
||
256 | } |
||
257 | |||
258 | $geometries = []; |
||
259 | $m = []; |
||
260 | while (strlen($dataString) > 0) { |
||
261 | if ($dataString[0] === ',') { |
||
262 | $dataString = substr($dataString, 1); |
||
263 | } |
||
264 | // Matches the first balanced parenthesis group (or term EMPTY) |
||
265 | preg_match( |
||
266 | '/\((?>[^()]+|(?R))*\)|EMPTY/', |
||
267 | $dataString, |
||
268 | $m, |
||
269 | PREG_OFFSET_CAPTURE |
||
270 | ); |
||
271 | if (!isset($m[0])) { |
||
272 | // something weird happened, we stop here before running in an infinite loop |
||
273 | break; |
||
274 | } |
||
275 | $cutPosition = strlen($m[0][0]) + $m[0][1]; |
||
276 | $geometries[] = $this->parseTypeAndGetData(trim(substr($dataString, 0, $cutPosition))); |
||
277 | $dataString = trim(substr($dataString, $cutPosition)); |
||
278 | } |
||
279 | |||
280 | return new GeometryCollection($geometries); |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Serialize geometries into a WKT string. |
||
285 | * |
||
286 | * @param Geometry $geometry |
||
287 | * @return string The WKT string representation of the input geometries |
||
288 | */ |
||
289 | public function write(Geometry $geometry): string |
||
290 | { |
||
291 | // If geos is installed, then we take a shortcut and let it write the WKT |
||
292 | if (geoPHP::geosInstalled()) { |
||
293 | /** @noinspection PhpUndefinedClassInspection */ |
||
294 | $writer = new \GEOSWKTWriter(); |
||
295 | /** @noinspection PhpUndefinedMethodInspection */ |
||
296 | $writer->setRoundingPrecision(14); |
||
297 | /** @noinspection PhpUndefinedMethodInspection */ |
||
298 | $writer->setTrim(true); |
||
299 | /** @noinspection PhpUndefinedMethodInspection */ |
||
300 | return $writer->write($geometry->getGeos()); |
||
301 | } |
||
302 | |||
303 | $this->measured = $geometry->isMeasured(); |
||
304 | $this->hasZ = $geometry->hasZ(); |
||
305 | |||
306 | if ($geometry->isEmpty()) { |
||
307 | return strtoupper($geometry->geometryType()) . ' EMPTY'; |
||
308 | } |
||
309 | |||
310 | $data = $this->extractData($geometry); |
||
311 | if (!empty($data)) { |
||
312 | $extension = ''; |
||
313 | if ($this->hasZ) { |
||
314 | $extension .= 'Z'; |
||
315 | } |
||
316 | if ($this->measured) { |
||
317 | $extension .= 'M'; |
||
318 | } |
||
319 | return strtoupper($geometry->geometryType()) . ($extension ? ' ' . $extension : '') . ' (' . $data . ')'; |
||
320 | } |
||
321 | return ''; |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * Extract geometry to a WKT string |
||
326 | * |
||
327 | * @param Geometry|Collection $geometry A Geometry object |
||
328 | * @return string |
||
329 | */ |
||
330 | public function extractData($geometry): string |
||
382 | } |
||
383 | } |
||
384 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.