• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/frmts/wcs/wcsdataset110.cpp
1
/******************************************************************************
2
 *
3
 * Project:  WCS Client Driver
4
 * Purpose:  Implementation of Dataset class for WCS 1.1.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2006, Frank Warmerdam
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2017, Ari Jolma
11
 * Copyright (c) 2017, Finnish Environment Institute
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15

16
#include "cpl_string.h"
17
#include "cpl_minixml.h"
18
#include "cpl_http.h"
19
#include "gmlutils.h"
20
#include "gdal_frmts.h"
21
#include "gdal_pam.h"
22
#include "ogr_spatialref.h"
23
#include "gmlcoverage.h"
24

25
#include <algorithm>
26

27
#include "wcsdataset.h"
28
#include "wcsutils.h"
29

30
using namespace WCSUtils;
31

32
/************************************************************************/
33
/*                         GetNativeExtent()                            */
34
/*                                                                      */
35
/************************************************************************/
36

37
std::vector<double> WCSDataset110::GetNativeExtent(int nXOff, int nYOff,
36✔
38
                                                   int nXSize, int nYSize,
39
                                                   CPL_UNUSED int nBufXSize,
40
                                                   CPL_UNUSED int nBufYSize)
41
{
42
    std::vector<double> extent;
36✔
43

44
    // outer edges of outer pixels.
45
    extent.push_back(m_gt[0] + (nXOff)*m_gt[1]);
36✔
46
    extent.push_back(m_gt[3] + (nYOff + nYSize) * m_gt[5]);
36✔
47
    extent.push_back(m_gt[0] + (nXOff + nXSize) * m_gt[1]);
36✔
48
    extent.push_back(m_gt[3] + (nYOff)*m_gt[5]);
36✔
49

50
    bool no_shrink = CPLGetXMLBoolean(psService, "OuterExtents");
36✔
51

52
    // WCS 1.1 extents are centers of outer pixels.
53
    if (!no_shrink)
36✔
54
    {
55
        extent[2] -= m_gt[1] * 0.5;
18✔
56
        extent[0] += m_gt[1] * 0.5;
18✔
57
        extent[1] -= m_gt[5] * 0.5;
18✔
58
        extent[3] += m_gt[5] * 0.5;
18✔
59
    }
60

61
    double dfXStep, dfYStep;
62

63
    if (!no_shrink)
36✔
64
    {
65
        dfXStep = (nXSize / (double)nBufXSize) * m_gt[1];
18✔
66
        dfYStep = (nYSize / (double)nBufYSize) * m_gt[5];
18✔
67
        // Carefully adjust bounds for pixel centered values at new
68
        // sampling density.
69
        if (nBufXSize != nXSize || nBufYSize != nYSize)
18✔
70
        {
71
            dfXStep = (nXSize / (double)nBufXSize) * m_gt[1];
6✔
72
            dfYStep = (nYSize / (double)nBufYSize) * m_gt[5];
6✔
73

74
            extent[0] = nXOff * m_gt[1] + m_gt[0] + dfXStep * 0.5;
6✔
75
            extent[2] = extent[0] + (nBufXSize - 1) * dfXStep;
6✔
76

77
            extent[3] = nYOff * m_gt[5] + m_gt[3] + dfYStep * 0.5;
6✔
78
            extent[1] = extent[3] + (nBufYSize - 1) * dfYStep;
6✔
79
        }
80
    }
81
    else
82
    {
83
        double adjust =
84
            CPLAtof(CPLGetXMLValue(psService, "BufSizeAdjust", "0.0"));
18✔
85
        dfXStep = (nXSize / ((double)nBufXSize + adjust)) * m_gt[1];
18✔
86
        dfYStep = (nYSize / ((double)nBufYSize + adjust)) * m_gt[5];
18✔
87
    }
88

89
    extent.push_back(dfXStep);
36✔
90
    extent.push_back(dfYStep);
36✔
91

92
    return extent;
72✔
93
}
94

95
/************************************************************************/
96
/*                        GetCoverageRequest()                          */
97
/*                                                                      */
98
/************************************************************************/
99

100
std::string WCSDataset110::GetCoverageRequest(bool scaled, int /* nBufXSize */,
36✔
101
                                              int /* nBufYSize */,
102
                                              const std::vector<double> &extent,
103
                                              const std::string &osBandList)
104
{
105
    CPLString osRequest;
72✔
106

107
    /* -------------------------------------------------------------------- */
108
    /*      URL encode strings that could have questionable characters.     */
109
    /* -------------------------------------------------------------------- */
110
    CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
72✔
111

112
    char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
36✔
113
    osCoverage = pszEncoded;
36✔
114
    CPLFree(pszEncoded);
36✔
115

116
    CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
72✔
117

118
    pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
36✔
119
    osFormat = pszEncoded;
36✔
120
    CPLFree(pszEncoded);
36✔
121

122
    CPLString osRangeSubset = CPLGetXMLValue(psService, "FieldName", "");
72✔
123

124
    // todo: MapServer seems to require interpolation
125

126
    CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
72✔
127
    if (interpolation == "")
36✔
128
    {
129
        // old undocumented key for interpolation in service
130
        interpolation = CPLGetXMLValue(psService, "Resample", "");
36✔
131
    }
132
    if (interpolation != "")
36✔
133
    {
134
        osRangeSubset += ":" + interpolation;
×
135
    }
136

137
    if (osBandList != "")
36✔
138
    {
139
        if (osBandIdentifier != "")
×
140
        {
141
            osRangeSubset += CPLString().Printf(
×
142
                "[%s[%s]]", osBandIdentifier.c_str(), osBandList.c_str());
×
143
        }
144
    }
145

146
    osRangeSubset = "&RangeSubset=" + URLEncode(osRangeSubset);
36✔
147

148
    double bbox_0 = extent[0],  // min X
36✔
149
        bbox_1 = extent[1],     // min Y
36✔
150
        bbox_2 = extent[2],     // max X
36✔
151
        bbox_3 = extent[3];     // max Y
36✔
152

153
    if (axis_order_swap)
36✔
154
    {
155
        bbox_0 = extent[1];  // min Y
21✔
156
        bbox_1 = extent[0];  // min X
21✔
157
        bbox_2 = extent[3];  // max Y
21✔
158
        bbox_3 = extent[2];  // max X
21✔
159
    }
160
    std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
36✔
161
    request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
36✔
162
    request += CPLString().Printf(
72✔
163
        "&VERSION=%s&REQUEST=GetCoverage&IDENTIFIER=%s"
164
        "&FORMAT=%s&BOUNDINGBOX=%.15g,%.15g,%.15g,%.15g,%s%s",
165
        CPLGetXMLValue(psService, "Version", ""), osCoverage.c_str(),
36✔
166
        osFormat.c_str(), bbox_0, bbox_1, bbox_2, bbox_3, osCRS.c_str(),
167
        osRangeSubset.c_str());
36✔
168
    double origin_1 = extent[0],  // min X
36✔
169
        origin_2 = extent[3],     // max Y
36✔
170
        offset_1 = extent[4],     // dX
36✔
171
        offset_2 = extent[5];     // dY
36✔
172

173
    if (axis_order_swap)
36✔
174
    {
175
        origin_1 = extent[3];  // max Y
21✔
176
        origin_2 = extent[0];  // min X
21✔
177
        offset_1 = extent[5];  // dY
21✔
178
        offset_2 = extent[4];  // dX
21✔
179
    }
180
    CPLString offsets;
72✔
181
    if (CPLGetXMLBoolean(psService, "OffsetsPositive"))
36✔
182
    {
183
        offset_1 = fabs(offset_1);
9✔
184
        offset_2 = fabs(offset_2);
9✔
185
    }
186
    if (EQUAL(CPLGetXMLValue(psService, "NrOffsets", "4"), "2"))
36✔
187
    {
188
        offsets = CPLString().Printf("%.15g,%.15g", offset_1, offset_2);
18✔
189
    }
190
    else
191
    {
192
        if (axis_order_swap)
18✔
193
        {
194
            // Only tested with GeoServer but this is the correct offset(?)
195
            offsets = CPLString().Printf("0,%.15g,%.15g,0", offset_2, offset_1);
12✔
196
        }
197
        else
198
        {
199
            offsets = CPLString().Printf("%.15g,0,0,%.15g", offset_1, offset_2);
6✔
200
        }
201
    }
202
    bool do_not_include =
203
        CPLGetXMLBoolean(psService, "GridCRSOptional") && !scaled;
36✔
204
    if (!do_not_include)
36✔
205
    {
206
        request += CPLString().Printf(
72✔
207
            "&GridBaseCRS=%s"
208
            "&GridCS=urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS"
209
            "&GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs"
210
            "&GridOrigin=%.15g,%.15g"
211
            "&GridOffsets=%s",
212
            osCRS.c_str(), origin_1, origin_2, offsets.c_str());
36✔
213
    }
214
    CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
72✔
215
    if (extra != "")
36✔
216
    {
217
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
72✔
218
        for (unsigned int i = 0; i < pairs.size(); ++i)
72✔
219
        {
220
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
36✔
221
            request =
222
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
36✔
223
        }
224
    }
225
    extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
36✔
226
    if (extra != "")
36✔
227
    {
228
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
72✔
229
        for (unsigned int i = 0; i < pairs.size(); ++i)
72✔
230
        {
231
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
36✔
232
            request =
233
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
36✔
234
        }
235
    }
236
    CPLDebug("WCS", "Requesting %s", request.c_str());
36✔
237
    return request;
72✔
238
}
239

240
/************************************************************************/
241
/*                        DescribeCoverageRequest()                     */
242
/*                                                                      */
243
/************************************************************************/
244

245
std::string WCSDataset110::DescribeCoverageRequest()
12✔
246
{
247
    std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
12✔
248
    request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
12✔
249
    request = CPLURLAddKVP(request.c_str(), "REQUEST", "DescribeCoverage");
12✔
250
    request = CPLURLAddKVP(request.c_str(), "VERSION",
24✔
251
                           CPLGetXMLValue(psService, "Version", "1.1.0"));
24✔
252
    request = CPLURLAddKVP(request.c_str(), "IDENTIFIERS",
24✔
253
                           CPLGetXMLValue(psService, "CoverageName", ""));
24✔
254
    CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
24✔
255
    if (extra != "")
12✔
256
    {
257
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
24✔
258
        for (unsigned int i = 0; i < pairs.size(); ++i)
24✔
259
        {
260
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
12✔
261
            request =
262
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
12✔
263
        }
264
    }
265
    extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
12✔
266
    if (extra != "")
12✔
267
    {
268
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
×
269
        for (unsigned int i = 0; i < pairs.size(); ++i)
×
270
        {
271
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
×
272
            request =
273
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
×
274
        }
275
    }
276
    return request;
24✔
277
}
278

279
/************************************************************************/
280
/*                         CoverageOffering()                           */
281
/*                                                                      */
282
/************************************************************************/
283

284
CPLXMLNode *WCSDataset110::CoverageOffering(CPLXMLNode *psDC)
19✔
285
{
286
    return CPLGetXMLNode(psDC, "=CoverageDescriptions.CoverageDescription");
19✔
287
}
288

289
/************************************************************************/
290
/*                          ExtractGridInfo()                           */
291
/*                                                                      */
292
/*      Collect info about grid from describe coverage for WCS 1.1.     */
293
/*                                                                      */
294
/************************************************************************/
295

296
bool WCSDataset110::ExtractGridInfo()
36✔
297

298
{
299
    CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageDescription");
36✔
300

301
    if (psCO == nullptr)
36✔
302
        return false;
×
303

304
    /* -------------------------------------------------------------------- */
305
    /*      We need to strip off name spaces so it is easier to             */
306
    /*      searchfor plain gml names.                                      */
307
    /* -------------------------------------------------------------------- */
308
    CPLStripXMLNamespace(psCO, nullptr, TRUE);
36✔
309

310
    /* -------------------------------------------------------------------- */
311
    /*      Verify we have a SpatialDomain and GridCRS.                     */
312
    /* -------------------------------------------------------------------- */
313
    CPLXMLNode *psSD = CPLGetXMLNode(psCO, "Domain.SpatialDomain");
36✔
314
    CPLXMLNode *psGCRS = CPLGetXMLNode(psSD, "GridCRS");
36✔
315

316
    if (psSD == nullptr || psGCRS == nullptr)
36✔
317
    {
318
        CPLError(CE_Failure, CPLE_AppDefined,
×
319
                 "Unable to find GridCRS in CoverageDescription,\n"
320
                 "unable to process WCS Coverage.");
321
        return false;
×
322
    }
323

324
    /* -------------------------------------------------------------------- */
325
    /*      Establish our coordinate system.                                */
326
    /*   This is needed before geometry since we may have axis order swap.  */
327
    /* -------------------------------------------------------------------- */
328
    CPLString crs = ParseCRS(psGCRS);
72✔
329

330
    if (crs.empty())
36✔
331
    {
332
        CPLError(CE_Failure, CPLE_AppDefined,
×
333
                 "Unable to find GridCRS.GridBaseCRS");
334
        return false;
×
335
    }
336

337
    // SetCRS should fail only if the CRS is really unknown to GDAL
338
    if (!SetCRS(crs, true))
36✔
339
    {
340
        CPLError(CE_Failure, CPLE_AppDefined,
×
341
                 "Unable to interpret GridBaseCRS '%s'.", crs.c_str());
342
        return false;
×
343
    }
344

345
    /* -------------------------------------------------------------------- */
346
    /*      Collect size, origin, and offsets for SetGeometry()             */
347
    /*                                                                      */
348
    /*      Extract Geotransform from GridCRS.                              */
349
    /*                                                                      */
350
    /* -------------------------------------------------------------------- */
351
    const char *pszGridType = CPLGetXMLValue(
36✔
352
        psGCRS, "GridType", "urn:ogc:def:method:WCS::2dSimpleGrid");
353
    bool swap =
354
        axis_order_swap && !CPLGetXMLBoolean(psService, "NoGridAxisSwap");
36✔
355
    std::vector<double> origin =
356
        Flist(Split(CPLGetXMLValue(psGCRS, "GridOrigin", ""), " ", swap));
72✔
357

358
    std::vector<std::string> offset_1 =
359
        Split(CPLGetXMLValue(psGCRS, "GridOffsets", ""), " ");
72✔
360
    std::vector<std::string> offset_2;
72✔
361
    size_t n = offset_1.size();
36✔
362
    if (n % 2 != 0)
36✔
363
    {
364
        CPLError(CE_Failure, CPLE_AppDefined,
×
365
                 "GridOffsets has incorrect amount of coefficients.\n"
366
                 "Unable to process WCS coverage.");
367
        return false;
×
368
    }
369
    for (unsigned int i = 0; i < n / 2; ++i)
90✔
370
    {
371
        CPLString s = offset_1.back();
54✔
372
        offset_1.erase(offset_1.end() - 1);
54✔
373
#if defined(__GNUC__)
374
#pragma GCC diagnostic push
375
#pragma GCC diagnostic ignored "-Wnull-dereference"
376
#endif
377
        offset_2.insert(offset_2.begin(), s);
54✔
378
#if defined(__GNUC__)
379
#pragma GCC diagnostic pop
380
#endif
381
    }
382
    std::vector<std::vector<double>> offsets;
72✔
383
    if (swap)
36✔
384
    {
385
        offsets.push_back(Flist(offset_2));
×
386
        offsets.push_back(Flist(offset_1));
×
387
    }
388
    else
389
    {
390
        offsets.push_back(Flist(offset_1));
36✔
391
        offsets.push_back(Flist(offset_2));
36✔
392
    }
393

394
    if (strstr(pszGridType, ":2dGridIn2dCrs") ||
36✔
395
        strstr(pszGridType, ":2dGridin2dCrs"))
18✔
396
    {
397
        if (!(offset_1.size() == 2 && origin.size() == 2))
18✔
398
        {
399
            CPLError(CE_Failure, CPLE_AppDefined,
×
400
                     "2dGridIn2dCrs does not have expected GridOrigin or\n"
401
                     "GridOffsets values - unable to process WCS coverage.");
402
            return false;
×
403
        }
404
    }
405

406
    else if (strstr(pszGridType, ":2dGridIn3dCrs"))
18✔
407
    {
408
        if (!(offset_1.size() == 3 && origin.size() == 3))
×
409
        {
410
            CPLError(CE_Failure, CPLE_AppDefined,
×
411
                     "2dGridIn3dCrs does not have expected GridOrigin or\n"
412
                     "GridOffsets values - unable to process WCS coverage.");
413
            return false;
×
414
        }
415
    }
416

417
    else if (strstr(pszGridType, ":2dSimpleGrid"))
18✔
418
    {
419
        if (!(offset_1.size() == 1 && origin.size() == 2))
18✔
420
        {
421
            CPLError(CE_Failure, CPLE_AppDefined,
×
422
                     "2dSimpleGrid does not have expected GridOrigin or\n"
423
                     "GridOffsets values - unable to process WCS coverage.");
424
            return false;
×
425
        }
426
    }
427

428
    else
429
    {
430
        CPLError(CE_Failure, CPLE_AppDefined,
×
431
                 "Unrecognized GridCRS.GridType value '%s',\n"
432
                 "unable to process WCS coverage.",
433
                 pszGridType);
434
        return false;
×
435
    }
436

437
    /* -------------------------------------------------------------------- */
438
    /*      Search for an ImageCRS for raster size.                         */
439
    /* -------------------------------------------------------------------- */
440
    std::vector<int> size;
72✔
441
    CPLXMLNode *psNode;
442

443
    for (psNode = psSD->psChild; psNode != nullptr && size.size() == 0;
108✔
444
         psNode = psNode->psNext)
72✔
445
    {
446
        if (psNode->eType != CXT_Element ||
72✔
447
            !EQUAL(psNode->pszValue, "BoundingBox"))
72✔
448
            continue;
18✔
449

450
        CPLString osBBCRS = ParseCRS(psNode);
108✔
451
        if (strstr(osBBCRS, ":imageCRS"))
54✔
452
        {
453
            std::vector<std::string> bbox = ParseBoundingBox(psNode);
36✔
454
            if (bbox.size() >= 2)
18✔
455
            {
456
                std::vector<int> low = Ilist(Split(bbox[0].c_str(), " "), 0, 2);
36✔
457
                std::vector<int> high =
458
                    Ilist(Split(bbox[1].c_str(), " "), 0, 2);
36✔
459
                if (low[0] == 0 && low[1] == 0)
18✔
460
                {
461
                    size.push_back(high[0]);
18✔
462
                    size.push_back(high[1]);
18✔
463
                }
464
            }
465
        }
466
    }
467

468
    /* -------------------------------------------------------------------- */
469
    /*      Otherwise we search for a bounding box in our coordinate        */
470
    /*      system and derive the size from that.                           */
471
    /* -------------------------------------------------------------------- */
472
    for (psNode = psSD->psChild; psNode != nullptr && size.size() == 0;
72✔
473
         psNode = psNode->psNext)
36✔
474
    {
475
        if (psNode->eType != CXT_Element ||
36✔
476
            !EQUAL(psNode->pszValue, "BoundingBox"))
36✔
477
            continue;
×
478

479
        CPLString osBBCRS = ParseCRS(psNode);
72✔
480
        if (osBBCRS == osCRS)
36✔
481
        {
482
            std::vector<std::string> bbox = ParseBoundingBox(psNode);
36✔
483
            bool not_rot =
484
                (offsets[0].size() == 1 && offsets[1].size() == 1) ||
36✔
485
                ((swap && offsets[0][0] == 0.0 && offsets[1][1] == 0.0) ||
×
486
                 (!swap && offsets[0][1] == 0.0 && offsets[1][0] == 0.0));
18✔
487
            if (bbox.size() >= 2 && not_rot)
18✔
488
            {
489
                std::vector<double> low =
490
                    Flist(Split(bbox[0].c_str(), " ", axis_order_swap), 0, 2);
36✔
491
                std::vector<double> high =
492
                    Flist(Split(bbox[1].c_str(), " ", axis_order_swap), 0, 2);
18✔
493
                double c1 = offsets[0][0];
18✔
494
                double c2 =
495
                    offsets[1].size() == 1 ? offsets[1][0] : offsets[1][1];
18✔
496
                size.push_back((int)((high[0] - low[0]) / c1 + 1.01));
18✔
497
                size.push_back((int)((high[1] - low[1]) / fabs(c2) + 1.01));
18✔
498
            }
499
        }
500
    }
501

502
    if (size.size() < 2)
36✔
503
    {
504
        CPLError(CE_Failure, CPLE_AppDefined,
×
505
                 "Could not determine the size of the grid.");
506
        return false;
×
507
    }
508

509
    SetGeometry(size, origin, offsets);
36✔
510

511
    /* -------------------------------------------------------------------- */
512
    /*      Do we have a coordinate system override?                        */
513
    /* -------------------------------------------------------------------- */
514
    const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
36✔
515

516
    if (pszProjOverride)
36✔
517
    {
518
        if (m_oSRS.SetFromUserInput(
×
519
                pszProjOverride,
520
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
×
521
            OGRERR_NONE)
522
        {
523
            CPLError(CE_Failure, CPLE_AppDefined,
×
524
                     "<SRS> element contents not parsable:\n%s",
525
                     pszProjOverride);
526
            return false;
×
527
        }
528
    }
529

530
    /* -------------------------------------------------------------------- */
531
    /*      Pick a format type if we don't already have one selected.       */
532
    /*                                                                      */
533
    /*      We will prefer anything that sounds like TIFF, otherwise        */
534
    /*      falling back to the first supported format.  Should we          */
535
    /*      consider preferring the nativeFormat if available?              */
536
    /* -------------------------------------------------------------------- */
537
    if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
36✔
538
    {
539
        CPLString osPreferredFormat;
24✔
540

541
        for (psNode = psCO->psChild; psNode != nullptr; psNode = psNode->psNext)
125✔
542
        {
543
            if (psNode->eType == CXT_Element &&
125✔
544
                EQUAL(psNode->pszValue, "SupportedFormat") && psNode->psChild &&
125✔
545
                psNode->psChild->eType == CXT_Text)
20✔
546
            {
547
                if (osPreferredFormat.empty())
20✔
548
                    osPreferredFormat = psNode->psChild->pszValue;
12✔
549

550
                if (strstr(psNode->psChild->pszValue, "tiff") != nullptr ||
20✔
551
                    strstr(psNode->psChild->pszValue, "TIFF") != nullptr ||
11✔
552
                    strstr(psNode->psChild->pszValue, "Tiff") != nullptr)
8✔
553
                {
554
                    osPreferredFormat = psNode->psChild->pszValue;
12✔
555
                    break;
12✔
556
                }
557
            }
558
        }
559

560
        if (!osPreferredFormat.empty())
12✔
561
        {
562
            bServiceDirty = true;
12✔
563
            CPLCreateXMLElementAndValue(psService, "PreferredFormat",
12✔
564
                                        osPreferredFormat);
565
        }
566
    }
567

568
    /* -------------------------------------------------------------------- */
569
    /*      Try to identify a nodata value.  For now we only support the    */
570
    /*      singleValue mechanism.                                          */
571
    /* -------------------------------------------------------------------- */
572
    if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
36✔
573
    {
574
        const char *pszSV =
575
            CPLGetXMLValue(psCO, "Range.Field.NullValue", nullptr);
24✔
576

577
        if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
24✔
578
        {
579
            bServiceDirty = true;
6✔
580
            CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
6✔
581
        }
582
    }
583

584
    /* -------------------------------------------------------------------- */
585
    /*      Grab the field name, if possible.                               */
586
    /* -------------------------------------------------------------------- */
587
    if (CPLGetXMLValue(psService, "FieldName", nullptr) == nullptr)
36✔
588
    {
589
        CPLString osFieldName =
590
            CPLGetXMLValue(psCO, "Range.Field.Identifier", "");
12✔
591

592
        if (!osFieldName.empty())
12✔
593
        {
594
            bServiceDirty = true;
12✔
595
            CPLCreateXMLElementAndValue(psService, "FieldName", osFieldName);
12✔
596
        }
597
        else
598
        {
599
            CPLError(
×
600
                CE_Failure, CPLE_AppDefined,
601
                "Unable to find required Identifier name %s for Range Field.",
602
                osCRS.c_str());
603
            return false;
×
604
        }
605
    }
606

607
    /* -------------------------------------------------------------------- */
608
    /*      Do we have a "Band" axis?  If so try to grab the bandcount      */
609
    /*      and data type from it.                                          */
610
    /* -------------------------------------------------------------------- */
611
    osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
36✔
612
    CPLXMLNode *psAxis =
613
        CPLGetXMLNode(psService, "CoverageDescription.Range.Field.Axis");
36✔
614

615
    if (osBandIdentifier.empty() &&
36✔
616
        (EQUAL(CPLGetXMLValue(psAxis, "Identifier", ""), "Band") ||
24✔
617
         EQUAL(CPLGetXMLValue(psAxis, "Identifier", ""), "Bands")) &&
81✔
618
        CPLGetXMLNode(psAxis, "AvailableKeys") != nullptr)
24✔
619
    {
620
        osBandIdentifier = CPLGetXMLValue(psAxis, "Identifier", "");
24✔
621

622
        // verify keys are ascending starting at 1
623
        CPLXMLNode *psValues = CPLGetXMLNode(psAxis, "AvailableKeys");
24✔
624
        CPLXMLNode *psSV;
625
        int iBand;
626

627
        for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
36✔
628
             psSV = psSV->psNext, iBand++)
12✔
629
        {
630
            if (psSV->eType != CXT_Element || !EQUAL(psSV->pszValue, "Key") ||
30✔
631
                psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
30✔
632
                atoi(psSV->psChild->pszValue) != iBand)
30✔
633
            {
634
                osBandIdentifier = "";
18✔
635
                break;
18✔
636
            }
637
        }
638

639
        if (!osBandIdentifier.empty())
24✔
640
        {
641
            if (CPLGetXMLValue(psService, "BandIdentifier", nullptr) == nullptr)
6✔
642
            {
643
                bServiceDirty = true;
6✔
644
                CPLSetXMLValue(psService, "BandIdentifier",
6✔
645
                               osBandIdentifier.c_str());
646
            }
647

648
            if (CPLGetXMLValue(psService, "BandCount", nullptr) == nullptr)
6✔
649
            {
650
                bServiceDirty = true;
6✔
651
                CPLSetXMLValue(psService, "BandCount",
6✔
652
                               CPLString().Printf("%d", iBand - 1));
12✔
653
            }
654
        }
655

656
        // Is this an ESRI server returning a GDAL recognised data type?
657
        CPLString osDataType = CPLGetXMLValue(psAxis, "DataType", "");
48✔
658
        if (GDALGetDataTypeByName(osDataType) != GDT_Unknown &&
24✔
659
            CPLGetXMLValue(psService, "BandType", nullptr) == nullptr)
×
660
        {
661
            bServiceDirty = true;
×
662
            CPLCreateXMLElementAndValue(psService, "BandType", osDataType);
×
663
        }
664
    }
665

666
    return true;
36✔
667
}
668

669
/************************************************************************/
670
/*                      ParseCapabilities()                             */
671
/************************************************************************/
672

673
CPLErr WCSDataset110::ParseCapabilities(CPLXMLNode *Capabilities,
19✔
674
                                        const std::string &url)
675
{
676
    CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
19✔
677

678
    // make sure this is a capabilities document
679
    if (strcmp(Capabilities->pszValue, "Capabilities") != 0)
19✔
680
    {
681
        CPLError(CE_Failure, CPLE_AppDefined,
×
682
                 "Error in capabilities document.\n");
683
        return CE_Failure;
×
684
    }
685

686
    char **metadata = nullptr;
19✔
687
    std::string path = "WCS_GLOBAL#";
38✔
688

689
    CPLString key = path + "version";
38✔
690
    metadata = CSLSetNameValue(metadata, key, Version());
19✔
691

692
    for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
271✔
693
         node = node->psNext)
252✔
694
    {
695
        const char *attr = node->pszValue;
252✔
696
        if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
252✔
697
        {
698
            key = path + "updateSequence";
13✔
699
            CPLString value = CPLGetXMLValue(node, nullptr, "");
13✔
700
            metadata = CSLSetNameValue(metadata, key, value);
13✔
701
        }
702
    }
703

704
    // identification metadata
705
    std::string path2 = path;
38✔
706
    CPLXMLNode *service = AddSimpleMetaData(
95✔
707
        &metadata, Capabilities, path2, "ServiceIdentification",
708
        {"Title", "Abstract", "Fees", "AccessConstraints"});
76✔
709
    CPLString kw = GetKeywords(service, "Keywords", "Keyword");
57✔
710
    if (kw != "")
19✔
711
    {
712
        CPLString name = path + "Keywords";
13✔
713
        metadata = CSLSetNameValue(metadata, name, kw);
13✔
714
    }
715
    CPLString profiles = GetKeywords(service, "", "Profile");
57✔
716
    if (profiles != "")
19✔
717
    {
718
        CPLString name = path + "Profiles";
7✔
719
        metadata = CSLSetNameValue(metadata, name, profiles);
7✔
720
    }
721

722
    // provider metadata
723
    path2 = path;
19✔
724
    CPLXMLNode *provider = AddSimpleMetaData(
38✔
725
        &metadata, Capabilities, path2, "ServiceProvider", {"ProviderName"});
19✔
726
    if (provider)
19✔
727
    {
728
        CPLXMLNode *site = CPLGetXMLNode(provider, "ProviderSite");
19✔
729
        if (site)
19✔
730
        {
731
            std::string path3 = path2 + "ProviderSite";
30✔
732
            CPLString value =
733
                CPLGetXMLValue(CPLGetXMLNode(site, "href"), nullptr, "");
15✔
734
            metadata = CSLSetNameValue(metadata, path3.c_str(), value);
15✔
735
        }
736
        std::string path3 = std::move(path2);
38✔
737
        CPLXMLNode *contact =
738
            AddSimpleMetaData(&metadata, provider, path3, "ServiceContact",
76✔
739
                              {"IndividualName", "PositionName", "Role"});
57✔
740
        if (contact)
19✔
741
        {
742
            std::string path4 = std::move(path3);
38✔
743
            CPLXMLNode *info =
744
                AddSimpleMetaData(&metadata, contact, path4, "ContactInfo",
57✔
745
                                  {"HoursOfService", "ContactInstructions"});
38✔
746
            if (info)
19✔
747
            {
748
                std::string path5 = path4;
38✔
749
                std::string path6 = path4;
38✔
750
                AddSimpleMetaData(&metadata, info, path5, "Address",
133✔
751
                                  {"DeliveryPoint", "City",
752
                                   "AdministrativeArea", "PostalCode",
753
                                   "Country", "ElectronicMailAddress"});
114✔
754
                AddSimpleMetaData(&metadata, info, path6, "Phone",
57✔
755
                                  {"Voice", "Facsimile"});
38✔
756
                CPL_IGNORE_RET_VAL(path4);
19✔
757
            }
758
        }
759
    }
760

761
    // operations metadata
762
    CPLString DescribeCoverageURL = "";
38✔
763
    CPLXMLNode *service2 = CPLGetXMLNode(Capabilities, "OperationsMetadata");
19✔
764
    if (service2)
19✔
765
    {
766
        for (CPLXMLNode *operation = service2->psChild; operation != nullptr;
91✔
767
             operation = operation->psNext)
72✔
768
        {
769
            if (operation->eType != CXT_Element ||
72✔
770
                !EQUAL(operation->pszValue, "Operation"))
68✔
771
            {
772
                continue;
15✔
773
            }
774
            if (EQUAL(CPLGetXMLValue(CPLGetXMLNode(operation, "name"), nullptr,
57✔
775
                                     ""),
776
                      "DescribeCoverage"))
777
            {
778
                DescribeCoverageURL = CPLGetXMLValue(
779
                    CPLGetXMLNode(CPLSearchXMLNode(operation, "Get"), "href"),
19✔
780
                    nullptr, "");
19✔
781
            }
782
        }
783
    }
784
    // if DescribeCoverageURL looks wrong, we change it
785
    if (DescribeCoverageURL.find("localhost") != std::string::npos)
19✔
786
    {
787
        DescribeCoverageURL = URLRemoveKey(url.c_str(), "request");
2✔
788
    }
789

790
    // service metadata (in 2.0)
791
    CPLString ext = "ServiceMetadata";
38✔
792
    CPLString formats = GetKeywords(Capabilities, ext, "formatSupported");
57✔
793
    if (formats != "")
19✔
794
    {
795
        CPLString name = path + "formatSupported";
7✔
796
        metadata = CSLSetNameValue(metadata, name, formats);
7✔
797
    }
798
    // wcs:Extensions: interpolation, CRS, others?
799
    ext += ".Extension";
19✔
800
    CPLString interpolation =
801
        GetKeywords(Capabilities, ext, "interpolationSupported");
57✔
802
    if (interpolation == "")
19✔
803
    {
804
        interpolation =
805
            GetKeywords(Capabilities, ext + ".InterpolationMetadata",
30✔
806
                        "InterpolationSupported");
15✔
807
    }
808
    if (interpolation != "")
19✔
809
    {
810
        CPLString name = path + "InterpolationSupported";
7✔
811
        metadata = CSLSetNameValue(metadata, name, interpolation);
7✔
812
    }
813
    CPLString crs = GetKeywords(Capabilities, ext, "crsSupported");
57✔
814
    if (crs == "")
19✔
815
    {
816
        crs = GetKeywords(Capabilities, ext + ".CrsMetadata", "crsSupported");
15✔
817
    }
818
    if (crs != "")
19✔
819
    {
820
        CPLString name = path + "crsSupported";
5✔
821
        metadata = CSLSetNameValue(metadata, name, crs);
5✔
822
    }
823

824
    this->SetMetadata(metadata, "");
19✔
825
    CSLDestroy(metadata);
19✔
826
    metadata = nullptr;
19✔
827

828
    // contents metadata
829
    CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "Contents");
19✔
830
    if (contents)
19✔
831
    {
832
        int index = 1;
19✔
833
        for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
150✔
834
             summary = summary->psNext)
131✔
835
        {
836
            if (summary->eType != CXT_Element ||
131✔
837
                !EQUAL(summary->pszValue, "CoverageSummary"))
129✔
838
            {
839
                continue;
26✔
840
            }
841
            CPLString path3;
105✔
842
            path3.Printf("SUBDATASET_%d_", index);
105✔
843
            index += 1;
105✔
844

845
            // the name and description of the subdataset:
846
            // GDAL Data Model:
847
            // The value of the _NAME is a string that can be passed to
848
            // GDALOpen() to access the file.
849

850
            CPLString key2 = path3 + "NAME";
105✔
851

852
            CPLString name = DescribeCoverageURL;
105✔
853
            name = CPLURLAddKVP(name, "version", this->Version());
105✔
854

855
            CPLXMLNode *node = CPLGetXMLNode(summary, "CoverageId");
105✔
856
            std::string id;
105✔
857
            if (node)
105✔
858
            {
859
                id = CPLGetXMLValue(node, nullptr, "");
60✔
860
            }
861
            else
862
            {
863
                node = CPLGetXMLNode(summary, "Identifier");
45✔
864
                if (node)
45✔
865
                {
866
                    id = CPLGetXMLValue(node, nullptr, "");
45✔
867
                }
868
                else
869
                {
870
                    // todo: maybe not an error since CoverageSummary may be
871
                    // within CoverageSummary (07-067r5 Fig4)
872
                    CSLDestroy(metadata);
×
873
                    CPLError(CE_Failure, CPLE_AppDefined,
×
874
                             "Error in capabilities document.\n");
875
                    return CE_Failure;
×
876
                }
877
            }
878
            name = CPLURLAddKVP(name, "coverage", id.c_str());
105✔
879
            name = "WCS:" + name;
105✔
880
            metadata = CSLSetNameValue(metadata, key2, name);
105✔
881

882
            key2 = path3 + "DESC";
105✔
883

884
            node = CPLGetXMLNode(summary, "Title");
105✔
885
            if (node)
105✔
886
            {
887
                metadata = CSLSetNameValue(metadata, key2,
45✔
888
                                           CPLGetXMLValue(node, nullptr, ""));
889
            }
890
            else
891
            {
892
                metadata = CSLSetNameValue(metadata, key2, id.c_str());
60✔
893
            }
894

895
            // todo: compose global bounding box from WGS84BoundingBox and
896
            // BoundingBox
897

898
            // further subdataset (coverage) parameters are parsed in
899
            // ParseCoverageCapabilities
900
        }
901
    }
902
    this->SetMetadata(metadata, "SUBDATASETS");
19✔
903
    CSLDestroy(metadata);
19✔
904
    return CE_None;
19✔
905
}
906

907
void WCSDataset110::ParseCoverageCapabilities(CPLXMLNode *capabilities,
19✔
908
                                              const std::string &coverage,
909
                                              CPLXMLNode *metadata)
910
{
911
    CPLStripXMLNamespace(capabilities, nullptr, TRUE);
19✔
912
    CPLXMLNode *contents = CPLGetXMLNode(capabilities, "Contents");
19✔
913
    if (contents)
19✔
914
    {
915
        for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
150✔
916
             summary = summary->psNext)
131✔
917
        {
918
            if (summary->eType != CXT_Element ||
131✔
919
                !EQUAL(summary->pszValue, "CoverageSummary"))
129✔
920
            {
921
                continue;
112✔
922
            }
923
            CPLXMLNode *node = CPLGetXMLNode(summary, "CoverageId");
105✔
924
            CPLString id;
105✔
925
            if (node)
105✔
926
            {
927
                id = CPLGetXMLValue(node, nullptr, "");
60✔
928
            }
929
            else
930
            {
931
                node = CPLGetXMLNode(summary, "Identifier");
45✔
932
                if (node)
45✔
933
                {
934
                    id = CPLGetXMLValue(node, nullptr, "");
45✔
935
                }
936
                else
937
                {
938
                    id = "";
×
939
                }
940
            }
941
            if (id != coverage)
105✔
942
            {
943
                continue;
86✔
944
            }
945

946
            // Description
947
            // todo: there could be Title and Abstract for each supported
948
            // language
949
            XMLCopyMetadata(summary, metadata, "Title");
19✔
950
            XMLCopyMetadata(summary, metadata, "Abstract");
19✔
951

952
            // 2.0.1 stuff
953
            XMLCopyMetadata(summary, metadata, "CoverageSubtype");
19✔
954

955
            // Keywords
956
            CPLString kw = GetKeywords(summary, "Keywords", "Keyword");
57✔
957
            CPLAddXMLAttributeAndValue(
19✔
958
                CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
959
                "Keywords");
960

961
            // WCSContents
962
            const char *tags[] = {"SupportedCRS", "SupportedFormat",
19✔
963
                                  "OtherSource"};
964
            for (unsigned int i = 0; i < CPL_ARRAYSIZE(tags); i++)
76✔
965
            {
966
                kw = GetKeywords(summary, "", tags[i]);
57✔
967
                CPLAddXMLAttributeAndValue(
57✔
968
                    CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
969
                    tags[i]);
970
            }
971

972
            // skipping WGS84BoundingBox, BoundingBox, Metadata, Extension
973
            // since those we'll get from coverage description
974
        }
975
    }
976
}
19✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc