Completed
Push — master ( 8d3032...15ec78 )
by Marcus
05:45
created

src/Location/Processor/Polyline/Simplify.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Simplify Polyline with the Douglas-Peucker-Algorithm
4
 *
5
 * The Algorithm is described here:
6
 * http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
7
 *
8
 * The formula for the Perpendicular Distance is described here:
9
 * http://biodiversityinformatics.amnh.org/open_source/pdc/documentation.php
10
 *
11
 * @author    Marcus Jaschen <[email protected]>
12
 * @license   https://opensource.org/licenses/GPL-3.0 GPL
13
 * @link      https://github.com/mjaschen/phpgeo
14
 */
15
16
namespace Location\Processor\Polyline;
17
18
use Location\Coordinate;
19
use Location\Line;
20
use Location\Polyline;
21
22
/**
23
 * Simplify Polyline with the Douglas-Peucker-Algorithm
24
 *
25
 * @deprecated This class is no longer supported. Please use
26
 * the `SimplifyDouglasPeucker` oder `SimplifyBearing` classes.
27
 *
28
 * @author   Marcus Jaschen <[email protected]>
29
 * @license  https://opensource.org/licenses/GPL-3.0 GPL
30
 * @link     https://github.com/mjaschen/phpgeo
31
 */
32
class Simplify
33
{
34
    /**
35
     * @var \Location\Polyline
36
     */
37
    protected $polyline;
38
39
    /**
40
     * @param Polyline $polyline
41
     */
42
    public function __construct(Polyline $polyline)
43
    {
44
        $this->polyline = $polyline;
45
    }
46
47
    /**
48
     * @param float $tolerance The maximum allowed deviation
49
     *
50
     * @return Polyline
51
     */
52 View Code Duplication
    public function simplify($tolerance)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
53
    {
54
        $simplifiedLine = $this->douglasPeucker(
55
            $this->polyline->getPoints(),
56
            $tolerance
57
        );
58
59
        $resultPolyline = new Polyline();
60
61
        foreach ($simplifiedLine as $point) {
62
            $resultPolyline->addPoint($point);
63
        }
64
65
        return $resultPolyline;
66
    }
67
68
    /**
69
     * @param array $line
70
     * @param float $tolerance
71
     *
72
     * @return array
73
     */
74 View Code Duplication
    protected function douglasPeucker($line = [], $tolerance)
75
    {
76
        $distanceMax = 0;
77
        $index       = 0;
78
79
        $lineSize = count($line);
80
81
        for ($i = 1; $i <= ($lineSize - 1); $i ++) {
82
            $distance = $this->getPerpendicularDistance($line[$i], new Line($line[0], $line[$lineSize - 1]));
83
84
            if ($distance > $distanceMax) {
85
                $index       = $i;
86
                $distanceMax = $distance;
87
            }
88
        }
89
90
        if ($distanceMax > $tolerance) {
91
            $lineSplitFirst  = array_slice($line, 0, $index);
92
            $lineSplitSecond = array_slice($line, $index, $lineSize);
93
94
            $recursiveResultsSplitFirst  = $this->douglasPeucker($lineSplitFirst, $tolerance);
95
            $recursiveResultsSplitSecond = $this->douglasPeucker($lineSplitSecond, $tolerance);
96
97
            array_pop($recursiveResultsSplitFirst);
98
99
            return array_merge($recursiveResultsSplitFirst, $recursiveResultsSplitSecond);
100
        }
101
102
        return [$line[0], $line[$lineSize - 1]];
103
    }
104
105
    /**
106
     * @param Coordinate $point
107
     * @param Line $line
108
     *
109
     * @return number
110
     */
111 View Code Duplication
    protected function getPerpendicularDistance(Coordinate $point, Line $line)
112
    {
113
        $ellipsoid = $point->getEllipsoid();
114
115
        $ellipsoidRadius = $ellipsoid->getArithmeticMeanRadius();
116
117
        $firstLinePointLat = $this->deg2radLatitude($line->getPoint1()->getLat());
118
        $firstLinePointLng = $this->deg2radLongitude($line->getPoint1()->getLng());
119
120
        $firstLinePointX = $ellipsoidRadius * cos($firstLinePointLng) * sin($firstLinePointLat);
121
        $firstLinePointY = $ellipsoidRadius * sin($firstLinePointLng) * sin($firstLinePointLat);
122
        $firstLinePointZ = $ellipsoidRadius * cos($firstLinePointLat);
123
124
        $secondLinePointLat = $this->deg2radLatitude($line->getPoint2()->getLat());
125
        $secondLinePointLng = $this->deg2radLongitude($line->getPoint2()->getLng());
126
127
        $secondLinePointX = $ellipsoidRadius * cos($secondLinePointLng) * sin($secondLinePointLat);
128
        $secondLinePointY = $ellipsoidRadius * sin($secondLinePointLng) * sin($secondLinePointLat);
129
        $secondLinePointZ = $ellipsoidRadius * cos($secondLinePointLat);
130
131
        $pointLat = $this->deg2radLatitude($point->getLat());
132
        $pointLng = $this->deg2radLongitude($point->getLng());
133
134
        $pointX = $ellipsoidRadius * cos($pointLng) * sin($pointLat);
135
        $pointY = $ellipsoidRadius * sin($pointLng) * sin($pointLat);
136
        $pointZ = $ellipsoidRadius * cos($pointLat);
137
138
        $normalizedX = $firstLinePointY * $secondLinePointZ - $firstLinePointZ * $secondLinePointY;
139
        $normalizedY = $firstLinePointZ * $secondLinePointX - $firstLinePointX * $secondLinePointZ;
140
        $normalizedZ = $firstLinePointX * $secondLinePointY - $firstLinePointY * $secondLinePointX;
141
142
        $length = sqrt($normalizedX * $normalizedX + $normalizedY * $normalizedY + $normalizedZ * $normalizedZ);
143
144
        if ($length == 0) {
145
            return 0;
146
        }
147
148
        $normalizedX /= $length;
149
        $normalizedY /= $length;
150
        $normalizedZ /= $length;
151
152
        $thetaPoint = $normalizedX * $pointX + $normalizedY * $pointY + $normalizedZ * $pointZ;
153
154
        $length = sqrt($pointX * $pointX + $pointY * $pointY + $pointZ * $pointZ);
155
156
        $thetaPoint /= $length;
157
158
        $distance = abs((M_PI / 2) - acos($thetaPoint));
159
160
        return $distance * $ellipsoidRadius;
161
    }
162
163
    /**
164
     * @param float $latitude
165
     *
166
     * @return float
167
     */
168
    protected function deg2radLatitude($latitude)
169
    {
170
        return deg2rad(90 - $latitude);
171
    }
172
173
    /**
174
     * @param float $longitude
175
     *
176
     * @return float
177
     */
178
    protected function deg2radLongitude($longitude)
179
    {
180
        if ($longitude > 0) {
181
            return deg2rad($longitude);
182
        }
183
184
        return deg2rad($longitude + 360);
185
    }
186
}
187