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 0 : static bool CompareStrings(const std::string &a, const std::string &b)
461 : {
462 0 : return a.compare(b) < 0;
463 : }
464 :
465 0 : std::vector<std::string> ReadCache(const std::string &cache)
466 : {
467 0 : std::vector<std::string> contents;
468 0 : const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
469 0 : char **data = CSLLoad(db.c_str());
470 0 : if (data)
471 : {
472 0 : for (int i = 0; data[i]; ++i)
473 : {
474 0 : char *val = strchr(data[i], '=');
475 0 : if (val != nullptr && *val == '=')
476 : {
477 0 : val += 1;
478 0 : if (strcmp(val, "bar") != 0)
479 : {
480 0 : contents.push_back(val);
481 : }
482 : }
483 : }
484 0 : CSLDestroy(data);
485 : }
486 0 : std::sort(contents.begin(), contents.end(), CompareStrings);
487 0 : return contents;
488 : }
489 :
490 : /* -------------------------------------------------------------------- */
491 : /* DeleteEntryFromCache */
492 : /* Examines the 'db' file in the cache, which contains */
493 : /* unique key=value pairs, one per line. This function */
494 : /* deletes pairs based on the given key and/or value. */
495 : /* If key or value is empty it is not considered. */
496 : /* The key is taken as a basename of a file in the cache */
497 : /* and all files with the basename is deleted. */
498 : /* -------------------------------------------------------------------- */
499 :
500 0 : bool DeleteEntryFromCache(const std::string &cache, const std::string &key,
501 : const std::string &value)
502 : {
503 : // Depending on which one of key and value is not "" delete the relevant
504 : // entry.
505 0 : const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
506 : char **data =
507 0 : CSLLoad(db.c_str()); // returns NULL in error and for empty files
508 0 : char **data2 = CSLAddNameValue(nullptr, "foo", "bar");
509 0 : std::string filename = "";
510 0 : if (data)
511 : {
512 0 : for (int i = 0; data[i]; ++i)
513 : {
514 0 : char *val = strchr(data[i], '=');
515 0 : if (val != nullptr && *val == '=')
516 : {
517 0 : *val = '\0';
518 0 : val = val + 1;
519 0 : if ((key != "" && key == data[i]) ||
520 0 : (value != "" && value == val) ||
521 0 : (strcmp(data[i], "foo") == 0))
522 : {
523 0 : if (key != "")
524 : {
525 0 : filename = data[i];
526 : }
527 0 : else if (value != "")
528 : {
529 0 : filename = data[i];
530 : }
531 0 : continue;
532 : }
533 0 : data2 = CSLAddNameValue(data2, data[i], val);
534 : }
535 : }
536 0 : CSLDestroy(data);
537 : }
538 0 : CSLSave(data2, db.c_str()); // returns 0 in error and for empty arrays
539 0 : CSLDestroy(data2);
540 0 : if (filename != "")
541 : {
542 0 : char **folder = VSIReadDir(cache.c_str());
543 0 : int size = folder ? CSLCount(folder) : 0;
544 0 : for (int i = 0; i < size; i++)
545 : {
546 0 : if (folder[i][0] == '.')
547 : {
548 0 : continue;
549 : }
550 0 : std::string name = folder[i];
551 0 : if (name.find(filename) != std::string::npos)
552 : {
553 : const std::string filepath =
554 0 : CPLFormFilenameSafe(cache.c_str(), name.c_str(), nullptr);
555 0 : if (VSIUnlink(filepath.c_str()) == -1)
556 : {
557 : // error but can't do much, raise a warning?
558 : }
559 : }
560 : }
561 0 : CSLDestroy(folder);
562 : }
563 0 : return true;
564 : }
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* SearchCache */
568 : /* The key,value pairs in the cache index file 'db' is searched */
569 : /* for the first pair where the value is the given url. If one */
570 : /* is found, the filename is formed from the cache directory name, */
571 : /* the key, and the ext. */
572 : /* -------------------------------------------------------------------- */
573 :
574 144 : CPLErr SearchCache(const std::string &cache, const std::string &url,
575 : std::string &filename, const std::string &ext, bool &found)
576 : {
577 144 : found = false;
578 288 : const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
579 144 : VSILFILE *f = VSIFOpenL(db.c_str(), "r");
580 144 : if (!f)
581 : {
582 0 : CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
583 0 : db.c_str(), errno);
584 0 : return CE_Failure;
585 : }
586 216 : while (const char *line = CPLReadLineL(f))
587 : {
588 144 : char *value = strchr((char *)line, '=');
589 144 : if (value == nullptr || *value != '=')
590 : {
591 0 : continue;
592 : }
593 144 : *value = '\0';
594 144 : if (url == (value + 1))
595 : {
596 72 : filename = line;
597 72 : found = true;
598 72 : break;
599 : }
600 72 : }
601 144 : VSIFCloseL(f);
602 144 : if (found)
603 : {
604 144 : filename = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
605 72 : nullptr);
606 72 : found = FileIsReadable(filename);
607 : // if not readable, we should delete the entry
608 : }
609 144 : return CE_None;
610 : }
611 :
612 : /* -------------------------------------------------------------------- */
613 : /* AddEntryToCache */
614 : /* A new unique key is created into the database based on the */
615 : /* filename replacing X with a random ascii character. */
616 : /* The returned filename is a path formed from the cache directory */
617 : /* name, the filename, and the ext. */
618 : /* -------------------------------------------------------------------- */
619 :
620 48 : CPLErr AddEntryToCache(const std::string &cache, const std::string &url,
621 : std::string &filename, const std::string &ext)
622 : {
623 : // todo: check for lock and do something if locked(?)
624 : // todo: lock the cache
625 : // assuming the url is not in the cache
626 96 : const std::string store = filename;
627 96 : const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
628 48 : VSILFILE *f = VSIFOpenL(db.c_str(), "a");
629 48 : if (!f)
630 : {
631 0 : CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
632 0 : db.c_str(), errno);
633 0 : return CE_Failure;
634 : }
635 :
636 : // create a new file into the cache using filename as template
637 144 : std::string path = "";
638 : VSIStatBufL stat;
639 23 : do
640 : {
641 71 : filename = store;
642 : static const char chars[] =
643 : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
644 426 : for (size_t i = 0; i < filename.length(); ++i)
645 : {
646 355 : if (filename.at(i) == 'X')
647 : {
648 : #ifndef __COVERITY__
649 355 : filename.replace(i, 1, 1, chars[rand() % (sizeof(chars) - 1)]);
650 : #else
651 : filename.replace(i, 1, 1, chars[i % (sizeof(chars) - 1)]);
652 : #endif
653 : }
654 : }
655 : // replace X with random character from a-zA-Z
656 142 : path = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
657 71 : nullptr);
658 71 : } while (VSIStatExL(path.c_str(), &stat, VSI_STAT_EXISTS_FLAG) == 0);
659 48 : VSILFILE *f2 = VSIFOpenL(path.c_str(), "w");
660 48 : if (f2)
661 : {
662 48 : VSIFCloseL(f2);
663 : }
664 :
665 : std::string entry =
666 96 : filename + "=" + url + "\n"; // '=' for compatibility with CSL
667 48 : VSIFWriteL(entry.c_str(), sizeof(char), entry.size(), f);
668 48 : VSIFCloseL(f);
669 :
670 48 : filename = std::move(path);
671 48 : return CE_None;
672 : }
673 :
674 : // steps into element 'from' and adds values of elements 'keys' into the
675 : // metadata 'path' is the key that is used for metadata and it is appended with
676 : // 'from' path may be later used to get metadata from elements below 'from' so
677 : // it is returned in the appended form
678 134 : CPLXMLNode *AddSimpleMetaData(char ***metadata, CPLXMLNode *node,
679 : std::string &path, const std::string &from,
680 : const std::vector<std::string> &keys)
681 : {
682 134 : CPLXMLNode *node2 = CPLGetXMLNode(node, from.c_str());
683 134 : if (node2)
684 : {
685 134 : path = path + from + ".";
686 556 : for (unsigned int i = 0; i < keys.size(); i++)
687 : {
688 422 : CPLXMLNode *node3 = CPLGetXMLNode(node2, keys[i].c_str());
689 422 : if (node3)
690 : {
691 512 : const std::string name = path + keys[i];
692 256 : CPLString value = CPLGetXMLValue(node3, nullptr, "");
693 256 : value.Trim();
694 256 : *metadata =
695 256 : CSLSetNameValue(*metadata, name.c_str(), value.c_str());
696 : }
697 : }
698 : }
699 134 : return node2;
700 : }
701 :
702 211 : std::string GetKeywords(CPLXMLNode *root, const std::string &path,
703 : const std::string &kw)
704 : {
705 211 : std::string words = "";
706 : CPLXMLNode *keywords =
707 211 : (path != "") ? CPLGetXMLNode(root, path.c_str()) : root;
708 211 : if (keywords)
709 : {
710 262 : std::vector<unsigned int> epsg_codes;
711 36077 : for (CPLXMLNode *node = keywords->psChild; node != nullptr;
712 35946 : node = node->psNext)
713 : {
714 35946 : if (node->eType != CXT_Element)
715 : {
716 6 : continue;
717 : }
718 35940 : if (kw == node->pszValue)
719 : {
720 35886 : CPLString word = CPLGetXMLValue(node, nullptr, "");
721 17943 : word.Trim();
722 :
723 : // crs, replace "http://www.opengis.net/def/crs/EPSG/0/"
724 : // or "urn:ogc:def:crs:EPSG::" with EPSG:
725 17943 : const char *const epsg[] = {
726 : "http://www.opengis.net/def/crs/EPSG/0/",
727 : "urn:ogc:def:crs:EPSG::"};
728 53829 : for (unsigned int i = 0; i < CPL_ARRAYSIZE(epsg); i++)
729 : {
730 35886 : size_t pos = word.find(epsg[i]);
731 35886 : if (pos == 0)
732 : {
733 : std::string code =
734 17564 : word.substr(strlen(epsg[i]), std::string::npos);
735 17564 : if (code.find_first_not_of(DIGITS) == std::string::npos)
736 : {
737 17564 : epsg_codes.push_back(atoi(code.c_str()));
738 17564 : continue;
739 : }
740 : }
741 : }
742 :
743 : // profiles, remove http://www.opengis.net/spec/
744 : // interpolation, remove
745 : // http://www.opengis.net/def/interpolation/OGC/1/
746 :
747 17943 : const char *const spec[] = {
748 : "http://www.opengis.net/spec/",
749 : "http://www.opengis.net/def/interpolation/OGC/1/"};
750 53829 : for (unsigned int i = 0; i < CPL_ARRAYSIZE(spec); i++)
751 : {
752 35886 : size_t pos = word.find(spec[i]);
753 35886 : if (pos != std::string::npos)
754 : {
755 118 : word.erase(pos, strlen(spec[i]));
756 : }
757 : }
758 :
759 17943 : if (words != "")
760 : {
761 17881 : words += ",";
762 : }
763 17943 : words += word;
764 : }
765 : }
766 131 : if (epsg_codes.size() > 0)
767 : {
768 8 : std::string codes;
769 8 : std::sort(epsg_codes.begin(), epsg_codes.end());
770 8 : unsigned int pajazzo = 0, i = 0, a = 0, b = 0;
771 : while (1)
772 : {
773 : // cppcheck-suppress containerOutOfBounds
774 17572 : unsigned int c = i < epsg_codes.size() ? epsg_codes[i] : 0;
775 17572 : if (pajazzo == 1)
776 : {
777 1967 : if (c > a + 1)
778 : {
779 1194 : if (codes != "")
780 : {
781 1189 : codes += ",";
782 : }
783 1194 : codes += CPLString().Printf("%i", a);
784 1194 : a = c;
785 : }
786 773 : else if (c >= a)
787 : {
788 765 : b = c;
789 765 : pajazzo = 2;
790 : }
791 : }
792 15605 : else if (pajazzo == 2)
793 : {
794 15597 : if (c > b + 1)
795 : {
796 765 : if (codes != "")
797 : {
798 762 : codes += ",";
799 : }
800 765 : codes += CPLString().Printf("%i:%i", a, b);
801 765 : a = c;
802 765 : pajazzo = 1;
803 : }
804 14832 : else if (c >= b)
805 : {
806 14832 : b = c;
807 : }
808 : }
809 : else
810 : { // pajazzo == 0
811 8 : a = c;
812 8 : pajazzo = 1;
813 : }
814 17572 : if (i == epsg_codes.size())
815 : {
816 : // must empty the pajazzo before leaving
817 8 : if (codes != "")
818 : {
819 8 : codes += ",";
820 : }
821 8 : if (pajazzo == 1)
822 : {
823 8 : codes += CPLString().Printf("%i", a);
824 : }
825 0 : else if (pajazzo == 2)
826 : {
827 0 : codes += CPLString().Printf("%i:%i", a, b);
828 : }
829 8 : break;
830 : }
831 17564 : ++i;
832 17564 : }
833 8 : if (words != "")
834 : {
835 8 : words += ",";
836 : }
837 8 : words += "EPSG:" + codes;
838 : }
839 : }
840 211 : return words;
841 : }
842 :
843 147 : std::string ParseCRS(CPLXMLNode *node)
844 : {
845 : // test for attrs crs (OWS) and srsName (GML), and text contents of subnode
846 : // (GridBaseCRS)
847 147 : std::string crs = CPLGetXMLValue(node, "crs", "");
848 147 : if (crs == "")
849 : {
850 57 : crs = CPLGetXMLValue(node, "srsName", "");
851 57 : if (crs == "")
852 : {
853 36 : crs = CPLGetXMLValue(node, "GridBaseCRS", "");
854 : }
855 : }
856 147 : if (crs == "")
857 : {
858 0 : return crs;
859 : }
860 : // split compound names
861 : // see for example
862 : // http://www.eurogeographics.org/sites/default/files/2016-01-18_INSPIRE-KEN-CovFaq.pdf
863 147 : size_t pos = crs.find("?");
864 147 : if (pos != std::string::npos)
865 : {
866 3 : if (crs.find("crs-compound?") != std::string::npos)
867 : { // 1=uri&2=uri...
868 : // assuming the first is for X,Y
869 3 : crs = crs.substr(pos + 1);
870 3 : pos = crs.find("&");
871 3 : if (pos != std::string::npos)
872 : {
873 3 : pos = pos - 2;
874 : }
875 3 : crs = crs.substr(2, pos);
876 : }
877 : }
878 147 : return crs;
879 : }
880 :
881 : // if appropriate, try to create WKT description from CRS name
882 : // return false if failure
883 : // appropriate means, that the name is a real CRS
884 57 : bool CRS2Projection(const std::string &crs, OGRSpatialReference *sr,
885 : char **projection)
886 : {
887 57 : if (*projection != nullptr)
888 : {
889 0 : CPLFree(*projection);
890 : }
891 57 : *projection = nullptr;
892 57 : if (crs.empty())
893 : {
894 0 : return true;
895 : }
896 114 : if (crs.find(":imageCRS") != std::string::npos ||
897 114 : crs.find("/Index1D") != std::string::npos ||
898 114 : crs.find("/Index2D") != std::string::npos ||
899 171 : crs.find("/Index3D") != std::string::npos ||
900 57 : crs.find("/AnsiDate") != std::string::npos)
901 : {
902 : // not a map projection
903 0 : return true;
904 : }
905 114 : std::string crs2 = crs;
906 : // rasdaman uses urls, which return gml:ProjectedCRS XML, which is not
907 : // recognized by GDAL currently
908 57 : if (crs2.find("EPSG") != std::string::npos)
909 : { // ...EPSG...(\d+)
910 57 : size_t pos1 = crs2.find_last_of(DIGITS);
911 57 : if (pos1 != std::string::npos)
912 : {
913 57 : size_t pos2 = pos1 - 1;
914 57 : char c = crs2.at(pos2);
915 231 : while (strchr(DIGITS, c))
916 : {
917 174 : pos2 = pos2 - 1;
918 174 : c = crs2.at(pos2);
919 : }
920 57 : crs2 = "EPSGA:" + crs2.substr(pos2 + 1, pos1 - pos2);
921 : }
922 : }
923 114 : OGRSpatialReference local_sr;
924 57 : OGRSpatialReference *sr_pointer = sr != nullptr ? sr : &local_sr;
925 57 : if (sr_pointer->SetFromUserInput(
926 : crs2.c_str(),
927 57 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
928 : OGRERR_NONE)
929 : {
930 57 : sr_pointer->exportToWkt(projection);
931 57 : return true;
932 : }
933 0 : return false;
934 : }
935 :
936 57 : bool CRSImpliesAxisOrderSwap(const std::string &crs, bool &swap,
937 : char **projection)
938 : {
939 114 : OGRSpatialReference oSRS;
940 57 : char *tmp = nullptr;
941 57 : swap = false;
942 57 : if (!CRS2Projection(crs, &oSRS, &tmp))
943 : {
944 0 : CPLError(CE_Failure, CPLE_AppDefined,
945 : "Unable to interpret coverage CRS '%s'.", crs.c_str());
946 0 : CPLFree(tmp);
947 0 : return false;
948 : }
949 57 : if (tmp)
950 : {
951 57 : if (projection != nullptr)
952 : {
953 57 : *projection = tmp;
954 : }
955 : else
956 : {
957 0 : CPLFree(tmp);
958 : }
959 57 : swap = oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting();
960 : }
961 57 : return true;
962 : }
963 :
964 21 : std::vector<std::vector<int>> ParseGridEnvelope(CPLXMLNode *node,
965 : bool swap_the_first_two)
966 : {
967 21 : std::vector<std::vector<int>> envelope;
968 : std::vector<std::string> array =
969 42 : Split(CPLGetXMLValue(node, "low", ""), " ", swap_the_first_two);
970 42 : std::vector<int> lows;
971 66 : for (unsigned int i = 0; i < array.size(); ++i)
972 : {
973 45 : lows.push_back(atoi(array[i].c_str()));
974 : }
975 21 : envelope.push_back(std::move(lows));
976 21 : array = Split(CPLGetXMLValue(node, "high", ""), " ", swap_the_first_two);
977 42 : std::vector<int> highs;
978 66 : for (unsigned int i = 0; i < array.size(); ++i)
979 : {
980 45 : highs.push_back(atoi(array[i].c_str()));
981 : }
982 21 : envelope.push_back(std::move(highs));
983 42 : return envelope;
984 : }
985 :
986 57 : std::vector<std::string> ParseBoundingBox(CPLXMLNode *node)
987 : {
988 57 : std::vector<std::string> bbox;
989 171 : std::string lc = CPLGetXMLValue(node, "lowerCorner", ""), uc;
990 57 : if (lc == "")
991 : {
992 0 : lc = CPLGetXMLValue(node, "LowerCorner", "");
993 : }
994 57 : if (lc == "")
995 : {
996 0 : for (CPLXMLNode *n = node->psChild; n != nullptr; n = n->psNext)
997 : {
998 0 : if (n->eType != CXT_Element || !EQUAL(n->pszValue, "pos"))
999 : {
1000 0 : continue;
1001 : }
1002 0 : if (lc == "")
1003 : {
1004 0 : lc = CPLGetXMLValue(node, nullptr, "");
1005 : }
1006 : else
1007 : {
1008 0 : uc = CPLGetXMLValue(node, nullptr, "");
1009 : }
1010 : }
1011 : }
1012 : else
1013 : {
1014 57 : uc = CPLGetXMLValue(node, "upperCorner", "");
1015 57 : if (uc == "")
1016 : {
1017 0 : uc = CPLGetXMLValue(node, "UpperCorner", "");
1018 : }
1019 : }
1020 57 : if (lc != "" && uc != "")
1021 : {
1022 57 : bbox.push_back(lc);
1023 57 : bbox.push_back(uc);
1024 : }
1025 : // time extent if node is an EnvelopeWithTimePeriod
1026 57 : lc = CPLGetXMLValue(node, "beginPosition", "");
1027 57 : if (lc != "")
1028 : {
1029 0 : uc = CPLGetXMLValue(node, "endPosition", "");
1030 0 : bbox.push_back(lc + "," + uc);
1031 : }
1032 114 : return bbox;
1033 : }
1034 :
1035 : } // namespace WCSUtils
|