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

Polygon::area()   B

Complexity

Conditions 10
Paths 19

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 31
rs 7.6666
c 0
b 0
f 0
cc 10
nc 19
nop 2

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
/**
4
 * Polygon: A polygon is a plane figure that is bounded by a closed path, 
5
 * composed of a finite sequence of straight line segments
6
 */
7
class Polygon extends Collection
8
{
9
  protected $geom_type = 'Polygon';
10
11
  // The boundary of a polygin is it's outer ring
12
  public function boundary() {
13
    return $this->exteriorRing();
14
  }
15
16
  public function area($exterior_only = FALSE, $signed = FALSE) {
17
    if ($this->isEmpty()) return 0;
18
    
19
    if ($this->geos() && $exterior_only == FALSE) {
20
      return $this->geos()->area();
21
    }
22
    
23
    $exterior_ring = $this->components[0];
24
    $pts = $exterior_ring->getComponents();
25
    
26
    $c = count($pts);
27
    if((int)$c == '0') return NULL;
28
    $a = '0';
29
    foreach($pts as $k => $p){
30
      $j = ($k + 1) % $c;
31
      $a = $a + ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX());
32
    }
33
    
34
    if ($signed) $area = ($a / 2);
35
    else $area = abs(($a / 2));
36
    
37
    if ($exterior_only == TRUE) {
38
      return $area;
39
    }
40
    foreach ($this->components as $delta => $component) {
41
      if ($delta != 0) {
42
        $inner_poly = new Polygon(array($component));
43
        $area -= $inner_poly->area();
44
      }
45
    }
46
    return $area;
47
  }
48
  
49
  public function centroid() {
50
    if ($this->isEmpty()) return NULL;
51
    
52
    if ($this->geos()) {
53
      return geoPHP::geosToGeometry($this->geos()->centroid());
54
    }
55
    
56
    $exterior_ring = $this->components[0];
57
    $pts = $exterior_ring->getComponents();
58
    
59
    $c = count($pts);
60
    if((int)$c == '0') return NULL;
61
    $cn = array('x' => '0', 'y' => '0');
62
    $a = $this->area(TRUE, TRUE);
63
    
64
    // If this is a polygon with no area. Just return the first point.
65
    if ($a == 0) {
66
      return $this->exteriorRing()->pointN(1);
67
    }
68
    
69
    foreach($pts as $k => $p){
70
      $j = ($k + 1) % $c;
71
      $P = ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX());
72
      $cn['x'] = $cn['x'] + ($p->getX() + $pts[$j]->getX()) * $P;
73
      $cn['y'] = $cn['y'] + ($p->getY() + $pts[$j]->getY()) * $P;
74
    }
75
    
76
    $cn['x'] = $cn['x'] / ( 6 * $a);
77
    $cn['y'] = $cn['y'] / ( 6 * $a);
78
    
79
    $centroid = new Point($cn['x'], $cn['y']);
80
    return $centroid;
81
  }
82
83
	/**
84
	 * Find the outermost point from the centroid
85
	 *
86
	 * @returns Point The outermost point
87
	 */
88
  public function outermostPoint() {
89
		$centroid = $this->getCentroid();
90
91
		$max = array('length' => 0, 'point' => null);
92
93
		foreach($this->getPoints() as $point) {
94
			$lineString = new LineString(array($centroid, $point));
95
96
			if($lineString->length() > $max['length']) {
97
				$max['length'] = $lineString->length();
98
				$max['point'] = $point;
99
			}
100
		}
101
102
		return $max['point'];
103
  }
104
105
  public function exteriorRing() {
106
    if ($this->isEmpty()) return new LineString();
107
    return $this->components[0];
108
  }
109
  
110
  public function numInteriorRings() {
111
    if ($this->isEmpty()) return 0;
112
    return $this->numGeometries()-1;
113
  }
114
  
115
  public function interiorRingN($n) {
116
    return $this->geometryN($n+1);
117
  }
118
  
119
  public function dimension() {
120
    if ($this->isEmpty()) return 0;
121
    return 2;
122
  }
123
124
  public function isSimple() {
125
    if ($this->geos()) {
126
      return $this->geos()->isSimple();
127
    }
128
    
129
    $segments = $this->explode();
130
    
131
    foreach ($segments as $i => $segment) {
132
      foreach ($segments as $j => $check_segment) {
133
        if ($i != $j) {
134
          if ($segment->lineSegmentIntersect($check_segment)) {
135
            return FALSE;
136
          }
137
        }
138
      }
139
    }
140
    return TRUE;
141
  }
142
143
  /**
144
   * For a given point, determine whether it's bounded by the given polygon.
145
   * Adapted from http://www.assemblysys.com/dataServices/php_pointinpolygon.php
146
   * @see http://en.wikipedia.org/wiki/Point%5Fin%5Fpolygon
147
   *
148
   * @param Point $point 
149
   * @param boolean $pointOnBoundary - whether a boundary should be considered "in" or not
150
   * @param boolean $pointOnVertex - whether a vertex should be considered "in" or not
151
   * @return boolean
152
   */
153
  public function pointInPolygon($point, $pointOnBoundary = true, $pointOnVertex = true) {
154
    $vertices = $this->getPoints();
155
156
    // Check if the point sits exactly on a vertex
157
    if ($this->pointOnVertex($point, $vertices)) {
158
      return $pointOnVertex ? TRUE : FALSE;
159
    }
160
  
161
    // Check if the point is inside the polygon or on the boundary
162
    $intersections = 0; 
163
    $vertices_count = count($vertices);
164
165
    for ($i=1; $i < $vertices_count; $i++) {
166
      $vertex1 = $vertices[$i-1]; 
167
      $vertex2 = $vertices[$i];
168
      if ($vertex1->y() == $vertex2->y() 
169
      && $vertex1->y() == $point->y() 
170
      && $point->x() > min($vertex1->x(), $vertex2->x()) 
171
      && $point->x() < max($vertex1->x(), $vertex2->x())) {
172
        // Check if point is on an horizontal polygon boundary
173
        return $pointOnBoundary ? TRUE : FALSE;
174
      }
175
      if ($point->y() > min($vertex1->y(), $vertex2->y())
176
      && $point->y() <= max($vertex1->y(), $vertex2->y())
177
      && $point->x() <= max($vertex1->x(), $vertex2->x())
178
      && $vertex1->y() != $vertex2->y()) {
179
        $xinters = 
180
          ($point->y() - $vertex1->y()) * ($vertex2->x() - $vertex1->x())
181
          / ($vertex2->y() - $vertex1->y()) 
182
          + $vertex1->x();
183
        if ($xinters == $point->x()) {
184
          // Check if point is on the polygon boundary (other than horizontal)
185
          return $pointOnBoundary ? TRUE : FALSE;
186
        }
187
        if ($vertex1->x() == $vertex2->x() || $point->x() <= $xinters) {
188
          $intersections++;
189
        }
190
      } 
191
    } 
192
    // If the number of edges we passed through is even, then it's in the polygon.
193
    if ($intersections % 2 != 0) {
194
      return TRUE;
195
    }
196
    else {
197
      return FALSE;
198
    }
199
  }
200
  
201
  public function pointOnVertex($point) {
202
    foreach($this->getPoints() as $vertex) {
203
      if ($point->equals($vertex)) {
204
        return true;
205
      }
206
    }
207
  }
208
209
210
  // Not valid for this geometry type
211
  // --------------------------------
212
  public function length() { return NULL; }
213
  
214
}
215
216