Completed
Push — master ( a2b4f8...0c6d78 )
by Marcus
01:54
created

SimplifyDouglasPeucker::douglasPeucker()   B

Complexity

Conditions 6
Paths 15

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 8.6417
c 0
b 0
f 0
cc 6
nc 15
nop 1
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * Simplify Polyline with the Douglas-Peucker-Algorithm
6
 *
7
 * The Algorithm is described here:
8
 * http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
9
 *
10
 * The formula for the Perpendicular Distance is described here:
11
 * http://biodiversityinformatics.amnh.org/open_source/pdc/documentation.php
12
 *
13
 * @author    Marcus Jaschen <[email protected]>
14
 * @license   https://opensource.org/licenses/MIT
15
 * @link      https://github.com/mjaschen/phpgeo
16
 */
17
18
namespace Location\Processor\Polyline;
19
20
use Location\Line;
21
use Location\Polyline;
22
use Location\Utility\PerpendicularDistance;
23
24
/**
25
 * Simplify Polyline with the Douglas-Peucker-Algorithm
26
 *
27
 * @author   Marcus Jaschen <[email protected]>
28
 * @license  https://opensource.org/licenses/MIT
29
 * @link     https://github.com/mjaschen/phpgeo
30
 */
31
class SimplifyDouglasPeucker implements SimplifyInterface
32
{
33
    /**
34
     * @var float
35
     */
36
    private $tolerance;
37
38
    /**
39
     * @param float $tolerance the perpendicular distance threshold in meters
40
     */
41
    public function __construct(float $tolerance)
42
    {
43
        $this->tolerance = $tolerance;
44
    }
45
46
    /**
47
     * @param \Location\Polyline $polyline
48
     *
49
     * @return \Location\Polyline
50
     */
51
    public function simplify(Polyline $polyline): Polyline
52
    {
53
        $resultPolyline = new Polyline();
54
        $simplifiedLine = $this->douglasPeucker($polyline->getPoints());
55
56
        foreach ($simplifiedLine as $point) {
57
            $resultPolyline->addPoint($point);
58
        }
59
60
        return $resultPolyline;
61
    }
62
63
    /**
64
     * @param array $line
65
     *
66
     * @return array
67
     */
68
    protected function douglasPeucker(array $line): array
69
    {
70
        $distanceMax = 0;
71
        $index       = 0;
72
73
        $lineSize = count($line);
74
75
        $pdCalc = new PerpendicularDistance();
76
77
        for ($i = 1; $i <= ($lineSize - 2); $i++) {
78
            $distance = $pdCalc->getPerpendicularDistance($line[$i], new Line($line[0], $line[$lineSize - 1]));
79
80
            if ($distance > $distanceMax) {
81
                $index       = $i;
82
                $distanceMax = $distance;
83
            }
84
        }
85
86
        if ($distanceMax > $this->tolerance) {
87
            $lineSplitFirst  = array_slice($line, 0, $index+1);
88
            $lineSplitSecond = array_slice($line, $index, $lineSize-$index);
89
90
            if (count($lineSplitFirst) > 2) {
91
                $resultsSplit1 = $this->douglasPeucker($lineSplitFirst);
92
            } else {
93
                $resultsSplit1 = $lineSplitFirst;
94
            }
95
96
            if (count($lineSplitSecond) > 2) {
97
                $resultsSplit2 = $this->douglasPeucker($lineSplitSecond);
98
            } else {
99
                $resultsSplit2 = $lineSplitSecond;
100
            }
101
102
            array_pop($resultsSplit1);
103
104
            return array_merge($resultsSplit1, $resultsSplit2);
105
        }
106
107
        return [$line[0], $line[$lineSize - 1]];
108
    }
109
}
110