Coverage Report

Created: 2025-08-25 06:57

/src/geos/src/operation/overlayng/ElevationModel.cpp
Line
Count
Source (jump to first uncovered line)
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
2.47M
    T clamp(const T& v, const T& low, const T& high) {
44
2.47M
        return v < low ? low : high < v ? high : v;
45
2.47M
    }
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
162k
{
57
162k
    Envelope extent;
58
162k
    if (! geom1.isEmpty() ) {
59
158k
      extent.expandToInclude(geom1.getEnvelopeInternal());
60
158k
    }
61
162k
    if (! geom2.isEmpty() ) {
62
131k
      extent.expandToInclude(geom2.getEnvelopeInternal());
63
131k
    }
64
162k
    std::unique_ptr<ElevationModel> model ( new ElevationModel(extent, DEFAULT_CELL_NUM, DEFAULT_CELL_NUM) );
65
162k
    if (! geom1.isEmpty() ) model->add(geom1);
66
162k
    if (! geom2.isEmpty() ) model->add(geom2);
67
162k
    return model;
68
162k
}
69
70
/* public static */
71
std::unique_ptr<ElevationModel>
72
ElevationModel::create(const geom::Geometry& geom1)
73
69.1k
{
74
69.1k
    Envelope extent;
75
69.1k
    if (! geom1.isEmpty() ) {
76
69.1k
      extent.expandToInclude(geom1.getEnvelopeInternal());
77
69.1k
    }
78
69.1k
    std::unique_ptr<ElevationModel> model ( new ElevationModel(extent, DEFAULT_CELL_NUM, DEFAULT_CELL_NUM) );
79
69.1k
    if (! geom1.isEmpty() ) model->add(geom1);
80
69.1k
    return model;
81
69.1k
}
82
83
const int ElevationModel::DEFAULT_CELL_NUM = 3;
84
85
ElevationModel::ElevationModel(const Envelope& nExtent, int nNumCellX, int nNumCellY)
86
    :
87
231k
    extent(nExtent),
88
231k
    numCellX(nNumCellX),
89
231k
    numCellY(nNumCellY)
90
231k
{
91
92
231k
    cellSizeX = extent.getWidth() / numCellX;
93
231k
    cellSizeY = extent.getHeight() / numCellY;
94
231k
    if(cellSizeX <= 0.0) {
95
16.0k
        numCellX = 1;
96
16.0k
    }
97
231k
    if(cellSizeY <= 0.0) {
98
5.34k
        numCellY = 1;
99
5.34k
    }
100
231k
    cells.resize(static_cast<std::size_t>(numCellX) * static_cast<std::size_t>(numCellY));
101
231k
}
102
103
/* public */
104
void
105
ElevationModel::add(const Geometry& geom)
106
360k
{
107
360k
    class Filter: public geom::CoordinateSequenceFilter {
108
360k
        ElevationModel& model;
109
360k
        bool hasZ;
110
360k
    public:
111
360k
        Filter(ElevationModel& nModel) : model(nModel), hasZ(true) {}
112
113
360k
        void filter_ro(const geom::CoordinateSequence& seq, std::size_t i) override
114
1.40M
        {
115
1.40M
            if (! seq.hasZ()) {
116
343k
                hasZ = false;
117
343k
                return;
118
343k
            }
119
1.05M
            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
1.05M
            model.add(c.x, c.y, c.z);
125
1.05M
        }
126
127
1.82M
        bool isDone() const override {
128
            // no need to scan if no Z present
129
1.82M
            return ! hasZ;
130
1.82M
        }
131
132
360k
        bool isGeometryChanged() const override {
133
0
            return false;
134
0
        }
135
136
360k
    };
137
138
360k
    Filter filter(*this);
139
360k
    geom.apply_ro(filter);
140
141
360k
}
142
143
/* protected */
144
void
145
ElevationModel::add(double x, double y, double z)
146
1.05M
{
147
1.05M
    if (std::isnan(z))
148
9.37k
      return;
149
1.04M
    hasZValue = true;
150
1.04M
    ElevationCell& cell = getCell(x, y); //, true);
151
1.04M
    cell.add(z);
152
1.04M
}
153
154
/* private */
155
void
156
ElevationModel::init()
157
8.37k
{
158
8.37k
    isInitialized = true;
159
8.37k
    int numCells = 0;
160
8.37k
    double sumZ = 0.0;
161
162
#if GEOS_DEBUG
163
    int offset = 0;
164
#endif
165
8.37k
    for (ElevationCell& cell : cells )
166
66.1k
    {
167
66.1k
        if (!cell.isNull()) {
168
18.9k
          cell.compute();
169
#if GEOS_DEBUG
170
          std::cerr << "init: cell" << offset
171
                    << " getZ: " << cell.getZ()
172
                    << std::endl;
173
#endif
174
18.9k
          numCells++;
175
18.9k
          sumZ += cell.getZ();
176
18.9k
        }
177
#if GEOS_DEBUG
178
        ++offset;
179
#endif
180
66.1k
    }
181
8.37k
    averageZ = DoubleNotANumber;
182
8.37k
    if (numCells > 0) {
183
8.37k
      averageZ = sumZ / numCells;
184
8.37k
    }
185
#if GEOS_DEBUG
186
    std::cerr << "init: numCells: " << numCells
187
              << " averageZ: " << averageZ << std::endl;
188
#endif
189
8.37k
}
190
191
/* public */
192
double
193
ElevationModel::getZ(double x, double y)
194
189k
{
195
189k
    if (! isInitialized)
196
0
    {
197
0
      init();
198
0
    }
199
189k
    const ElevationModel::ElevationCell& cell = getCell(x, y); //, false);
200
189k
    if (cell.isNull())
201
12.2k
    {
202
12.2k
      return averageZ;
203
12.2k
    }
204
177k
    return cell.getZ();
205
189k
}
206
207
/* public */
208
void
209
ElevationModel::populateZ(Geometry& geom)
210
120k
{
211
    // short-circuit if no Zs are present in model
212
120k
    if (! hasZValue)
213
111k
      return;
214
215
8.37k
    if (! isInitialized)
216
8.37k
      init();
217
218
8.37k
    class Filter: public geom::CoordinateFilter {
219
8.37k
        ElevationModel& model;
220
8.37k
    public:
221
8.37k
        Filter(ElevationModel& nModel) : model(nModel) {}
222
223
8.37k
        void filter_rw(geom::Coordinate* c) const override
224
679k
        {
225
#if GEOS_DEBUG
226
            std::cerr << "Input coord:  " << *c << std::endl;
227
#endif
228
679k
            if (std::isnan( c->z )) {
229
189k
                c->z = model.getZ(c->x, c->y);
230
#if GEOS_DEBUG
231
                std::cerr << "New coord: " << *c << std::endl;
232
#endif
233
189k
            }
234
679k
        }
235
236
8.37k
    };
237
238
8.37k
    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
8.37k
    geom.apply_rw(&filter);
246
8.37k
}
247
248
/* private */
249
ElevationModel::ElevationCell&
250
ElevationModel::getCell(double x, double y) //, bool isCreateIfMissing
251
1.23M
{
252
1.23M
    int ix = 0;
253
1.23M
    if (numCellX > 1) {
254
1.23M
      ix = (int) ((x - extent.getMinX()) / cellSizeX);
255
1.23M
      ix = clamp(ix, 0, numCellX - 1);
256
1.23M
    }
257
1.23M
    int iy = 0;
258
1.23M
    if (numCellY > 1) {
259
1.23M
      iy = (int) ((y - extent.getMinY()) / cellSizeY);
260
1.23M
      iy = clamp(iy, 0, numCellY - 1);
261
1.23M
    }
262
1.23M
    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
1.23M
    assert ( cellOffset < numCellX * numCellY );
269
1.23M
    ElevationModel::ElevationCell& cell = cells[static_cast<std::size_t>(cellOffset)];
270
1.23M
    return cell;
271
1.23M
}
272
273
} // namespace geos.operation.overlayng
274
} // namespace geos.operation
275
} // namespace geos