Completed
Push — milestone/4.0 ( 4a7219...1803cd )
by Marcus
01:48
created

SimplifyDouglasPeucker::douglasPeucker()   B

Complexity

Conditions 6
Paths 15

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

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