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