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

LineString   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Importance

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