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.back() == '&')
58 : {
59 96 : retval.erase(retval.size() - 1);
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 = CPLGetDirname(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 = CPLFormFilename(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 = CPLFormFilename(dir, subdir.c_str(), nullptr);
410 : }
411 : }
412 1 : cache = CPLFormFilename(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 : std::string filepath =
429 0 : CPLFormFilename(cache.c_str(), folder[i], nullptr);
430 0 : remove(filepath.c_str());
431 : }
432 0 : CSLDestroy(folder);
433 : }
434 : // make sure the index exists and is writable
435 194 : std::string db = CPLFormFilename(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 : std::string db = CPLFormFilename(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 : std::string db = CPLFormFilename(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 : std::string filepath =
554 0 : CPLFormFilename(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 : std::string db = CPLFormFilename(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 : filename =
605 72 : CPLFormFilename(cache.c_str(), (filename + ext).c_str(), 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 = CPLFormFilename(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 24 : do
640 : {
641 72 : filename = store;
642 : static const char chars[] =
643 : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
644 432 : for (size_t i = 0; i < filename.length(); ++i)
645 : {
646 360 : if (filename.at(i) == 'X')
647 : {
648 : // coverity[dont_call]
649 360 : filename.replace(i, 1, 1, chars[rand() % (sizeof(chars) - 1)]);
650 : }
651 : }
652 : // replace X with random character from a-zA-Z
653 : path =
654 72 : CPLFormFilename(cache.c_str(), (filename + ext).c_str(), nullptr);
655 72 : } while (VSIStatExL(path.c_str(), &stat, VSI_STAT_EXISTS_FLAG) == 0);
656 48 : VSILFILE *f2 = VSIFOpenL(path.c_str(), "w");
657 48 : if (f2)
658 : {
659 48 : VSIFCloseL(f2);
660 : }
661 :
662 : std::string entry =
663 96 : filename + "=" + url + "\n"; // '=' for compatibility with CSL
664 48 : VSIFWriteL(entry.c_str(), sizeof(char), entry.size(), f);
665 48 : VSIFCloseL(f);
666 :
667 48 : filename = std::move(path);
668 48 : return CE_None;
669 : }
670 :
671 : // steps into element 'from' and adds values of elements 'keys' into the
672 : // metadata 'path' is the key that is used for metadata and it is appended with
673 : // 'from' path may be later used to get metadata from elements below 'from' so
674 : // it is returned in the appended form
675 134 : CPLXMLNode *AddSimpleMetaData(char ***metadata, CPLXMLNode *node,
676 : std::string &path, const std::string &from,
677 : const std::vector<std::string> &keys)
678 : {
679 134 : CPLXMLNode *node2 = CPLGetXMLNode(node, from.c_str());
680 134 : if (node2)
681 : {
682 134 : path = path + from + ".";
683 556 : for (unsigned int i = 0; i < keys.size(); i++)
684 : {
685 422 : CPLXMLNode *node3 = CPLGetXMLNode(node2, keys[i].c_str());
686 422 : if (node3)
687 : {
688 512 : const std::string name = path + keys[i];
689 256 : CPLString value = CPLGetXMLValue(node3, nullptr, "");
690 256 : value.Trim();
691 256 : *metadata =
692 256 : CSLSetNameValue(*metadata, name.c_str(), value.c_str());
693 : }
694 : }
695 : }
696 134 : return node2;
697 : }
698 :
699 211 : std::string GetKeywords(CPLXMLNode *root, const std::string &path,
700 : const std::string &kw)
701 : {
702 211 : std::string words = "";
703 : CPLXMLNode *keywords =
704 211 : (path != "") ? CPLGetXMLNode(root, path.c_str()) : root;
705 211 : if (keywords)
706 : {
707 262 : std::vector<unsigned int> epsg_codes;
708 36077 : for (CPLXMLNode *node = keywords->psChild; node != nullptr;
709 35946 : node = node->psNext)
710 : {
711 35946 : if (node->eType != CXT_Element)
712 : {
713 6 : continue;
714 : }
715 35940 : if (kw == node->pszValue)
716 : {
717 35886 : CPLString word = CPLGetXMLValue(node, nullptr, "");
718 17943 : word.Trim();
719 :
720 : // crs, replace "http://www.opengis.net/def/crs/EPSG/0/"
721 : // or "urn:ogc:def:crs:EPSG::" with EPSG:
722 17943 : const char *const epsg[] = {
723 : "http://www.opengis.net/def/crs/EPSG/0/",
724 : "urn:ogc:def:crs:EPSG::"};
725 53829 : for (unsigned int i = 0; i < CPL_ARRAYSIZE(epsg); i++)
726 : {
727 35886 : size_t pos = word.find(epsg[i]);
728 35886 : if (pos == 0)
729 : {
730 : std::string code =
731 17564 : word.substr(strlen(epsg[i]), std::string::npos);
732 17564 : if (code.find_first_not_of(DIGITS) == std::string::npos)
733 : {
734 17564 : epsg_codes.push_back(atoi(code.c_str()));
735 17564 : continue;
736 : }
737 : }
738 : }
739 :
740 : // profiles, remove http://www.opengis.net/spec/
741 : // interpolation, remove
742 : // http://www.opengis.net/def/interpolation/OGC/1/
743 :
744 17943 : const char *const spec[] = {
745 : "http://www.opengis.net/spec/",
746 : "http://www.opengis.net/def/interpolation/OGC/1/"};
747 53829 : for (unsigned int i = 0; i < CPL_ARRAYSIZE(spec); i++)
748 : {
749 35886 : size_t pos = word.find(spec[i]);
750 35886 : if (pos != std::string::npos)
751 : {
752 118 : word.erase(pos, strlen(spec[i]));
753 : }
754 : }
755 :
756 17943 : if (words != "")
757 : {
758 17881 : words += ",";
759 : }
760 17943 : words += word;
761 : }
762 : }
763 131 : if (epsg_codes.size() > 0)
764 : {
765 8 : std::string codes;
766 8 : std::sort(epsg_codes.begin(), epsg_codes.end());
767 8 : unsigned int pajazzo = 0, i = 0, a = 0, b = 0;
768 : while (1)
769 : {
770 : // cppcheck-suppress containerOutOfBounds
771 17572 : unsigned int c = i < epsg_codes.size() ? epsg_codes[i] : 0;
772 17572 : if (pajazzo == 1)
773 : {
774 1967 : if (c > a + 1)
775 : {
776 1194 : if (codes != "")
777 : {
778 1189 : codes += ",";
779 : }
780 1194 : codes += CPLString().Printf("%i", a);
781 1194 : a = c;
782 : }
783 773 : else if (c >= a)
784 : {
785 765 : b = c;
786 765 : pajazzo = 2;
787 : }
788 : }
789 15605 : else if (pajazzo == 2)
790 : {
791 15597 : if (c > b + 1)
792 : {
793 765 : if (codes != "")
794 : {
795 762 : codes += ",";
796 : }
797 765 : codes += CPLString().Printf("%i:%i", a, b);
798 765 : a = c;
799 765 : pajazzo = 1;
800 : }
801 14832 : else if (c >= b)
802 : {
803 14832 : b = c;
804 : }
805 : }
806 : else
807 : { // pajazzo == 0
808 8 : a = c;
809 8 : pajazzo = 1;
810 : }
811 17572 : if (i == epsg_codes.size())
812 : {
813 : // must empty the pajazzo before leaving
814 8 : if (codes != "")
815 : {
816 8 : codes += ",";
817 : }
818 8 : if (pajazzo == 1)
819 : {
820 8 : codes += CPLString().Printf("%i", a);
821 : }
822 0 : else if (pajazzo == 2)
823 : {
824 0 : codes += CPLString().Printf("%i:%i", a, b);
825 : }
826 8 : break;
827 : }
828 17564 : ++i;
829 17564 : }
830 8 : if (words != "")
831 : {
832 8 : words += ",";
833 : }
834 8 : words += "EPSG:" + codes;
835 : }
836 : }
837 211 : return words;
838 : }
839 :
840 147 : std::string ParseCRS(CPLXMLNode *node)
841 : {
842 : // test for attrs crs (OWS) and srsName (GML), and text contents of subnode
843 : // (GridBaseCRS)
844 147 : std::string crs = CPLGetXMLValue(node, "crs", "");
845 147 : if (crs == "")
846 : {
847 57 : crs = CPLGetXMLValue(node, "srsName", "");
848 57 : if (crs == "")
849 : {
850 36 : crs = CPLGetXMLValue(node, "GridBaseCRS", "");
851 : }
852 : }
853 147 : if (crs == "")
854 : {
855 0 : return crs;
856 : }
857 : // split compound names
858 : // see for example
859 : // http://www.eurogeographics.org/sites/default/files/2016-01-18_INSPIRE-KEN-CovFaq.pdf
860 147 : size_t pos = crs.find("?");
861 147 : if (pos != std::string::npos)
862 : {
863 3 : if (crs.find("crs-compound?") != std::string::npos)
864 : { // 1=uri&2=uri...
865 : // assuming the first is for X,Y
866 3 : crs = crs.substr(pos + 1);
867 3 : pos = crs.find("&");
868 3 : if (pos != std::string::npos)
869 : {
870 3 : pos = pos - 2;
871 : }
872 3 : crs = crs.substr(2, pos);
873 : }
874 : }
875 147 : return crs;
876 : }
877 :
878 : // if appropriate, try to create WKT description from CRS name
879 : // return false if failure
880 : // appropriate means, that the name is a real CRS
881 57 : bool CRS2Projection(const std::string &crs, OGRSpatialReference *sr,
882 : char **projection)
883 : {
884 57 : if (*projection != nullptr)
885 : {
886 0 : CPLFree(*projection);
887 : }
888 57 : *projection = nullptr;
889 57 : if (crs.empty())
890 : {
891 0 : return true;
892 : }
893 114 : if (crs.find(":imageCRS") != std::string::npos ||
894 114 : crs.find("/Index1D") != std::string::npos ||
895 114 : crs.find("/Index2D") != std::string::npos ||
896 171 : crs.find("/Index3D") != std::string::npos ||
897 57 : crs.find("/AnsiDate") != std::string::npos)
898 : {
899 : // not a map projection
900 0 : return true;
901 : }
902 114 : std::string crs2 = crs;
903 : // rasdaman uses urls, which return gml:ProjectedCRS XML, which is not
904 : // recognized by GDAL currently
905 57 : if (crs2.find("EPSG") != std::string::npos)
906 : { // ...EPSG...(\d+)
907 57 : size_t pos1 = crs2.find_last_of(DIGITS);
908 57 : if (pos1 != std::string::npos)
909 : {
910 57 : size_t pos2 = pos1 - 1;
911 57 : char c = crs2.at(pos2);
912 231 : while (strchr(DIGITS, c))
913 : {
914 174 : pos2 = pos2 - 1;
915 174 : c = crs2.at(pos2);
916 : }
917 57 : crs2 = "EPSGA:" + crs2.substr(pos2 + 1, pos1 - pos2);
918 : }
919 : }
920 114 : OGRSpatialReference local_sr;
921 57 : OGRSpatialReference *sr_pointer = sr != nullptr ? sr : &local_sr;
922 57 : if (sr_pointer->SetFromUserInput(
923 : crs2.c_str(),
924 57 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
925 : OGRERR_NONE)
926 : {
927 57 : sr_pointer->exportToWkt(projection);
928 57 : return true;
929 : }
930 0 : return false;
931 : }
932 :
933 57 : bool CRSImpliesAxisOrderSwap(const std::string &crs, bool &swap,
934 : char **projection)
935 : {
936 114 : OGRSpatialReference oSRS;
937 57 : char *tmp = nullptr;
938 57 : swap = false;
939 57 : if (!CRS2Projection(crs, &oSRS, &tmp))
940 : {
941 0 : CPLError(CE_Failure, CPLE_AppDefined,
942 : "Unable to interpret coverage CRS '%s'.", crs.c_str());
943 0 : CPLFree(tmp);
944 0 : return false;
945 : }
946 57 : if (tmp)
947 : {
948 57 : if (projection != nullptr)
949 : {
950 57 : *projection = tmp;
951 : }
952 : else
953 : {
954 0 : CPLFree(tmp);
955 : }
956 57 : swap = oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting();
957 : }
958 57 : return true;
959 : }
960 :
961 21 : std::vector<std::vector<int>> ParseGridEnvelope(CPLXMLNode *node,
962 : bool swap_the_first_two)
963 : {
964 21 : std::vector<std::vector<int>> envelope;
965 : std::vector<std::string> array =
966 42 : Split(CPLGetXMLValue(node, "low", ""), " ", swap_the_first_two);
967 42 : std::vector<int> lows;
968 66 : for (unsigned int i = 0; i < array.size(); ++i)
969 : {
970 45 : lows.push_back(atoi(array[i].c_str()));
971 : }
972 21 : envelope.push_back(lows);
973 21 : array = Split(CPLGetXMLValue(node, "high", ""), " ", swap_the_first_two);
974 42 : std::vector<int> highs;
975 66 : for (unsigned int i = 0; i < array.size(); ++i)
976 : {
977 45 : highs.push_back(atoi(array[i].c_str()));
978 : }
979 21 : envelope.push_back(highs);
980 42 : return envelope;
981 : }
982 :
983 57 : std::vector<std::string> ParseBoundingBox(CPLXMLNode *node)
984 : {
985 57 : std::vector<std::string> bbox;
986 171 : std::string lc = CPLGetXMLValue(node, "lowerCorner", ""), uc;
987 57 : if (lc == "")
988 : {
989 0 : lc = CPLGetXMLValue(node, "LowerCorner", "");
990 : }
991 57 : if (lc == "")
992 : {
993 0 : for (CPLXMLNode *n = node->psChild; n != nullptr; n = n->psNext)
994 : {
995 0 : if (n->eType != CXT_Element || !EQUAL(n->pszValue, "pos"))
996 : {
997 0 : continue;
998 : }
999 0 : if (lc == "")
1000 : {
1001 0 : lc = CPLGetXMLValue(node, nullptr, "");
1002 : }
1003 : else
1004 : {
1005 0 : uc = CPLGetXMLValue(node, nullptr, "");
1006 : }
1007 : }
1008 : }
1009 : else
1010 : {
1011 57 : uc = CPLGetXMLValue(node, "upperCorner", "");
1012 57 : if (uc == "")
1013 : {
1014 0 : uc = CPLGetXMLValue(node, "UpperCorner", "");
1015 : }
1016 : }
1017 57 : if (lc != "" && uc != "")
1018 : {
1019 57 : bbox.push_back(lc);
1020 57 : bbox.push_back(uc);
1021 : }
1022 : // time extent if node is an EnvelopeWithTimePeriod
1023 57 : lc = CPLGetXMLValue(node, "beginPosition", "");
1024 57 : if (lc != "")
1025 : {
1026 0 : uc = CPLGetXMLValue(node, "endPosition", "");
1027 0 : bbox.push_back(lc + "," + uc);
1028 : }
1029 114 : return bbox;
1030 : }
1031 :
1032 : } // namespace WCSUtils
|