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