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

Collection   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 75
eloc 128
dl 0
loc 300
rs 2.4
c 0
b 0
f 0

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * Collection: Abstract class for compound geometries
5
 *
6
 * A geometry is a collection if it is made up of other
7
 * component geometries. Therefore everything but a Point
8
 * is a Collection. For example a LingString is a collection
9
 * of Points. A Polygon is a collection of LineStrings etc.
10
 */
11
abstract class Collection extends Geometry
12
{
13
  public $components = array();
14
15
  /**
16
   * Constructor: Checks and sets component geometries
17
   *
18
   * @param array $components array of geometries
19
   */
20
  public function __construct($components = array()) {
21
    if (!is_array($components)) {
22
      throw new Exception("Component geometries must be passed as an array");
23
    }
24
    foreach ($components as $component) {
25
      if ($component instanceof Geometry) {
26
        $this->components[] = $component;
27
      }
28
      else {
29
        throw new Exception("Cannot create a collection with non-geometries");
30
      }
31
    }
32
  }
33
34
  /**
35
   * Returns Collection component geometries
36
   *
37
   * @return array
38
   */
39
  public function getComponents() {
40
    return $this->components;
41
  }
42
43
  /*
44
   * Author : Adam Cherti
45
   *
46
   * inverts x and y coordinates
47
   * Useful for old data still using lng lat
48
   *
49
   * @return void
50
   *
51
   * */
52
  public function invertxy()
53
  {
54
	for($i=0;$i<count($this->components);$i++)
55
	{
56
		if( method_exists($this->components[$i], 'invertxy' ) )
57
			$this->components[$i]->invertxy();
58
	}
59
  }
60
61
  public function centroid() {
62
    if ($this->isEmpty()) return NULL;
63
64
    if ($this->geos()) {
65
      $geos_centroid = $this->geos()->centroid();
66
      if ($geos_centroid->typeName() == 'Point') {
67
        return geoPHP::geosToGeometry($this->geos()->centroid());
68
      }
69
    }
70
71
    // As a rough estimate, we say that the centroid of a colletion is the centroid of it's envelope
72
    // @@TODO: Make this the centroid of the convexHull
73
    // Note: Outside of polygons, geometryCollections and the trivial case of points, there is no standard on what a "centroid" is
74
    $centroid = $this->envelope()->centroid();
75
76
    return $centroid;
77
  }
78
79
  public function getBBox() {
80
    if ($this->isEmpty()) return NULL;
81
82
    if ($this->geos()) {
83
      $envelope = $this->geos()->envelope();
84
      if ($envelope->typeName() == 'Point') {
85
        return geoPHP::geosToGeometry($envelope)->getBBOX();
86
      }
87
88
      $geos_ring = $envelope->exteriorRing();
89
      return array(
90
        'maxy' => $geos_ring->pointN(3)->getY(),
91
        'miny' => $geos_ring->pointN(1)->getY(),
92
        'maxx' => $geos_ring->pointN(1)->getX(),
93
        'minx' => $geos_ring->pointN(3)->getX(),
94
      );
95
    }
96
97
    // Go through each component and get the max and min x and y
98
    $i = 0;
99
    foreach ($this->components as $component) {
100
      $component_bbox = $component->getBBox();
101
102
      // On the first run through, set the bbox to the component bbox
103
      if ($i == 0) {
104
        $maxx = $component_bbox['maxx'];
105
        $maxy = $component_bbox['maxy'];
106
        $minx = $component_bbox['minx'];
107
        $miny = $component_bbox['miny'];
108
      }
109
110
      // Do a check and replace on each boundary, slowly growing the bbox
111
      $maxx = $component_bbox['maxx'] > $maxx ? $component_bbox['maxx'] : $maxx;
112
      $maxy = $component_bbox['maxy'] > $maxy ? $component_bbox['maxy'] : $maxy;
113
      $minx = $component_bbox['minx'] < $minx ? $component_bbox['minx'] : $minx;
114
      $miny = $component_bbox['miny'] < $miny ? $component_bbox['miny'] : $miny;
115
      $i++;
116
    }
117
118
    return array(
119
      'maxy' => $maxy,
120
      'miny' => $miny,
121
      'maxx' => $maxx,
122
      'minx' => $minx,
123
    );
124
  }
125
126
  public function asArray() {
127
    $array = array();
128
    foreach ($this->components as $component) {
129
      $array[] = $component->asArray();
130
    }
131
    return $array;
132
  }
133
134
  public function area() {
135
    if ($this->geos()) {
136
      return $this->geos()->area();
137
    }
138
139
    $area = 0;
140
    foreach ($this->components as $component) {
141
      $area += $component->area();
142
    }
143
    return $area;
144
  }
145
146
  // By default, the boundary of a collection is the boundary of it's components
147
  public function boundary() {
148
    if ($this->isEmpty()) return new LineString();
149
150
    if ($this->geos()) {
151
      return $this->geos()->boundary();
152
    }
153
154
    $components_boundaries = array();
155
    foreach ($this->components as $component) {
156
      $components_boundaries[] = $component->boundary();
157
    }
158
    return geoPHP::geometryReduce($components_boundaries);
159
  }
160
161
  public function numGeometries() {
162
    return count($this->components);
163
  }
164
165
  // Note that the standard is 1 based indexing
166
  public function geometryN($n) {
167
    $n = intval($n);
168
    if (array_key_exists($n-1, $this->components)) {
169
      return $this->components[$n-1];
170
    }
171
    else {
172
      return NULL;
173
    }
174
  }
175
176
  public function length() {
177
    $length = 0;
178
    foreach ($this->components as $delta => $component) {
179
      $length += $component->length();
180
    }
181
    return $length;
182
  }
183
184
  public function greatCircleLength($radius = 6378137) {
185
    $length = 0;
186
    foreach ($this->components as $component) {
187
      $length += $component->greatCircleLength($radius);
188
    }
189
    return $length;
190
  }
191
192
  public function haversineLength() {
193
    $length = 0;
194
    foreach ($this->components as $component) {
195
      $length += $component->haversineLength();
196
    }
197
    return $length;
198
  }
199
200
  public function dimension() {
201
    $dimension = 0;
202
    foreach ($this->components as $component) {
203
      if ($component->dimension() > $dimension) {
204
        $dimension = $component->dimension();
205
      }
206
    }
207
    return $dimension;
208
  }
209
210
  // A collection is empty if it has no components OR all it's components are empty
211
  public function isEmpty() {
212
    if (!count($this->components)) {
213
      return TRUE;
214
    }
215
    else {
216
      foreach ($this->components as $component) {
217
        if (!$component->isEmpty()) return FALSE;
218
      }
219
      return TRUE;
220
    }
221
  }
222
223
  public function numPoints() {
224
    $num = 0;
225
    foreach ($this->components as $component) {
226
      $num += $component->numPoints();
227
    }
228
    return $num;
229
  }
230
231
  public function getPoints() {
232
    $points = array();
233
    foreach ($this->components as $component) {
234
      $points = array_merge($points, $component->getPoints());
235
    }
236
    return $points;
237
  }
238
239
  public function equals($geometry) {
240
    if ($this->geos()) {
241
      return $this->geos()->equals($geometry->geos());
242
    }
243
244
    // To test for equality we check to make sure that there is a matching point
245
    // in the other geometry for every point in this geometry.
246
    // This is slightly more strict than the standard, which
247
    // uses Within(A,B) = true and Within(B,A) = true
248
    // @@TODO: Eventually we could fix this by using some sort of simplification
249
    // method that strips redundant vertices (that are all in a row)
250
251
    $this_points = $this->getPoints();
252
    $other_points = $geometry->getPoints();
253
254
    // First do a check to make sure they have the same number of vertices
255
    if (count($this_points) != count($other_points)) {
256
      return FALSE;
257
    }
258
259
    foreach ($this_points as $point) {
260
      $found_match = FALSE;
261
      foreach ($other_points as $key => $test_point) {
262
        if ($point->equals($test_point)) {
263
          $found_match = TRUE;
264
          unset($other_points[$key]);
265
          break;
266
        }
267
      }
268
      if (!$found_match) {
269
        return FALSE;
270
      }
271
    }
272
273
    // All points match, return TRUE
274
    return TRUE;
275
  }
276
277
  public function isSimple() {
278
    if ($this->geos()) {
279
      return $this->geos()->isSimple();
280
    }
281
282
    // A collection is simple if all it's components are simple
283
    foreach ($this->components as $component) {
284
      if (!$component->isSimple()) return FALSE;
285
    }
286
287
    return TRUE;
288
  }
289
290
  public function explode() {
291
    $parts = array();
292
    foreach ($this->components as $component) {
293
      $parts = array_merge($parts, $component->explode());
294
    }
295
    return $parts;
296
  }
297
298
  // Not valid for this geometry type
299
  // --------------------------------
300
  public function x()                { return NULL; }
301
  public function y()                { return NULL; }
302
  public function startPoint()       { return NULL; }
303
  public function endPoint()         { return NULL; }
304
  public function isRing()           { return NULL; }
305
  public function isClosed()         { return NULL; }
306
  public function pointN($n)         { return NULL; }
307
  public function exteriorRing()     { return NULL; }
308
  public function numInteriorRings() { return NULL; }
309
  public function interiorRingN($n)  { return NULL; }
310
  public function pointOnSurface()   { return NULL; }
311
}
312
313