Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WCS Client Driver
4 : * Purpose: Implementation of Dataset class for WCS 1.0.
5 : * Author: Frank Warmerdam, warmerdam@pobox.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 "cpl_string.h"
33 : #include "cpl_minixml.h"
34 : #include "cpl_http.h"
35 : #include "gmlutils.h"
36 : #include "gdal_frmts.h"
37 : #include "gdal_pam.h"
38 : #include "ogr_spatialref.h"
39 : #include "gmlcoverage.h"
40 :
41 : #include <algorithm>
42 :
43 : #include "wcsdataset.h"
44 : #include "wcsutils.h"
45 :
46 : using namespace WCSUtils;
47 :
48 : /************************************************************************/
49 : /* GetExtent() */
50 : /* */
51 : /************************************************************************/
52 :
53 15 : std::vector<double> WCSDataset100::GetExtent(int nXOff, int nYOff, int nXSize,
54 : int nYSize, CPL_UNUSED int,
55 : CPL_UNUSED int)
56 : {
57 15 : std::vector<double> extent;
58 : // WCS 1.0 extents are the outer edges of outer pixels.
59 15 : extent.push_back(adfGeoTransform[0] + (nXOff)*adfGeoTransform[1]);
60 0 : extent.push_back(adfGeoTransform[3] +
61 15 : (nYOff + nYSize) * adfGeoTransform[5]);
62 0 : extent.push_back(adfGeoTransform[0] +
63 15 : (nXOff + nXSize) * adfGeoTransform[1]);
64 15 : extent.push_back(adfGeoTransform[3] + (nYOff)*adfGeoTransform[5]);
65 15 : return extent;
66 : }
67 :
68 : /************************************************************************/
69 : /* GetCoverageRequest() */
70 : /* */
71 : /************************************************************************/
72 :
73 15 : std::string WCSDataset100::GetCoverageRequest(bool /* scaled */, int nBufXSize,
74 : int nBufYSize,
75 : const std::vector<double> &extent,
76 : const std::string &osBandList)
77 : {
78 :
79 : /* -------------------------------------------------------------------- */
80 : /* URL encode strings that could have questionable characters. */
81 : /* -------------------------------------------------------------------- */
82 30 : CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
83 :
84 15 : char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
85 15 : osCoverage = pszEncoded;
86 15 : CPLFree(pszEncoded);
87 :
88 30 : CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
89 :
90 15 : pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
91 15 : osFormat = pszEncoded;
92 15 : CPLFree(pszEncoded);
93 :
94 : /* -------------------------------------------------------------------- */
95 : /* Do we have a time we want to use? */
96 : /* -------------------------------------------------------------------- */
97 30 : CPLString osTime;
98 :
99 : osTime =
100 15 : CSLFetchNameValueDef(papszSDSModifiers, "time", osDefaultTime.c_str());
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Construct a "simple" GetCoverage request (WCS 1.0). */
104 : /* -------------------------------------------------------------------- */
105 15 : std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
106 15 : request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
107 15 : request = CPLURLAddKVP(request.c_str(), "REQUEST", "GetCoverage");
108 30 : request = CPLURLAddKVP(request.c_str(), "VERSION",
109 30 : CPLGetXMLValue(psService, "Version", "1.0.0"));
110 15 : request = CPLURLAddKVP(request.c_str(), "COVERAGE", osCoverage.c_str());
111 15 : request = CPLURLAddKVP(request.c_str(), "FORMAT", osFormat.c_str());
112 30 : request += CPLString().Printf(
113 15 : "&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s", extent[0],
114 15 : extent[1], extent[2], extent[3], nBufXSize, nBufYSize, osCRS.c_str());
115 30 : CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
116 15 : if (extra != "")
117 : {
118 30 : std::vector<std::string> pairs = Split(extra.c_str(), "&");
119 30 : for (unsigned int i = 0; i < pairs.size(); ++i)
120 : {
121 15 : std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
122 : request =
123 15 : CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
124 : }
125 : }
126 15 : extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
127 15 : if (extra != "")
128 : {
129 30 : std::vector<std::string> pairs = Split(extra.c_str(), "&");
130 30 : for (unsigned int i = 0; i < pairs.size(); ++i)
131 : {
132 15 : std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
133 : request =
134 15 : CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
135 : }
136 : }
137 :
138 30 : CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
139 15 : if (interpolation == "")
140 : {
141 : // old undocumented key for interpolation in service
142 15 : interpolation = CPLGetXMLValue(psService, "Resample", "");
143 : }
144 15 : if (interpolation != "")
145 : {
146 0 : request += "&INTERPOLATION=" + interpolation;
147 : }
148 :
149 15 : if (osTime != "")
150 : {
151 0 : request += "&time=";
152 0 : request += osTime;
153 : }
154 :
155 15 : if (osBandList != "")
156 : {
157 0 : request += CPLString().Printf("&%s=%s", osBandIdentifier.c_str(),
158 0 : osBandList.c_str());
159 : }
160 30 : return request;
161 : }
162 :
163 : /************************************************************************/
164 : /* DescribeCoverageRequest() */
165 : /* */
166 : /************************************************************************/
167 :
168 6 : std::string WCSDataset100::DescribeCoverageRequest()
169 : {
170 6 : std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
171 6 : request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
172 6 : request = CPLURLAddKVP(request.c_str(), "REQUEST", "DescribeCoverage");
173 12 : request = CPLURLAddKVP(request.c_str(), "VERSION",
174 12 : CPLGetXMLValue(psService, "Version", "1.0.0"));
175 12 : request = CPLURLAddKVP(request.c_str(), "COVERAGE",
176 12 : CPLGetXMLValue(psService, "CoverageName", ""));
177 12 : CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
178 6 : if (extra != "")
179 : {
180 10 : std::vector<std::string> pairs = Split(extra.c_str(), "&");
181 10 : for (unsigned int i = 0; i < pairs.size(); ++i)
182 : {
183 5 : std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
184 : request =
185 5 : CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
186 : }
187 : }
188 6 : extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
189 6 : if (extra != "")
190 : {
191 0 : std::vector<std::string> pairs = Split(extra.c_str(), "&");
192 0 : for (unsigned int i = 0; i < pairs.size(); ++i)
193 : {
194 0 : std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
195 : request =
196 0 : CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
197 : }
198 : }
199 12 : return request;
200 : }
201 :
202 : /************************************************************************/
203 : /* CoverageOffering() */
204 : /* */
205 : /************************************************************************/
206 :
207 5 : CPLXMLNode *WCSDataset100::CoverageOffering(CPLXMLNode *psDC)
208 : {
209 5 : return CPLGetXMLNode(psDC, "=CoverageDescription.CoverageOffering");
210 : }
211 :
212 : /************************************************************************/
213 : /* ExtractGridInfo() */
214 : /* */
215 : /* Collect info about grid from describe coverage for WCS 1.0.0 */
216 : /* and above. */
217 : /************************************************************************/
218 :
219 15 : bool WCSDataset100::ExtractGridInfo()
220 :
221 : {
222 15 : CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageOffering");
223 :
224 15 : if (psCO == nullptr)
225 0 : return FALSE;
226 :
227 : /* -------------------------------------------------------------------- */
228 : /* We need to strip off name spaces so it is easier to */
229 : /* searchfor plain gml names. */
230 : /* -------------------------------------------------------------------- */
231 15 : CPLStripXMLNamespace(psCO, nullptr, TRUE);
232 :
233 : /* -------------------------------------------------------------------- */
234 : /* Verify we have a Rectified Grid. */
235 : /* -------------------------------------------------------------------- */
236 : CPLXMLNode *psRG =
237 15 : CPLGetXMLNode(psCO, "domainSet.spatialDomain.RectifiedGrid");
238 :
239 15 : if (psRG == nullptr)
240 : {
241 0 : CPLError(CE_Failure, CPLE_AppDefined,
242 : "Unable to find RectifiedGrid in CoverageOffering,\n"
243 : "unable to process WCS Coverage.");
244 0 : return FALSE;
245 : }
246 :
247 : /* -------------------------------------------------------------------- */
248 : /* Extract size, geotransform and coordinate system. */
249 : /* Projection is, if it is, from Point.srsName */
250 : /* -------------------------------------------------------------------- */
251 15 : char *pszProjection = nullptr;
252 15 : if (WCSParseGMLCoverage(psRG, &nRasterXSize, &nRasterYSize, adfGeoTransform,
253 15 : &pszProjection) != CE_None)
254 : {
255 0 : CPLFree(pszProjection);
256 0 : return FALSE;
257 : }
258 15 : if (pszProjection)
259 0 : m_oSRS.SetFromUserInput(
260 : pszProjection,
261 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
262 15 : CPLFree(pszProjection);
263 :
264 : // MapServer have origin at pixel boundary
265 15 : if (CPLGetXMLBoolean(psService, "OriginAtBoundary"))
266 : {
267 3 : adfGeoTransform[0] += adfGeoTransform[1] * 0.5;
268 3 : adfGeoTransform[0] += adfGeoTransform[2] * 0.5;
269 3 : adfGeoTransform[3] += adfGeoTransform[4] * 0.5;
270 3 : adfGeoTransform[3] += adfGeoTransform[5] * 0.5;
271 : }
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* Fallback to nativeCRSs declaration. */
275 : /* -------------------------------------------------------------------- */
276 : const char *pszNativeCRSs =
277 15 : CPLGetXMLValue(psCO, "supportedCRSs.nativeCRSs", nullptr);
278 :
279 15 : if (pszNativeCRSs == nullptr)
280 : pszNativeCRSs =
281 9 : CPLGetXMLValue(psCO, "supportedCRSs.requestResponseCRSs", nullptr);
282 :
283 15 : if (pszNativeCRSs == nullptr)
284 : pszNativeCRSs =
285 0 : CPLGetXMLValue(psCO, "supportedCRSs.requestCRSs", nullptr);
286 :
287 15 : if (pszNativeCRSs == nullptr)
288 : pszNativeCRSs =
289 0 : CPLGetXMLValue(psCO, "supportedCRSs.responseCRSs", nullptr);
290 :
291 15 : if (pszNativeCRSs != nullptr && m_oSRS.IsEmpty())
292 : {
293 15 : if (m_oSRS.SetFromUserInput(
294 : pszNativeCRSs,
295 15 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
296 : OGRERR_NONE)
297 : {
298 15 : CPLDebug("WCS", "<nativeCRSs> element contents not parsable:\n%s",
299 : pszNativeCRSs);
300 : }
301 : }
302 :
303 : // We should try to use the services name for the CRS if possible.
304 15 : if (pszNativeCRSs != nullptr &&
305 15 : (STARTS_WITH_CI(pszNativeCRSs, "EPSG:") ||
306 0 : STARTS_WITH_CI(pszNativeCRSs, "AUTO:") ||
307 0 : STARTS_WITH_CI(pszNativeCRSs, "Image ") ||
308 0 : STARTS_WITH_CI(pszNativeCRSs, "Engineering ") ||
309 0 : STARTS_WITH_CI(pszNativeCRSs, "OGC:")))
310 : {
311 15 : osCRS = pszNativeCRSs;
312 :
313 15 : size_t nDivider = osCRS.find(" ");
314 :
315 15 : if (nDivider != std::string::npos)
316 0 : osCRS.resize(nDivider - 1);
317 : }
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Do we have a coordinate system override? */
321 : /* -------------------------------------------------------------------- */
322 15 : const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
323 :
324 15 : if (pszProjOverride)
325 : {
326 0 : if (m_oSRS.SetFromUserInput(
327 : pszProjOverride,
328 0 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
329 : OGRERR_NONE)
330 : {
331 0 : CPLError(CE_Failure, CPLE_AppDefined,
332 : "<SRS> element contents not parsable:\n%s",
333 : pszProjOverride);
334 0 : return FALSE;
335 : }
336 :
337 0 : if (STARTS_WITH_CI(pszProjOverride, "EPSG:") ||
338 0 : STARTS_WITH_CI(pszProjOverride, "AUTO:") ||
339 0 : STARTS_WITH_CI(pszProjOverride, "OGC:") ||
340 0 : STARTS_WITH_CI(pszProjOverride, "Image ") ||
341 0 : STARTS_WITH_CI(pszProjOverride, "Engineering "))
342 0 : osCRS = pszProjOverride;
343 : }
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Build CRS name to use. */
347 : /* -------------------------------------------------------------------- */
348 15 : if (!m_oSRS.IsEmpty() && osCRS == "")
349 : {
350 0 : const char *pszAuth = m_oSRS.GetAuthorityName(nullptr);
351 0 : if (pszAuth != nullptr && EQUAL(pszAuth, "EPSG"))
352 : {
353 0 : pszAuth = m_oSRS.GetAuthorityCode(nullptr);
354 0 : if (pszAuth)
355 : {
356 0 : osCRS = "EPSG:";
357 0 : osCRS += pszAuth;
358 : }
359 : else
360 : {
361 0 : CPLError(CE_Failure, CPLE_AppDefined,
362 : "Unable to define CRS to use.");
363 0 : return FALSE;
364 : }
365 : }
366 : }
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Pick a format type if we don't already have one selected. */
370 : /* */
371 : /* We will prefer anything that sounds like TIFF, otherwise */
372 : /* falling back to the first supported format. Should we */
373 : /* consider preferring the nativeFormat if available? */
374 : /* -------------------------------------------------------------------- */
375 15 : if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
376 : {
377 5 : CPLXMLNode *psSF = CPLGetXMLNode(psCO, "supportedFormats");
378 : CPLXMLNode *psNode;
379 5 : char **papszFormatList = nullptr;
380 5 : CPLString osPreferredFormat;
381 : int iFormat;
382 :
383 5 : if (psSF == nullptr)
384 : {
385 0 : CPLError(
386 : CE_Failure, CPLE_AppDefined,
387 : "No <PreferredFormat> tag in service definition file, and no\n"
388 : "<supportedFormats> in coverageOffering.");
389 0 : return FALSE;
390 : }
391 :
392 36 : for (psNode = psSF->psChild; psNode != nullptr; psNode = psNode->psNext)
393 : {
394 31 : if (psNode->eType == CXT_Element &&
395 27 : EQUAL(psNode->pszValue, "formats") &&
396 27 : psNode->psChild != nullptr &&
397 27 : psNode->psChild->eType == CXT_Text)
398 : {
399 : // This check is looking for deprecated WCS 1.0 capabilities
400 : // with multiple formats space delimited in a single <formats>
401 : // element per GDAL ticket 1748 (done by MapServer 4.10 and
402 : // earlier for instance).
403 27 : if (papszFormatList == nullptr && psNode->psNext == nullptr &&
404 1 : strstr(psNode->psChild->pszValue, " ") != nullptr &&
405 0 : strstr(psNode->psChild->pszValue, ";") == nullptr)
406 : {
407 : char **papszSubList =
408 0 : CSLTokenizeString(psNode->psChild->pszValue);
409 : papszFormatList =
410 0 : CSLInsertStrings(papszFormatList, -1, papszSubList);
411 0 : CSLDestroy(papszSubList);
412 : }
413 : else
414 : {
415 27 : papszFormatList = CSLAddString(papszFormatList,
416 27 : psNode->psChild->pszValue);
417 : }
418 : }
419 : }
420 :
421 7 : for (iFormat = 0;
422 7 : papszFormatList != nullptr && papszFormatList[iFormat] != nullptr;
423 : iFormat++)
424 : {
425 7 : if (osPreferredFormat.empty())
426 5 : osPreferredFormat = papszFormatList[iFormat];
427 :
428 7 : if (strstr(papszFormatList[iFormat], "tiff") != nullptr ||
429 7 : strstr(papszFormatList[iFormat], "TIFF") != nullptr ||
430 3 : strstr(papszFormatList[iFormat], "Tiff") != nullptr)
431 : {
432 5 : osPreferredFormat = papszFormatList[iFormat];
433 5 : break;
434 : }
435 : }
436 :
437 5 : CSLDestroy(papszFormatList);
438 :
439 5 : if (!osPreferredFormat.empty())
440 : {
441 5 : bServiceDirty = true;
442 5 : CPLCreateXMLElementAndValue(psService, "PreferredFormat",
443 : osPreferredFormat);
444 : }
445 : }
446 :
447 : /* -------------------------------------------------------------------- */
448 : /* Try to identify a nodata value. For now we only support the */
449 : /* singleValue mechanism. */
450 : /* -------------------------------------------------------------------- */
451 15 : if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
452 : {
453 15 : const char *pszSV = CPLGetXMLValue(
454 : psCO, "rangeSet.RangeSet.nullValues.singleValue", nullptr);
455 :
456 15 : if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
457 : {
458 0 : bServiceDirty = true;
459 0 : CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
460 : }
461 : }
462 :
463 : /* -------------------------------------------------------------------- */
464 : /* Do we have a Band range type. For now we look for a fairly */
465 : /* specific configuration. The rangeset my have one axis named */
466 : /* "Band", with a set of ascending numerical values. */
467 : /* -------------------------------------------------------------------- */
468 15 : osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
469 15 : CPLXMLNode *psAD = CPLGetXMLNode(
470 : psService,
471 : "CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription");
472 : CPLXMLNode *psValues;
473 :
474 22 : if (osBandIdentifier.empty() && psAD != nullptr &&
475 7 : (EQUAL(CPLGetXMLValue(psAD, "name", ""), "Band") ||
476 23 : EQUAL(CPLGetXMLValue(psAD, "name", ""), "Bands")) &&
477 7 : ((psValues = CPLGetXMLNode(psAD, "values")) != nullptr))
478 : {
479 : CPLXMLNode *psSV;
480 : int iBand;
481 :
482 7 : osBandIdentifier = CPLGetXMLValue(psAD, "name", "");
483 :
484 13 : for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
485 6 : psSV = psSV->psNext, iBand++)
486 : {
487 9 : if (psSV->eType != CXT_Element ||
488 9 : !EQUAL(psSV->pszValue, "singleValue") ||
489 6 : psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
490 6 : atoi(psSV->psChild->pszValue) != iBand)
491 : {
492 3 : osBandIdentifier = "";
493 3 : break;
494 : }
495 : }
496 :
497 7 : if (!osBandIdentifier.empty())
498 : {
499 4 : bServiceDirty = true;
500 4 : CPLSetXMLValue(psService, "BandIdentifier",
501 : osBandIdentifier.c_str());
502 : }
503 : }
504 :
505 : /* -------------------------------------------------------------------- */
506 : /* Do we have a temporal domain? If so, try to identify a */
507 : /* default time value. */
508 : /* -------------------------------------------------------------------- */
509 15 : osDefaultTime = CPLGetXMLValue(psService, "DefaultTime", "");
510 : CPLXMLNode *psTD =
511 15 : CPLGetXMLNode(psService, "CoverageOffering.domainSet.temporalDomain");
512 30 : CPLString osServiceURL = CPLGetXMLValue(psService, "ServiceURL", "");
513 : CPLString osCoverageExtra =
514 15 : CPLGetXMLValue(psService, "GetCoverageExtra", "");
515 :
516 15 : if (psTD != nullptr)
517 : {
518 : CPLXMLNode *psTime;
519 :
520 : // collect all the allowed time positions.
521 :
522 0 : for (psTime = psTD->psChild; psTime != nullptr; psTime = psTime->psNext)
523 : {
524 0 : if (psTime->eType == CXT_Element &&
525 0 : EQUAL(psTime->pszValue, "timePosition") &&
526 0 : psTime->psChild != nullptr &&
527 0 : psTime->psChild->eType == CXT_Text)
528 0 : aosTimePositions.push_back(psTime->psChild->pszValue);
529 : }
530 :
531 : // we will default to the last - likely the most recent - entry.
532 :
533 0 : if (!aosTimePositions.empty() && osDefaultTime.empty() &&
534 0 : osServiceURL.ifind("time=") == std::string::npos &&
535 0 : osCoverageExtra.ifind("time=") == std::string::npos)
536 : {
537 0 : osDefaultTime = aosTimePositions.back();
538 0 : bServiceDirty = true;
539 0 : CPLCreateXMLElementAndValue(psService, "DefaultTime",
540 : osDefaultTime.c_str());
541 : }
542 : }
543 :
544 15 : return true;
545 : }
546 :
547 : /************************************************************************/
548 : /* ParseCapabilities() */
549 : /************************************************************************/
550 :
551 5 : CPLErr WCSDataset100::ParseCapabilities(CPLXMLNode *Capabilities,
552 : const std::string & /* url */)
553 : {
554 :
555 5 : CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
556 :
557 5 : if (strcmp(Capabilities->pszValue, "WCS_Capabilities") != 0)
558 : {
559 0 : CPLError(CE_Failure, CPLE_AppDefined,
560 : "Error in capabilities document.\n");
561 0 : return CE_Failure;
562 : }
563 :
564 5 : char **metadata = nullptr;
565 10 : CPLString path = "WCS_GLOBAL#";
566 :
567 10 : CPLString key = path + "version";
568 5 : metadata = CSLSetNameValue(metadata, key, Version());
569 :
570 60 : for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
571 55 : node = node->psNext)
572 : {
573 55 : const char *attr = node->pszValue;
574 55 : if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
575 : {
576 4 : key = path + "updateSequence";
577 4 : CPLString value = CPLGetXMLValue(node, nullptr, "");
578 4 : metadata = CSLSetNameValue(metadata, key, value);
579 : }
580 : }
581 :
582 : // identification metadata
583 10 : CPLString path2 = path;
584 30 : CPLXMLNode *service = AddSimpleMetaData(
585 : &metadata, Capabilities, path2, "Service",
586 25 : {"description", "name", "label", "fees", "accessConstraints"});
587 5 : if (service)
588 : {
589 10 : CPLString path3 = std::move(path2);
590 15 : CPLString kw = GetKeywords(service, "keywords", "keyword");
591 5 : if (kw != "")
592 : {
593 4 : CPLString name = path + "keywords";
594 4 : metadata = CSLSetNameValue(metadata, name, kw);
595 : }
596 20 : CPLXMLNode *party = AddSimpleMetaData(
597 : &metadata, service, path3, "responsibleParty",
598 15 : {"individualName", "organisationName", "positionName"});
599 5 : CPLXMLNode *info = CPLGetXMLNode(party, "contactInfo");
600 5 : if (party && info)
601 : {
602 10 : CPLString path4 = path3 + "contactInfo.";
603 5 : CPLString path5 = path4;
604 35 : AddSimpleMetaData(&metadata, info, path4, "address",
605 : {"deliveryPoint", "city", "administrativeArea",
606 : "postalCode", "country",
607 30 : "electronicMailAddress"});
608 15 : AddSimpleMetaData(&metadata, info, path5, "phone",
609 10 : {"voice", "facsimile"});
610 : }
611 : }
612 :
613 : // provider metadata
614 : // operations metadata
615 10 : CPLString DescribeCoverageURL;
616 : DescribeCoverageURL = CPLGetXMLValue(
617 5 : CPLGetXMLNode(
618 : CPLGetXMLNode(
619 : CPLSearchXMLNode(
620 : CPLSearchXMLNode(Capabilities, "DescribeCoverage"), "Get"),
621 : "OnlineResource"),
622 : "href"),
623 5 : nullptr, "");
624 : // if DescribeCoverageURL looks wrong (i.e. has localhost) should we change
625 : // it?
626 :
627 5 : this->SetMetadata(metadata, "");
628 5 : CSLDestroy(metadata);
629 5 : metadata = nullptr;
630 :
631 5 : if (CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "ContentMetadata"))
632 : {
633 5 : int index = 1;
634 23 : for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
635 18 : summary = summary->psNext)
636 : {
637 18 : if (summary->eType != CXT_Element ||
638 18 : !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
639 : {
640 0 : continue;
641 : }
642 18 : CPLString path3;
643 18 : path3.Printf("SUBDATASET_%d_", index);
644 18 : index += 1;
645 :
646 : // the name and description of the subdataset:
647 : // GDAL Data Model:
648 : // The value of the _NAME is a string that can be passed to
649 : // GDALOpen() to access the file.
650 :
651 18 : CPLXMLNode *node = CPLGetXMLNode(summary, "name");
652 18 : if (node)
653 : {
654 36 : CPLString key2 = path3 + "NAME";
655 36 : CPLString name = CPLGetXMLValue(node, nullptr, "");
656 18 : CPLString value = DescribeCoverageURL;
657 18 : value = CPLURLAddKVP(value, "VERSION", this->Version());
658 18 : value = CPLURLAddKVP(value, "COVERAGE", name);
659 18 : metadata = CSLSetNameValue(metadata, key2, value);
660 : }
661 : else
662 : {
663 0 : CSLDestroy(metadata);
664 0 : CPLError(CE_Failure, CPLE_AppDefined,
665 : "Error in capabilities document.\n");
666 0 : return CE_Failure;
667 : }
668 :
669 18 : node = CPLGetXMLNode(summary, "label");
670 18 : if (node)
671 : {
672 18 : CPLString key2 = path3 + "DESC";
673 18 : metadata = CSLSetNameValue(metadata, key2,
674 : CPLGetXMLValue(node, nullptr, ""));
675 : }
676 : else
677 : {
678 0 : CSLDestroy(metadata);
679 0 : CPLError(CE_Failure, CPLE_AppDefined,
680 : "Error in capabilities document.\n");
681 0 : return CE_Failure;
682 : }
683 :
684 : // todo: compose global bounding box from lonLatEnvelope
685 :
686 : // further subdataset (coverage) parameters are parsed in
687 : // ParseCoverageCapabilities
688 : }
689 : }
690 5 : this->SetMetadata(metadata, "SUBDATASETS");
691 5 : CSLDestroy(metadata);
692 5 : return CE_None;
693 : }
694 :
695 5 : void WCSDataset100::ParseCoverageCapabilities(CPLXMLNode *capabilities,
696 : const std::string &coverage,
697 : CPLXMLNode *metadata)
698 : {
699 5 : CPLStripXMLNamespace(capabilities, nullptr, TRUE);
700 5 : if (CPLXMLNode *contents = CPLGetXMLNode(capabilities, "ContentMetadata"))
701 : {
702 23 : for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
703 18 : summary = summary->psNext)
704 : {
705 18 : if (summary->eType != CXT_Element ||
706 18 : !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
707 : {
708 13 : continue;
709 : }
710 :
711 18 : CPLXMLNode *node = CPLGetXMLNode(summary, "name");
712 18 : if (node)
713 : {
714 18 : CPLString name = CPLGetXMLValue(node, nullptr, "");
715 18 : if (name != coverage)
716 : {
717 13 : continue;
718 : }
719 : }
720 :
721 5 : XMLCopyMetadata(summary, metadata, "label");
722 5 : XMLCopyMetadata(summary, metadata, "description");
723 :
724 15 : CPLString kw = GetKeywords(summary, "keywords", "keyword");
725 5 : CPLAddXMLAttributeAndValue(
726 : CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
727 : "keywords");
728 :
729 : // skip metadataLink
730 : }
731 : }
732 5 : }
|