Coverage Report

Created: 2026-09-01 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/simplify/DouglasPeuckerLineSimplifier.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2006 Refractions Research Inc.
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************
14
 *
15
 * Last port: simplify/DouglasPeuckerLineSimplifier.java rev. 1.4
16
 *
17
 **********************************************************************/
18
19
#include <geos/simplify/DouglasPeuckerLineSimplifier.h>
20
#include <geos/geom/Coordinate.h>
21
#include <geos/geom/LineSegment.h>
22
#include <geos/geom/LinearRing.h>
23
#include <geos/util.h>
24
#include <geos/util/IllegalArgumentException.h>
25
26
#include <vector>
27
#include <memory> // for unique_ptr
28
29
using geos::geom::CoordinateSequence;
30
31
namespace geos {
32
33
/// Line simplification algorithms
34
namespace simplify { // geos::simplify
35
36
/*public static*/
37
std::unique_ptr<CoordinateSequence>
38
DouglasPeuckerLineSimplifier::simplify(
39
    const CoordinateSequence& nPts,
40
    double distanceTolerance,
41
    bool preserveClosedEndpoint)
42
0
{
43
0
    DouglasPeuckerLineSimplifier simp(nPts);
44
0
    simp.setDistanceTolerance(distanceTolerance);
45
0
    simp.setPreserveClosedEndpoint(preserveClosedEndpoint);
46
0
    return simp.simplify();
47
0
}
48
49
/*public*/
50
DouglasPeuckerLineSimplifier::DouglasPeuckerLineSimplifier(
51
    const CoordinateSequence& nPts)
52
    :
53
0
    pts(nPts)
54
0
{
55
0
}
56
57
/*public*/
58
void
59
DouglasPeuckerLineSimplifier::setDistanceTolerance(
60
    double nDistanceTolerance)
61
0
{
62
0
    if (std::isnan(nDistanceTolerance)) {
63
0
        throw util::IllegalArgumentException("Tolerance must not be NaN");
64
0
    }
65
0
    distanceTolerance = nDistanceTolerance;
66
0
}
67
68
void
69
DouglasPeuckerLineSimplifier::setPreserveClosedEndpoint(bool preserve)
70
0
{
71
0
    preserveEndpoint = preserve;
72
0
}
73
74
/*public*/
75
std::unique_ptr<CoordinateSequence>
76
DouglasPeuckerLineSimplifier::simplify()
77
0
{
78
0
    auto coordList = detail::make_unique<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
79
80
    // empty coordlist is the simplest, won't simplify further
81
0
    if(pts.isEmpty()) {
82
0
        return coordList;
83
0
    }
84
85
0
    usePt = std::vector<bool>(pts.size(), true);
86
0
    simplifySection(0, pts.size() - 1);
87
88
    // Add continuous ranges of retained points from pts to coordList
89
0
    std::size_t from = 0;
90
0
    while (from < pts.size() && !usePt[from]) {
91
0
        from++;
92
0
    }
93
0
    for(std::size_t to = from + 1; to <= pts.size(); to++) {
94
0
        if (to == pts.size() || !usePt[to]) {
95
0
            coordList->add(pts, from, to - 1);
96
0
            while (to < pts.size() && !usePt[to]) {
97
0
                to++;
98
0
            }
99
0
            from = to;
100
0
        }
101
0
    }
102
103
    // TODO avoid copying entire sequence?
104
0
    bool simplifyRing = !preserveEndpoint && pts.isRing();
105
0
    if (simplifyRing && coordList->size() > geom::LinearRing::MINIMUM_VALID_SIZE) {
106
0
        geom::LineSegment seg(coordList->getAt(coordList->size() - 2), coordList->getAt(1));
107
0
        if (seg.distance(coordList->getAt(0)) <= distanceTolerance) {
108
0
            auto ret = detail::make_unique<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
109
0
            ret->add(*coordList, 1, coordList->size() - 2);
110
0
            ret->closeRing();
111
0
            coordList = std::move(ret);
112
0
        }
113
0
    }
114
115
0
    return coordList;
116
0
}
117
118
/*private*/
119
void
120
DouglasPeuckerLineSimplifier::simplifySection(
121
    std::size_t i,
122
    std::size_t j)
123
0
{
124
0
    if((i + 1) == j) {
125
126
0
        return;
127
0
    }
128
129
0
    geom::LineSegment seg(pts[i], pts[j]);
130
0
    double maxDistance = -1.0;
131
132
0
    std::size_t maxIndex = i;
133
134
0
    for(std::size_t k = i + 1; k < j; k++) {
135
0
        double distance = seg.distance(pts[k]);
136
0
        if(distance > maxDistance) {
137
0
            maxDistance = distance;
138
0
            maxIndex = k;
139
0
        }
140
0
    }
141
0
    if(maxDistance <= distanceTolerance) {
142
0
        for(std::size_t k = i + 1; k < j; k++) {
143
0
            usePt[k] = false;
144
0
        }
145
0
    }
146
0
    else {
147
0
        simplifySection(i, maxIndex);
148
0
        simplifySection(maxIndex, j);
149
0
    }
150
0
}
151
152
} // namespace geos::simplify
153
} // namespace geos