Coverage Report

Created: 2026-10-28 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/overlayng/ElevationModel.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2020 Sandro Santilli <strk@kbt.io>
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
 * Last Port: operation/overlayng/ElevationModel.java 4c88fea52
16
 *
17
 **********************************************************************/
18
19
#include <geos/operation/overlayng/ElevationModel.h>
20
#include <geos/geom/Coordinate.h>
21
#include <geos/geom/Geometry.h>
22
#include <geos/geom/CoordinateSequence.h>
23
#include <geos/geom/CoordinateFilter.h>
24
#include <geos/geom/CoordinateSequenceFilter.h>
25
#include <geos/geom/Envelope.h>
26
27
#include <memory> // for unique_ptr
28
29
#ifndef GEOS_DEBUG
30
# define GEOS_DEBUG 0
31
#endif
32
33
#if GEOS_DEBUG
34
# include <iostream>
35
#endif
36
37
namespace geos {      // geos.
38
namespace operation { // geos.operation
39
namespace overlayng { // geos.operation.overlayng
40
41
namespace {
42
    template<typename T>
43
34.5M
    T clamp(const T& v, const T& low, const T& high) {
44
34.5M
        return v < low ? low : high < v ? high : v;
45
34.5M
    }
46
}
47
48
using geos::geom::Coordinate;
49
using geos::geom::CoordinateSequence;
50
using geos::geom::Geometry;
51
using geos::geom::Envelope;
52
53
/* public static */
54
std::unique_ptr<ElevationModel>
55
ElevationModel::create(const geom::Geometry& geom1, const geom::Geometry& geom2)
56
485k
{
57
485k
    Envelope extent;
58
485k
    if (! geom1.isEmpty() ) {
59
470k
      extent.expandToInclude(geom1.getEnvelopeInternal());
60
470k
    }
61
485k
    if (! geom2.isEmpty() ) {
62
355k
      extent.expandToInclude(geom2.getEnvelopeInternal());
63
355k
    }
64
485k
    std::unique_ptr<ElevationModel> model ( new ElevationModel(extent, DEFAULT_CELL_NUM, DEFAULT_CELL_NUM) );
65
485k
    if (! geom1.isEmpty() ) model->add(geom1);
66
485k
    if (! geom2.isEmpty() ) model->add(geom2);
67
485k
    return model;
68
485k
}
69
70
/* public static */
71
std::unique_ptr<ElevationModel>
72
ElevationModel::create(const geom::Geometry& geom1)
73
206k
{
74
206k
    Envelope extent;
75
206k
    if (! geom1.isEmpty() ) {
76
206k
      extent.expandToInclude(geom1.getEnvelopeInternal());
77
206k
    }
78
206k
    std::unique_ptr<ElevationModel> model ( new ElevationModel(extent, DEFAULT_CELL_NUM, DEFAULT_CELL_NUM) );
79
206k
    if (! geom1.isEmpty() ) model->add(geom1);
80
206k
    return model;
81
206k
}
82
83
const int ElevationModel::DEFAULT_CELL_NUM = 3;
84
85
ElevationModel::ElevationModel(const Envelope& nExtent, int nNumCellX, int nNumCellY)
86
    :
87
692k
    extent(nExtent),
88
692k
    numCellX(nNumCellX),
89
692k
    numCellY(nNumCellY)
90
692k
{
91
92
692k
    cellSizeX = extent.getWidth() / numCellX;
93
692k
    cellSizeY = extent.getHeight() / numCellY;
94
692k
    if(cellSizeX <= 0.0) {
95
19.4k
        numCellX = 1;
96
19.4k
    }
97
692k
    if(cellSizeY <= 0.0) {
98
17.0k
        numCellY = 1;
99
17.0k
    }
100
692k
    cells.resize(static_cast<std::size_t>(numCellX) * static_cast<std::size_t>(numCellY));
101
692k
}
102
103
/* public */
104
void
105
ElevationModel::add(const Geometry& geom)
106
1.03M
{
107
1.03M
    class Filter: public geom::CoordinateSequenceFilter {
108
1.03M
        ElevationModel& model;
109
1.03M
        bool hasZ;
110
1.03M
    public:
111
1.03M
        Filter(ElevationModel& nModel) : model(nModel), hasZ(true) {}
112
113
1.03M
        void filter_ro(const geom::CoordinateSequence& seq, std::size_t i) override
114
12.5M
        {
115
12.5M
            if (! seq.hasZ()) {
116
948k
                hasZ = false;
117
948k
                return;
118
948k
            }
119
11.6M
            const Coordinate& c = seq.getAt(i);
120
#if GEOS_DEBUG
121
            std::cerr << "Coordinate " << i << " of added geom is: "
122
<< c << std::endl;
123
#endif
124
11.6M
            model.add(c.x, c.y, c.z);
125
11.6M
        }
126
127
14.1M
        bool isDone() const override {
128
            // no need to scan if no Z present
129
14.1M
            return ! hasZ;
130
14.1M
        }
131
132
1.03M
        bool isGeometryChanged() const override {
133
0
            return false;
134
0
        }
135
136
1.03M
    };
137
138
1.03M
    Filter filter(*this);
139
1.03M
    geom.apply_ro(filter);
140
141
1.03M
}
142
143
/* protected */
144
void
145
ElevationModel::add(double x, double y, double z)
146
11.6M
{
147
11.6M
    if (std::isnan(z))
148
73.5k
      return;
149
11.5M
    hasZValue = true;
150
11.5M
    ElevationCell& cell = getCell(x, y); //, true);
151
11.5M
    cell.add(z);
152
11.5M
}
153
154
/* private */
155
void
156
ElevationModel::init()
157
44.0k
{
158
44.0k
    isInitialized = true;
159
44.0k
    int numCells = 0;
160
44.0k
    double sumZ = 0.0;
161
162
#if GEOS_DEBUG
163
    int offset = 0;
164
#endif
165
44.0k
    for (ElevationCell& cell : cells )
166
347k
    {
167
347k
        if (!cell.isNull()) {
168
104k
          cell.compute();
169
#if GEOS_DEBUG
170
          std::cerr << "init: cell" << offset
171
                    << " getZ: " << cell.getZ()
172
                    << std::endl;
173
#endif
174
104k
          numCells++;
175
104k
          sumZ += cell.getZ();
176
104k
        }
177
#if GEOS_DEBUG
178
        ++offset;
179
#endif
180
347k
    }
181
44.0k
    averageZ = DoubleNotANumber;
182
44.0k
    if (numCells > 0) {
183
44.0k
      averageZ = sumZ / numCells;
184
44.0k
    }
185
#if GEOS_DEBUG
186
    std::cerr << "init: numCells: " << numCells
187
              << " averageZ: " << averageZ << std::endl;
188
#endif
189
44.0k
}
190
191
/* public */
192
double
193
ElevationModel::getZ(double x, double y)
194
5.77M
{
195
5.77M
    if (! isInitialized)
196
0
    {
197
0
      init();
198
0
    }
199
5.77M
    const ElevationModel::ElevationCell& cell = getCell(x, y); //, false);
200
5.77M
    if (cell.isNull())
201
498k
    {
202
498k
      return averageZ;
203
498k
    }
204
5.28M
    return cell.getZ();
205
5.77M
}
206
207
/* public */
208
void
209
ElevationModel::populateZ(Geometry& geom)
210
364k
{
211
    // short-circuit if no Zs are present in model
212
364k
    if (! hasZValue)
213
320k
      return;
214
215
44.0k
    if (! isInitialized)
216
44.0k
      init();
217
218
44.0k
    class Filter: public geom::CoordinateFilter {
219
44.0k
        ElevationModel& model;
220
44.0k
    public:
221
44.0k
        Filter(ElevationModel& nModel) : model(nModel) {}
222
223
44.0k
        void filter_rw(geom::Coordinate* c) const override
224
12.2M
        {
225
#if GEOS_DEBUG
226
            std::cerr << "Input coord:  " << *c << std::endl;
227
#endif
228
12.2M
            if (std::isnan( c->z )) {
229
5.77M
                c->z = model.getZ(c->x, c->y);
230
#if GEOS_DEBUG
231
                std::cerr << "New coord: " << *c << std::endl;
232
#endif
233
5.77M
            }
234
12.2M
        }
235
236
44.0k
    };
237
238
44.0k
    Filter filter(*this);
239
#if GEOS_DEBUG
240
    std::cerr << "ElevationModel::poplateZ calling apply_rw(CoordinateSequenceFilter&) against" <<
241
              //std::type_name<decltype(ci)>()
242
              typeid(geom).name()
243
              << std::endl;
244
#endif
245
44.0k
    geom.apply_rw(&filter);
246
44.0k
}
247
248
/* private */
249
ElevationModel::ElevationCell&
250
ElevationModel::getCell(double x, double y) //, bool isCreateIfMissing
251
17.3M
{
252
17.3M
    int ix = 0;
253
17.3M
    if (numCellX > 1) {
254
17.3M
      ix = (int) ((x - extent.getMinX()) / cellSizeX);
255
17.3M
      ix = clamp(ix, 0, numCellX - 1);
256
17.3M
    }
257
17.3M
    int iy = 0;
258
17.3M
    if (numCellY > 1) {
259
17.2M
      iy = (int) ((y - extent.getMinY()) / cellSizeY);
260
17.2M
      iy = clamp(iy, 0, numCellY - 1);
261
17.2M
    }
262
17.3M
    int cellOffset = getCellOffset(ix, iy);
263
#if GEOS_DEBUG
264
    std::cerr << "Cell of coordinate " << x << "," << y
265
              << " is " << ix << "," << iy
266
              << " offset " << cellOffset << std::endl;
267
#endif
268
    assert ( cellOffset < numCellX * numCellY );
269
17.3M
    ElevationModel::ElevationCell& cell = cells[static_cast<std::size_t>(cellOffset)];
270
17.3M
    return cell;
271
17.3M
}
272
273
} // namespace geos.operation.overlayng
274
} // namespace geos.operation
275
} // namespace geos