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