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