Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WMS Client Driver
4 : * Purpose: Definition of GDALWMSMetaDataset class
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "wmsmetadataset.h"
14 :
15 : #include "gdal_openinfo.h"
16 :
17 : int VersionStringToInt(const char *version);
18 :
19 : /************************************************************************/
20 : /* GDALWMSMetaDataset() */
21 : /************************************************************************/
22 :
23 : GDALWMSMetaDataset::GDALWMSMetaDataset() = default;
24 :
25 : /************************************************************************/
26 : /* ~GDALWMSMetaDataset() */
27 : /************************************************************************/
28 :
29 6 : GDALWMSMetaDataset::~GDALWMSMetaDataset()
30 : {
31 3 : CSLDestroy(papszSubDatasets);
32 6 : }
33 :
34 : /************************************************************************/
35 : /* AddSubDataset() */
36 : /************************************************************************/
37 :
38 9 : void GDALWMSMetaDataset::AddSubDataset(const char *pszName, const char *pszDesc)
39 : {
40 : char szName[80];
41 9 : int nCount = CSLCount(papszSubDatasets) / 2;
42 :
43 9 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
44 9 : papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszName);
45 :
46 9 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
47 9 : papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszDesc);
48 9 : }
49 :
50 : /************************************************************************/
51 : /* DownloadGetCapabilities() */
52 : /************************************************************************/
53 :
54 : GDALDataset *
55 1 : GDALWMSMetaDataset::DownloadGetCapabilities(GDALOpenInfo *poOpenInfo)
56 : {
57 1 : const char *pszURL = poOpenInfo->pszFilename;
58 1 : if (STARTS_WITH_CI(pszURL, "WMS:"))
59 0 : pszURL += 4;
60 :
61 2 : CPLString osFormat = CPLURLGetValue(pszURL, "FORMAT");
62 2 : CPLString osTransparent = CPLURLGetValue(pszURL, "TRANSPARENT");
63 2 : CPLString osVersion = CPLURLGetValue(pszURL, "VERSION");
64 2 : CPLString osPreferredSRS = CPLURLGetValue(pszURL, "SRS");
65 1 : if (osPreferredSRS.empty())
66 1 : osPreferredSRS = CPLURLGetValue(pszURL, "CRS");
67 :
68 1 : if (osVersion.empty())
69 1 : osVersion = "1.1.1";
70 :
71 2 : CPLString osURL(pszURL);
72 1 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS");
73 1 : osURL = CPLURLAddKVP(osURL, "VERSION", osVersion);
74 1 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities");
75 : /* Remove all other keywords */
76 1 : osURL = CPLURLAddKVP(osURL, "LAYERS", nullptr);
77 1 : osURL = CPLURLAddKVP(osURL, "SRS", nullptr);
78 1 : osURL = CPLURLAddKVP(osURL, "CRS", nullptr);
79 1 : osURL = CPLURLAddKVP(osURL, "BBOX", nullptr);
80 1 : osURL = CPLURLAddKVP(osURL, "FORMAT", nullptr);
81 1 : osURL = CPLURLAddKVP(osURL, "TRANSPARENT", nullptr);
82 1 : osURL = CPLURLAddKVP(osURL, "STYLES", nullptr);
83 1 : osURL = CPLURLAddKVP(osURL, "WIDTH", nullptr);
84 1 : osURL = CPLURLAddKVP(osURL, "HEIGHT", nullptr);
85 :
86 1 : CPLHTTPResult *psResult = CPLHTTPFetch(osURL, nullptr);
87 1 : if (psResult == nullptr)
88 : {
89 0 : return nullptr;
90 : }
91 1 : if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
92 : {
93 0 : CPLError(CE_Failure, CPLE_AppDefined,
94 : "Error returned by server : %s (%d)",
95 0 : (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
96 : psResult->nStatus);
97 0 : CPLHTTPDestroyResult(psResult);
98 0 : return nullptr;
99 : }
100 1 : if (psResult->pabyData == nullptr)
101 : {
102 0 : CPLError(CE_Failure, CPLE_AppDefined,
103 : "Empty content returned by server");
104 0 : CPLHTTPDestroyResult(psResult);
105 0 : return nullptr;
106 : }
107 :
108 : CPLXMLNode *psXML =
109 1 : CPLParseXMLString(reinterpret_cast<const char *>(psResult->pabyData));
110 1 : if (psXML == nullptr)
111 : {
112 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
113 : psResult->pabyData);
114 0 : CPLHTTPDestroyResult(psResult);
115 0 : return nullptr;
116 : }
117 :
118 : GDALDataset *poRet =
119 1 : AnalyzeGetCapabilities(psXML, osFormat, osTransparent, osPreferredSRS);
120 :
121 1 : CPLHTTPDestroyResult(psResult);
122 1 : CPLDestroyXMLNode(psXML);
123 :
124 1 : return poRet;
125 : }
126 :
127 : /************************************************************************/
128 : /* DownloadGetTileService() */
129 : /************************************************************************/
130 :
131 : GDALDataset *
132 0 : GDALWMSMetaDataset::DownloadGetTileService(GDALOpenInfo *poOpenInfo)
133 : {
134 0 : const char *pszURL = poOpenInfo->pszFilename;
135 0 : if (STARTS_WITH_CI(pszURL, "WMS:"))
136 0 : pszURL += 4;
137 :
138 0 : CPLString osURL(pszURL);
139 0 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS");
140 0 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetTileService");
141 : /* Remove all other keywords */
142 0 : osURL = CPLURLAddKVP(osURL, "VERSION", nullptr);
143 0 : osURL = CPLURLAddKVP(osURL, "LAYERS", nullptr);
144 0 : osURL = CPLURLAddKVP(osURL, "SRS", nullptr);
145 0 : osURL = CPLURLAddKVP(osURL, "CRS", nullptr);
146 0 : osURL = CPLURLAddKVP(osURL, "BBOX", nullptr);
147 0 : osURL = CPLURLAddKVP(osURL, "FORMAT", nullptr);
148 0 : osURL = CPLURLAddKVP(osURL, "TRANSPARENT", nullptr);
149 0 : osURL = CPLURLAddKVP(osURL, "STYLES", nullptr);
150 0 : osURL = CPLURLAddKVP(osURL, "WIDTH", nullptr);
151 0 : osURL = CPLURLAddKVP(osURL, "HEIGHT", nullptr);
152 :
153 0 : CPLHTTPResult *psResult = CPLHTTPFetch(osURL, nullptr);
154 0 : if (psResult == nullptr)
155 : {
156 0 : return nullptr;
157 : }
158 0 : if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
159 : {
160 0 : CPLError(CE_Failure, CPLE_AppDefined,
161 : "Error returned by server : %s (%d)",
162 0 : (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
163 : psResult->nStatus);
164 0 : CPLHTTPDestroyResult(psResult);
165 0 : return nullptr;
166 : }
167 0 : if (psResult->pabyData == nullptr)
168 : {
169 0 : CPLError(CE_Failure, CPLE_AppDefined,
170 : "Empty content returned by server");
171 0 : CPLHTTPDestroyResult(psResult);
172 0 : return nullptr;
173 : }
174 :
175 : CPLXMLNode *psXML =
176 0 : CPLParseXMLString(reinterpret_cast<const char *>(psResult->pabyData));
177 0 : if (psXML == nullptr)
178 : {
179 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
180 : psResult->pabyData);
181 0 : CPLHTTPDestroyResult(psResult);
182 0 : return nullptr;
183 : }
184 :
185 0 : GDALDataset *poRet = AnalyzeGetTileService(psXML, poOpenInfo);
186 :
187 0 : CPLHTTPDestroyResult(psResult);
188 0 : CPLDestroyXMLNode(psXML);
189 :
190 0 : return poRet;
191 : }
192 :
193 : /************************************************************************/
194 : /* GetMetadataDomainList() */
195 : /************************************************************************/
196 :
197 0 : char **GDALWMSMetaDataset::GetMetadataDomainList()
198 : {
199 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
200 0 : TRUE, "SUBDATASETS", nullptr);
201 : }
202 :
203 : /************************************************************************/
204 : /* GetMetadata() */
205 : /************************************************************************/
206 :
207 2 : char **GDALWMSMetaDataset::GetMetadata(const char *pszDomain)
208 :
209 : {
210 2 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
211 2 : return papszSubDatasets;
212 :
213 0 : return GDALPamDataset::GetMetadata(pszDomain);
214 : }
215 :
216 : /************************************************************************/
217 : /* AddSubDataset() */
218 : /************************************************************************/
219 :
220 5 : void GDALWMSMetaDataset::AddSubDataset(
221 : const char *pszLayerName, const char *pszTitle,
222 : CPL_UNUSED const char *pszAbstract, const char *pszSRS, const char *pszMinX,
223 : const char *pszMinY, const char *pszMaxX, const char *pszMaxY,
224 : const std::string &osFormat, const std::string &osTransparent)
225 : {
226 10 : CPLString osSubdatasetName = "WMS:";
227 5 : osSubdatasetName += osGetURL;
228 5 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS");
229 5 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion);
230 5 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap");
231 5 : char *pszEscapedLayerName = CPLEscapeString(pszLayerName, -1, CPLES_URL);
232 : osSubdatasetName =
233 5 : CPLURLAddKVP(osSubdatasetName, "LAYERS", pszEscapedLayerName);
234 5 : CPLFree(pszEscapedLayerName);
235 5 : if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
236 : {
237 5 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "CRS", pszSRS);
238 : }
239 : else
240 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SRS", pszSRS);
241 10 : osSubdatasetName = CPLURLAddKVP(
242 : osSubdatasetName, "BBOX",
243 5 : CPLSPrintf("%s,%s,%s,%s", pszMinX, pszMinY, pszMaxX, pszMaxY));
244 5 : if (!osFormat.empty())
245 : osSubdatasetName =
246 0 : CPLURLAddKVP(osSubdatasetName, "FORMAT", osFormat.c_str());
247 5 : if (!osTransparent.empty())
248 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TRANSPARENT",
249 0 : osTransparent.c_str());
250 :
251 5 : if (pszTitle)
252 : {
253 10 : if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
254 5 : osXMLEncoding != "UTF-8")
255 : {
256 : char *pszRecodedTitle =
257 0 : CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
258 0 : if (pszRecodedTitle)
259 0 : AddSubDataset(osSubdatasetName, pszRecodedTitle);
260 : else
261 0 : AddSubDataset(osSubdatasetName, pszTitle);
262 0 : CPLFree(pszRecodedTitle);
263 : }
264 : else
265 : {
266 5 : AddSubDataset(osSubdatasetName, pszTitle);
267 : }
268 : }
269 : else
270 : {
271 0 : AddSubDataset(osSubdatasetName, pszLayerName);
272 : }
273 5 : }
274 :
275 : /************************************************************************/
276 : /* AddWMSCSubDataset() */
277 : /************************************************************************/
278 :
279 0 : void GDALWMSMetaDataset::AddWMSCSubDataset(WMSCTileSetDesc &oWMSCTileSetDesc,
280 : const char *pszTitle,
281 : const CPLString &osTransparent)
282 : {
283 0 : CPLString osSubdatasetName = "WMS:";
284 0 : osSubdatasetName += osGetURL;
285 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS");
286 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion);
287 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap");
288 : osSubdatasetName =
289 0 : CPLURLAddKVP(osSubdatasetName, "LAYERS", oWMSCTileSetDesc.osLayers);
290 0 : if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
291 : osSubdatasetName =
292 0 : CPLURLAddKVP(osSubdatasetName, "CRS", oWMSCTileSetDesc.osSRS);
293 : else
294 : osSubdatasetName =
295 0 : CPLURLAddKVP(osSubdatasetName, "SRS", oWMSCTileSetDesc.osSRS);
296 : osSubdatasetName =
297 0 : CPLURLAddKVP(osSubdatasetName, "BBOX",
298 : CPLSPrintf("%s,%s,%s,%s", oWMSCTileSetDesc.osMinX.c_str(),
299 : oWMSCTileSetDesc.osMinY.c_str(),
300 : oWMSCTileSetDesc.osMaxX.c_str(),
301 0 : oWMSCTileSetDesc.osMaxY.c_str()));
302 :
303 : osSubdatasetName =
304 0 : CPLURLAddKVP(osSubdatasetName, "FORMAT", oWMSCTileSetDesc.osFormat);
305 0 : if (!osTransparent.empty())
306 : osSubdatasetName =
307 0 : CPLURLAddKVP(osSubdatasetName, "TRANSPARENT", osTransparent);
308 0 : if (oWMSCTileSetDesc.nTileWidth != oWMSCTileSetDesc.nTileHeight)
309 0 : CPLDebug("WMS", "Weird: nTileWidth != nTileHeight for %s",
310 : oWMSCTileSetDesc.osLayers.c_str());
311 : osSubdatasetName =
312 0 : CPLURLAddKVP(osSubdatasetName, "TILESIZE",
313 0 : CPLSPrintf("%d", oWMSCTileSetDesc.nTileWidth));
314 : osSubdatasetName =
315 0 : CPLURLAddKVP(osSubdatasetName, "OVERVIEWCOUNT",
316 0 : CPLSPrintf("%d", oWMSCTileSetDesc.nResolutions - 1));
317 : osSubdatasetName =
318 0 : CPLURLAddKVP(osSubdatasetName, "MINRESOLUTION",
319 0 : CPLSPrintf("%.16f", oWMSCTileSetDesc.dfMinResolution));
320 0 : osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TILED", "true");
321 :
322 0 : if (pszTitle)
323 : {
324 0 : if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
325 0 : osXMLEncoding != "UTF-8")
326 : {
327 : char *pszRecodedTitle =
328 0 : CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
329 0 : if (pszRecodedTitle)
330 0 : AddSubDataset(osSubdatasetName, pszRecodedTitle);
331 : else
332 0 : AddSubDataset(osSubdatasetName, pszTitle);
333 0 : CPLFree(pszRecodedTitle);
334 : }
335 : else
336 : {
337 0 : AddSubDataset(osSubdatasetName, pszTitle);
338 : }
339 : }
340 : else
341 : {
342 0 : AddSubDataset(osSubdatasetName, oWMSCTileSetDesc.osLayers);
343 : }
344 0 : }
345 :
346 : /************************************************************************/
347 : /* ExploreLayer() */
348 : /************************************************************************/
349 :
350 5 : void GDALWMSMetaDataset::ExploreLayer(CPLXMLNode *psXML,
351 : const CPLString &osFormat,
352 : const CPLString &osTransparent,
353 : const CPLString &osPreferredSRS,
354 : const char *pszSRS, const char *pszMinX,
355 : const char *pszMinY, const char *pszMaxX,
356 : const char *pszMaxY)
357 : {
358 5 : const char *pszName = CPLGetXMLValue(psXML, "Name", nullptr);
359 5 : const char *pszTitle = CPLGetXMLValue(psXML, "Title", nullptr);
360 5 : const char *pszAbstract = CPLGetXMLValue(psXML, "Abstract", nullptr);
361 :
362 5 : CPLXMLNode *psSRS = nullptr;
363 5 : const char *pszSRSLocal = nullptr;
364 5 : const char *pszMinXLocal = nullptr;
365 5 : const char *pszMinYLocal = nullptr;
366 5 : const char *pszMaxXLocal = nullptr;
367 5 : const char *pszMaxYLocal = nullptr;
368 :
369 : const bool bIsWMS130 =
370 5 : VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0");
371 :
372 5 : const auto GetCRS = [bIsWMS130](const CPLXMLNode *psNode)
373 : {
374 : const char *pszVal =
375 5 : CPLGetXMLValue(psNode, bIsWMS130 ? "CRS" : "SRS", nullptr);
376 5 : if (!pszVal && !bIsWMS130)
377 : {
378 : // Some WMS servers have a non-conformant WMS 1.1.1 implementation
379 : // that use "CRS" instead of "SRS"
380 : // like https://mapy.geoportal.gov.pl/wss/service/PZGIK/BDOO/WMS/aktualne?service=wms&request=getcapabilities
381 0 : pszVal = CPLGetXMLValue(psNode, "CRS", nullptr);
382 0 : if (pszVal)
383 : {
384 : static bool bWarned = false;
385 0 : if (!bWarned)
386 : {
387 0 : bWarned = true;
388 0 : CPLError(CE_Warning, CPLE_AppDefined,
389 : "WMS server uses non-standard CRS attribute for "
390 : "WMS 1.1.0. Things might mis-behave. Perhaps try "
391 : "using VERSION=1.3.0");
392 : }
393 : }
394 : }
395 5 : return pszVal;
396 5 : };
397 :
398 : /* Use local bounding box if available, otherwise use the one */
399 : /* that comes from an upper layer */
400 : /* such as in http://neowms.sci.gsfc.nasa.gov/wms/wms */
401 5 : CPLXMLNode *psIter = psXML->psChild;
402 42 : while (psIter != nullptr)
403 : {
404 42 : if (psIter->eType == CXT_Element &&
405 29 : strcmp(psIter->pszValue, "BoundingBox") == 0)
406 : {
407 5 : psSRS = psIter;
408 5 : pszSRSLocal = GetCRS(psSRS);
409 5 : if (osPreferredSRS.empty() || pszSRSLocal == nullptr)
410 5 : break;
411 0 : if (EQUAL(osPreferredSRS, pszSRSLocal))
412 0 : break;
413 0 : psSRS = nullptr;
414 0 : pszSRSLocal = nullptr;
415 : }
416 37 : psIter = psIter->psNext;
417 : }
418 :
419 5 : if (psSRS == nullptr)
420 : {
421 0 : psSRS = CPLGetXMLNode(psXML, "LatLonBoundingBox");
422 0 : pszSRSLocal = GetCRS(psSRS);
423 0 : if (pszSRSLocal == nullptr)
424 0 : pszSRSLocal = "EPSG:4326";
425 : }
426 :
427 5 : if (pszSRSLocal != nullptr && psSRS != nullptr)
428 : {
429 5 : pszMinXLocal = CPLGetXMLValue(psSRS, "minx", nullptr);
430 5 : pszMinYLocal = CPLGetXMLValue(psSRS, "miny", nullptr);
431 5 : pszMaxXLocal = CPLGetXMLValue(psSRS, "maxx", nullptr);
432 5 : pszMaxYLocal = CPLGetXMLValue(psSRS, "maxy", nullptr);
433 :
434 5 : if (pszMinXLocal && pszMinYLocal && pszMaxXLocal && pszMaxYLocal)
435 : {
436 5 : pszSRS = pszSRSLocal;
437 5 : pszMinX = pszMinXLocal;
438 5 : pszMinY = pszMinYLocal;
439 5 : pszMaxX = pszMaxXLocal;
440 5 : pszMaxY = pszMaxYLocal;
441 : }
442 : }
443 :
444 5 : if (pszName != nullptr && pszSRS && pszMinX && pszMinY && pszMaxX &&
445 : pszMaxY)
446 : {
447 10 : CPLString osLocalTransparent(osTransparent);
448 5 : if (osLocalTransparent.empty())
449 : {
450 5 : const char *pszOpaque = CPLGetXMLValue(psXML, "opaque", "0");
451 5 : if (EQUAL(pszOpaque, "1"))
452 0 : osLocalTransparent = "FALSE";
453 : }
454 :
455 10 : WMSCKeyType oWMSCKey(pszName, pszSRS);
456 : std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
457 5 : osMapWMSCTileSet.find(oWMSCKey);
458 5 : if (oIter != osMapWMSCTileSet.end())
459 : {
460 0 : AddWMSCSubDataset(oIter->second, pszTitle, osLocalTransparent);
461 : }
462 : else
463 : {
464 5 : AddSubDataset(pszName, pszTitle, pszAbstract, pszSRS, pszMinX,
465 : pszMinY, pszMaxX, pszMaxY, osFormat,
466 : osLocalTransparent);
467 : }
468 : }
469 :
470 5 : psIter = psXML->psChild;
471 59 : for (; psIter != nullptr; psIter = psIter->psNext)
472 : {
473 54 : if (psIter->eType == CXT_Element)
474 : {
475 41 : if (EQUAL(psIter->pszValue, "Layer"))
476 4 : ExploreLayer(psIter, osFormat, osTransparent, osPreferredSRS,
477 : pszSRS, pszMinX, pszMinY, pszMaxX, pszMaxY);
478 : }
479 : }
480 5 : }
481 :
482 : /************************************************************************/
483 : /* ParseWMSCTileSets() */
484 : /************************************************************************/
485 :
486 0 : void GDALWMSMetaDataset::ParseWMSCTileSets(CPLXMLNode *psXML)
487 : {
488 0 : CPLXMLNode *psIter = psXML->psChild;
489 0 : for (; psIter; psIter = psIter->psNext)
490 : {
491 0 : if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileSet"))
492 : {
493 0 : const char *pszSRS = CPLGetXMLValue(psIter, "SRS", nullptr);
494 0 : if (pszSRS == nullptr)
495 0 : continue;
496 :
497 0 : CPLXMLNode *psBoundingBox = CPLGetXMLNode(psIter, "BoundingBox");
498 0 : if (psBoundingBox == nullptr)
499 0 : continue;
500 :
501 : const char *pszMinX =
502 0 : CPLGetXMLValue(psBoundingBox, "minx", nullptr);
503 : const char *pszMinY =
504 0 : CPLGetXMLValue(psBoundingBox, "miny", nullptr);
505 : const char *pszMaxX =
506 0 : CPLGetXMLValue(psBoundingBox, "maxx", nullptr);
507 : const char *pszMaxY =
508 0 : CPLGetXMLValue(psBoundingBox, "maxy", nullptr);
509 0 : if (pszMinX == nullptr || pszMinY == nullptr ||
510 0 : pszMaxX == nullptr || pszMaxY == nullptr)
511 0 : continue;
512 :
513 0 : double dfMinX = CPLAtofM(pszMinX);
514 0 : double dfMinY = CPLAtofM(pszMinY);
515 0 : double dfMaxX = CPLAtofM(pszMaxX);
516 0 : double dfMaxY = CPLAtofM(pszMaxY);
517 0 : if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
518 0 : continue;
519 :
520 0 : const char *pszFormat = CPLGetXMLValue(psIter, "Format", nullptr);
521 0 : if (pszFormat == nullptr)
522 0 : continue;
523 0 : if (strstr(pszFormat, "kml"))
524 0 : continue;
525 :
526 0 : const char *pszTileWidth = CPLGetXMLValue(psIter, "Width", nullptr);
527 : const char *pszTileHeight =
528 0 : CPLGetXMLValue(psIter, "Height", nullptr);
529 0 : if (pszTileWidth == nullptr || pszTileHeight == nullptr)
530 0 : continue;
531 :
532 0 : int nTileWidth = atoi(pszTileWidth);
533 0 : int nTileHeight = atoi(pszTileHeight);
534 0 : if (nTileWidth < 128 || nTileHeight < 128)
535 0 : continue;
536 :
537 0 : const char *pszLayers = CPLGetXMLValue(psIter, "Layers", nullptr);
538 0 : if (pszLayers == nullptr)
539 0 : continue;
540 :
541 : const char *pszResolutions =
542 0 : CPLGetXMLValue(psIter, "Resolutions", nullptr);
543 0 : if (pszResolutions == nullptr)
544 0 : continue;
545 : char **papszTokens =
546 0 : CSLTokenizeStringComplex(pszResolutions, " ", 0, 0);
547 0 : double dfMinResolution = 0;
548 : int i;
549 0 : for (i = 0; papszTokens && papszTokens[i]; i++)
550 : {
551 0 : double dfResolution = CPLAtofM(papszTokens[i]);
552 0 : if (i == 0 || dfResolution < dfMinResolution)
553 0 : dfMinResolution = dfResolution;
554 : }
555 0 : CSLDestroy(papszTokens);
556 0 : int nResolutions = i;
557 0 : if (nResolutions == 0)
558 0 : continue;
559 :
560 0 : const char *pszStyles = CPLGetXMLValue(psIter, "Styles", "");
561 :
562 : /* http://demo.opengeo.org/geoserver/gwc/service/wms?tiled=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
563 : */
564 : /* has different variations of formats for the same (formats, SRS)
565 : * tuple, so just */
566 : /* keep the first one which is a png format */
567 0 : WMSCKeyType oWMSCKey(pszLayers, pszSRS);
568 : std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
569 0 : osMapWMSCTileSet.find(oWMSCKey);
570 0 : if (oIter != osMapWMSCTileSet.end())
571 0 : continue;
572 :
573 0 : WMSCTileSetDesc oWMSCTileSet;
574 0 : oWMSCTileSet.osLayers = pszLayers;
575 0 : oWMSCTileSet.osSRS = pszSRS;
576 0 : oWMSCTileSet.osMinX = pszMinX;
577 0 : oWMSCTileSet.osMinY = pszMinY;
578 0 : oWMSCTileSet.osMaxX = pszMaxX;
579 0 : oWMSCTileSet.osMaxY = pszMaxY;
580 0 : oWMSCTileSet.dfMinX = dfMinX;
581 0 : oWMSCTileSet.dfMinY = dfMinY;
582 0 : oWMSCTileSet.dfMaxX = dfMaxX;
583 0 : oWMSCTileSet.dfMaxY = dfMaxY;
584 0 : oWMSCTileSet.nResolutions = nResolutions;
585 0 : oWMSCTileSet.dfMinResolution = dfMinResolution;
586 0 : oWMSCTileSet.osFormat = pszFormat;
587 0 : oWMSCTileSet.osStyle = pszStyles;
588 0 : oWMSCTileSet.nTileWidth = nTileWidth;
589 0 : oWMSCTileSet.nTileHeight = nTileHeight;
590 :
591 0 : osMapWMSCTileSet[oWMSCKey] = std::move(oWMSCTileSet);
592 : }
593 : }
594 0 : }
595 :
596 : /************************************************************************/
597 : /* AnalyzeGetCapabilities() */
598 : /************************************************************************/
599 :
600 1 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetCapabilities(
601 : CPLXMLNode *psXML, const std::string &osFormat,
602 : const std::string &osTransparent, const std::string &osPreferredSRS)
603 : {
604 1 : const char *pszEncoding = nullptr;
605 1 : if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
606 1 : pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
607 :
608 1 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMT_MS_Capabilities");
609 1 : if (psRoot == nullptr)
610 1 : psRoot = CPLGetXMLNode(psXML, "=WMS_Capabilities");
611 1 : if (psRoot == nullptr)
612 0 : return nullptr;
613 1 : CPLXMLNode *psCapability = CPLGetXMLNode(psRoot, "Capability");
614 1 : if (psCapability == nullptr)
615 0 : return nullptr;
616 :
617 1 : CPLXMLNode *psOnlineResource = CPLGetXMLNode(
618 : psCapability, "Request.GetMap.DCPType.HTTP.Get.OnlineResource");
619 1 : if (psOnlineResource == nullptr)
620 0 : return nullptr;
621 : const char *pszGetURL =
622 1 : CPLGetXMLValue(psOnlineResource, "xlink:href", nullptr);
623 1 : if (pszGetURL == nullptr)
624 0 : return nullptr;
625 :
626 1 : CPLXMLNode *psLayer = CPLGetXMLNode(psCapability, "Layer");
627 1 : if (psLayer == nullptr)
628 0 : return nullptr;
629 :
630 : CPLXMLNode *psVendorSpecificCapabilities =
631 1 : CPLGetXMLNode(psCapability, "VendorSpecificCapabilities");
632 :
633 1 : GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
634 1 : const char *pszVersion = CPLGetXMLValue(psRoot, "version", nullptr);
635 1 : if (pszVersion)
636 1 : poDS->osVersion = pszVersion;
637 : else
638 0 : poDS->osVersion = "1.1.1";
639 1 : poDS->osGetURL = pszGetURL;
640 1 : poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
641 1 : if (psVendorSpecificCapabilities)
642 0 : poDS->ParseWMSCTileSets(psVendorSpecificCapabilities);
643 1 : poDS->ExploreLayer(psLayer, osFormat, osTransparent, osPreferredSRS);
644 :
645 1 : return poDS;
646 : }
647 :
648 : /************************************************************************/
649 : /* AddTiledSubDataset() */
650 : /************************************************************************/
651 :
652 : // tiledWMS only
653 4 : void GDALWMSMetaDataset::AddTiledSubDataset(const char *pszTiledGroupName,
654 : const char *pszTitle,
655 : const char *const *papszChanges)
656 : {
657 : CPLString osSubdatasetName =
658 8 : "<GDAL_WMS><Service name=\"TiledWMS\"><ServerUrl>";
659 4 : osSubdatasetName += osGetURL;
660 4 : osSubdatasetName += "</ServerUrl><TiledGroupName>";
661 4 : osSubdatasetName += pszTiledGroupName;
662 4 : osSubdatasetName += "</TiledGroupName>";
663 :
664 4 : for (int i = 0; papszChanges != nullptr && papszChanges[i] != nullptr; i++)
665 : {
666 0 : char *key = nullptr;
667 0 : const char *value = CPLParseNameValue(papszChanges[i], &key);
668 0 : if (value != nullptr && key != nullptr)
669 : osSubdatasetName +=
670 0 : CPLSPrintf("<Change key=\"${%s}\">%s</Change>", key, value);
671 0 : CPLFree(key);
672 : }
673 :
674 4 : osSubdatasetName += "</Service></GDAL_WMS>";
675 :
676 4 : if (pszTitle)
677 : {
678 4 : if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
679 0 : osXMLEncoding != "UTF-8")
680 : {
681 : char *pszRecodedTitle =
682 0 : CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
683 0 : if (pszRecodedTitle)
684 0 : AddSubDataset(osSubdatasetName, pszRecodedTitle);
685 : else
686 0 : AddSubDataset(osSubdatasetName, pszTitle);
687 0 : CPLFree(pszRecodedTitle);
688 : }
689 : else
690 : {
691 4 : AddSubDataset(osSubdatasetName, pszTitle);
692 : }
693 : }
694 : else
695 : {
696 0 : AddSubDataset(osSubdatasetName, pszTiledGroupName);
697 : }
698 4 : }
699 :
700 : /************************************************************************/
701 : /* AnalyzeGetTileServiceRecurse() */
702 : /************************************************************************/
703 : // tiledWMS only
704 2 : void GDALWMSMetaDataset::AnalyzeGetTileServiceRecurse(CPLXMLNode *psXML,
705 : GDALOpenInfo *poOpenInfo)
706 : {
707 : // Only list tiled groups that contain the string in the open option
708 : // TiledGroupName, if given
709 2 : char **papszLocalOpenOptions =
710 2 : poOpenInfo ? poOpenInfo->papszOpenOptions : nullptr;
711 : CPLString osMatch(
712 4 : CSLFetchNameValueDef(papszLocalOpenOptions, "TiledGroupName", ""));
713 2 : osMatch.toupper();
714 : // Also pass the change patterns, if provided
715 : char **papszChanges =
716 2 : CSLFetchNameValueMultiple(papszLocalOpenOptions, "Change");
717 :
718 2 : CPLXMLNode *psIter = psXML->psChild;
719 10 : for (; psIter != nullptr; psIter = psIter->psNext)
720 : {
721 8 : if (psIter->eType == CXT_Element &&
722 8 : EQUAL(psIter->pszValue, "TiledGroup"))
723 : {
724 4 : const char *pszName = CPLGetXMLValue(psIter, "Name", nullptr);
725 4 : if (pszName)
726 : {
727 4 : const char *pszTitle = CPLGetXMLValue(psIter, "Title", nullptr);
728 4 : if (osMatch.empty())
729 : {
730 4 : AddTiledSubDataset(pszName, pszTitle, papszChanges);
731 : }
732 : else
733 : {
734 0 : CPLString osNameUpper(pszName);
735 0 : osNameUpper.toupper();
736 0 : if (std::string::npos != osNameUpper.find(osMatch))
737 0 : AddTiledSubDataset(pszName, pszTitle, papszChanges);
738 : }
739 4 : }
740 : }
741 4 : else if (psIter->eType == CXT_Element &&
742 4 : EQUAL(psIter->pszValue, "TiledGroups"))
743 : {
744 0 : AnalyzeGetTileServiceRecurse(psIter, poOpenInfo);
745 : }
746 : }
747 2 : CPLFree(papszChanges);
748 2 : }
749 :
750 : /************************************************************************/
751 : /* AnalyzeGetTileService() */
752 : /************************************************************************/
753 : // tiledWMS only
754 2 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetTileService(CPLXMLNode *psXML,
755 : GDALOpenInfo *poOpenInfo)
756 : {
757 2 : const char *pszEncoding = nullptr;
758 2 : if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
759 0 : pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
760 :
761 2 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMS_Tile_Service");
762 2 : if (psRoot == nullptr)
763 0 : return nullptr;
764 2 : CPLXMLNode *psTiledPatterns = CPLGetXMLNode(psRoot, "TiledPatterns");
765 2 : if (psTiledPatterns == nullptr)
766 0 : return nullptr;
767 :
768 : const char *pszURL =
769 2 : CPLGetXMLValue(psTiledPatterns, "OnlineResource.xlink:href", nullptr);
770 2 : if (pszURL == nullptr)
771 0 : return nullptr;
772 :
773 2 : GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
774 2 : poDS->osGetURL = pszURL;
775 2 : poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
776 :
777 2 : poDS->AnalyzeGetTileServiceRecurse(psTiledPatterns, poOpenInfo);
778 :
779 2 : return poDS;
780 : }
781 :
782 : /************************************************************************/
783 : /* AnalyzeTileMapService() */
784 : /************************************************************************/
785 :
786 0 : GDALDataset *GDALWMSMetaDataset::AnalyzeTileMapService(CPLXMLNode *psXML)
787 : {
788 0 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TileMapService");
789 0 : if (psRoot == nullptr)
790 0 : return nullptr;
791 0 : CPLXMLNode *psTileMaps = CPLGetXMLNode(psRoot, "TileMaps");
792 0 : if (psTileMaps == nullptr)
793 0 : return nullptr;
794 :
795 0 : GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
796 :
797 0 : CPLXMLNode *psIter = psTileMaps->psChild;
798 0 : for (; psIter != nullptr; psIter = psIter->psNext)
799 : {
800 0 : if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileMap"))
801 : {
802 0 : const char *pszHref = CPLGetXMLValue(psIter, "href", nullptr);
803 0 : const char *pszTitle = CPLGetXMLValue(psIter, "title", nullptr);
804 0 : if (pszHref && pszTitle)
805 : {
806 0 : CPLString osHref(pszHref);
807 0 : const char *pszDup100 = strstr(pszHref, "1.0.0/1.0.0/");
808 0 : if (pszDup100)
809 : {
810 0 : osHref.resize(pszDup100 - pszHref);
811 0 : osHref += pszDup100 + strlen("1.0.0/");
812 : }
813 0 : poDS->AddSubDataset(osHref, pszTitle);
814 : }
815 : }
816 : }
817 :
818 0 : return poDS;
819 : }
|