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