Polygon.class.php 5.8 KB

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