LineString.class.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. /**
  3. * LineString. A collection of Points representing a line.
  4. * A line can have more than one segment.
  5. */
  6. class LineString extends Collection
  7. {
  8. protected $geom_type = 'LineString';
  9. /**
  10. * Constructor
  11. *
  12. * @param array $points An array of at least two points with
  13. * which to build the LineString
  14. */
  15. public function __construct($points = array()) {
  16. if (count($points) == 1) {
  17. throw new Exception("Cannot construct a LineString with a single point");
  18. }
  19. // Call the Collection constructor to build the LineString
  20. parent::__construct($points);
  21. }
  22. // The boundary of a linestring is itself
  23. public function boundary() {
  24. return $this;
  25. }
  26. public function startPoint() {
  27. return $this->pointN(1);
  28. }
  29. public function endPoint() {
  30. $last_n = $this->numPoints();
  31. return $this->pointN($last_n);
  32. }
  33. public function isClosed() {
  34. return ($this->startPoint()->equals($this->endPoint()));
  35. }
  36. public function isRing() {
  37. return ($this->isClosed() && $this->isSimple());
  38. }
  39. public function numPoints() {
  40. return $this->numGeometries();
  41. }
  42. public function pointN($n) {
  43. return $this->geometryN($n);
  44. }
  45. public function dimension() {
  46. if ($this->isEmpty()) return 0;
  47. return 1;
  48. }
  49. public function area() {
  50. return 0;
  51. }
  52. public function length() {
  53. if ($this->geos()) {
  54. return $this->geos()->length();
  55. }
  56. $length = 0;
  57. foreach ($this->getPoints() as $delta => $point) {
  58. $previous_point = $this->geometryN($delta);
  59. if ($previous_point) {
  60. $length += sqrt(pow(($previous_point->getX() - $point->getX()), 2) + pow(($previous_point->getY()- $point->getY()), 2));
  61. }
  62. }
  63. return $length;
  64. }
  65. public function greatCircleLength($radius = 6378137) {
  66. $length = 0;
  67. $points = $this->getPoints();
  68. for($i=0; $i<$this->numPoints()-1; $i++) {
  69. $point = $points[$i];
  70. $next_point = $points[$i+1];
  71. if (!is_object($next_point)) {continue;}
  72. // Great circle method
  73. $lat1 = deg2rad($point->getY());
  74. $lat2 = deg2rad($next_point->getY());
  75. $lon1 = deg2rad($point->getX());
  76. $lon2 = deg2rad($next_point->getX());
  77. $dlon = $lon2 - $lon1;
  78. $length +=
  79. $radius *
  80. atan2(
  81. sqrt(
  82. pow(cos($lat2) * sin($dlon), 2) +
  83. pow(cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dlon), 2)
  84. )
  85. ,
  86. sin($lat1) * sin($lat2) +
  87. cos($lat1) * cos($lat2) * cos($dlon)
  88. );
  89. }
  90. // Returns length in meters.
  91. return $length;
  92. }
  93. public function haversineLength() {
  94. $degrees = 0;
  95. $points = $this->getPoints();
  96. for($i=0; $i<$this->numPoints()-1; $i++) {
  97. $point = $points[$i];
  98. $next_point = $points[$i+1];
  99. if (!is_object($next_point)) {continue;}
  100. $degree = rad2deg(
  101. acos(
  102. sin(deg2rad($point->getY())) * sin(deg2rad($next_point->getY())) +
  103. cos(deg2rad($point->getY())) * cos(deg2rad($next_point->getY())) *
  104. cos(deg2rad(abs($point->getX() - $next_point->getX())))
  105. )
  106. );
  107. $degrees += $degree;
  108. }
  109. // Returns degrees
  110. return $degrees;
  111. }
  112. public function explode() {
  113. $parts = array();
  114. $points = $this->getPoints();
  115. foreach ($points as $i => $point) {
  116. if (isset($points[$i+1])) {
  117. $parts[] = new LineString(array($point, $points[$i+1]));
  118. }
  119. }
  120. return $parts;
  121. }
  122. public function isSimple() {
  123. if ($this->geos()) {
  124. return $this->geos()->isSimple();
  125. }
  126. $segments = $this->explode();
  127. foreach ($segments as $i => $segment) {
  128. foreach ($segments as $j => $check_segment) {
  129. if ($i != $j) {
  130. if ($segment->lineSegmentIntersect($check_segment)) {
  131. return FALSE;
  132. }
  133. }
  134. }
  135. }
  136. return TRUE;
  137. }
  138. // Utility function to check if any line sigments intersect
  139. // Derived from http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
  140. public function lineSegmentIntersect($segment) {
  141. $p0_x = $this->startPoint()->x();
  142. $p0_y = $this->startPoint()->y();
  143. $p1_x = $this->endPoint()->x();
  144. $p1_y = $this->endPoint()->y();
  145. $p2_x = $segment->startPoint()->x();
  146. $p2_y = $segment->startPoint()->y();
  147. $p3_x = $segment->endPoint()->x();
  148. $p3_y = $segment->endPoint()->y();
  149. $s1_x = $p1_x - $p0_x; $s1_y = $p1_y - $p0_y;
  150. $s2_x = $p3_x - $p2_x; $s2_y = $p3_y - $p2_y;
  151. $fps = (-$s2_x * $s1_y) + ($s1_x * $s2_y);
  152. $fpt = (-$s2_x * $s1_y) + ($s1_x * $s2_y);
  153. if ($fps == 0 || $fpt == 0) {
  154. return FALSE;
  155. }
  156. $s = (-$s1_y * ($p0_x - $p2_x) + $s1_x * ($p0_y - $p2_y)) / $fps;
  157. $t = ( $s2_x * ($p0_y - $p2_y) - $s2_y * ($p0_x - $p2_x)) / $fpt;
  158. if ($s > 0 && $s < 1 && $t > 0 && $t < 1) {
  159. // Collision detected
  160. return TRUE;
  161. }
  162. return FALSE;
  163. }
  164. }