Coverage Report

Created: 2026-07-16 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/index/VertexSequencePackedRtree.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************/
14
15
16
17
#include <geos/geom/Coordinate.h>
18
#include <geos/geom/CoordinateSequence.h>
19
#include <geos/geom/Envelope.h>
20
#include <geos/index/VertexSequencePackedRtree.h>
21
22
#include <algorithm>
23
24
using geos::geom::Envelope;
25
26
namespace geos {
27
namespace index {
28
29
30
/**
31
* Creates a new tree over the given sequence of coordinates.
32
* The sequence should be spatially coherent to provide query performance.
33
*
34
* @param pts a sequence of points
35
*/
36
/* public */
37
VertexSequencePackedRtree::VertexSequencePackedRtree(const CoordinateSequence& pts)
38
0
    : items(pts)
39
0
    , removedItems(pts.size(), false)
40
0
{
41
0
    build();
42
0
}
43
44
/* public */
45
std::vector<Envelope>
46
VertexSequencePackedRtree::getBounds()
47
0
{
48
0
    std::vector<Envelope> boundsCopy = bounds;
49
    // std::vector<Envelope> boundsCopy;
50
    // std::copy(bounds.begin(), bounds.end(), boundsCopy.begin());
51
0
    return boundsCopy;
52
0
}
53
54
//-- Index Build --------------------------------------------------------------
55
56
/* private */
57
void
58
VertexSequencePackedRtree::build()
59
0
{
60
0
    levelOffset = computeLevelOffsets();
61
0
    bounds = createBounds();
62
0
}
63
64
/**
65
* Computes the level offsets.
66
* This is the position in the <tt>bounds</tt> array of each level.
67
*
68
* The levelOffsets array includes a sentinel value of offset[0] = 0.
69
* The top level is always of size 1,
70
* and so also indicates the total number of bounds.
71
*
72
* @return the level offsets
73
*/
74
/* private */
75
std::vector<std::size_t>
76
VertexSequencePackedRtree::computeLevelOffsets()
77
0
{
78
0
    std::vector<std::size_t> offsets;
79
0
    offsets.push_back(0u);
80
0
    std::size_t szLevel = items.size();
81
0
    std::size_t currOffset = 0;
82
0
    do {
83
0
        szLevel = levelNodeCount(szLevel);
84
0
        currOffset += szLevel;
85
0
        offsets.push_back(currOffset);
86
0
    } while (szLevel > 1);
87
0
    return offsets;
88
0
}
89
90
/* private static */
91
std::size_t
92
VertexSequencePackedRtree::ceilDivisor(std::size_t num, std::size_t denom)
93
0
{
94
0
    std::size_t div = num / denom;
95
0
    return div * denom >= num ? div : div + 1;
96
0
}
97
98
/* private */
99
std::size_t
100
VertexSequencePackedRtree::levelNodeCount(std::size_t numNodes)
101
0
{
102
0
    return ceilDivisor(numNodes, nodeCapacity);
103
0
}
104
105
/* private */
106
std::vector<Envelope>
107
VertexSequencePackedRtree::createBounds()
108
0
{
109
0
    std::size_t bndsSize = levelOffset[levelOffset.size() - 1] + 1;
110
0
    std::vector<Envelope> bnds(bndsSize);
111
0
    fillItemBounds(bnds);
112
113
0
    for (std::size_t lvl = 1; lvl < levelOffset.size(); lvl++) {
114
0
        fillLevelBounds(lvl, bnds);
115
0
    }
116
0
    return bnds;
117
0
}
118
119
120
/* private */
121
void
122
VertexSequencePackedRtree::fillItemBounds(std::vector<Envelope>& bnds)
123
0
{
124
0
    std::size_t nodeStart = 0;
125
0
    std::size_t bndIndex = 0;
126
0
    do {
127
0
        std::size_t nodeEnd = clampMax(nodeStart + nodeCapacity, items.size());
128
0
        bnds[bndIndex++] = computeItemEnvelope(items, nodeStart, nodeEnd);
129
0
        nodeStart = nodeEnd;
130
0
    }
131
0
    while (nodeStart < items.size());
132
0
}
133
134
135
/* private */
136
void
137
VertexSequencePackedRtree::fillLevelBounds(std::size_t lvl, std::vector<Envelope>& bnds)
138
0
{
139
0
    std::size_t levelStart = levelOffset[lvl - 1];
140
0
    std::size_t levelEnd = levelOffset[lvl];
141
0
    std::size_t nodeStart = levelStart;
142
0
    std::size_t levelBndIndex = levelOffset[lvl];
143
0
    do {
144
0
        std::size_t nodeEnd = clampMax(nodeStart + nodeCapacity, levelEnd);
145
0
        bnds[levelBndIndex++] = computeNodeEnvelope(bnds, nodeStart, nodeEnd);
146
0
        nodeStart = nodeEnd;
147
0
    }
148
0
    while (nodeStart < levelEnd);
149
0
}
150
151
152
/* private static */
153
Envelope
154
VertexSequencePackedRtree::computeNodeEnvelope(const std::vector<Envelope>& bnds,
155
    std::size_t start, std::size_t end)
156
0
{
157
0
    Envelope env;
158
0
    for (std::size_t i = start; i < end; i++) {
159
0
        env.expandToInclude(bnds[i]);
160
0
    }
161
0
    return env;
162
0
}
163
164
/* private static */
165
Envelope
166
VertexSequencePackedRtree::computeItemEnvelope(const geom::CoordinateSequence& items,
167
    std::size_t start, std::size_t end)
168
0
{
169
0
    Envelope env;
170
0
    for (std::size_t i = start; i < end; i++) {
171
0
        env.expandToInclude(items[i]);
172
0
    }
173
0
    return env;
174
0
}
175
176
//-- Index Query --------------------------------------------------------------
177
178
/**
179
* Queries the index to find all items which intersect an extent.
180
* The query result is a list of the indices of input coordinates
181
* which intersect the extent.
182
*
183
* @param queryEnv the query extent
184
* @return an array of the indices of the input coordinates
185
*/
186
/* public */
187
void
188
VertexSequencePackedRtree::query(const Envelope& queryEnv, std::vector<std::size_t>& result) const
189
0
{
190
0
    std::size_t level = levelOffset.size() - 1;
191
0
    queryNode(queryEnv, level, 0, result);
192
0
}
193
194
195
/* private */
196
void
197
VertexSequencePackedRtree::queryNode(const Envelope& queryEnv,
198
    std::size_t level, std::size_t nodeIndex,
199
    std::vector<std::size_t>& result) const
200
0
{
201
0
    std::size_t boundsIndex = levelOffset[level] + nodeIndex;
202
0
    Envelope nodeEnv = bounds[boundsIndex];
203
204
    //--- node is empty
205
0
    if (nodeEnv.isNull())
206
0
        return;
207
0
    if (! queryEnv.intersects(nodeEnv))
208
0
        return;
209
210
0
    std::size_t childNodeIndex = nodeIndex * nodeCapacity;
211
0
    if (level == 0) {
212
0
        queryItemRange(queryEnv, childNodeIndex, result);
213
0
    }
214
0
    else {
215
0
        queryNodeRange(queryEnv, level - 1, childNodeIndex, result);
216
0
    }
217
0
}
218
219
/* private */
220
void
221
VertexSequencePackedRtree::queryNodeRange(const Envelope& queryEnv,
222
    std::size_t level, std::size_t nodeStartIndex,
223
    std::vector<std::size_t>& result) const
224
0
{
225
0
    std::size_t levelMax = levelSize(level);
226
0
    for (std::size_t i = 0; i < nodeCapacity; i++) {
227
0
        std::size_t index = nodeStartIndex + i;
228
0
        if (index >= levelMax)
229
0
            return;
230
0
        queryNode(queryEnv, level, index, result);
231
0
    }
232
0
}
233
234
235
/* private */
236
std::size_t
237
VertexSequencePackedRtree::levelSize(std::size_t level) const
238
0
{
239
0
    return levelOffset[level + 1] - levelOffset[level];
240
0
}
241
242
243
/* private */
244
void
245
VertexSequencePackedRtree::queryItemRange(const Envelope& queryEnv,
246
    std::size_t itemIndex, std::vector<std::size_t>& result) const
247
0
{
248
0
    for (std::size_t i = 0; i < nodeCapacity; i++) {
249
0
        std::size_t index = itemIndex + i;
250
0
        if (index >= items.size())
251
0
            return;
252
0
        const Coordinate& p = items[index];
253
0
        bool removed = removedItems[index];
254
0
        if ( (!removed) && queryEnv.contains(p))
255
0
            result.push_back(index);
256
0
    }
257
0
}
258
259
//-- Index Modify --------------------------------------------------------------
260
261
/**
262
* Removes the input item at the given index from the spatial index.
263
*
264
* @param index the index of the item in the input
265
*/
266
/* public */
267
void
268
VertexSequencePackedRtree::remove(std::size_t index)
269
0
{
270
0
    removedItems[index] = true;
271
272
    //--- prune the item parent node if all its items are removed
273
0
    std::size_t nodeIndex = index / nodeCapacity;
274
0
    if (! isItemsNodeEmpty(nodeIndex))
275
0
        return;
276
277
0
    bounds[nodeIndex].setToNull();
278
279
0
    if (levelOffset.size() <= 2)
280
0
        return;
281
282
    //-- prune the node parent if all children removed
283
0
    std::size_t nodeLevelIndex = nodeIndex / nodeCapacity;
284
0
    if (! isNodeEmpty(1, nodeLevelIndex))
285
0
        return;
286
287
0
    std::size_t nodeIndex1 = levelOffset[1] + nodeLevelIndex;
288
0
    bounds[nodeIndex1].setToNull();
289
290
    // TODO: propagate removal up the tree nodes?
291
0
}
292
293
/* private */
294
bool
295
VertexSequencePackedRtree::isNodeEmpty(std::size_t level, std::size_t index)
296
0
{
297
0
    std::size_t start = index * nodeCapacity;
298
0
    std::size_t end = clampMax(start + nodeCapacity, levelOffset[level]);
299
0
    for (std::size_t i = start; i < end; i++) {
300
0
        if (!bounds[i].isNull())
301
0
            return false;
302
0
    }
303
0
    return true;
304
0
}
305
306
/* private */
307
bool
308
VertexSequencePackedRtree::isItemsNodeEmpty(std::size_t nodeIndex)
309
0
{
310
0
    std::size_t start = nodeIndex * nodeCapacity;
311
0
    std::size_t end = clampMax(start + nodeCapacity, items.size());
312
0
    for (std::size_t i = start; i < end; i++) {
313
0
        if (!removedItems[i]) return false;
314
0
    }
315
0
    return true;
316
0
}
317
318
/* private static */
319
std::size_t
320
VertexSequencePackedRtree::clampMax(std::size_t x, std::size_t max)
321
0
{
322
0
    return x > max ? max: x;
323
0
}
324
325
326
} // namespace geos.index
327
} // namespace geos
328