1 /**********************************************************************
2  *
3  * GEOS - Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2001-2002 Vivid Solutions Inc.
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: original (by strk)
16  *
17  **********************************************************************/
18 
19 #include <geos/constants.h>
20 #include <geos/operation/overlay/ElevationMatrix.h>
21 #include <geos/util/IllegalArgumentException.h>
22 #include <geos/geom/Geometry.h>
23 #include <geos/geom/Coordinate.h>
24 #include <geos/geom/CoordinateSequence.h>
25 
26 #include <cassert>
27 #include <cmath>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31 
32 #ifndef GEOS_DEBUG
33 #define GEOS_DEBUG 0
34 #endif
35 #define PARANOIA_LEVEL 0
36 
37 #ifdef _MSC_VER
38 #pragma warning(disable:4355)
39 #endif
40 
41 using namespace std;
42 using namespace geos::geom;
43 
44 namespace geos {
45 namespace operation { // geos.operation
46 namespace overlay { // geos.operation.overlay
47 
ElevationMatrixFilter(ElevationMatrix & newEm)48 ElevationMatrixFilter::ElevationMatrixFilter(ElevationMatrix& newEm):
49     em(newEm)
50 { }
51 
52 void
filter_rw(Coordinate * c) const53 ElevationMatrixFilter::filter_rw(Coordinate* c) const
54 {
55 #if GEOS_DEBUG
56     cerr << "ElevationMatrixFilter::filter_rw(" << c->toString() << ") called"
57          << endl;
58 #endif
59 
60     // already has a Z value, nothing to do
61     if(! std::isnan(c->z)) {
62         return;
63     }
64 
65     double p_avgElevation = em.getAvgElevation();
66 
67     try {
68         const ElevationMatrixCell& emc = em.getCell(*c);
69         c->z = emc.getAvg();
70         if(std::isnan(c->z)) {
71             c->z = p_avgElevation;
72         }
73 #if GEOS_DEBUG
74         cerr << "  z set to " << c->z << endl;
75 #endif
76     }
77     catch(const util::IllegalArgumentException& /* ex */) {
78         c->z = avgElevation;
79     }
80 }
81 
82 void
filter_ro(const Coordinate * c)83 ElevationMatrixFilter::filter_ro(const Coordinate* c)
84 {
85 #if GEOS_DEBUG
86     cerr << "ElevationMatrixFilter::filter_ro(" << c->toString() << ") called"
87          << endl;
88 #endif
89     em.add(*c);
90 }
91 
92 
ElevationMatrix(const Envelope & newEnv,unsigned int newRows,unsigned int newCols)93 ElevationMatrix::ElevationMatrix(const Envelope& newEnv,
94                                  unsigned int newRows, unsigned int newCols):
95     filter(*this),
96     env(newEnv), cols(newCols), rows(newRows),
97     avgElevationComputed(false),
98     avgElevation(DoubleNotANumber),
99     cells(newRows * newCols)
100 {
101     cellwidth = env.getWidth() / cols;
102     cellheight = env.getHeight() / rows;
103     if(! cellwidth) {
104         cols = 1;
105     }
106     if(! cellheight) {
107         rows = 1;
108     }
109 }
110 
111 void
add(const Geometry * geom)112 ElevationMatrix::add(const Geometry* geom)
113 {
114 #if GEOS_DEBUG
115     cerr << "ElevationMatrix::add(Geometry *) called" << endl;
116 #endif // GEOS_DEBUG
117 
118     // Cannot add Geometries to an ElevationMatrix after it's average
119     // elevation has been computed
120     assert(!avgElevationComputed);
121 
122     //ElevationMatrixFilter filter(this);
123     geom->apply_ro(&filter);
124 
125 }
126 
127 #if 0
128 void
129 ElevationMatrix::add(const CoordinateSequence* cs)
130 {
131     unsigned int ncoords = cs->getSize();
132     for(unsigned int i = 0; i < ncoords; i++) {
133         add(cs->getAt(i));
134     }
135 }
136 #endif
137 
138 void
add(const Coordinate & c)139 ElevationMatrix::add(const Coordinate& c)
140 {
141     if(std::isnan(c.z)) {
142         return;
143     }
144     try {
145         ElevationMatrixCell& emc = getCell(c);
146         emc.add(c);
147     }
148     catch(const util::IllegalArgumentException& exp) {
149         // coordinate do not overlap matrix
150         cerr << "ElevationMatrix::add(" << c.toString()
151              << "): Coordinate does not overlap grid extent: "
152              << exp.what() << endl;
153         return;
154     }
155 }
156 
157 ElevationMatrixCell&
getCell(const Coordinate & c)158 ElevationMatrix::getCell(const Coordinate& c)
159 {
160     int col, row;
161 
162     if(! cellwidth) {
163         col = 0;
164     }
165     else {
166         double xoffset = c.x - env.getMinX();
167         col = (int)(xoffset / cellwidth);
168         if(col == (int)cols) {
169             col = cols - 1;
170         }
171     }
172     if(! cellheight) {
173         row = 0;
174     }
175     else {
176         double yoffset = c.y - env.getMinY();
177         row = (int)(yoffset / cellheight);
178         if(row == (int)rows) {
179             row = rows - 1;
180         }
181     }
182     int celloffset = (cols * row) + col;
183 
184     if(celloffset < 0 || celloffset >= (int)(cols * rows)) {
185         ostringstream s;
186         s << "ElevationMatrix::getCell got a Coordinate out of grid extent (" << env.toString() << ") - cols:" << cols <<
187           " rows:" << rows;
188         throw util::IllegalArgumentException(s.str());
189     }
190 
191     return cells[celloffset];
192 }
193 
194 const ElevationMatrixCell&
getCell(const Coordinate & c) const195 ElevationMatrix::getCell(const Coordinate& c) const
196 {
197     return (const ElevationMatrixCell&)
198            ((ElevationMatrix*)this)->getCell(c);
199 }
200 
201 double
getAvgElevation() const202 ElevationMatrix::getAvgElevation() const
203 {
204     if(avgElevationComputed) {
205         return avgElevation;
206     }
207     double ztot = 0;
208     int zvals = 0;
209     for(unsigned int r = 0; r < rows; r++) {
210         for(unsigned int c = 0; c < cols; c++) {
211             const ElevationMatrixCell& cell = cells[(r * cols) + c];
212             double e = cell.getAvg();
213             if(!std::isnan(e)) {
214                 zvals++;
215                 ztot += e;
216             }
217         }
218     }
219     if(zvals) {
220         avgElevation = ztot / zvals;
221     }
222     else {
223         avgElevation = DoubleNotANumber;
224     }
225 
226     avgElevationComputed = true;
227 
228     return avgElevation;
229 }
230 
231 string
print() const232 ElevationMatrix::print() const
233 {
234     ostringstream ret;
235     ret << "Cols:" << cols << " Rows:" << rows << " AvgElevation:" << getAvgElevation() << endl;
236     for(unsigned int r = 0; r < rows; r++) {
237         for(unsigned int c = 0; c < cols; c++) {
238             ret << cells[(r * cols) + c].print() << '\t';
239         }
240         ret << endl;
241     }
242     return ret.str();
243 }
244 
245 void
elevate(Geometry * g) const246 ElevationMatrix::elevate(Geometry* g) const
247 {
248 
249     // Nothing to do if no elevation info in matrix
250     if(std::isnan(getAvgElevation())) {
251         return;
252     }
253 
254     g->apply_rw(&filter);
255 }
256 
257 } // namespace geos.operation.overlay
258 } // namespace geos.operation
259 } // namespace geos;
260