Passed
Push — master ( 58539d...96b0d3 )
by Mark
01:31
created

GeoRSS   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 44
eloc 129
dl 0
loc 229
rs 8.8798
c 0
b 0
f 0

How to fix   Complexity   

Complex Class

Complex classes like GeoRSS 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 GeoRSS, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * Copyright (c) Patrick Hayes
4
 *
5
 * This code is open-source and licenced under the Modified BSD License.
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
/**
11
 * PHP Geometry/GeoRSS encoder/decoder
12
 */
13
class GeoRSS extends GeoAdapter
14
{
15
  private $namespace = FALSE;
16
  private $nss = ''; // Name-space string. eg 'georss:'
17
  
18
  /**
19
   * Read GeoRSS string into geometry objects
20
   *
21
   * @param string $georss - an XML feed containing geoRSS
22
   *
23
   * @return Geometry|GeometryCollection
24
   */
25
  public function read($gpx) {
26
    return $this->geomFromText($gpx);
27
  }
28
29
  /**
30
   * Serialize geometries into a GeoRSS string.
31
   *
32
   * @param Geometry $geometry
33
   *
34
   * @return string The georss string representation of the input geometries
35
   */
36
  public function write(Geometry $geometry, $namespace = FALSE) {
37
    if ($namespace) {
38
      $this->namespace = $namespace;
39
      $this->nss = $namespace.':';    
40
    }
41
    return $this->geometryToGeoRSS($geometry);
42
  }
43
  
44
  public function geomFromText($text) {
45
    // Change to lower-case, strip all CDATA, and de-namespace
46
    $text = strtolower($text);
47
    $text = preg_replace('/<!\[cdata\[(.*?)\]\]>/s','',$text);
48
        
49
    // Load into DOMDOcument
50
    $xmlobj = new DOMDocument();
51
    @$xmlobj->loadXML($text);
52
    if ($xmlobj === false) {
53
      throw new Exception("Invalid GeoRSS: ". $text);
54
    }
55
    
56
    $this->xmlobj = $xmlobj;
57
    try {
58
      $geom = $this->geomFromXML();
59
    } catch(InvalidText $e) {
60
        throw new Exception("Cannot Read Geometry From GeoRSS: ". $text);
61
    } catch(Exception $e) {
62
        throw $e;
63
    }
64
65
    return $geom;
66
  }
67
  
68
  protected function geomFromXML() {
69
    $geometries = array();
70
    $geometries = array_merge($geometries, $this->parsePoints());
71
    $geometries = array_merge($geometries, $this->parseLines());
72
    $geometries = array_merge($geometries, $this->parsePolygons());
73
    $geometries = array_merge($geometries, $this->parseBoxes());
74
    $geometries = array_merge($geometries, $this->parseCircles());
75
    
76
    if (empty($geometries)) {
77
      throw new Exception("Invalid / Empty GeoRSS");
78
    }
79
    
80
    return geoPHP::geometryReduce($geometries); 
81
  }
82
  
83
  protected function getPointsFromCoords($string) {
84
    $coords = array();
85
86
    if (empty($string)) {
87
      return $coords;
88
    }
89
90
    $latlon = explode(' ',$string);
91
    foreach ($latlon as $key => $item) {
92
      if (!($key % 2)) {
93
        // It's a latitude
94
        $lat = $item;
95
      }
96
      else {
97
        // It's a longitude
98
        $lon = $item;
99
        $coords[] = new Point($lon, $lat);
100
      }
101
    }
102
    return $coords;
103
  }
104
  
105
  protected function parsePoints() {
106
    $points = array();
107
    $pt_elements = $this->xmlobj->getElementsByTagName('point');
108
    foreach ($pt_elements as $pt) {
109
      if ($pt->hasChildNodes()) {
110
        $point_array = $this->getPointsFromCoords(trim($pt->firstChild->nodeValue));
111
      }
112
      if (!empty($point_array)) {
113
        $points[] = $point_array[0];
114
      }
115
      else {
116
        $points[] = new Point();
117
      }
118
    }
119
    return $points;
120
  }
121
  
122
  protected function parseLines() {
123
    $lines = array();
124
    $line_elements = $this->xmlobj->getElementsByTagName('line');
125
    foreach ($line_elements as $line) {
126
      $components = $this->getPointsFromCoords(trim($line->firstChild->nodeValue));
127
      $lines[] = new LineString($components);
128
    }
129
    return $lines;
130
  }
131
  
132
  protected function parsePolygons() {
133
    $polygons = array();
134
    $poly_elements = $this->xmlobj->getElementsByTagName('polygon');
135
    foreach ($poly_elements as $poly) {
136
      if ($poly->hasChildNodes()) {
137
        $points = $this->getPointsFromCoords(trim($poly->firstChild->nodeValue));
138
        $exterior_ring = new LineString($points);
139
        $polygons[] = new Polygon(array($exterior_ring));
140
      }
141
      else {
142
        // It's an EMPTY polygon
143
        $polygons[] = new Polygon(); 
144
      }
145
    }
146
    return $polygons;
147
  }
148
  
149
  // Boxes are rendered into polygons
150
  protected function parseBoxes() {
151
    $polygons = array();
152
    $box_elements = $this->xmlobj->getElementsByTagName('box');
153
    foreach ($box_elements as $box) {
154
      $parts = explode(' ',trim($box->firstChild->nodeValue));
155
      $components = array(
156
        new Point($parts[3], $parts[2]),
157
        new Point($parts[3], $parts[0]),
158
        new Point($parts[1], $parts[0]),
159
        new Point($parts[1], $parts[2]),
160
        new Point($parts[3], $parts[2]),
161
      );
162
      $exterior_ring = new LineString($components);
163
      $polygons[] = new Polygon(array($exterior_ring));
164
    }
165
    return $polygons;
166
  }
167
168
  // Circles are rendered into points
169
  // @@TODO: Add good support once we have circular-string geometry support
170
  protected function parseCircles() {
171
    $points = array();
172
    $circle_elements = $this->xmlobj->getElementsByTagName('circle');
173
    foreach ($circle_elements as $circle) {
174
      $parts = explode(' ',trim($circle->firstChild->nodeValue));
175
      $points[] = new Point($parts[1], $parts[0]);
176
    }
177
    return $points;
178
  }
179
  
180
  protected function geometryToGeoRSS($geom) {
181
    $type = strtolower($geom->getGeomType());
182
    switch ($type) {
183
      case 'point':
184
        return $this->pointToGeoRSS($geom);
185
        break;
186
      case 'linestring':
187
        return $this->linestringToGeoRSS($geom);
188
        break;
189
      case 'polygon':
190
        return $this->PolygonToGeoRSS($geom);
191
        break;
192
      case 'multipoint':
193
      case 'multilinestring':
194
      case 'multipolygon':
195
      case 'geometrycollection':
196
        return $this->collectionToGeoRSS($geom);
197
        break;
198
    }
199
    return $output;
200
  }
201
  
202
  private function pointToGeoRSS($geom) {
203
    $out = '<'.$this->nss.'point>';
204
    if (!$geom->isEmpty()) {
205
      $out .= $geom->getY().' '.$geom->getX();
206
    }
207
    $out .= '</'.$this->nss.'point>';
208
    return $out;
209
  }
210
211
  private function linestringToGeoRSS($geom) {
212
    $output = '<'.$this->nss.'line>';
213
    foreach ($geom->getComponents() as $k => $point) {
214
      $output .= $point->getY().' '.$point->getX();
215
      if ($k < ($geom->numGeometries() -1)) $output .= ' ';
216
    }
217
    $output .= '</'.$this->nss.'line>';
218
    return $output;
219
  }
220
221
  private function polygonToGeoRSS($geom) {
222
    $output = '<'.$this->nss.'polygon>';
223
    $exterior_ring = $geom->exteriorRing();
224
    foreach ($exterior_ring->getComponents() as $k => $point) {
225
      $output .= $point->getY().' '.$point->getX();
226
      if ($k < ($exterior_ring->numGeometries() -1)) $output .= ' ';
227
    }
228
    $output .= '</'.$this->nss.'polygon>';
229
    return $output;
230
  }
231
  
232
  public function collectionToGeoRSS($geom) {
233
    $georss = '<'.$this->nss.'where>';
234
    $components = $geom->getComponents();
235
    foreach ($geom->getComponents() as $comp) {
236
      $georss .= $this->geometryToGeoRSS($comp);
237
    }
238
    
239
    $georss .= '</'.$this->nss.'where>';
240
    
241
    return $georss;
242
  }
243
244
}
245