• 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

77.23
/frmts/wcs/wcsdataset100.cpp
1
/******************************************************************************
2
 *
3
 * Project:  WCS Client Driver
4
 * Purpose:  Implementation of Dataset class for WCS 1.0.
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> WCSDataset100::GetNativeExtent(int nXOff, int nYOff,
15✔
38
                                                   int nXSize, int nYSize,
39
                                                   CPL_UNUSED int,
40
                                                   CPL_UNUSED int)
41
{
42
    std::vector<double> extent;
15✔
43
    // WCS 1.0 extents are the outer edges of outer pixels.
44
    extent.push_back(m_gt[0] + (nXOff)*m_gt[1]);
15✔
45
    extent.push_back(m_gt[3] + (nYOff + nYSize) * m_gt[5]);
15✔
46
    extent.push_back(m_gt[0] + (nXOff + nXSize) * m_gt[1]);
15✔
47
    extent.push_back(m_gt[3] + (nYOff)*m_gt[5]);
15✔
48
    return extent;
15✔
49
}
50

51
/************************************************************************/
52
/*                        GetCoverageRequest()                          */
53
/*                                                                      */
54
/************************************************************************/
55

56
std::string WCSDataset100::GetCoverageRequest(bool /* scaled */, int nBufXSize,
15✔
57
                                              int nBufYSize,
58
                                              const std::vector<double> &extent,
59
                                              const std::string &osBandList)
60
{
61

62
    /* -------------------------------------------------------------------- */
63
    /*      URL encode strings that could have questionable characters.     */
64
    /* -------------------------------------------------------------------- */
65
    CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
30✔
66

67
    char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
15✔
68
    osCoverage = pszEncoded;
15✔
69
    CPLFree(pszEncoded);
15✔
70

71
    CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
30✔
72

73
    pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
15✔
74
    osFormat = pszEncoded;
15✔
75
    CPLFree(pszEncoded);
15✔
76

77
    /* -------------------------------------------------------------------- */
78
    /*      Do we have a time we want to use?                               */
79
    /* -------------------------------------------------------------------- */
80
    CPLString osTime;
30✔
81

82
    osTime =
83
        CSLFetchNameValueDef(papszSDSModifiers, "time", osDefaultTime.c_str());
15✔
84

85
    /* -------------------------------------------------------------------- */
86
    /*      Construct a "simple" GetCoverage request (WCS 1.0).             */
87
    /* -------------------------------------------------------------------- */
88
    std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
15✔
89
    request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
15✔
90
    request = CPLURLAddKVP(request.c_str(), "REQUEST", "GetCoverage");
15✔
91
    request = CPLURLAddKVP(request.c_str(), "VERSION",
30✔
92
                           CPLGetXMLValue(psService, "Version", "1.0.0"));
30✔
93
    request = CPLURLAddKVP(request.c_str(), "COVERAGE", osCoverage.c_str());
15✔
94
    request = CPLURLAddKVP(request.c_str(), "FORMAT", osFormat.c_str());
15✔
95
    request += CPLString().Printf(
30✔
96
        "&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s", extent[0],
15✔
97
        extent[1], extent[2], extent[3], nBufXSize, nBufYSize, osCRS.c_str());
15✔
98
    CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
30✔
99
    if (extra != "")
15✔
100
    {
101
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
30✔
102
        for (unsigned int i = 0; i < pairs.size(); ++i)
30✔
103
        {
104
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
15✔
105
            request =
106
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
15✔
107
        }
108
    }
109
    extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
15✔
110
    if (extra != "")
15✔
111
    {
112
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
30✔
113
        for (unsigned int i = 0; i < pairs.size(); ++i)
30✔
114
        {
115
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
15✔
116
            request =
117
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
15✔
118
        }
119
    }
120

121
    CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
30✔
122
    if (interpolation == "")
15✔
123
    {
124
        // old undocumented key for interpolation in service
125
        interpolation = CPLGetXMLValue(psService, "Resample", "");
15✔
126
    }
127
    if (interpolation != "")
15✔
128
    {
129
        request += "&INTERPOLATION=" + interpolation;
×
130
    }
131

132
    if (osTime != "")
15✔
133
    {
134
        request += "&time=";
×
135
        request += osTime;
×
136
    }
137

138
    if (osBandList != "")
15✔
139
    {
140
        request += CPLString().Printf("&%s=%s", osBandIdentifier.c_str(),
×
141
                                      osBandList.c_str());
×
142
    }
143
    return request;
30✔
144
}
145

146
/************************************************************************/
147
/*                      DescribeCoverageRequest()                       */
148
/*                                                                      */
149
/************************************************************************/
150

151
std::string WCSDataset100::DescribeCoverageRequest()
6✔
152
{
153
    std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
6✔
154
    request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
6✔
155
    request = CPLURLAddKVP(request.c_str(), "REQUEST", "DescribeCoverage");
6✔
156
    request = CPLURLAddKVP(request.c_str(), "VERSION",
12✔
157
                           CPLGetXMLValue(psService, "Version", "1.0.0"));
12✔
158
    request = CPLURLAddKVP(request.c_str(), "COVERAGE",
12✔
159
                           CPLGetXMLValue(psService, "CoverageName", ""));
12✔
160
    CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
12✔
161
    if (extra != "")
6✔
162
    {
163
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
10✔
164
        for (unsigned int i = 0; i < pairs.size(); ++i)
10✔
165
        {
166
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
5✔
167
            request =
168
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
5✔
169
        }
170
    }
171
    extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
6✔
172
    if (extra != "")
6✔
173
    {
174
        std::vector<std::string> pairs = Split(extra.c_str(), "&");
×
175
        for (unsigned int i = 0; i < pairs.size(); ++i)
×
176
        {
177
            std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
×
178
            request =
179
                CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
×
180
        }
181
    }
182
    return request;
12✔
183
}
184

185
/************************************************************************/
186
/*                         CoverageOffering()                           */
187
/*                                                                      */
188
/************************************************************************/
189

190
CPLXMLNode *WCSDataset100::CoverageOffering(CPLXMLNode *psDC)
5✔
191
{
192
    return CPLGetXMLNode(psDC, "=CoverageDescription.CoverageOffering");
5✔
193
}
194

195
/************************************************************************/
196
/*                         ExtractGridInfo()                            */
197
/*                                                                      */
198
/*      Collect info about grid from describe coverage for WCS 1.0.0    */
199
/*      and above.                                                      */
200
/************************************************************************/
201

202
bool WCSDataset100::ExtractGridInfo()
15✔
203

204
{
205
    CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageOffering");
15✔
206

207
    if (psCO == nullptr)
15✔
208
        return FALSE;
×
209

210
    /* -------------------------------------------------------------------- */
211
    /*      We need to strip off name spaces so it is easier to             */
212
    /*      searchfor plain gml names.                                      */
213
    /* -------------------------------------------------------------------- */
214
    CPLStripXMLNamespace(psCO, nullptr, TRUE);
15✔
215

216
    /* -------------------------------------------------------------------- */
217
    /*      Verify we have a Rectified Grid.                                */
218
    /* -------------------------------------------------------------------- */
219
    CPLXMLNode *psRG =
220
        CPLGetXMLNode(psCO, "domainSet.spatialDomain.RectifiedGrid");
15✔
221

222
    if (psRG == nullptr)
15✔
223
    {
224
        CPLError(CE_Failure, CPLE_AppDefined,
×
225
                 "Unable to find RectifiedGrid in CoverageOffering,\n"
226
                 "unable to process WCS Coverage.");
227
        return FALSE;
×
228
    }
229

230
    /* -------------------------------------------------------------------- */
231
    /*      Extract size, geotransform and coordinate system.               */
232
    /*      Projection is, if it is, from Point.srsName                     */
233
    /* -------------------------------------------------------------------- */
234
    char *pszProjection = nullptr;
15✔
235
    if (WCSParseGMLCoverage(psRG, &nRasterXSize, &nRasterYSize, m_gt,
15✔
236
                            &pszProjection) != CE_None)
15✔
237
    {
238
        CPLFree(pszProjection);
×
239
        return FALSE;
×
240
    }
241
    if (pszProjection)
15✔
242
        m_oSRS.SetFromUserInput(
×
243
            pszProjection,
244
            OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
245
    CPLFree(pszProjection);
15✔
246

247
    // MapServer have origin at pixel boundary
248
    if (CPLGetXMLBoolean(psService, "OriginAtBoundary"))
15✔
249
    {
250
        m_gt[0] += m_gt[1] * 0.5;
3✔
251
        m_gt[0] += m_gt[2] * 0.5;
3✔
252
        m_gt[3] += m_gt[4] * 0.5;
3✔
253
        m_gt[3] += m_gt[5] * 0.5;
3✔
254
    }
255

256
    /* -------------------------------------------------------------------- */
257
    /*      Fallback to nativeCRSs declaration.                             */
258
    /* -------------------------------------------------------------------- */
259
    const char *pszNativeCRSs =
260
        CPLGetXMLValue(psCO, "supportedCRSs.nativeCRSs", nullptr);
15✔
261

262
    if (pszNativeCRSs == nullptr)
15✔
263
        pszNativeCRSs =
264
            CPLGetXMLValue(psCO, "supportedCRSs.requestResponseCRSs", nullptr);
9✔
265

266
    if (pszNativeCRSs == nullptr)
15✔
267
        pszNativeCRSs =
268
            CPLGetXMLValue(psCO, "supportedCRSs.requestCRSs", nullptr);
×
269

270
    if (pszNativeCRSs == nullptr)
15✔
271
        pszNativeCRSs =
272
            CPLGetXMLValue(psCO, "supportedCRSs.responseCRSs", nullptr);
×
273

274
    if (pszNativeCRSs != nullptr && m_oSRS.IsEmpty())
15✔
275
    {
276
        if (m_oSRS.SetFromUserInput(
15✔
277
                pszNativeCRSs,
278
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
15✔
279
            OGRERR_NONE)
280
        {
281
            CPLDebug("WCS", "<nativeCRSs> element contents not parsable:\n%s",
15✔
282
                     pszNativeCRSs);
283
        }
284
    }
285

286
    // We should try to use the services name for the CRS if possible.
287
    if (pszNativeCRSs != nullptr &&
15✔
288
        (STARTS_WITH_CI(pszNativeCRSs, "EPSG:") ||
15✔
289
         STARTS_WITH_CI(pszNativeCRSs, "AUTO:") ||
×
290
         STARTS_WITH_CI(pszNativeCRSs, "Image ") ||
×
291
         STARTS_WITH_CI(pszNativeCRSs, "Engineering ") ||
×
292
         STARTS_WITH_CI(pszNativeCRSs, "OGC:")))
×
293
    {
294
        osCRS = pszNativeCRSs;
15✔
295

296
        size_t nDivider = osCRS.find(" ");
15✔
297

298
        if (nDivider != std::string::npos)
15✔
299
            osCRS.resize(nDivider - 1);
×
300
    }
301

302
    /* -------------------------------------------------------------------- */
303
    /*      Do we have a coordinate system override?                        */
304
    /* -------------------------------------------------------------------- */
305
    const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
15✔
306

307
    if (pszProjOverride)
15✔
308
    {
309
        if (m_oSRS.SetFromUserInput(
×
310
                pszProjOverride,
311
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
×
312
            OGRERR_NONE)
313
        {
314
            CPLError(CE_Failure, CPLE_AppDefined,
×
315
                     "<SRS> element contents not parsable:\n%s",
316
                     pszProjOverride);
317
            return FALSE;
×
318
        }
319

320
        if (STARTS_WITH_CI(pszProjOverride, "EPSG:") ||
×
321
            STARTS_WITH_CI(pszProjOverride, "AUTO:") ||
×
322
            STARTS_WITH_CI(pszProjOverride, "OGC:") ||
×
323
            STARTS_WITH_CI(pszProjOverride, "Image ") ||
×
324
            STARTS_WITH_CI(pszProjOverride, "Engineering "))
×
325
            osCRS = pszProjOverride;
×
326
    }
327

328
    /* -------------------------------------------------------------------- */
329
    /*      Build CRS name to use.                                          */
330
    /* -------------------------------------------------------------------- */
331
    if (!m_oSRS.IsEmpty() && osCRS == "")
15✔
332
    {
333
        const char *pszAuth = m_oSRS.GetAuthorityName(nullptr);
×
334
        if (pszAuth != nullptr && EQUAL(pszAuth, "EPSG"))
×
335
        {
336
            pszAuth = m_oSRS.GetAuthorityCode(nullptr);
×
337
            if (pszAuth)
×
338
            {
339
                osCRS = "EPSG:";
×
340
                osCRS += pszAuth;
×
341
            }
342
            else
343
            {
344
                CPLError(CE_Failure, CPLE_AppDefined,
×
345
                         "Unable to define CRS to use.");
346
                return FALSE;
×
347
            }
348
        }
349
    }
350

351
    /* -------------------------------------------------------------------- */
352
    /*      Pick a format type if we don't already have one selected.       */
353
    /*                                                                      */
354
    /*      We will prefer anything that sounds like TIFF, otherwise        */
355
    /*      falling back to the first supported format.  Should we          */
356
    /*      consider preferring the nativeFormat if available?              */
357
    /* -------------------------------------------------------------------- */
358
    if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
15✔
359
    {
360
        CPLXMLNode *psSF = CPLGetXMLNode(psCO, "supportedFormats");
5✔
361
        CPLXMLNode *psNode;
362
        char **papszFormatList = nullptr;
5✔
363
        CPLString osPreferredFormat;
5✔
364
        int iFormat;
365

366
        if (psSF == nullptr)
5✔
367
        {
368
            CPLError(
×
369
                CE_Failure, CPLE_AppDefined,
370
                "No <PreferredFormat> tag in service definition file, and no\n"
371
                "<supportedFormats> in coverageOffering.");
372
            return FALSE;
×
373
        }
374

375
        for (psNode = psSF->psChild; psNode != nullptr; psNode = psNode->psNext)
36✔
376
        {
377
            if (psNode->eType == CXT_Element &&
31✔
378
                EQUAL(psNode->pszValue, "formats") &&
27✔
379
                psNode->psChild != nullptr &&
27✔
380
                psNode->psChild->eType == CXT_Text)
27✔
381
            {
382
                // This check is looking for deprecated WCS 1.0 capabilities
383
                // with multiple formats space delimited in a single <formats>
384
                // element per GDAL ticket 1748 (done by MapServer 4.10 and
385
                // earlier for instance).
386
                if (papszFormatList == nullptr && psNode->psNext == nullptr &&
27✔
387
                    strstr(psNode->psChild->pszValue, " ") != nullptr &&
1✔
388
                    strstr(psNode->psChild->pszValue, ";") == nullptr)
×
389
                {
390
                    char **papszSubList =
391
                        CSLTokenizeString(psNode->psChild->pszValue);
×
392
                    papszFormatList =
393
                        CSLInsertStrings(papszFormatList, -1, papszSubList);
×
394
                    CSLDestroy(papszSubList);
×
395
                }
396
                else
397
                {
398
                    papszFormatList = CSLAddString(papszFormatList,
27✔
399
                                                   psNode->psChild->pszValue);
27✔
400
                }
401
            }
402
        }
403

404
        for (iFormat = 0;
7✔
405
             papszFormatList != nullptr && papszFormatList[iFormat] != nullptr;
7✔
406
             iFormat++)
407
        {
408
            if (osPreferredFormat.empty())
7✔
409
                osPreferredFormat = papszFormatList[iFormat];
5✔
410

411
            if (strstr(papszFormatList[iFormat], "tiff") != nullptr ||
7✔
412
                strstr(papszFormatList[iFormat], "TIFF") != nullptr ||
7✔
413
                strstr(papszFormatList[iFormat], "Tiff") != nullptr)
3✔
414
            {
415
                osPreferredFormat = papszFormatList[iFormat];
5✔
416
                break;
5✔
417
            }
418
        }
419

420
        CSLDestroy(papszFormatList);
5✔
421

422
        if (!osPreferredFormat.empty())
5✔
423
        {
424
            bServiceDirty = true;
5✔
425
            CPLCreateXMLElementAndValue(psService, "PreferredFormat",
5✔
426
                                        osPreferredFormat);
427
        }
428
    }
429

430
    /* -------------------------------------------------------------------- */
431
    /*      Try to identify a nodata value.  For now we only support the    */
432
    /*      singleValue mechanism.                                          */
433
    /* -------------------------------------------------------------------- */
434
    if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
15✔
435
    {
436
        const char *pszSV = CPLGetXMLValue(
15✔
437
            psCO, "rangeSet.RangeSet.nullValues.singleValue", nullptr);
438

439
        if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
15✔
440
        {
441
            bServiceDirty = true;
×
442
            CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
×
443
        }
444
    }
445

446
    /* -------------------------------------------------------------------- */
447
    /*      Do we have a Band range type.  For now we look for a fairly     */
448
    /*      specific configuration.  The rangeset my have one axis named    */
449
    /*      "Band", with a set of ascending numerical values.               */
450
    /* -------------------------------------------------------------------- */
451
    osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
15✔
452
    CPLXMLNode *psAD = CPLGetXMLNode(
15✔
453
        psService,
454
        "CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription");
455
    CPLXMLNode *psValues;
456

457
    if (osBandIdentifier.empty() && psAD != nullptr &&
22✔
458
        (EQUAL(CPLGetXMLValue(psAD, "name", ""), "Band") ||
7✔
459
         EQUAL(CPLGetXMLValue(psAD, "name", ""), "Bands")) &&
23✔
460
        ((psValues = CPLGetXMLNode(psAD, "values")) != nullptr))
7✔
461
    {
462
        CPLXMLNode *psSV;
463
        int iBand;
464

465
        osBandIdentifier = CPLGetXMLValue(psAD, "name", "");
7✔
466

467
        for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
13✔
468
             psSV = psSV->psNext, iBand++)
6✔
469
        {
470
            if (psSV->eType != CXT_Element ||
9✔
471
                !EQUAL(psSV->pszValue, "singleValue") ||
9✔
472
                psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
6✔
473
                atoi(psSV->psChild->pszValue) != iBand)
6✔
474
            {
475
                osBandIdentifier = "";
3✔
476
                break;
3✔
477
            }
478
        }
479

480
        if (!osBandIdentifier.empty())
7✔
481
        {
482
            bServiceDirty = true;
4✔
483
            CPLSetXMLValue(psService, "BandIdentifier",
4✔
484
                           osBandIdentifier.c_str());
485
        }
486
    }
487

488
    /* -------------------------------------------------------------------- */
489
    /*      Do we have a temporal domain?  If so, try to identify a         */
490
    /*      default time value.                                             */
491
    /* -------------------------------------------------------------------- */
492
    osDefaultTime = CPLGetXMLValue(psService, "DefaultTime", "");
15✔
493
    CPLXMLNode *psTD =
494
        CPLGetXMLNode(psService, "CoverageOffering.domainSet.temporalDomain");
15✔
495
    CPLString osServiceURL = CPLGetXMLValue(psService, "ServiceURL", "");
30✔
496
    CPLString osCoverageExtra =
497
        CPLGetXMLValue(psService, "GetCoverageExtra", "");
15✔
498

499
    if (psTD != nullptr)
15✔
500
    {
501
        CPLXMLNode *psTime;
502

503
        // collect all the allowed time positions.
504

505
        for (psTime = psTD->psChild; psTime != nullptr; psTime = psTime->psNext)
×
506
        {
507
            if (psTime->eType == CXT_Element &&
×
508
                EQUAL(psTime->pszValue, "timePosition") &&
×
509
                psTime->psChild != nullptr &&
×
510
                psTime->psChild->eType == CXT_Text)
×
511
                aosTimePositions.push_back(psTime->psChild->pszValue);
×
512
        }
513

514
        // we will default to the last - likely the most recent - entry.
515

516
        if (!aosTimePositions.empty() && osDefaultTime.empty() &&
×
517
            osServiceURL.ifind("time=") == std::string::npos &&
×
518
            osCoverageExtra.ifind("time=") == std::string::npos)
×
519
        {
520
            osDefaultTime = aosTimePositions.back();
×
521
            bServiceDirty = true;
×
522
            CPLCreateXMLElementAndValue(psService, "DefaultTime",
×
523
                                        osDefaultTime.c_str());
524
        }
525
    }
526

527
    return true;
15✔
528
}
529

530
/************************************************************************/
531
/*                      ParseCapabilities()                             */
532
/************************************************************************/
533

534
CPLErr WCSDataset100::ParseCapabilities(CPLXMLNode *Capabilities,
5✔
535
                                        const std::string & /* url */)
536
{
537

538
    CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
5✔
539

540
    if (strcmp(Capabilities->pszValue, "WCS_Capabilities") != 0)
5✔
541
    {
542
        CPLError(CE_Failure, CPLE_AppDefined,
×
543
                 "Error in capabilities document.\n");
544
        return CE_Failure;
×
545
    }
546

547
    char **metadata = nullptr;
5✔
548
    CPLString path = "WCS_GLOBAL#";
10✔
549

550
    CPLString key = path + "version";
10✔
551
    metadata = CSLSetNameValue(metadata, key, Version());
5✔
552

553
    for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
60✔
554
         node = node->psNext)
55✔
555
    {
556
        const char *attr = node->pszValue;
55✔
557
        if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
55✔
558
        {
559
            key = path + "updateSequence";
4✔
560
            CPLString value = CPLGetXMLValue(node, nullptr, "");
4✔
561
            metadata = CSLSetNameValue(metadata, key, value);
4✔
562
        }
563
    }
564

565
    // identification metadata
566
    CPLString path2 = path;
10✔
567
    CPLXMLNode *service = AddSimpleMetaData(
30✔
568
        &metadata, Capabilities, path2, "Service",
569
        {"description", "name", "label", "fees", "accessConstraints"});
25✔
570
    if (service)
5✔
571
    {
572
        CPLString path3 = std::move(path2);
10✔
573
        CPLString kw = GetKeywords(service, "keywords", "keyword");
15✔
574
        if (kw != "")
5✔
575
        {
576
            CPLString name = path + "keywords";
4✔
577
            metadata = CSLSetNameValue(metadata, name, kw);
4✔
578
        }
579
        CPLXMLNode *party = AddSimpleMetaData(
20✔
580
            &metadata, service, path3, "responsibleParty",
581
            {"individualName", "organisationName", "positionName"});
15✔
582
        CPLXMLNode *info = CPLGetXMLNode(party, "contactInfo");
5✔
583
        if (party && info)
5✔
584
        {
585
            CPLString path4 = path3 + "contactInfo.";
10✔
586
            CPLString path5 = path4;
5✔
587
            AddSimpleMetaData(&metadata, info, path4, "address",
35✔
588
                              {"deliveryPoint", "city", "administrativeArea",
589
                               "postalCode", "country",
590
                               "electronicMailAddress"});
30✔
591
            AddSimpleMetaData(&metadata, info, path5, "phone",
15✔
592
                              {"voice", "facsimile"});
10✔
593
        }
594
    }
595

596
    // provider metadata
597
    // operations metadata
598
    CPLString DescribeCoverageURL;
10✔
599
    DescribeCoverageURL = CPLGetXMLValue(
600
        CPLGetXMLNode(
5✔
601
            CPLGetXMLNode(
602
                CPLSearchXMLNode(
603
                    CPLSearchXMLNode(Capabilities, "DescribeCoverage"), "Get"),
604
                "OnlineResource"),
605
            "href"),
606
        nullptr, "");
5✔
607
    // if DescribeCoverageURL looks wrong (i.e. has localhost) should we change
608
    // it?
609

610
    this->SetMetadata(metadata, "");
5✔
611
    CSLDestroy(metadata);
5✔
612
    metadata = nullptr;
5✔
613

614
    if (CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "ContentMetadata"))
5✔
615
    {
616
        int index = 1;
5✔
617
        for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
23✔
618
             summary = summary->psNext)
18✔
619
        {
620
            if (summary->eType != CXT_Element ||
18✔
621
                !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
18✔
622
            {
623
                continue;
×
624
            }
625
            CPLString path3;
18✔
626
            path3.Printf("SUBDATASET_%d_", index);
18✔
627
            index += 1;
18✔
628

629
            // the name and description of the subdataset:
630
            // GDAL Data Model:
631
            // The value of the _NAME is a string that can be passed to
632
            // GDALOpen() to access the file.
633

634
            CPLXMLNode *node = CPLGetXMLNode(summary, "name");
18✔
635
            if (node)
18✔
636
            {
637
                CPLString key2 = path3 + "NAME";
36✔
638
                CPLString name = CPLGetXMLValue(node, nullptr, "");
36✔
639
                CPLString value = DescribeCoverageURL;
18✔
640
                value = CPLURLAddKVP(value, "VERSION", this->Version());
18✔
641
                value = CPLURLAddKVP(value, "COVERAGE", name);
18✔
642
                metadata = CSLSetNameValue(metadata, key2, value);
18✔
643
            }
644
            else
645
            {
646
                CSLDestroy(metadata);
×
647
                CPLError(CE_Failure, CPLE_AppDefined,
×
648
                         "Error in capabilities document.\n");
649
                return CE_Failure;
×
650
            }
651

652
            node = CPLGetXMLNode(summary, "label");
18✔
653
            if (node)
18✔
654
            {
655
                CPLString key2 = path3 + "DESC";
18✔
656
                metadata = CSLSetNameValue(metadata, key2,
18✔
657
                                           CPLGetXMLValue(node, nullptr, ""));
658
            }
659
            else
660
            {
661
                CSLDestroy(metadata);
×
662
                CPLError(CE_Failure, CPLE_AppDefined,
×
663
                         "Error in capabilities document.\n");
664
                return CE_Failure;
×
665
            }
666

667
            // todo: compose global bounding box from lonLatEnvelope
668

669
            // further subdataset (coverage) parameters are parsed in
670
            // ParseCoverageCapabilities
671
        }
672
    }
673
    this->SetMetadata(metadata, "SUBDATASETS");
5✔
674
    CSLDestroy(metadata);
5✔
675
    return CE_None;
5✔
676
}
677

678
void WCSDataset100::ParseCoverageCapabilities(CPLXMLNode *capabilities,
5✔
679
                                              const std::string &coverage,
680
                                              CPLXMLNode *metadata)
681
{
682
    CPLStripXMLNamespace(capabilities, nullptr, TRUE);
5✔
683
    if (CPLXMLNode *contents = CPLGetXMLNode(capabilities, "ContentMetadata"))
5✔
684
    {
685
        for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
23✔
686
             summary = summary->psNext)
18✔
687
        {
688
            if (summary->eType != CXT_Element ||
18✔
689
                !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
18✔
690
            {
691
                continue;
13✔
692
            }
693

694
            CPLXMLNode *node = CPLGetXMLNode(summary, "name");
18✔
695
            if (node)
18✔
696
            {
697
                CPLString name = CPLGetXMLValue(node, nullptr, "");
18✔
698
                if (name != coverage)
18✔
699
                {
700
                    continue;
13✔
701
                }
702
            }
703

704
            XMLCopyMetadata(summary, metadata, "label");
5✔
705
            XMLCopyMetadata(summary, metadata, "description");
5✔
706

707
            CPLString kw = GetKeywords(summary, "keywords", "keyword");
15✔
708
            CPLAddXMLAttributeAndValue(
5✔
709
                CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
710
                "keywords");
711

712
            // skip metadataLink
713
        }
714
    }
715
}
5✔
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