Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/wcs/wcsutils.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  WCS Client Driver
4
 * Purpose:  Implementation of utilities.
5
 * Author:   Ari Jolma <ari dot jolma at gmail dot 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 "ogr_spatialref.h"
17
#include "wcsutils.h"
18
#include <algorithm>
19
20
0
#define DIGITS "0123456789"
21
22
namespace WCSUtils
23
{
24
25
void Swap(double &a, double &b)
26
0
{
27
0
    double tmp = a;
28
0
    a = b;
29
0
    b = tmp;
30
0
}
31
32
std::string URLEncode(const std::string &str)
33
0
{
34
0
    char *pszEncoded = CPLEscapeString(str.c_str(), -1, CPLES_URL);
35
0
    std::string str2 = pszEncoded;
36
0
    CPLFree(pszEncoded);
37
0
    return str2;
38
0
}
39
40
std::string URLRemoveKey(const char *url, const std::string &key)
41
330
{
42
330
    CPLString retval = url;
43
330
    const std::string key_is = key + "=";
44
342
    while (true)
45
342
    {
46
342
        size_t pos = retval.ifind(key_is);
47
342
        if (pos != std::string::npos)
48
12
        {
49
12
            size_t end = retval.find("&", pos);
50
12
            retval.erase(pos, end - pos + 1);
51
12
        }
52
330
        else
53
330
        {
54
330
            break;
55
330
        }
56
342
    }
57
330
    if (!retval.empty() && retval.back() == '&')
58
0
    {
59
0
        retval.pop_back();
60
0
    }
61
330
    std::string retValAsStdString = std::move(retval);
62
330
    return retValAsStdString;
63
330
}
64
65
std::vector<std::string> &SwapFirstTwo(std::vector<std::string> &array)
66
0
{
67
0
    if (array.size() >= 2)
68
0
    {
69
0
        std::swap(array[0], array[1]);
70
0
    }
71
0
    return array;
72
0
}
73
74
std::vector<std::string> Split(const char *value, const char *delim,
75
                               bool swap_the_first_two)
76
0
{
77
0
    std::vector<std::string> array;
78
0
    char **tokens = CSLTokenizeString2(
79
0
        value, delim,
80
0
        CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES | CSLT_HONOURSTRINGS);
81
0
    int n = CSLCount(tokens);
82
0
    for (int i = 0; i < n; ++i)
83
0
    {
84
0
        array.push_back(tokens[i]);
85
0
    }
86
0
    CSLDestroy(tokens);
87
0
    if (swap_the_first_two && array.size() >= 2)
88
0
    {
89
0
        return SwapFirstTwo(array);
90
0
    }
91
0
    return array;
92
0
}
93
94
std::string Join(const std::vector<std::string> &array, const char *delim,
95
                 bool swap_the_first_two)
96
0
{
97
0
    std::string str;
98
0
    const auto arraySize = array.size();
99
0
    for (unsigned int i = 0; i < arraySize; ++i)
100
0
    {
101
0
        if (i > 0)
102
0
        {
103
0
            str += delim;
104
0
        }
105
0
        if (swap_the_first_two)
106
0
        {
107
0
            if (i == 0 && arraySize >= 2)
108
0
            {
109
0
                str += array[1];
110
0
            }
111
0
            else if (i == 1)
112
0
            {
113
0
                str += array[0];
114
0
            }
115
0
        }
116
0
        else
117
0
        {
118
0
            str += array[i];
119
0
        }
120
0
    }
121
0
    return str;
122
0
}
123
124
std::vector<int> Ilist(const std::vector<std::string> &array, unsigned int from,
125
                       size_t count)
126
0
{
127
0
    std::vector<int> retval;
128
0
    for (unsigned int i = from; i < array.size() && i < from + count; ++i)
129
0
    {
130
0
        retval.push_back(atoi(array[i].c_str()));
131
0
    }
132
0
    return retval;
133
0
}
134
135
std::vector<double> Flist(const std::vector<std::string> &array,
136
                          unsigned int from, size_t count)
137
0
{
138
0
    std::vector<double> retval;
139
0
    for (unsigned int i = from; i < array.size() && i < from + count; ++i)
140
0
    {
141
0
        retval.push_back(CPLAtof(array[i].c_str()));
142
0
    }
143
0
    return retval;
144
0
}
145
146
int IndexOf(const std::string &str, const std::vector<std::string> &array)
147
0
{
148
0
    int index = -1;
149
0
    for (unsigned int i = 0; i < array.size(); ++i)
150
0
    {
151
0
        if (array[i] == str)
152
0
        {
153
0
            index = i;
154
0
            break;
155
0
        }
156
0
    }
157
0
    return index;
158
0
}
159
160
int IndexOf(int i, const std::vector<int> &array)
161
0
{
162
0
    int index = -1;
163
0
    for (unsigned int j = 0; j < array.size(); ++j)
164
0
    {
165
0
        if (array[j] == i)
166
0
        {
167
0
            index = j;
168
0
            break;
169
0
        }
170
0
    }
171
0
    return index;
172
0
}
173
174
std::vector<int> IndexOf(const std::vector<std::string> &strs,
175
                         const std::vector<std::string> &array)
176
0
{
177
0
    std::vector<int> retval;
178
0
    for (unsigned int i = 0; i < strs.size(); ++i)
179
0
    {
180
0
        retval.push_back(IndexOf(strs[i], array));
181
0
    }
182
0
    return retval;
183
0
}
184
185
int IndexOf(const std::string &key,
186
            const std::vector<std::vector<std::string>> &kvps)
187
0
{
188
0
    int index = -1;
189
0
    for (unsigned int i = 0; i < kvps.size(); ++i)
190
0
    {
191
0
        if (kvps[i].size() > 1 && key == kvps[i][0])
192
0
        {
193
0
            index = i;
194
0
            break;
195
0
        }
196
0
    }
197
0
    return index;
198
0
}
199
200
bool Contains(const std::vector<int> &array, int value)
201
0
{
202
0
    for (unsigned int i = 0; i < array.size(); ++i)
203
0
    {
204
0
        if (array[i] == value)
205
0
        {
206
0
            return true;
207
0
        }
208
0
    }
209
0
    return false;
210
0
}
211
212
std::string FromParenthesis(const std::string &s)
213
0
{
214
0
    size_t beg = s.find_first_of("(");
215
0
    size_t end = s.find_last_of(")");
216
0
    if (beg == std::string::npos || end == std::string::npos)
217
0
    {
218
0
        return "";
219
0
    }
220
0
    return s.substr(beg + 1, end - beg - 1);
221
0
}
222
223
std::vector<std::string>
224
ParseSubset(const std::vector<std::string> &subset_array,
225
            const std::string &dim)
226
0
{
227
    // array is SUBSET defs, a SUBSET def is dim[,crs](low[,high])
228
0
    std::vector<std::string> retval;
229
0
    unsigned int i;
230
0
    std::string params;
231
0
    for (i = 0; i < subset_array.size(); ++i)
232
0
    {
233
0
        params = subset_array[i];
234
0
        size_t pos = params.find(dim + "(");
235
0
        if (pos != std::string::npos)
236
0
        {
237
0
            retval.push_back("");  // crs
238
0
            break;
239
0
        }
240
0
        pos = params.find(dim + ",");
241
0
        if (pos != std::string::npos)
242
0
        {
243
0
            params.erase(0, pos + 1);
244
0
            pos = params.find("(");
245
0
            retval.push_back(params.substr(0, pos - 1));
246
0
            break;
247
0
        }
248
0
    }
249
0
    if (retval.size() > 0)
250
0
    {
251
0
        std::vector<std::string> params_array =
252
0
            Split(FromParenthesis(params).c_str(), ",");
253
0
        retval.push_back(params_array[0]);
254
0
        if (params_array.size() > 1)
255
0
        {
256
0
            retval.push_back(params_array[1]);
257
0
        }
258
0
        else
259
0
        {
260
0
            retval.push_back("");
261
0
        }
262
0
    }
263
0
    return retval;
264
0
}
265
266
/* -------------------------------------------------------------------- */
267
/*      FileIsReadable                                                  */
268
/* -------------------------------------------------------------------- */
269
270
bool FileIsReadable(const std::string &filename)
271
0
{
272
0
    VSILFILE *file = VSIFOpenL(filename.c_str(), "r");
273
0
    if (file)
274
0
    {
275
0
        VSIFCloseL(file);
276
0
        return true;
277
0
    }
278
0
    return false;
279
0
}
280
281
std::string RemoveExt(const std::string &filename)
282
0
{
283
0
    size_t pos = filename.find_last_of(".");
284
0
    if (pos != std::string::npos)
285
0
    {
286
0
        return filename.substr(0, pos);
287
0
    }
288
0
    return filename;
289
0
}
290
291
/* -------------------------------------------------------------------- */
292
/*      MakeDir                                                         */
293
/* -------------------------------------------------------------------- */
294
295
bool MakeDir(const std::string &dirname)
296
374
{
297
374
    VSIStatBufL stat;
298
374
    if (VSIStatL(dirname.c_str(), &stat) != 0)
299
0
    {
300
0
        std::string parent = CPLGetDirnameSafe(dirname.c_str());
301
0
        if (!parent.empty() && parent != ".")
302
0
        {
303
0
            if (!MakeDir(parent))
304
0
            {
305
0
                return false;
306
0
            }
307
0
        }
308
0
        return VSIMkdir(dirname.c_str(), 0755) == 0;
309
0
    }
310
374
    return true;
311
374
}
312
313
/************************************************************************/
314
/*                       SearchChildWithValue()                         */
315
/************************************************************************/
316
317
CPLXMLNode *SearchChildWithValue(CPLXMLNode *node, const char *path,
318
                                 const char *value)
319
0
{
320
0
    if (node == nullptr)
321
0
    {
322
0
        return nullptr;
323
0
    }
324
0
    for (CPLXMLNode *child = node->psChild; child != nullptr;
325
0
         child = child->psNext)
326
0
    {
327
0
        if (EQUAL(CPLGetXMLValue(child, path, ""), value))
328
0
        {
329
0
            return child;
330
0
        }
331
0
    }
332
0
    return nullptr;
333
0
}
334
335
bool CPLGetXMLBoolean(CPLXMLNode *poRoot, const char *pszPath)
336
0
{
337
    // returns true if path exists and does not contain untrue value
338
0
    poRoot = CPLGetXMLNode(poRoot, pszPath);
339
0
    if (poRoot == nullptr)
340
0
    {
341
0
        return false;
342
0
    }
343
0
    return CPLTestBool(CPLGetXMLValue(poRoot, nullptr, ""));
344
0
}
345
346
bool CPLUpdateXML(CPLXMLNode *poRoot, const char *pszPath,
347
                  const char *new_value)
348
0
{
349
0
    std::string old_value = CPLGetXMLValue(poRoot, pszPath, "");
350
0
    if (new_value != old_value)
351
0
    {
352
0
        CPLSetXMLValue(poRoot, pszPath, new_value);
353
0
        return true;
354
0
    }
355
0
    return false;
356
0
}
357
358
/* -------------------------------------------------------------------- */
359
/*      XMLCopyMetadata                                                 */
360
/*      Copy child node 'key' into metadata as MDI element.             */
361
/* -------------------------------------------------------------------- */
362
363
void XMLCopyMetadata(CPLXMLNode *parent, CPLXMLNode *metadata,
364
                     const std::string &key)
365
0
{
366
0
    CPLXMLNode *node = CPLGetXMLNode(parent, key.c_str());
367
0
    if (node)
368
0
    {
369
0
        CPLAddXMLAttributeAndValue(
370
0
            CPLCreateXMLElementAndValue(metadata, "MDI",
371
0
                                        CPLGetXMLValue(node, nullptr, "")),
372
0
            "key", key.c_str());
373
0
    }
374
0
}
375
376
/* -------------------------------------------------------------------- */
377
/*      SetupCache                                                      */
378
/*      Cache is a directory                                            */
379
/*      The file db is the cache index with lines of unique_key=URL     */
380
/* -------------------------------------------------------------------- */
381
382
bool SetupCache(std::string &cache, bool clear)
383
374
{
384
374
    if (cache == "")
385
374
    {
386
#ifdef _WIN32
387
        const char *home = CPLGetConfigOption("USERPROFILE", nullptr);
388
#else
389
374
        const char *home = CPLGetConfigOption("HOME", nullptr);
390
374
#endif
391
374
        if (home)
392
374
        {
393
374
            cache = CPLFormFilenameSafe(home, ".gdal", nullptr);
394
374
        }
395
0
        else
396
0
        {
397
0
            const char *dir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
398
0
            if (!dir)
399
0
                dir = CPLGetConfigOption("TMPDIR", nullptr);
400
0
            if (!dir)
401
0
                dir = CPLGetConfigOption("TEMP", nullptr);
402
0
            const char *username = CPLGetConfigOption("USERNAME", nullptr);
403
0
            if (!username)
404
0
                username = CPLGetConfigOption("USER", nullptr);
405
0
            if (dir && username)
406
0
            {
407
0
                std::string subdir = ".gdal_";
408
0
                subdir += username;
409
0
                cache = CPLFormFilenameSafe(dir, subdir.c_str(), nullptr);
410
0
            }
411
0
        }
412
374
        cache = CPLFormFilenameSafe(cache.c_str(), "wcs_cache", nullptr);
413
374
    }
414
374
    if (!MakeDir(cache))
415
0
    {
416
0
        return false;
417
0
    }
418
374
    if (clear)
419
0
    {
420
0
        char **folder = VSIReadDir(cache.c_str());
421
0
        int size = folder ? CSLCount(folder) : 0;
422
0
        for (int i = 0; i < size; i++)
423
0
        {
424
0
            if (folder[i][0] == '.')
425
0
            {
426
0
                continue;
427
0
            }
428
0
            const std::string filepath =
429
0
                CPLFormFilenameSafe(cache.c_str(), folder[i], nullptr);
430
0
            CPL_IGNORE_RET_VAL(VSIUnlink(filepath.c_str()));
431
0
        }
432
0
        CSLDestroy(folder);
433
0
    }
434
    // make sure the index exists and is writable
435
374
    const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
436
374
    VSILFILE *f = VSIFOpenL(db.c_str(), "r");
437
374
    if (f)
438
374
    {
439
374
        VSIFCloseL(f);
440
374
    }
441
0
    else
442
0
    {
443
0
        f = VSIFOpenL(db.c_str(), "w");
444
0
        if (f)
445
0
        {
446
0
            VSIFCloseL(f);
447
0
        }
448
0
        else
449
0
        {
450
0
            CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
451
0
                     db.c_str(), errno);
452
0
            return false;
453
0
        }
454
0
    }
455
374
    srand((unsigned int)time(
456
374
        nullptr));  // not to have the same names in the cache
457
374
    return true;
458
374
}
459
460
// recent libc++ std::sort() involve unsigned integer overflow in some
461
// situation
462
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
463
static void SortStringList(std::vector<std::string> &strList)
464
207
{
465
207
    std::sort(strList.begin(), strList.end());
466
207
}
467
468
std::vector<std::string> ReadCache(const std::string &cache)
469
207
{
470
207
    std::vector<std::string> contents;
471
207
    const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
472
207
    char **data = CSLLoad(db.c_str());
473
207
    if (data)
474
207
    {
475
38.5k
        for (int i = 0; data[i]; ++i)
476
38.3k
        {
477
38.3k
            char *val = strchr(data[i], '=');
478
38.3k
            if (val != nullptr && *val == '=')
479
38.3k
            {
480
38.3k
                val += 1;
481
38.3k
                if (strcmp(val, "bar") != 0)
482
38.1k
                {
483
38.1k
                    contents.push_back(val);
484
38.1k
                }
485
38.3k
            }
486
38.3k
        }
487
207
        CSLDestroy(data);
488
207
    }
489
207
    SortStringList(contents);
490
207
    return contents;
491
207
}
492
493
/* -------------------------------------------------------------------- */
494
/*      DeleteEntryFromCache                                            */
495
/*      Examines the 'db' file in the cache, which contains             */
496
/*      unique key=value pairs, one per line. This function             */
497
/*      deletes pairs based on the given key and/or value.              */
498
/*      If key or value is empty it is not considered.                  */
499
/*      The key is taken as a basename of a file in the cache           */
500
/*      and all files with the basename is deleted.                     */
501
/* -------------------------------------------------------------------- */
502
503
bool DeleteEntryFromCache(const std::string &cache, const std::string &key,
504
                          const std::string &value)
505
165
{
506
    // Depending on which one of key and value is not "" delete the relevant
507
    // entry.
508
165
    const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
509
165
    char **data =
510
165
        CSLLoad(db.c_str());  // returns NULL in error and for empty files
511
165
    char **data2 = CSLAddNameValue(nullptr, "foo", "bar");
512
165
    std::string filename = "";
513
165
    if (data)
514
165
    {
515
24.8k
        for (int i = 0; data[i]; ++i)
516
24.7k
        {
517
24.7k
            char *val = strchr(data[i], '=');
518
24.7k
            if (val != nullptr && *val == '=')
519
24.6k
            {
520
24.6k
                *val = '\0';
521
24.6k
                val = val + 1;
522
24.6k
                if ((key != "" && key == data[i]) ||
523
24.6k
                    (value != "" && value == val) ||
524
24.6k
                    (strcmp(data[i], "foo") == 0))
525
182
                {
526
182
                    if (key != "")
527
0
                    {
528
0
                        filename = data[i];
529
0
                    }
530
182
                    else if (value != "")
531
182
                    {
532
182
                        filename = data[i];
533
182
                    }
534
182
                    continue;
535
182
                }
536
24.4k
                data2 = CSLAddNameValue(data2, data[i], val);
537
24.4k
            }
538
24.7k
        }
539
165
        CSLDestroy(data);
540
165
    }
541
165
    CSLSave(data2, db.c_str());  // returns 0 in error and for empty arrays
542
165
    CSLDestroy(data2);
543
165
    if (filename != "")
544
165
    {
545
165
        char **folder = VSIReadDir(cache.c_str());
546
165
        int size = folder ? CSLCount(folder) : 0;
547
14.7k
        for (int i = 0; i < size; i++)
548
14.5k
        {
549
14.5k
            if (folder[i][0] == '.')
550
330
            {
551
330
                continue;
552
330
            }
553
14.2k
            std::string name = folder[i];
554
14.2k
            if (name.find(filename) != std::string::npos)
555
17
            {
556
17
                const std::string filepath =
557
17
                    CPLFormFilenameSafe(cache.c_str(), name.c_str(), nullptr);
558
17
                if (VSIUnlink(filepath.c_str()) == -1)
559
0
                {
560
                    // error but can't do much, raise a warning?
561
0
                }
562
17
            }
563
14.2k
        }
564
165
        CSLDestroy(folder);
565
165
    }
566
165
    return true;
567
165
}
568
569
/* -------------------------------------------------------------------- */
570
/*      SearchCache                                                     */
571
/*      The key,value pairs in the cache index file 'db' is searched    */
572
/*      for the first pair where the value is the given url. If one     */
573
/*      is found, the filename is formed from the cache directory name, */
574
/*      the key, and the ext.                                           */
575
/* -------------------------------------------------------------------- */
576
577
CPLErr SearchCache(const std::string &cache, const std::string &url,
578
                   std::string &filename, const std::string &ext, bool &found)
579
330
{
580
330
    found = false;
581
330
    const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
582
330
    VSILFILE *f = VSIFOpenL(db.c_str(), "r");
583
330
    if (!f)
584
0
    {
585
0
        CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
586
0
                 db.c_str(), errno);
587
0
        return CE_Failure;
588
0
    }
589
49.0k
    while (const char *line = CPLReadLineL(f))
590
48.7k
    {
591
48.7k
        char *value = strchr((char *)line, '=');
592
48.7k
        if (value == nullptr || *value != '=')
593
0
        {
594
0
            continue;
595
0
        }
596
48.7k
        *value = '\0';
597
48.7k
        if (url == (value + 1))
598
0
        {
599
0
            filename = line;
600
0
            found = true;
601
0
            break;
602
0
        }
603
48.7k
    }
604
330
    VSIFCloseL(f);
605
330
    if (found)
606
0
    {
607
0
        filename = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
608
0
                                       nullptr);
609
0
        found = FileIsReadable(filename);
610
        // if not readable, we should delete the entry
611
0
    }
612
330
    return CE_None;
613
330
}
614
615
/* -------------------------------------------------------------------- */
616
/*      AddEntryToCache                                                 */
617
/*      A new unique key is created into the database based on the      */
618
/*      filename replacing X with a random ascii character.             */
619
/*      The returned filename is a path formed from the cache directory */
620
/*      name, the filename, and the ext.                                */
621
/* -------------------------------------------------------------------- */
622
623
CPLErr AddEntryToCache(const std::string &cache, const std::string &url,
624
                       std::string &filename, const std::string &ext)
625
165
{
626
    // todo: check for lock and do something if locked(?)
627
    // todo: lock the cache
628
    // assuming the url is not in the cache
629
165
    const std::string store = filename;
630
165
    const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
631
165
    VSILFILE *f = VSIFOpenL(db.c_str(), "a");
632
165
    if (!f)
633
0
    {
634
0
        CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
635
0
                 db.c_str(), errno);
636
0
        return CE_Failure;
637
0
    }
638
639
    // create a new file into the cache using filename as template
640
165
    std::string path = "";
641
165
    VSIStatBufL stat;
642
165
    do
643
986
    {
644
986
        filename = store;
645
986
        static const char chars[] =
646
986
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
647
5.91k
        for (size_t i = 0; i < filename.length(); ++i)
648
4.93k
        {
649
4.93k
            if (filename.at(i) == 'X')
650
4.93k
            {
651
4.93k
#ifndef __COVERITY__
652
4.93k
                filename.replace(i, 1, 1, chars[rand() % (sizeof(chars) - 1)]);
653
#else
654
                filename.replace(i, 1, 1, chars[i % (sizeof(chars) - 1)]);
655
#endif
656
4.93k
            }
657
4.93k
        }
658
        // replace X with random character from a-zA-Z
659
986
        path = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
660
986
                                   nullptr);
661
986
    } while (VSIStatExL(path.c_str(), &stat, VSI_STAT_EXISTS_FLAG) == 0);
662
165
    VSILFILE *f2 = VSIFOpenL(path.c_str(), "w");
663
165
    if (f2)
664
165
    {
665
165
        VSIFCloseL(f2);
666
165
    }
667
668
165
    std::string entry =
669
165
        filename + "=" + url + "\n";  // '=' for compatibility with CSL
670
165
    VSIFWriteL(entry.c_str(), sizeof(char), entry.size(), f);
671
165
    VSIFCloseL(f);
672
673
165
    filename = std::move(path);
674
165
    return CE_None;
675
165
}
676
677
// steps into element 'from' and adds values of elements 'keys' into the
678
// metadata 'path' is the key that is used for metadata and it is appended with
679
// 'from' path may be later used to get metadata from elements below 'from' so
680
// it is returned in the appended form
681
CPLXMLNode *AddSimpleMetaData(char ***metadata, CPLXMLNode *node,
682
                              std::string &path, const std::string &from,
683
                              const std::vector<std::string> &keys)
684
0
{
685
0
    CPLXMLNode *node2 = CPLGetXMLNode(node, from.c_str());
686
0
    if (node2)
687
0
    {
688
0
        path = path + from + ".";
689
0
        for (unsigned int i = 0; i < keys.size(); i++)
690
0
        {
691
0
            CPLXMLNode *node3 = CPLGetXMLNode(node2, keys[i].c_str());
692
0
            if (node3)
693
0
            {
694
0
                const std::string name = path + keys[i];
695
0
                CPLString value = CPLGetXMLValue(node3, nullptr, "");
696
0
                value.Trim();
697
0
                *metadata =
698
0
                    CSLSetNameValue(*metadata, name.c_str(), value.c_str());
699
0
            }
700
0
        }
701
0
    }
702
0
    return node2;
703
0
}
704
705
std::string GetKeywords(CPLXMLNode *root, const std::string &path,
706
                        const std::string &kw)
707
0
{
708
0
    std::string words = "";
709
0
    CPLXMLNode *keywords =
710
0
        (path != "") ? CPLGetXMLNode(root, path.c_str()) : root;
711
0
    if (keywords)
712
0
    {
713
0
        std::vector<unsigned int> epsg_codes;
714
0
        for (CPLXMLNode *node = keywords->psChild; node != nullptr;
715
0
             node = node->psNext)
716
0
        {
717
0
            if (node->eType != CXT_Element)
718
0
            {
719
0
                continue;
720
0
            }
721
0
            if (kw == node->pszValue)
722
0
            {
723
0
                CPLString word = CPLGetXMLValue(node, nullptr, "");
724
0
                word.Trim();
725
726
                // crs, replace "http://www.opengis.net/def/crs/EPSG/0/"
727
                // or "urn:ogc:def:crs:EPSG::" with EPSG:
728
0
                const char *const epsg[] = {
729
0
                    "http://www.opengis.net/def/crs/EPSG/0/",
730
0
                    "urn:ogc:def:crs:EPSG::"};
731
0
                for (unsigned int i = 0; i < CPL_ARRAYSIZE(epsg); i++)
732
0
                {
733
0
                    size_t pos = word.find(epsg[i]);
734
0
                    if (pos == 0)
735
0
                    {
736
0
                        std::string code =
737
0
                            word.substr(strlen(epsg[i]), std::string::npos);
738
0
                        if (code.find_first_not_of(DIGITS) == std::string::npos)
739
0
                        {
740
0
                            epsg_codes.push_back(atoi(code.c_str()));
741
0
                            continue;
742
0
                        }
743
0
                    }
744
0
                }
745
746
                // profiles, remove http://www.opengis.net/spec/
747
                // interpolation, remove
748
                // http://www.opengis.net/def/interpolation/OGC/1/
749
750
0
                const char *const spec[] = {
751
0
                    "http://www.opengis.net/spec/",
752
0
                    "http://www.opengis.net/def/interpolation/OGC/1/"};
753
0
                for (unsigned int i = 0; i < CPL_ARRAYSIZE(spec); i++)
754
0
                {
755
0
                    size_t pos = word.find(spec[i]);
756
0
                    if (pos != std::string::npos)
757
0
                    {
758
0
                        word.erase(pos, strlen(spec[i]));
759
0
                    }
760
0
                }
761
762
0
                if (words != "")
763
0
                {
764
0
                    words += ",";
765
0
                }
766
0
                words += word;
767
0
            }
768
0
        }
769
0
        if (epsg_codes.size() > 0)
770
0
        {
771
0
            std::string codes;
772
0
            std::sort(epsg_codes.begin(), epsg_codes.end());
773
0
            unsigned int pajazzo = 0, i = 0, a = 0, b = 0;
774
0
            while (1)
775
0
            {
776
                // cppcheck-suppress containerOutOfBounds
777
0
                unsigned int c = i < epsg_codes.size() ? epsg_codes[i] : 0;
778
0
                if (pajazzo == 1)
779
0
                {
780
0
                    if (c > a + 1)
781
0
                    {
782
0
                        if (codes != "")
783
0
                        {
784
0
                            codes += ",";
785
0
                        }
786
0
                        codes += CPLString().Printf("%i", a);
787
0
                        a = c;
788
0
                    }
789
0
                    else if (c >= a)
790
0
                    {
791
0
                        b = c;
792
0
                        pajazzo = 2;
793
0
                    }
794
0
                }
795
0
                else if (pajazzo == 2)
796
0
                {
797
0
                    if (c > b + 1)
798
0
                    {
799
0
                        if (codes != "")
800
0
                        {
801
0
                            codes += ",";
802
0
                        }
803
0
                        codes += CPLString().Printf("%i:%i", a, b);
804
0
                        a = c;
805
0
                        pajazzo = 1;
806
0
                    }
807
0
                    else if (c >= b)
808
0
                    {
809
0
                        b = c;
810
0
                    }
811
0
                }
812
0
                else
813
0
                {  // pajazzo == 0
814
0
                    a = c;
815
0
                    pajazzo = 1;
816
0
                }
817
0
                if (i == epsg_codes.size())
818
0
                {
819
                    // must empty the pajazzo before leaving
820
0
                    if (codes != "")
821
0
                    {
822
0
                        codes += ",";
823
0
                    }
824
0
                    if (pajazzo == 1)
825
0
                    {
826
0
                        codes += CPLString().Printf("%i", a);
827
0
                    }
828
0
                    else if (pajazzo == 2)
829
0
                    {
830
0
                        codes += CPLString().Printf("%i:%i", a, b);
831
0
                    }
832
0
                    break;
833
0
                }
834
0
                ++i;
835
0
            }
836
0
            if (words != "")
837
0
            {
838
0
                words += ",";
839
0
            }
840
0
            words += "EPSG:" + codes;
841
0
        }
842
0
    }
843
0
    return words;
844
0
}
845
846
std::string ParseCRS(CPLXMLNode *node)
847
0
{
848
    // test for attrs crs (OWS) and srsName (GML), and text contents of subnode
849
    // (GridBaseCRS)
850
0
    std::string crs = CPLGetXMLValue(node, "crs", "");
851
0
    if (crs == "")
852
0
    {
853
0
        crs = CPLGetXMLValue(node, "srsName", "");
854
0
        if (crs == "")
855
0
        {
856
0
            crs = CPLGetXMLValue(node, "GridBaseCRS", "");
857
0
        }
858
0
    }
859
0
    if (crs == "")
860
0
    {
861
0
        return crs;
862
0
    }
863
    // split compound names
864
    // see for example
865
    // http://www.eurogeographics.org/sites/default/files/2016-01-18_INSPIRE-KEN-CovFaq.pdf
866
0
    size_t pos = crs.find("?");
867
0
    if (pos != std::string::npos)
868
0
    {
869
0
        if (crs.find("crs-compound?") != std::string::npos)
870
0
        {  // 1=uri&2=uri...
871
            // assuming the first is for X,Y
872
0
            crs = crs.substr(pos + 1);
873
0
            pos = crs.find("&");
874
0
            if (pos != std::string::npos)
875
0
            {
876
0
                pos = pos - 2;
877
0
            }
878
0
            crs = crs.substr(2, pos);
879
0
        }
880
0
    }
881
0
    return crs;
882
0
}
883
884
// if appropriate, try to create WKT description from CRS name
885
// return false if failure
886
// appropriate means, that the name is a real CRS
887
bool CRS2Projection(const std::string &crs, OGRSpatialReference *sr,
888
                    char **projection)
889
0
{
890
0
    if (*projection != nullptr)
891
0
    {
892
0
        CPLFree(*projection);
893
0
    }
894
0
    *projection = nullptr;
895
0
    if (crs.empty())
896
0
    {
897
0
        return true;
898
0
    }
899
0
    if (crs.find(":imageCRS") != std::string::npos ||
900
0
        crs.find("/Index1D") != std::string::npos ||
901
0
        crs.find("/Index2D") != std::string::npos ||
902
0
        crs.find("/Index3D") != std::string::npos ||
903
0
        crs.find("/AnsiDate") != std::string::npos)
904
0
    {
905
        // not a map projection
906
0
        return true;
907
0
    }
908
0
    std::string crs2 = crs;
909
    // rasdaman uses urls, which return gml:ProjectedCRS XML, which is not
910
    // recognized by GDAL currently
911
0
    if (crs2.find("EPSG") != std::string::npos)
912
0
    {  // ...EPSG...(\d+)
913
0
        size_t pos1 = crs2.find_last_of(DIGITS);
914
0
        if (pos1 != std::string::npos)
915
0
        {
916
0
            size_t pos2 = pos1 - 1;
917
0
            char c = crs2.at(pos2);
918
0
            while (strchr(DIGITS, c))
919
0
            {
920
0
                pos2 = pos2 - 1;
921
0
                c = crs2.at(pos2);
922
0
            }
923
0
            crs2 = "EPSGA:" + crs2.substr(pos2 + 1, pos1 - pos2);
924
0
        }
925
0
    }
926
0
    OGRSpatialReference local_sr;
927
0
    OGRSpatialReference *sr_pointer = sr != nullptr ? sr : &local_sr;
928
0
    if (sr_pointer->SetFromUserInput(
929
0
            crs2.c_str(),
930
0
            OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
931
0
        OGRERR_NONE)
932
0
    {
933
0
        sr_pointer->exportToWkt(projection);
934
0
        return true;
935
0
    }
936
0
    return false;
937
0
}
938
939
bool CRSImpliesAxisOrderSwap(const std::string &crs, bool &swap,
940
                             char **projection)
941
0
{
942
0
    OGRSpatialReference oSRS;
943
0
    char *tmp = nullptr;
944
0
    swap = false;
945
0
    if (!CRS2Projection(crs, &oSRS, &tmp))
946
0
    {
947
0
        CPLError(CE_Failure, CPLE_AppDefined,
948
0
                 "Unable to interpret coverage CRS '%s'.", crs.c_str());
949
0
        CPLFree(tmp);
950
0
        return false;
951
0
    }
952
0
    if (tmp)
953
0
    {
954
0
        if (projection != nullptr)
955
0
        {
956
0
            *projection = tmp;
957
0
        }
958
0
        else
959
0
        {
960
0
            CPLFree(tmp);
961
0
        }
962
0
        swap = oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting();
963
0
    }
964
0
    return true;
965
0
}
966
967
std::vector<std::vector<int>> ParseGridEnvelope(CPLXMLNode *node,
968
                                                bool swap_the_first_two)
969
0
{
970
0
    std::vector<std::vector<int>> envelope;
971
0
    std::vector<std::string> array =
972
0
        Split(CPLGetXMLValue(node, "low", ""), " ", swap_the_first_two);
973
0
    std::vector<int> lows;
974
0
    for (unsigned int i = 0; i < array.size(); ++i)
975
0
    {
976
0
        lows.push_back(atoi(array[i].c_str()));
977
0
    }
978
0
    envelope.push_back(std::move(lows));
979
0
    array = Split(CPLGetXMLValue(node, "high", ""), " ", swap_the_first_two);
980
0
    std::vector<int> highs;
981
0
    for (unsigned int i = 0; i < array.size(); ++i)
982
0
    {
983
0
        highs.push_back(atoi(array[i].c_str()));
984
0
    }
985
0
    envelope.push_back(std::move(highs));
986
0
    return envelope;
987
0
}
988
989
std::vector<std::string> ParseBoundingBox(CPLXMLNode *node)
990
0
{
991
0
    std::vector<std::string> bbox;
992
0
    std::string lc = CPLGetXMLValue(node, "lowerCorner", ""), uc;
993
0
    if (lc == "")
994
0
    {
995
0
        lc = CPLGetXMLValue(node, "LowerCorner", "");
996
0
    }
997
0
    if (lc == "")
998
0
    {
999
0
        for (CPLXMLNode *n = node->psChild; n != nullptr; n = n->psNext)
1000
0
        {
1001
0
            if (n->eType != CXT_Element || !EQUAL(n->pszValue, "pos"))
1002
0
            {
1003
0
                continue;
1004
0
            }
1005
0
            if (lc == "")
1006
0
            {
1007
0
                lc = CPLGetXMLValue(node, nullptr, "");
1008
0
            }
1009
0
            else
1010
0
            {
1011
0
                uc = CPLGetXMLValue(node, nullptr, "");
1012
0
            }
1013
0
        }
1014
0
    }
1015
0
    else
1016
0
    {
1017
0
        uc = CPLGetXMLValue(node, "upperCorner", "");
1018
0
        if (uc == "")
1019
0
        {
1020
0
            uc = CPLGetXMLValue(node, "UpperCorner", "");
1021
0
        }
1022
0
    }
1023
0
    if (lc != "" && uc != "")
1024
0
    {
1025
0
        bbox.push_back(lc);
1026
0
        bbox.push_back(uc);
1027
0
    }
1028
    // time extent if node is an EnvelopeWithTimePeriod
1029
0
    lc = CPLGetXMLValue(node, "beginPosition", "");
1030
0
    if (lc != "")
1031
0
    {
1032
0
        uc = CPLGetXMLValue(node, "endPosition", "");
1033
0
        bbox.push_back(lc + "," + uc);
1034
0
    }
1035
0
    return bbox;
1036
0
}
1037
1038
}  // namespace WCSUtils