| Total Complexity | 47 |
| Total Lines | 284 |
| Duplicated Lines | 0 % |
| Changes | 4 | ||
| Bugs | 0 | Features | 0 |
Complex classes like GeoJSON 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 GeoJSON, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 21 | class GeoJSON implements GeoAdapter |
||
| 22 | { |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Given an object or a string, return a Geometry |
||
| 26 | * |
||
| 27 | * @param string $input The GeoJSON string or object |
||
| 28 | * @return Geometry |
||
| 29 | * @throws \Exception |
||
| 30 | */ |
||
| 31 | public function read(string $input): Geometry |
||
| 32 | { |
||
| 33 | #if (is_string($input)) { |
||
| 34 | $input = json_decode($input); |
||
| 35 | #} |
||
| 36 | if (!is_object($input)) { |
||
| 37 | throw new \Exception('Invalid JSON'); |
||
| 38 | } |
||
| 39 | if (!isset($input->type) || !is_string($input->type)) { |
||
| 40 | throw new \Exception('Invalid GeoJSON'); |
||
| 41 | } |
||
| 42 | |||
| 43 | /** @var \stdClass $input */ |
||
| 44 | return $this->parseJSONObjects($input); |
||
| 45 | } |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @param \stdClass $input stdClass |
||
| 49 | * @return Geometry |
||
| 50 | */ |
||
| 51 | private function parseJSONObjects(\stdClass $input): Geometry |
||
| 52 | { |
||
| 53 | // FeatureCollection |
||
| 54 | if ($input->type === 'FeatureCollection') { |
||
| 55 | $geometries = []; |
||
| 56 | foreach ($input->features as $feature) { |
||
| 57 | $geometries[] = $this->parseJSONObjects($feature); |
||
| 58 | } |
||
| 59 | return geoPHP::buildGeometry($geometries); |
||
| 60 | } |
||
| 61 | |||
| 62 | // Feature |
||
| 63 | if ($input->type === 'Feature') { |
||
| 64 | return $this->geoJSONFeatureToGeometry($input); |
||
| 65 | } |
||
| 66 | |||
| 67 | // Geometry |
||
| 68 | return $this->geoJSONObjectToGeometry($input); |
||
| 69 | } |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @param \stdClass $input |
||
| 73 | * @return int|null |
||
| 74 | */ |
||
| 75 | private function getSRID($input) |
||
| 76 | { |
||
| 77 | if (isset($input->crs->properties->name)) { |
||
| 78 | $m = []; |
||
| 79 | // parse CRS codes in forms "EPSG:1234" and "urn:ogc:def:crs:EPSG::1234" |
||
| 80 | preg_match('#EPSG[:]+(\d+)#', $input->crs->properties->name, $m); |
||
| 81 | return isset($m[1]) ? (int) $m[1] : null; |
||
| 82 | } |
||
| 83 | |||
| 84 | return null; |
||
| 85 | } |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @param \stdClass $obj |
||
| 89 | * @return Geometry |
||
| 90 | * @throws \Exception |
||
| 91 | */ |
||
| 92 | private function geoJSONFeatureToGeometry($obj): Geometry |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * @param \stdClass $obj |
||
| 106 | * @return Geometry |
||
| 107 | * @throws \Exception |
||
| 108 | */ |
||
| 109 | private function geoJSONObjectToGeometry($obj): Geometry |
||
| 110 | { |
||
| 111 | $type = $obj->type; |
||
| 112 | |||
| 113 | if ($type === 'GeometryCollection') { |
||
| 114 | return $this->geoJSONObjectToGeometryCollection($obj); |
||
| 115 | } |
||
| 116 | $method = 'arrayTo' . $type; |
||
| 117 | /** @var GeometryCollection $geometry */ |
||
| 118 | $geometry = $this->$method($obj->coordinates); |
||
| 119 | $geometry->setSRID($this->getSRID($obj)); |
||
| 120 | |||
| 121 | return $geometry; |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * @param array<int|float> $coordinates Array of coordinates |
||
| 126 | * @return Point |
||
| 127 | */ |
||
| 128 | private function arrayToPoint(array $coordinates): Point |
||
| 139 | } |
||
| 140 | } |
||
| 141 | |||
| 142 | /** |
||
| 143 | * @param array<array> $array |
||
| 144 | * @return LineString |
||
| 145 | */ |
||
| 146 | private function arrayToLineString(array $array): LineString |
||
| 147 | { |
||
| 148 | $points = []; |
||
| 149 | foreach ($array as $componentArray) { |
||
| 150 | $points[] = $this->arrayToPoint($componentArray); |
||
| 151 | } |
||
| 152 | return new LineString($points); |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @param array<array> $array |
||
| 157 | * @return Polygon |
||
| 158 | */ |
||
| 159 | private function arrayToPolygon(array $array): Polygon |
||
| 160 | { |
||
| 161 | $lines = []; |
||
| 162 | foreach ($array as $componentArray) { |
||
| 163 | $lines[] = $this->arrayToLineString($componentArray); |
||
| 164 | } |
||
| 165 | return new Polygon($lines); |
||
| 166 | } |
||
| 167 | |||
| 168 | /** |
||
| 169 | * @noinspection PhpUnusedPrivateMethodInspection |
||
| 170 | * @param array<array> $array |
||
| 171 | * @return MultiPoint |
||
| 172 | */ |
||
| 173 | private function arrayToMultiPoint(array $array): MultiPoint |
||
| 174 | { |
||
| 175 | $points = []; |
||
| 176 | foreach ($array as $componentArray) { |
||
| 177 | $points[] = $this->arrayToPoint($componentArray); |
||
| 178 | } |
||
| 179 | return new MultiPoint($points); |
||
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * @noinspection PhpUnusedPrivateMethodInspection |
||
| 184 | * @param array<array> $array |
||
| 185 | * @return MultiLineString |
||
| 186 | */ |
||
| 187 | private function arrayToMultiLineString(array $array): MultiLineString |
||
| 188 | { |
||
| 189 | $lines = []; |
||
| 190 | foreach ($array as $componentArray) { |
||
| 191 | $lines[] = $this->arrayToLineString($componentArray); |
||
| 192 | } |
||
| 193 | return new MultiLineString($lines); |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * @noinspection PhpUnusedPrivateMethodInspection |
||
| 198 | * @param array<array> $array |
||
| 199 | * @return MultiPolygon |
||
| 200 | */ |
||
| 201 | private function arrayToMultiPolygon(array $array): MultiPolygon |
||
| 208 | } |
||
| 209 | |||
| 210 | /** |
||
| 211 | * @param \stdClass $obj |
||
| 212 | * @throws \Exception |
||
| 213 | * @return GeometryCollection |
||
| 214 | */ |
||
| 215 | private function geoJSONObjectToGeometryCollection($obj): GeometryCollection |
||
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Serializes an object into a geojson string |
||
| 234 | * |
||
| 235 | * @param Geometry $geometry The object to serialize |
||
| 236 | * @return string The GeoJSON string |
||
| 237 | */ |
||
| 238 | public function write(Geometry $geometry): string |
||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Creates a geoJSON array |
||
| 246 | * |
||
| 247 | * If the root geometry is a GeometryCollection and any of its geometries has data, |
||
| 248 | * the root element will be a FeatureCollection with Feature elements (with the data). |
||
| 249 | * If the root geometry has data, it will be included in a Feature object that contains the data. |
||
| 250 | * |
||
| 251 | * The geometry should have geographical coordinates since CRS support has been removed from from geoJSON specification (RFC 7946) |
||
| 252 | * The geometry should'nt be measured, since geoJSON specification (RFC 7946) only supports the dimensional positions. |
||
| 253 | * |
||
| 254 | * @param Geometry|GeometryCollection $geometry |
||
| 255 | * @param bool $isRoot Is geometry the root geometry? |
||
| 256 | * @return array<string, mixed> |
||
| 257 | */ |
||
| 258 | public function getArray(Geometry $geometry, bool $isRoot = true): array |
||
| 305 | } |
||
| 306 | } |
||
| 307 |