Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
// }