/src/gdal/ogr/ogrsf_frmts/wasp/ogrwasplayer.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: WAsP Translator |
4 | | * Purpose: Implements OGRWAsPLayer class. |
5 | | * Author: Vincent Mora, vincent dot mora at oslandia dot com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2014, Oslandia <info at oslandia dot com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "ogrwasp.h" |
14 | | #include "cpl_conv.h" |
15 | | #include "cpl_string.h" |
16 | | #include "ogrsf_frmts.h" |
17 | | |
18 | | #include <cassert> |
19 | | #include <map> |
20 | | #include <sstream> |
21 | | |
22 | | /************************************************************************/ |
23 | | /* OGRWAsPLayer() */ |
24 | | /************************************************************************/ |
25 | | |
26 | | OGRWAsPLayer::OGRWAsPLayer(GDALDataset *poDS, const char *pszName, |
27 | | VSILFILE *hFileHandle, |
28 | | OGRSpatialReference *poSpatialRef) |
29 | 15.2k | : m_poDS(poDS), bMerge(false), iFeatureCount(0), sName(pszName), |
30 | 15.2k | hFile(hFileHandle), sFirstField{}, sSecondField{}, sGeomField{}, |
31 | 15.2k | iFirstFieldIdx(0), iSecondFieldIdx(1), iGeomFieldIdx(0), |
32 | 15.2k | poLayerDefn(new OGRFeatureDefn(pszName)), |
33 | 15.2k | poSpatialReference(poSpatialRef), iOffsetFeatureBegin(VSIFTellL(hFile)), |
34 | 15.2k | eMode(READ_ONLY) |
35 | 15.2k | { |
36 | 15.2k | SetDescription(poLayerDefn->GetName()); |
37 | 15.2k | poLayerDefn->Reference(); |
38 | 15.2k | poLayerDefn->GetGeomFieldDefn(0)->SetType(wkbLineString25D); |
39 | 15.2k | poLayerDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialReference); |
40 | 15.2k | if (poSpatialReference) |
41 | 3.69k | poSpatialReference->Reference(); |
42 | 15.2k | } |
43 | | |
44 | | OGRWAsPLayer::OGRWAsPLayer( |
45 | | GDALDataset *poDS, const char *pszName, VSILFILE *hFileHandle, |
46 | | OGRSpatialReference *poSpatialRef, const CPLString &sFirstFieldParam, |
47 | | const CPLString &sSecondFieldParam, const CPLString &sGeomFieldParam, |
48 | | bool bMergeParam, double *pdfToleranceParam, |
49 | | double *pdfAdjacentPointToleranceParam, double *pdfPointToCircleRadiusParam) |
50 | 0 | : m_poDS(poDS), bMerge(bMergeParam), iFeatureCount(0), sName(pszName), |
51 | 0 | hFile(hFileHandle), sFirstField(sFirstFieldParam), |
52 | 0 | sSecondField(sSecondFieldParam), sGeomField(sGeomFieldParam), |
53 | 0 | iFirstFieldIdx(-1), iSecondFieldIdx(-1), |
54 | 0 | iGeomFieldIdx(sGeomFieldParam.empty() ? 0 : -1), |
55 | 0 | poLayerDefn(new OGRFeatureDefn(pszName)), |
56 | 0 | poSpatialReference(poSpatialRef), |
57 | 0 | iOffsetFeatureBegin(VSIFTellL(hFile)), // Avoids coverity warning. |
58 | 0 | eMode(WRITE_ONLY), pdfTolerance(pdfToleranceParam), |
59 | 0 | pdfAdjacentPointTolerance(pdfAdjacentPointToleranceParam), |
60 | 0 | pdfPointToCircleRadius(pdfPointToCircleRadiusParam) |
61 | 0 | { |
62 | 0 | SetDescription(poLayerDefn->GetName()); |
63 | 0 | poLayerDefn->Reference(); |
64 | 0 | poLayerDefn->GetGeomFieldDefn(0)->SetType(wkbLineString25D); |
65 | 0 | poLayerDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialReference); |
66 | 0 | if (poSpatialReference) |
67 | 0 | poSpatialReference->Reference(); |
68 | 0 | } |
69 | | |
70 | | /************************************************************************/ |
71 | | /* ~OGRWAsPLayer() */ |
72 | | /************************************************************************/ |
73 | | |
74 | | OGRWAsPLayer::~OGRWAsPLayer() |
75 | | |
76 | 15.2k | { |
77 | 15.2k | if (bMerge) |
78 | 0 | { |
79 | | /* If polygon where used, we have to merge lines before output */ |
80 | | /* lines must be merged if they have the same left/right values */ |
81 | | /* and touch at end points. */ |
82 | | /* Those lines appear when polygon with the same roughness touch */ |
83 | | /* since the boundary between them is not wanted */ |
84 | | /* We do it here since we are sure we have all polygons */ |
85 | | /* We first detect touching lines, then the kind of touching, */ |
86 | | /* candidates for merging are pairs of neighbors with corresponding */ |
87 | | /* left/right values. Finally we merge */ |
88 | |
|
89 | 0 | typedef std::map<std::pair<double, double>, std::vector<int>> PointMap; |
90 | 0 | PointMap oMap; |
91 | 0 | for (int i = 0; i < static_cast<int>(oBoundaries.size()); i++) |
92 | 0 | { |
93 | 0 | const Boundary &p = oBoundaries[i]; |
94 | 0 | OGRPoint startP, endP; |
95 | 0 | p.poLine->StartPoint(&startP); |
96 | 0 | p.poLine->EndPoint(&endP); |
97 | 0 | oMap[std::make_pair(startP.getX(), startP.getY())].push_back(i); |
98 | 0 | oMap[std::make_pair(endP.getX(), endP.getY())].push_back(i); |
99 | 0 | } |
100 | |
|
101 | 0 | std::vector<int> endNeighbors(oBoundaries.size(), -1); |
102 | 0 | std::vector<int> startNeighbors(oBoundaries.size(), -1); |
103 | 0 | for (PointMap::const_iterator it = oMap.begin(); it != oMap.end(); ++it) |
104 | 0 | { |
105 | 0 | if (it->second.size() != 2) |
106 | 0 | continue; |
107 | 0 | int i = it->second[0]; |
108 | 0 | int j = it->second[1]; |
109 | |
|
110 | 0 | const Boundary &p = oBoundaries[i]; |
111 | 0 | OGRPoint startP, endP; |
112 | 0 | p.poLine->StartPoint(&startP); |
113 | 0 | p.poLine->EndPoint(&endP); |
114 | 0 | const Boundary &q = oBoundaries[j]; |
115 | 0 | OGRPoint startQ, endQ; |
116 | 0 | q.poLine->StartPoint(&startQ); |
117 | 0 | q.poLine->EndPoint(&endQ); |
118 | 0 | if (isEqual(p.dfRight, q.dfRight) && isEqual(p.dfLeft, q.dfLeft)) |
119 | 0 | { |
120 | 0 | if (endP.Equals(&startQ)) |
121 | 0 | { |
122 | 0 | endNeighbors[i] = j; |
123 | 0 | startNeighbors[j] = i; |
124 | 0 | } |
125 | 0 | if (endQ.Equals(&startP)) |
126 | 0 | { |
127 | 0 | endNeighbors[j] = i; |
128 | 0 | startNeighbors[i] = j; |
129 | 0 | } |
130 | 0 | } |
131 | 0 | if (isEqual(p.dfRight, q.dfLeft) && isEqual(p.dfLeft, q.dfRight)) |
132 | 0 | { |
133 | 0 | if (startP.Equals(&startQ)) |
134 | 0 | { |
135 | 0 | startNeighbors[i] = j; |
136 | 0 | startNeighbors[j] = i; |
137 | 0 | } |
138 | 0 | if (endP.Equals(&endQ)) |
139 | 0 | { |
140 | 0 | endNeighbors[j] = i; |
141 | 0 | endNeighbors[i] = j; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | /* output all end lines (one neighbor only) and all their neighbors*/ |
147 | 0 | if (!oBoundaries.empty()) |
148 | 0 | { |
149 | 0 | std::vector<bool> oHasBeenMerged(oBoundaries.size(), false); |
150 | 0 | for (size_t i = 0; i < oBoundaries.size(); i++) |
151 | 0 | { |
152 | 0 | if (!oHasBeenMerged[i] && |
153 | 0 | (startNeighbors[i] < 0 || endNeighbors[i] < 0)) |
154 | 0 | { |
155 | 0 | oHasBeenMerged[i] = true; |
156 | 0 | Boundary *p = &oBoundaries[i]; |
157 | 0 | int j = startNeighbors[i] < 0 ? endNeighbors[i] |
158 | 0 | : startNeighbors[i]; |
159 | 0 | if (startNeighbors[i] >= 0) |
160 | 0 | { |
161 | | /* reverse the line and left/right */ |
162 | 0 | p->poLine->reversePoints(); |
163 | 0 | std::swap(p->dfLeft, p->dfRight); |
164 | 0 | } |
165 | 0 | while (j >= 0) |
166 | 0 | { |
167 | 0 | assert(!oHasBeenMerged[j]); |
168 | 0 | oHasBeenMerged[j] = true; |
169 | |
|
170 | 0 | OGRLineString *other = oBoundaries[j].poLine; |
171 | 0 | OGRPoint endP, startOther; |
172 | 0 | p->poLine->EndPoint(&endP); |
173 | 0 | other->StartPoint(&startOther); |
174 | 0 | if (!endP.Equals(&startOther)) |
175 | 0 | other->reversePoints(); |
176 | 0 | p->poLine->addSubLineString(other, 1); |
177 | | |
178 | | /* next neighbor */ |
179 | 0 | if (endNeighbors[j] >= 0 && |
180 | 0 | !oHasBeenMerged[endNeighbors[j]]) |
181 | 0 | j = endNeighbors[j]; |
182 | 0 | else if (startNeighbors[j] >= 0 && |
183 | 0 | !oHasBeenMerged[startNeighbors[j]]) |
184 | 0 | j = startNeighbors[j]; |
185 | 0 | else |
186 | 0 | j = -1; |
187 | 0 | } |
188 | 0 | WriteRoughness(p->poLine, p->dfLeft, p->dfRight); |
189 | 0 | } |
190 | 0 | } |
191 | | /* output all rings */ |
192 | 0 | for (size_t i = 0; i < oBoundaries.size(); i++) |
193 | 0 | { |
194 | 0 | if (oHasBeenMerged[i]) |
195 | 0 | continue; |
196 | 0 | oHasBeenMerged[i] = true; |
197 | 0 | Boundary *p = &oBoundaries[i]; |
198 | 0 | int j = |
199 | 0 | startNeighbors[i] < 0 ? endNeighbors[i] : startNeighbors[i]; |
200 | 0 | assert(j != -1); |
201 | 0 | if (startNeighbors[i] >= 0) |
202 | 0 | { |
203 | | /* reverse the line and left/right */ |
204 | 0 | p->poLine->reversePoints(); |
205 | 0 | std::swap(p->dfLeft, p->dfRight); |
206 | 0 | } |
207 | 0 | while (!oHasBeenMerged[j]) |
208 | 0 | { |
209 | 0 | oHasBeenMerged[j] = true; |
210 | |
|
211 | 0 | OGRLineString *other = oBoundaries[j].poLine; |
212 | 0 | OGRPoint endP, startOther; |
213 | 0 | p->poLine->EndPoint(&endP); |
214 | 0 | other->StartPoint(&startOther); |
215 | 0 | if (!endP.Equals(&startOther)) |
216 | 0 | other->reversePoints(); |
217 | 0 | p->poLine->addSubLineString(other, 1); |
218 | | |
219 | | /* next neighbor */ |
220 | 0 | if (endNeighbors[j] >= 0) |
221 | 0 | j = endNeighbors[j]; |
222 | 0 | else if (startNeighbors[j] >= 0) |
223 | 0 | j = startNeighbors[j]; |
224 | 0 | else |
225 | 0 | assert(false); /* there must be a neighbor since it is a |
226 | | ring */ |
227 | 0 | } |
228 | 0 | WriteRoughness(p->poLine, p->dfLeft, p->dfRight); |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | 15.2k | else |
233 | 15.2k | { |
234 | 15.2k | for (size_t i = 0; i < oBoundaries.size(); i++) |
235 | 0 | { |
236 | 0 | Boundary *p = &oBoundaries[i]; |
237 | 0 | WriteRoughness(p->poLine, p->dfLeft, p->dfRight); |
238 | 0 | } |
239 | 15.2k | } |
240 | 15.2k | poLayerDefn->Release(); |
241 | 15.2k | if (poSpatialReference) |
242 | 3.69k | poSpatialReference->Release(); |
243 | 15.2k | for (size_t i = 0; i < oZones.size(); i++) |
244 | 0 | delete oZones[i].poPolygon; |
245 | 15.2k | for (size_t i = 0; i < oBoundaries.size(); i++) |
246 | 0 | delete oBoundaries[i].poLine; |
247 | 15.2k | } |
248 | | |
249 | | /************************************************************************/ |
250 | | /* Simplify() */ |
251 | | /************************************************************************/ |
252 | | OGRLineString *OGRWAsPLayer::Simplify(const OGRLineString &line) const |
253 | 0 | { |
254 | 0 | if (!line.getNumPoints()) |
255 | 0 | return line.clone(); |
256 | | |
257 | 0 | std::unique_ptr<OGRLineString> poLine( |
258 | 0 | (pdfTolerance.get() && *pdfTolerance > 0 |
259 | 0 | ? line.Simplify(*pdfTolerance)->toLineString() |
260 | 0 | : line.clone())); |
261 | |
|
262 | 0 | OGRPoint startPt, endPt; |
263 | 0 | poLine->StartPoint(&startPt); |
264 | 0 | poLine->EndPoint(&endPt); |
265 | 0 | const bool isRing = CPL_TO_BOOL(startPt.Equals(&endPt)); |
266 | |
|
267 | 0 | if (pdfAdjacentPointTolerance.get() && *pdfAdjacentPointTolerance > 0) |
268 | 0 | { |
269 | | /* remove consecutive points that are too close */ |
270 | 0 | auto newLine = std::make_unique<OGRLineString>(); |
271 | 0 | const double dist = *pdfAdjacentPointTolerance; |
272 | 0 | OGRPoint pt; |
273 | 0 | poLine->StartPoint(&pt); |
274 | 0 | newLine->addPoint(&pt); |
275 | 0 | const int iNumPoints = poLine->getNumPoints(); |
276 | 0 | for (int v = 1; v < iNumPoints; v++) |
277 | 0 | { |
278 | 0 | if (fabs(poLine->getX(v) - pt.getX()) > dist || |
279 | 0 | fabs(poLine->getY(v) - pt.getY()) > dist) |
280 | 0 | { |
281 | 0 | poLine->getPoint(v, &pt); |
282 | 0 | newLine->addPoint(&pt); |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | /* force closed loop if initially closed */ |
287 | 0 | if (isRing) |
288 | 0 | newLine->setPoint(newLine->getNumPoints() - 1, &startPt); |
289 | |
|
290 | 0 | poLine.reset(newLine.release()); |
291 | 0 | } |
292 | |
|
293 | 0 | if (pdfPointToCircleRadius.get() && *pdfPointToCircleRadius > 0) |
294 | 0 | { |
295 | 0 | const double radius = *pdfPointToCircleRadius; |
296 | |
|
297 | 0 | #undef WASP_EXPERIMENTAL_CODE |
298 | | #ifdef WASP_EXPERIMENTAL_CODE |
299 | | if (3 == poLine->getNumPoints() && isRing) |
300 | | { |
301 | | OGRPoint p0, p1; |
302 | | poLine->getPoint(0, &p0); |
303 | | poLine->getPoint(1, &p1); |
304 | | const double dir[2] = {p1.getX() - p0.getX(), |
305 | | p1.getY() - p0.getY()}; |
306 | | const double dirNrm = sqrt(dir[0] * dir[0] + dir[1] * dir[1]); |
307 | | if (dirNrm > radius) |
308 | | { |
309 | | /* convert to rectangle by finding the direction */ |
310 | | /* and offsetting */ |
311 | | const double ortho[2] = {-radius * dir[1] / dirNrm, |
312 | | radius * dir[0] / dirNrm}; |
313 | | poLine->setNumPoints(5); |
314 | | poLine->setPoint(0, p0.getX() - ortho[0], p0.getY() - ortho[1]); |
315 | | poLine->setPoint(1, p1.getX() - ortho[0], p1.getY() - ortho[1]); |
316 | | poLine->setPoint(2, p1.getX() + ortho[0], p1.getY() + ortho[1]); |
317 | | poLine->setPoint(3, p0.getX() + ortho[0], p0.getY() + ortho[1]); |
318 | | poLine->setPoint(4, p0.getX() - ortho[0], p0.getY() - ortho[1]); |
319 | | } |
320 | | else |
321 | | { |
322 | | /* reduce to a point to be dealt with just after*/ |
323 | | poLine->setNumPoints(1); |
324 | | poLine->setPoint(0, 0.5 * (p0.getX() + p1.getX()), |
325 | | 0.5 * (p0.getY() + p1.getY())); |
326 | | } |
327 | | } |
328 | | #endif |
329 | |
|
330 | 0 | if (1 == poLine->getNumPoints()) |
331 | 0 | { |
332 | 0 | const int nbPt = 8; |
333 | 0 | const double cx = poLine->getX(0); |
334 | 0 | const double cy = poLine->getY(0); |
335 | 0 | poLine->setNumPoints(nbPt + 1); |
336 | 0 | for (int v = 0; v <= nbPt; v++) |
337 | 0 | { |
338 | | /* the % is necessary to make sure the ring */ |
339 | | /* is really closed and not open due to */ |
340 | | /* roundoff error of cos(2pi) and sin(2pi) */ |
341 | 0 | poLine->setPoint( |
342 | 0 | v, cx + radius * cos((v % nbPt) * (2 * M_PI / nbPt)), |
343 | 0 | cy + radius * sin((v % nbPt) * (2 * M_PI / nbPt))); |
344 | 0 | } |
345 | 0 | } |
346 | 0 | } |
347 | |
|
348 | 0 | return poLine.release(); |
349 | 0 | } |
350 | | |
351 | | /************************************************************************/ |
352 | | /* WriteElevation() */ |
353 | | /************************************************************************/ |
354 | | |
355 | | OGRErr OGRWAsPLayer::WriteElevation(OGRLineString *poGeom, const double &dfZ) |
356 | | |
357 | 0 | { |
358 | 0 | std::unique_ptr<OGRLineString> poLine(Simplify(*poGeom)); |
359 | |
|
360 | 0 | const int iNumPoints = poLine->getNumPoints(); |
361 | 0 | if (!iNumPoints) |
362 | 0 | return OGRERR_NONE; /* empty geom */ |
363 | | |
364 | 0 | VSIFPrintfL(hFile, "%11.3f %11d", dfZ, iNumPoints); |
365 | |
|
366 | 0 | for (int v = 0; v < iNumPoints; v++) |
367 | 0 | { |
368 | 0 | if (!(v % 3)) |
369 | 0 | VSIFPrintfL(hFile, "\n"); |
370 | 0 | VSIFPrintfL(hFile, "%11.1f %11.1f ", poLine->getX(v), poLine->getY(v)); |
371 | 0 | } |
372 | 0 | VSIFPrintfL(hFile, "\n"); |
373 | |
|
374 | 0 | return OGRERR_NONE; |
375 | 0 | } |
376 | | |
377 | | OGRErr OGRWAsPLayer::WriteElevation(OGRGeometry *poGeom, const double &dfZ) |
378 | | |
379 | 0 | { |
380 | 0 | switch (poGeom->getGeometryType()) |
381 | 0 | { |
382 | 0 | case wkbLineString: |
383 | 0 | case wkbLineString25D: |
384 | 0 | return WriteElevation(poGeom->toLineString(), dfZ); |
385 | 0 | case wkbMultiLineString25D: |
386 | 0 | case wkbMultiLineString: |
387 | 0 | { |
388 | 0 | for (auto &&poMember : poGeom->toGeometryCollection()) |
389 | 0 | { |
390 | 0 | const OGRErr err = WriteElevation(poMember, dfZ); |
391 | 0 | if (OGRERR_NONE != err) |
392 | 0 | return err; |
393 | 0 | } |
394 | 0 | return OGRERR_NONE; |
395 | 0 | } |
396 | 0 | default: |
397 | 0 | { |
398 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
399 | 0 | "Cannot handle geometry of type %s", |
400 | 0 | OGRGeometryTypeToName(poGeom->getGeometryType())); |
401 | 0 | break; |
402 | 0 | } |
403 | 0 | } |
404 | 0 | return OGRERR_FAILURE; /* avoid visual warning */ |
405 | 0 | } |
406 | | |
407 | | /************************************************************************/ |
408 | | /* WriteRoughness() */ |
409 | | /************************************************************************/ |
410 | | |
411 | | OGRErr OGRWAsPLayer::WriteRoughness(OGRPolygon *poGeom, const double &dfZ) |
412 | | |
413 | 0 | { |
414 | | /* text intersection with polygons in the stack */ |
415 | | /* for linestrings intersections, write linestring */ |
416 | | /* for polygon intersection error */ |
417 | | /* for point intersection do nothing */ |
418 | |
|
419 | 0 | OGRErr err = OGRERR_NONE; |
420 | 0 | OGREnvelope oEnvelope; |
421 | 0 | poGeom->getEnvelope(&oEnvelope); |
422 | 0 | for (size_t i = 0; i < oZones.size(); i++) |
423 | 0 | { |
424 | 0 | const bool bIntersects = |
425 | 0 | CPL_TO_BOOL(oEnvelope.Intersects(oZones[i].oEnvelope)); |
426 | 0 | if (bIntersects && |
427 | 0 | (!bMerge || !isEqual(dfZ, oZones[i].dfZ))) /* boundary */ |
428 | 0 | { |
429 | 0 | OGRGeometry *poIntersection = |
430 | 0 | oZones[i].poPolygon->Intersection(poGeom); |
431 | 0 | if (poIntersection) |
432 | 0 | { |
433 | 0 | switch (poIntersection->getGeometryType()) |
434 | 0 | { |
435 | 0 | case wkbLineString: |
436 | 0 | case wkbLineString25D: |
437 | 0 | { |
438 | 0 | Boundary oB = {poIntersection->toLineString()->clone(), |
439 | 0 | dfZ, oZones[i].dfZ}; |
440 | 0 | oBoundaries.push_back(oB); |
441 | 0 | } |
442 | 0 | break; |
443 | 0 | case wkbMultiLineString: |
444 | 0 | case wkbMultiLineString25D: |
445 | 0 | { |
446 | | /*TODO join the multilinestring into linestring*/ |
447 | 0 | OGRLineString *poLine = nullptr; |
448 | 0 | OGRPoint *poStart = new OGRPoint; |
449 | 0 | OGRPoint *poEnd = new OGRPoint; |
450 | 0 | for (auto &&poSubLine : |
451 | 0 | poIntersection->toMultiLineString()) |
452 | 0 | { |
453 | 0 | poSubLine->StartPoint(poStart); |
454 | |
|
455 | 0 | if (poLine == nullptr) |
456 | 0 | { |
457 | 0 | poLine = poSubLine->clone(); |
458 | 0 | } |
459 | 0 | else if (poLine->getNumPoints() == 0 || |
460 | 0 | poStart->Equals(poEnd)) |
461 | 0 | { |
462 | 0 | poLine->addSubLineString(poSubLine, 1); |
463 | 0 | } |
464 | 0 | else |
465 | 0 | { |
466 | 0 | Boundary oB = {poLine, dfZ, oZones[i].dfZ}; |
467 | 0 | oBoundaries.push_back(oB); |
468 | 0 | poLine = poSubLine->clone(); |
469 | 0 | } |
470 | 0 | poLine->EndPoint(poEnd); |
471 | 0 | } |
472 | 0 | Boundary oB = {poLine, dfZ, oZones[i].dfZ}; |
473 | 0 | oBoundaries.push_back(oB); |
474 | 0 | delete poStart; |
475 | 0 | delete poEnd; |
476 | 0 | } |
477 | 0 | break; |
478 | 0 | case wkbPolygon: |
479 | 0 | case wkbPolygon25D: |
480 | 0 | { |
481 | 0 | OGREnvelope oErrorRegion = oZones[i].oEnvelope; |
482 | 0 | oErrorRegion.Intersect(oEnvelope); |
483 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
484 | 0 | "Overlapping polygons in rectangle (%.16g " |
485 | 0 | "%.16g, %.16g %.16g))", |
486 | 0 | oErrorRegion.MinX, oErrorRegion.MinY, |
487 | 0 | oErrorRegion.MaxX, oErrorRegion.MaxY); |
488 | 0 | err = OGRERR_FAILURE; |
489 | 0 | } |
490 | 0 | break; |
491 | 0 | case wkbGeometryCollection: |
492 | 0 | case wkbGeometryCollection25D: |
493 | 0 | { |
494 | 0 | for (auto &&poMember : |
495 | 0 | poIntersection->toGeometryCollection()) |
496 | 0 | { |
497 | 0 | const OGRwkbGeometryType eType = |
498 | 0 | poMember->getGeometryType(); |
499 | 0 | if (wkbFlatten(eType) == wkbPolygon) |
500 | 0 | { |
501 | 0 | OGREnvelope oErrorRegion = oZones[i].oEnvelope; |
502 | 0 | oErrorRegion.Intersect(oEnvelope); |
503 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
504 | 0 | "Overlapping polygons in rectangle " |
505 | 0 | "(%.16g %.16g, %.16g %.16g))", |
506 | 0 | oErrorRegion.MinX, oErrorRegion.MinY, |
507 | 0 | oErrorRegion.MaxX, oErrorRegion.MaxY); |
508 | 0 | err = OGRERR_FAILURE; |
509 | 0 | } |
510 | 0 | } |
511 | 0 | } |
512 | 0 | break; |
513 | 0 | case wkbPoint: |
514 | 0 | case wkbPoint25D: |
515 | | /* do nothing */ |
516 | 0 | break; |
517 | 0 | default: |
518 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
519 | 0 | "Unhandled polygon intersection of type %s", |
520 | 0 | OGRGeometryTypeToName( |
521 | 0 | poIntersection->getGeometryType())); |
522 | 0 | err = OGRERR_FAILURE; |
523 | 0 | } |
524 | 0 | } |
525 | 0 | delete poIntersection; |
526 | 0 | } |
527 | 0 | } |
528 | | |
529 | 0 | Zone oZ = {oEnvelope, poGeom->clone(), dfZ}; |
530 | 0 | oZones.push_back(oZ); |
531 | 0 | return err; |
532 | 0 | } |
533 | | |
534 | | OGRErr OGRWAsPLayer::WriteRoughness(OGRLineString *poGeom, |
535 | | const double &dfZleft, |
536 | | const double &dfZright) |
537 | | |
538 | 0 | { |
539 | 0 | std::unique_ptr<OGRLineString> poLine(Simplify(*poGeom)); |
540 | |
|
541 | 0 | const int iNumPoints = poLine->getNumPoints(); |
542 | 0 | if (!iNumPoints) |
543 | 0 | return OGRERR_NONE; /* empty geom */ |
544 | | |
545 | 0 | VSIFPrintfL(hFile, "%11.3f %11.3f %11d", dfZleft, dfZright, iNumPoints); |
546 | |
|
547 | 0 | for (int v = 0; v < iNumPoints; v++) |
548 | 0 | { |
549 | 0 | if (!(v % 3)) |
550 | 0 | VSIFPrintfL(hFile, "\n "); |
551 | 0 | VSIFPrintfL(hFile, "%11.1f %11.1f ", poLine->getX(v), poLine->getY(v)); |
552 | 0 | } |
553 | 0 | VSIFPrintfL(hFile, "\n"); |
554 | |
|
555 | 0 | return OGRERR_NONE; |
556 | 0 | } |
557 | | |
558 | | OGRErr OGRWAsPLayer::WriteRoughness(OGRGeometry *poGeom, const double &dfZleft, |
559 | | const double &dfZright) |
560 | | |
561 | 0 | { |
562 | 0 | switch (poGeom->getGeometryType()) |
563 | 0 | { |
564 | 0 | case wkbLineString: |
565 | 0 | case wkbLineString25D: |
566 | 0 | return WriteRoughness(poGeom->toLineString(), dfZleft, dfZright); |
567 | 0 | case wkbPolygon: |
568 | 0 | case wkbPolygon25D: |
569 | 0 | return WriteRoughness(poGeom->toPolygon(), dfZleft); |
570 | 0 | case wkbMultiPolygon: |
571 | 0 | case wkbMultiPolygon25D: |
572 | 0 | case wkbMultiLineString25D: |
573 | 0 | case wkbMultiLineString: |
574 | 0 | { |
575 | 0 | for (auto &&poMember : poGeom->toGeometryCollection()) |
576 | 0 | { |
577 | 0 | const OGRErr err = WriteRoughness(poMember, dfZleft, dfZright); |
578 | 0 | if (OGRERR_NONE != err) |
579 | 0 | return err; |
580 | 0 | } |
581 | 0 | return OGRERR_NONE; |
582 | 0 | } |
583 | 0 | default: |
584 | 0 | { |
585 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
586 | 0 | "Cannot handle geometry of type %s", |
587 | 0 | OGRGeometryTypeToName(poGeom->getGeometryType())); |
588 | 0 | break; |
589 | 0 | } |
590 | 0 | } |
591 | 0 | return OGRERR_FAILURE; /* avoid visual warning */ |
592 | 0 | } |
593 | | |
594 | | /************************************************************************/ |
595 | | /* ICreateFeature() */ |
596 | | /************************************************************************/ |
597 | | |
598 | | OGRErr OGRWAsPLayer::ICreateFeature(OGRFeature *poFeature) |
599 | | |
600 | 0 | { |
601 | 0 | if (WRITE_ONLY != eMode) |
602 | 0 | { |
603 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Layer is open read only"); |
604 | 0 | return OGRERR_FAILURE; |
605 | 0 | } |
606 | | |
607 | | /* This mainly checks for errors or inconsistencies */ |
608 | | /* the real work is done by WriteElevation or WriteRoughness */ |
609 | 0 | if (-1 == iFirstFieldIdx && !sFirstField.empty()) |
610 | 0 | { |
611 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Cannot find field %s", |
612 | 0 | sFirstField.c_str()); |
613 | 0 | return OGRERR_FAILURE; |
614 | 0 | } |
615 | 0 | if (-1 == iSecondFieldIdx && !sSecondField.empty()) |
616 | 0 | { |
617 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Cannot find field %s", |
618 | 0 | sSecondField.c_str()); |
619 | 0 | return OGRERR_FAILURE; |
620 | 0 | } |
621 | 0 | if (-1 == iGeomFieldIdx && !sGeomField.empty()) |
622 | 0 | { |
623 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Cannot find field %s", |
624 | 0 | sSecondField.c_str()); |
625 | 0 | return OGRERR_FAILURE; |
626 | 0 | } |
627 | 0 | OGRGeometry *geom = poFeature->GetGeomFieldRef(iGeomFieldIdx); |
628 | 0 | if (!geom) |
629 | 0 | return OGRERR_NONE; /* null geom, nothing to do */ |
630 | | |
631 | 0 | const OGRwkbGeometryType geomType = geom->getGeometryType(); |
632 | 0 | const bool bPolygon = |
633 | 0 | (geomType == wkbPolygon) || (geomType == wkbPolygon25D) || |
634 | 0 | (geomType == wkbMultiPolygon) || (geomType == wkbMultiPolygon25D); |
635 | 0 | const bool bRoughness = (-1 != iSecondFieldIdx) || bPolygon; |
636 | |
|
637 | 0 | double z1 = 0.0; |
638 | 0 | if (-1 != iFirstFieldIdx) |
639 | 0 | { |
640 | 0 | if (!poFeature->IsFieldSetAndNotNull(iFirstFieldIdx)) |
641 | 0 | { |
642 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Field %d %s is NULL", |
643 | 0 | iFirstFieldIdx, sFirstField.c_str()); |
644 | 0 | return OGRERR_FAILURE; |
645 | 0 | } |
646 | 0 | z1 = poFeature->GetFieldAsDouble(iFirstFieldIdx); |
647 | 0 | } |
648 | 0 | else |
649 | 0 | { |
650 | | /* Case of z value for elevation or roughness, so we compute it */ |
651 | 0 | OGRPoint centroid; |
652 | 0 | if (geom->getCoordinateDimension() != 3) |
653 | 0 | { |
654 | |
|
655 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
656 | 0 | "No field defined and no Z coordinate"); |
657 | 0 | return OGRERR_FAILURE; |
658 | 0 | } |
659 | 0 | z1 = AvgZ(geom); |
660 | 0 | } |
661 | | |
662 | 0 | double z2 = 0.0; |
663 | 0 | if (-1 != iSecondFieldIdx) |
664 | 0 | { |
665 | 0 | if (!poFeature->IsFieldSetAndNotNull(iSecondFieldIdx)) |
666 | 0 | { |
667 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Field %d %s is NULL", |
668 | 0 | iSecondFieldIdx, sSecondField.c_str()); |
669 | 0 | return OGRERR_FAILURE; |
670 | 0 | } |
671 | 0 | z2 = poFeature->GetFieldAsDouble(iSecondFieldIdx); |
672 | 0 | } |
673 | 0 | else if (bRoughness && !bPolygon) |
674 | 0 | { |
675 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "No right roughness field"); |
676 | 0 | return OGRERR_FAILURE; |
677 | 0 | } |
678 | | |
679 | 0 | return bRoughness ? WriteRoughness(geom, z1, z2) : WriteElevation(geom, z1); |
680 | 0 | } |
681 | | |
682 | | /************************************************************************/ |
683 | | /* CreateField() */ |
684 | | /************************************************************************/ |
685 | | |
686 | | OGRErr OGRWAsPLayer::CreateField(const OGRFieldDefn *poField, |
687 | | CPL_UNUSED int bApproxOK) |
688 | 290 | { |
689 | 290 | poLayerDefn->AddFieldDefn(poField); |
690 | | |
691 | | /* Update field indexes */ |
692 | 290 | if (-1 == iFirstFieldIdx && !sFirstField.empty()) |
693 | 0 | iFirstFieldIdx = poLayerDefn->GetFieldIndex(sFirstField.c_str()); |
694 | 290 | if (-1 == iSecondFieldIdx && !sSecondField.empty()) |
695 | 0 | iSecondFieldIdx = poLayerDefn->GetFieldIndex(sSecondField.c_str()); |
696 | | |
697 | 290 | return OGRERR_NONE; |
698 | 290 | } |
699 | | |
700 | | /************************************************************************/ |
701 | | /* CreateGeomField() */ |
702 | | /************************************************************************/ |
703 | | |
704 | | OGRErr OGRWAsPLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn, |
705 | | CPL_UNUSED int bApproxOK) |
706 | 0 | { |
707 | 0 | OGRGeomFieldDefn oFieldDefn(poGeomFieldIn); |
708 | 0 | auto poSRSOri = poGeomFieldIn->GetSpatialRef(); |
709 | 0 | if (poSRSOri) |
710 | 0 | { |
711 | 0 | auto poSRS = poSRSOri->Clone(); |
712 | 0 | poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
713 | 0 | oFieldDefn.SetSpatialRef(poSRS); |
714 | 0 | poSRS->Release(); |
715 | 0 | } |
716 | 0 | poLayerDefn->AddGeomFieldDefn(&oFieldDefn); |
717 | | |
718 | | /* Update geom field index */ |
719 | 0 | if (-1 == iGeomFieldIdx) |
720 | 0 | { |
721 | 0 | iGeomFieldIdx = poLayerDefn->GetGeomFieldIndex(sGeomField.c_str()); |
722 | 0 | } |
723 | |
|
724 | 0 | return OGRERR_NONE; |
725 | 0 | } |
726 | | |
727 | | /************************************************************************/ |
728 | | /* GetNextRawFeature() */ |
729 | | /************************************************************************/ |
730 | | |
731 | | OGRFeature *OGRWAsPLayer::GetNextRawFeature() |
732 | | |
733 | 4.59k | { |
734 | 4.59k | if (READ_ONLY != eMode) |
735 | 0 | { |
736 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Layer is open write only"); |
737 | 0 | return nullptr; |
738 | 0 | } |
739 | | |
740 | 4.59k | const char *pszLine = CPLReadLineL(hFile); |
741 | 4.59k | if (!pszLine) |
742 | 26 | return nullptr; |
743 | | |
744 | 4.56k | double dfValues[4] = {0}; |
745 | 4.56k | int iNumValues = 0; |
746 | 4.56k | { |
747 | 4.56k | std::istringstream iss(pszLine); |
748 | 14.2k | while (iNumValues < 4 && (iss >> dfValues[iNumValues])) |
749 | 9.71k | { |
750 | 9.71k | ++iNumValues; |
751 | 9.71k | } |
752 | | |
753 | 4.56k | if (iNumValues < 2) |
754 | 34 | { |
755 | 34 | if (iNumValues) |
756 | 15 | CPLError(CE_Failure, CPLE_FileIO, "No enough values"); |
757 | 34 | return nullptr; |
758 | 34 | } |
759 | 4.56k | } |
760 | | |
761 | 4.53k | if (poLayerDefn->GetFieldCount() != iNumValues - 1) |
762 | 19 | { |
763 | 19 | CPLError(CE_Failure, CPLE_FileIO, |
764 | 19 | "looking for %d values and found %d on line: %s", |
765 | 19 | poLayerDefn->GetFieldCount(), iNumValues - 1, pszLine); |
766 | 19 | return nullptr; |
767 | 19 | } |
768 | 4.51k | const double dfNumPairToRead = dfValues[iNumValues - 1]; |
769 | 4.51k | if (!(dfNumPairToRead >= 0 && dfNumPairToRead < 1000000) || |
770 | 4.50k | static_cast<int>(dfNumPairToRead) != dfNumPairToRead) |
771 | 20 | { |
772 | 20 | CPLError(CE_Failure, CPLE_FileIO, "Invalid coordinate number: %f", |
773 | 20 | dfNumPairToRead); |
774 | 20 | return nullptr; |
775 | 20 | } |
776 | | |
777 | 4.49k | auto poFeature = std::make_unique<OGRFeature>(poLayerDefn); |
778 | 4.49k | poFeature->SetFID(++iFeatureCount); |
779 | 9.59k | for (int i = 0; i < iNumValues - 1; i++) |
780 | 5.10k | poFeature->SetField(i, dfValues[i]); |
781 | | |
782 | 4.49k | const int iNumValuesToRead = static_cast<int>(2 * dfNumPairToRead); |
783 | 4.49k | int iReadValues = 0; |
784 | 4.49k | std::vector<double> values(iNumValuesToRead); |
785 | 22.6k | for (pszLine = CPLReadLineL(hFile); pszLine; |
786 | 18.1k | pszLine = iNumValuesToRead > iReadValues ? CPLReadLineL(hFile) |
787 | 18.1k | : nullptr) |
788 | 18.1k | { |
789 | 18.1k | std::istringstream iss(pszLine); |
790 | 40.0k | while (iNumValuesToRead > iReadValues && (iss >> values[iReadValues])) |
791 | 21.8k | { |
792 | 21.8k | ++iReadValues; |
793 | 21.8k | } |
794 | 18.1k | } |
795 | 4.49k | if (iNumValuesToRead != iReadValues) |
796 | 101 | { |
797 | 101 | CPLError(CE_Failure, CPLE_FileIO, "No enough values for linestring"); |
798 | 101 | return nullptr; |
799 | 101 | } |
800 | 4.39k | auto poLine = std::make_unique<OGRLineString>(); |
801 | 4.39k | poLine->setCoordinateDimension(3); |
802 | 4.39k | poLine->assignSpatialReference(poSpatialReference); |
803 | 14.9k | for (int i = 0; i < iNumValuesToRead; i += 2) |
804 | 10.5k | { |
805 | 10.5k | poLine->addPoint(values[i], values[i + 1], 0); |
806 | 10.5k | } |
807 | 4.39k | poFeature->SetGeomFieldDirectly(0, poLine.release()); |
808 | | |
809 | 4.39k | return poFeature.release(); |
810 | 4.49k | } |
811 | | |
812 | | /************************************************************************/ |
813 | | /* TestCapability() */ |
814 | | /************************************************************************/ |
815 | | |
816 | | int OGRWAsPLayer::TestCapability(const char *pszCap) const |
817 | | |
818 | 0 | { |
819 | 0 | return (WRITE_ONLY == eMode && (EQUAL(pszCap, OLCSequentialWrite) || |
820 | 0 | EQUAL(pszCap, OLCCreateField) || |
821 | 0 | EQUAL(pszCap, OLCCreateGeomField) || |
822 | 0 | EQUAL(pszCap, OLCZGeometries))); |
823 | 0 | } |
824 | | |
825 | | /************************************************************************/ |
826 | | /* TestCapability() */ |
827 | | /************************************************************************/ |
828 | | |
829 | | void OGRWAsPLayer::ResetReading() |
830 | 0 | { |
831 | 0 | iFeatureCount = 0; |
832 | 0 | VSIFSeekL(hFile, iOffsetFeatureBegin, SEEK_SET); |
833 | 0 | } |
834 | | |
835 | | /************************************************************************/ |
836 | | /* AvgZ() */ |
837 | | /************************************************************************/ |
838 | | |
839 | | double OGRWAsPLayer::AvgZ(OGRLineString *poGeom) |
840 | | |
841 | 0 | { |
842 | 0 | const int iNumPoints = poGeom->getNumPoints(); |
843 | 0 | double sum = 0; |
844 | 0 | for (int v = 0; v < iNumPoints; v++) |
845 | 0 | { |
846 | 0 | sum += poGeom->getZ(v); |
847 | 0 | } |
848 | 0 | return iNumPoints ? sum / iNumPoints : 0; |
849 | 0 | } |
850 | | |
851 | | double OGRWAsPLayer::AvgZ(OGRPolygon *poGeom) |
852 | | |
853 | 0 | { |
854 | 0 | return AvgZ(poGeom->getExteriorRing()); |
855 | 0 | } |
856 | | |
857 | | double OGRWAsPLayer::AvgZ(OGRGeometryCollection *poGeom) |
858 | | |
859 | 0 | { |
860 | 0 | return poGeom->getNumGeometries() ? AvgZ(poGeom->getGeometryRef(0)) : 0; |
861 | 0 | } |
862 | | |
863 | | double OGRWAsPLayer::AvgZ(OGRGeometry *poGeom) |
864 | | |
865 | 0 | { |
866 | 0 | switch (poGeom->getGeometryType()) |
867 | 0 | { |
868 | 0 | case wkbLineString: |
869 | 0 | case wkbLineString25D: |
870 | 0 | return AvgZ(static_cast<OGRLineString *>(poGeom)); |
871 | 0 | case wkbPolygon: |
872 | 0 | case wkbPolygon25D: |
873 | 0 | return AvgZ(static_cast<OGRPolygon *>(poGeom)); |
874 | 0 | case wkbMultiLineString: |
875 | 0 | case wkbMultiLineString25D: |
876 | |
|
877 | 0 | case wkbMultiPolygon: |
878 | 0 | case wkbMultiPolygon25D: |
879 | 0 | return AvgZ(static_cast<OGRGeometryCollection *>(poGeom)); |
880 | 0 | default: |
881 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
882 | 0 | "Unsupported geometry type in OGRWAsPLayer::AvgZ()"); |
883 | 0 | break; |
884 | 0 | } |
885 | 0 | return 0; /* avoid warning */ |
886 | 0 | } |
887 | | |
888 | | /************************************************************************/ |
889 | | /* DouglasPeucker() */ |
890 | | /************************************************************************/ |
891 | | |
892 | | // void DouglasPeucker(PointList[], epsilon) |
893 | | // |
894 | | //{ |
895 | | // // Find the point with the maximum distance |
896 | | // double dmax = 0; |
897 | | // int index = 0; |
898 | | // int end = length(PointList). |
899 | | // for (int i = 1; i<end; i++) |
900 | | // { |
901 | | // const double d = shortestDistanceToSegment(PointList[i], |
902 | | // Line(PointList[0], PointList[end])) if ( d > dmax ) |
903 | | // { |
904 | | // index = i |
905 | | // dmax = d |
906 | | // } |
907 | | // } |
908 | | // // If max distance is greater than epsilon, recursively simplify |
909 | | // if ( dmax > epsilon ) |
910 | | // { |
911 | | // // Recursive call |
912 | | // recResults1[] = DouglasPeucker(PointList[1...index], epsilon) |
913 | | // recResults2[] = DouglasPeucker(PointList[index...end], epsilon) |
914 | | // |
915 | | // // Build the result list |
916 | | // ResultList[] = {recResults1[1...end-1] recResults2[1...end]} |
917 | | // } |
918 | | // else |
919 | | // { |
920 | | // ResultList[] = {PointList[1], PointList[end]} |
921 | | // } |
922 | | // // Return the result |
923 | | // return ResultList[] |
924 | | // } |