1 : /******************************************************************************
2 : * $Id: wmsdriver.cpp 22576 2011-06-24 13:14:21Z warmerdam $
3 : *
4 : * Project: WMS Client Driver
5 : * Purpose: Implementation of Dataset and RasterBand classes for WMS
6 : * and other similar services.
7 : * Author: Adam Nowacki, nowak@xpam.de
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2007, Adam Nowacki
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "stdinc.h"
32 : #include "wmsmetadataset.h"
33 :
34 : /************************************************************************/
35 : /* GDALWMSDatasetGetConfigFromURL() */
36 : /************************************************************************/
37 :
38 : static
39 2 : CPLXMLNode * GDALWMSDatasetGetConfigFromURL(GDALOpenInfo *poOpenInfo)
40 : {
41 2 : const char* pszBaseURL = poOpenInfo->pszFilename;
42 2 : if (EQUALN(pszBaseURL, "WMS:", 4))
43 2 : pszBaseURL += 4;
44 :
45 2 : CPLString osLayer = CPLURLGetValue(pszBaseURL, "LAYERS");
46 2 : CPLString osVersion = CPLURLGetValue(pszBaseURL, "VERSION");
47 2 : CPLString osSRS = CPLURLGetValue(pszBaseURL, "SRS");
48 2 : CPLString osCRS = CPLURLGetValue(pszBaseURL, "CRS");
49 2 : CPLString osBBOX = CPLURLGetValue(pszBaseURL, "BBOX");
50 2 : CPLString osFormat = CPLURLGetValue(pszBaseURL, "FORMAT");
51 2 : CPLString osTransparent = CPLURLGetValue(pszBaseURL, "TRANSPARENT");
52 :
53 : /* GDAL specific extensions to alter the default settings */
54 2 : CPLString osOverviewCount = CPLURLGetValue(pszBaseURL, "OVERVIEWCOUNT");
55 2 : CPLString osTileSize = CPLURLGetValue(pszBaseURL, "TILESIZE");
56 2 : CPLString osMinResolution = CPLURLGetValue(pszBaseURL, "MINRESOLUTION");
57 2 : CPLString osBBOXOrder = CPLURLGetValue(pszBaseURL, "BBOXORDER");
58 :
59 2 : CPLString osBaseURL = pszBaseURL;
60 : /* Remove all keywords to get base URL */
61 :
62 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "VERSION", NULL);
63 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "REQUEST", NULL);
64 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "LAYERS", NULL);
65 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "SRS", NULL);
66 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "CRS", NULL);
67 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "BBOX", NULL);
68 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "FORMAT", NULL);
69 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "TRANSPARENT", NULL);
70 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "STYLES", NULL);
71 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "WIDTH", NULL);
72 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "HEIGHT", NULL);
73 :
74 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "OVERVIEWCOUNT", NULL);
75 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "TILESIZE", NULL);
76 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "MINRESOLUTION", NULL);
77 2 : osBaseURL = CPLURLAddKVP(osBaseURL, "BBOXORDER", NULL);
78 :
79 2 : if (osBaseURL.size() > 0 && osBaseURL[osBaseURL.size() - 1] == '&')
80 1 : osBaseURL.resize(osBaseURL.size() - 1);
81 :
82 2 : if (osVersion.size() == 0)
83 0 : osVersion = "1.1.1";
84 :
85 2 : CPLString osSRSTag;
86 2 : CPLString osSRSValue;
87 2 : if(VersionStringToInt(osVersion.c_str())>= VersionStringToInt("1.3.0"))
88 : {
89 0 : if (osSRS.size())
90 : {
91 : CPLError(CE_Warning, CPLE_AppDefined,
92 0 : "WMS version 1.3 and above expects CRS however SRS was set instead.");
93 : }
94 0 : osSRSValue = osCRS;
95 0 : osSRSTag = "CRS";
96 : }
97 : else
98 : {
99 2 : if (osCRS.size())
100 : {
101 : CPLError(CE_Warning, CPLE_AppDefined,
102 0 : "WMS version 1.1.1 and below expects SRS however CRS was set instead.");
103 : }
104 2 : osSRSValue = osSRS;
105 2 : osSRSTag = "SRS";
106 : }
107 :
108 2 : if (osSRSValue.size() == 0)
109 0 : osSRSValue = "EPSG:4326";
110 :
111 2 : if (osBBOX.size() == 0)
112 : {
113 0 : if (osBBOXOrder.compare("yxYX") == 0)
114 0 : osBBOX = "-90,-180,90,180";
115 : else
116 0 : osBBOX = "-180,-90,180,90";
117 : }
118 :
119 2 : char** papszTokens = CSLTokenizeStringComplex(osBBOX, ",", 0, 0);
120 2 : if (CSLCount(papszTokens) != 4)
121 : {
122 0 : CSLDestroy(papszTokens);
123 0 : return NULL;
124 : }
125 2 : const char* pszMinX = papszTokens[0];
126 2 : const char* pszMinY = papszTokens[1];
127 2 : const char* pszMaxX = papszTokens[2];
128 2 : const char* pszMaxY = papszTokens[3];
129 :
130 2 : if (osBBOXOrder.compare("yxYX") == 0)
131 : {
132 0 : std::swap(pszMinX, pszMinY);
133 0 : std::swap(pszMaxX, pszMaxY);
134 : }
135 :
136 2 : double dfMinX = CPLAtofM(pszMinX);
137 2 : double dfMinY = CPLAtofM(pszMinY);
138 2 : double dfMaxX = CPLAtofM(pszMaxX);
139 2 : double dfMaxY = CPLAtofM(pszMaxY);
140 :
141 2 : if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
142 : {
143 0 : CSLDestroy(papszTokens);
144 0 : return NULL;
145 : }
146 :
147 2 : int nTileSize = atoi(osTileSize);
148 2 : if (nTileSize <= 128 || nTileSize > 2048)
149 1 : nTileSize = 1024;
150 :
151 : int nXSize, nYSize;
152 :
153 2 : int nOverviewCount = (osOverviewCount.size()) ? atoi(osOverviewCount) : 20;
154 :
155 2 : if (osMinResolution.size() != 0)
156 : {
157 1 : double dfMinResolution = CPLAtofM(osMinResolution);
158 :
159 7 : while (nOverviewCount > 20)
160 : {
161 5 : nOverviewCount --;
162 5 : dfMinResolution *= 2;
163 : }
164 :
165 1 : nXSize = (int) ((dfMaxX - dfMinX) / dfMinResolution + 0.5);
166 1 : nYSize = (int) ((dfMaxY - dfMinY) / dfMinResolution + 0.5);
167 : }
168 : else
169 : {
170 1 : double dfRatio = (dfMaxX - dfMinX) / (dfMaxY - dfMinY);
171 1 : if (dfRatio > 1)
172 : {
173 1 : nXSize = nTileSize;
174 1 : nYSize = (int) (nXSize / dfRatio);
175 : }
176 : else
177 : {
178 0 : nYSize = nTileSize;
179 0 : nXSize = (int) (nYSize * dfRatio);
180 : }
181 :
182 1 : if (nOverviewCount < 0 || nOverviewCount > 20)
183 0 : nOverviewCount = 20;
184 :
185 1 : nXSize = nXSize * (1 << nOverviewCount);
186 1 : nYSize = nYSize * (1 << nOverviewCount);
187 : }
188 :
189 2 : int bTransparent = osTransparent.size() ? CSLTestBoolean(osTransparent) : FALSE;
190 :
191 2 : if (osFormat.size() == 0)
192 : {
193 1 : if (!bTransparent)
194 : {
195 1 : osFormat = "image/jpeg";
196 : }
197 : else
198 : {
199 0 : osFormat = "image/png";
200 : }
201 : }
202 :
203 2 : char* pszEscapedURL = CPLEscapeString(osBaseURL.c_str(), -1, CPLES_XML);
204 2 : char* pszEscapedLayerURL = CPLEscapeString(osLayer.c_str(), -1, CPLES_URL);
205 2 : char* pszEscapedLayerXML = CPLEscapeString(pszEscapedLayerURL, -1, CPLES_XML);
206 :
207 : CPLString osXML = CPLSPrintf(
208 : "<GDAL_WMS>\n"
209 : " <Service name=\"WMS\">\n"
210 : " <Version>%s</Version>\n"
211 : " <ServerUrl>%s</ServerUrl>\n"
212 : " <Layers>%s</Layers>\n"
213 : " <%s>%s</%s>\n"
214 : " <ImageFormat>%s</ImageFormat>\n"
215 : " <Transparent>%s</Transparent>\n"
216 : " <BBoxOrder>%s</BBoxOrder>\n"
217 : " </Service>\n"
218 : " <DataWindow>\n"
219 : " <UpperLeftX>%s</UpperLeftX>\n"
220 : " <UpperLeftY>%s</UpperLeftY>\n"
221 : " <LowerRightX>%s</LowerRightX>\n"
222 : " <LowerRightY>%s</LowerRightY>\n"
223 : " <SizeX>%d</SizeX>\n"
224 : " <SizeY>%d</SizeY>\n"
225 : " </DataWindow>\n"
226 : " <BandsCount>%d</BandsCount>\n"
227 : " <BlockSizeX>%d</BlockSizeX>\n"
228 : " <BlockSizeY>%d</BlockSizeY>\n"
229 : " <OverviewCount>%d</OverviewCount>\n"
230 : "</GDAL_WMS>\n",
231 : osVersion.c_str(),
232 : pszEscapedURL,
233 : pszEscapedLayerXML,
234 : osSRSTag.c_str(),
235 : osSRSValue.c_str(),
236 : osSRSTag.c_str(),
237 : osFormat.c_str(),
238 : (bTransparent) ? "TRUE" : "FALSE",
239 : (osBBOXOrder.size()) ? osBBOXOrder.c_str() : "xyXY",
240 : pszMinX, pszMaxY, pszMaxX, pszMinY,
241 : nXSize, nYSize,
242 : (bTransparent) ? 4 : 3,
243 : nTileSize, nTileSize,
244 2 : nOverviewCount);
245 :
246 2 : CPLFree(pszEscapedURL);
247 2 : CPLFree(pszEscapedLayerURL);
248 2 : CPLFree(pszEscapedLayerXML);
249 :
250 2 : CSLDestroy(papszTokens);
251 :
252 2 : CPLDebug("WMS", "Opening WMS :\n%s", osXML.c_str());
253 :
254 2 : return CPLParseXMLString(osXML);
255 : }
256 :
257 : /************************************************************************/
258 : /* GDALWMSDatasetGetConfigFromTileMap() */
259 : /************************************************************************/
260 :
261 : static
262 0 : CPLXMLNode * GDALWMSDatasetGetConfigFromTileMap(CPLXMLNode* psXML)
263 : {
264 0 : CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TileMap" );
265 0 : if (psRoot == NULL)
266 0 : return NULL;
267 :
268 0 : CPLXMLNode* psTileSets = CPLGetXMLNode(psRoot, "TileSets");
269 0 : if (psTileSets == NULL)
270 0 : return NULL;
271 :
272 0 : const char* pszURL = CPLGetXMLValue(psRoot, "tilemapservice", NULL);
273 :
274 0 : int bCanChangeURL = TRUE;
275 :
276 0 : CPLString osURL;
277 0 : if (pszURL)
278 : {
279 0 : osURL = pszURL;
280 : /* Special hack for http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/basic/ */
281 0 : if (strlen(pszURL) > 10 &&
282 : strncmp(pszURL, "http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/",
283 : strlen("http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/")) == 0 &&
284 : strcmp(pszURL + strlen(pszURL) - strlen("1.0.0/"), "1.0.0/") == 0)
285 : {
286 0 : osURL.resize(strlen(pszURL) - strlen("1.0.0/"));
287 0 : bCanChangeURL = FALSE;
288 : }
289 0 : osURL += "${z}/${x}/${y}.${format}";
290 : }
291 :
292 0 : const char* pszSRS = CPLGetXMLValue(psRoot, "SRS", NULL);
293 0 : if (pszSRS == NULL)
294 0 : return NULL;
295 :
296 0 : CPLXMLNode* psBoundingBox = CPLGetXMLNode( psRoot, "BoundingBox" );
297 0 : if (psBoundingBox == NULL)
298 0 : return NULL;
299 :
300 0 : const char* pszMinX = CPLGetXMLValue(psBoundingBox, "minx", NULL);
301 0 : const char* pszMinY = CPLGetXMLValue(psBoundingBox, "miny", NULL);
302 0 : const char* pszMaxX = CPLGetXMLValue(psBoundingBox, "maxx", NULL);
303 0 : const char* pszMaxY = CPLGetXMLValue(psBoundingBox, "maxy", NULL);
304 0 : if (pszMinX == NULL || pszMinY == NULL || pszMaxX == NULL || pszMaxY == NULL)
305 0 : return NULL;
306 :
307 0 : double dfMinX = CPLAtofM(pszMinX);
308 0 : double dfMinY = CPLAtofM(pszMinY);
309 0 : double dfMaxX = CPLAtofM(pszMaxX);
310 0 : double dfMaxY = CPLAtofM(pszMaxY);
311 0 : if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
312 0 : return NULL;
313 :
314 0 : CPLXMLNode* psTileFormat = CPLGetXMLNode( psRoot, "TileFormat" );
315 0 : if (psTileFormat == NULL)
316 0 : return NULL;
317 :
318 0 : const char* pszTileWidth = CPLGetXMLValue(psTileFormat, "width", NULL);
319 0 : const char* pszTileHeight = CPLGetXMLValue(psTileFormat, "height", NULL);
320 0 : const char* pszTileFormat = CPLGetXMLValue(psTileFormat, "extension", NULL);
321 0 : if (pszTileWidth == NULL || pszTileHeight == NULL || pszTileFormat == NULL)
322 0 : return NULL;
323 :
324 0 : int nTileWidth = atoi(pszTileWidth);
325 0 : int nTileHeight = atoi(pszTileHeight);
326 0 : if (nTileWidth < 128 || nTileHeight < 128)
327 0 : return NULL;
328 :
329 0 : CPLXMLNode* psIter = psTileSets->psChild;
330 0 : int nLevelCount = 0;
331 0 : double dfPixelSize = 0;
332 0 : for(; psIter != NULL; psIter = psIter->psNext)
333 : {
334 0 : if (psIter->eType == CXT_Element &&
335 : EQUAL(psIter->pszValue, "TileSet"))
336 : {
337 : const char* pszOrder =
338 0 : CPLGetXMLValue(psIter, "order", NULL);
339 0 : if (pszOrder == NULL)
340 : {
341 0 : CPLDebug("WMS", "Cannot find order attribute");
342 0 : return NULL;
343 : }
344 0 : if (atoi(pszOrder) != nLevelCount)
345 : {
346 0 : CPLDebug("WMS", "Expected order=%d, got %s", nLevelCount, pszOrder);
347 0 : return NULL;
348 : }
349 :
350 : const char* pszHref =
351 0 : CPLGetXMLValue(psIter, "href", NULL);
352 0 : if (nLevelCount == 0 && pszHref != NULL)
353 : {
354 0 : if (bCanChangeURL && strlen(pszHref) > 10 &&
355 : strcmp(pszHref + strlen(pszHref) - strlen("/0"), "/0") == 0)
356 : {
357 0 : osURL = pszHref;
358 0 : osURL.resize(strlen(pszHref) - strlen("/0"));
359 0 : osURL += "/${z}/${x}/${y}.${format}";
360 : }
361 : }
362 : const char* pszUnitsPerPixel =
363 0 : CPLGetXMLValue(psIter, "units-per-pixel", NULL);
364 0 : if (pszUnitsPerPixel == NULL)
365 0 : return NULL;
366 0 : dfPixelSize = CPLAtofM(pszUnitsPerPixel);
367 :
368 0 : nLevelCount++;
369 : }
370 : }
371 :
372 0 : if (nLevelCount == 0 || osURL.size() == 0)
373 0 : return NULL;
374 :
375 0 : int nXSize = 0;
376 0 : int nYSize = 0;
377 :
378 0 : while(nLevelCount > 0)
379 : {
380 0 : GIntBig nXSizeBig = (GIntBig)((dfMaxX - dfMinX) / dfPixelSize + 0.5);
381 0 : GIntBig nYSizeBig = (GIntBig)((dfMaxY - dfMinY) / dfPixelSize + 0.5);
382 0 : if (nXSizeBig < INT_MAX && nYSizeBig < INT_MAX)
383 : {
384 0 : nXSize = (int)nXSizeBig;
385 0 : nYSize = (int)nYSizeBig;
386 0 : break;
387 : }
388 0 : CPLDebug("WMS", "Dropping one overview level so raster size fits into 32bit...");
389 0 : dfPixelSize *= 2;
390 0 : nLevelCount --;
391 : }
392 :
393 0 : char* pszEscapedURL = CPLEscapeString(osURL.c_str(), -1, CPLES_XML);
394 :
395 : CPLString osXML = CPLSPrintf(
396 : "<GDAL_WMS>\n"
397 : " <Service name=\"TMS\">\n"
398 : " <ServerUrl>%s</ServerUrl>\n"
399 : " <Format>%s</Format>\n"
400 : " </Service>\n"
401 : " <DataWindow>\n"
402 : " <UpperLeftX>%s</UpperLeftX>\n"
403 : " <UpperLeftY>%s</UpperLeftY>\n"
404 : " <LowerRightX>%s</LowerRightX>\n"
405 : " <LowerRightY>%s</LowerRightY>\n"
406 : " <TileLevel>%d</TileLevel>\n"
407 : " <SizeX>%d</SizeX>\n"
408 : " <SizeY>%d</SizeY>\n"
409 : " </DataWindow>\n"
410 : " <Projection>%s</Projection>\n"
411 : " <BlockSizeX>%d</BlockSizeX>\n"
412 : " <BlockSizeY>%d</BlockSizeY>\n"
413 : " <BandsCount>%d</BandsCount>\n"
414 : "</GDAL_WMS>\n",
415 : pszEscapedURL,
416 : pszTileFormat,
417 : pszMinX, pszMaxY, pszMaxX, pszMinY,
418 : nLevelCount - 1,
419 : nXSize, nYSize,
420 : pszSRS,
421 0 : nTileWidth, nTileHeight, 3);
422 0 : CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str());
423 :
424 0 : CPLFree(pszEscapedURL);
425 :
426 0 : return CPLParseXMLString(osXML);
427 : }
428 :
429 : /************************************************************************/
430 : /* GDALWMSDatasetGetConfigFromArcGISJSON() */
431 : /************************************************************************/
432 :
433 1 : static CPLXMLNode* GDALWMSDatasetGetConfigFromArcGISJSON(const char* pszURL,
434 : const char* pszContent)
435 : {
436 : /* TODO : use JSONC library to parse. But we don't really need it */
437 1 : CPLString osTmpFilename(CPLSPrintf("/vsimem/WMSArcGISJSON%p", pszURL));
438 : VSILFILE* fp = VSIFileFromMemBuffer( osTmpFilename,
439 : (GByte*)pszContent,
440 : strlen(pszContent),
441 1 : FALSE);
442 : const char* pszLine;
443 1 : int nTileWidth = -1, nTileHeight = -1;
444 1 : int nWKID = -1;
445 1 : double dfMinX = 0, dfMaxY = 0;
446 1 : int bHasMinX = FALSE, bHasMaxY = FALSE;
447 1 : int nExpectedLevel = 0;
448 1 : double dfBaseResolution = 0;
449 120 : while((pszLine = CPLReadLine2L(fp, 4096, NULL)) != NULL)
450 : {
451 : const char* pszPtr;
452 118 : if ((pszPtr = strstr(pszLine, "\"rows\" : ")) != NULL)
453 1 : nTileHeight = atoi(pszPtr + strlen("\"rows\" : "));
454 117 : else if ((pszPtr = strstr(pszLine, "\"cols\" : ")) != NULL)
455 1 : nTileWidth = atoi(pszPtr + strlen("\"cols\" : "));
456 116 : else if ((pszPtr = strstr(pszLine, "\"wkid\" : ")) != NULL)
457 : {
458 4 : int nVal = atoi(pszPtr + strlen("\"wkid\" : "));
459 4 : if (nWKID < 0)
460 1 : nWKID = nVal;
461 3 : else if (nWKID != nVal)
462 : {
463 0 : CPLDebug("WMS", "Inconsisant WKID values : %d, %d", nVal, nWKID);
464 0 : VSIFCloseL(fp);
465 0 : return NULL;
466 : }
467 : }
468 112 : else if ((pszPtr = strstr(pszLine, "\"x\" : ")) != NULL)
469 : {
470 1 : bHasMinX = TRUE;
471 1 : dfMinX = CPLAtofM(pszPtr + strlen("\"x\" : "));
472 : }
473 111 : else if ((pszPtr = strstr(pszLine, "\"y\" : ")) != NULL)
474 : {
475 1 : bHasMaxY = TRUE;
476 1 : dfMaxY = CPLAtofM(pszPtr + strlen("\"y\" : "));
477 : }
478 110 : else if ((pszPtr = strstr(pszLine, "\"level\" : ")) != NULL)
479 : {
480 20 : int nLevel = atoi(pszPtr + strlen("\"level\" : "));
481 20 : if (nLevel != nExpectedLevel)
482 : {
483 0 : CPLDebug("WMS", "Expected level : %d, got : %d", nExpectedLevel, nLevel);
484 0 : VSIFCloseL(fp);
485 0 : return NULL;
486 : }
487 :
488 20 : if ((pszPtr = strstr(pszLine, "\"resolution\" : ")) != NULL)
489 : {
490 20 : double dfResolution = CPLAtofM(pszPtr + strlen("\"resolution\" : "));
491 20 : if (nLevel == 0)
492 1 : dfBaseResolution = dfResolution;
493 : }
494 : else
495 : {
496 0 : CPLDebug("WMS", "Did not get resolution");
497 0 : VSIFCloseL(fp);
498 0 : return NULL;
499 : }
500 20 : nExpectedLevel ++;
501 : }
502 : }
503 1 : VSIFCloseL(fp);
504 :
505 1 : int nLevelCount = nExpectedLevel - 1;
506 1 : if (nLevelCount < 1)
507 : {
508 0 : CPLDebug("WMS", "Did not get levels");
509 0 : return NULL;
510 : }
511 :
512 1 : if (nTileWidth <= 0)
513 : {
514 0 : CPLDebug("WMS", "Did not get tile width");
515 0 : return NULL;
516 : }
517 1 : if (nTileHeight <= 0)
518 : {
519 0 : CPLDebug("WMS", "Did not get tile height");
520 0 : return NULL;
521 : }
522 1 : if (nWKID <= 0)
523 : {
524 0 : CPLDebug("WMS", "Did not get WKID");
525 0 : return NULL;
526 : }
527 1 : if (!bHasMinX)
528 : {
529 0 : CPLDebug("WMS", "Did not get min x");
530 0 : return NULL;
531 : }
532 1 : if (!bHasMaxY)
533 : {
534 0 : CPLDebug("WMS", "Did not get max y");
535 0 : return NULL;
536 : }
537 :
538 1 : if (nWKID == 102100)
539 1 : nWKID = 3857;
540 :
541 1 : const char* pszEndURL = strstr(pszURL, "/MapServer?f=json");
542 1 : CPLAssert(pszEndURL);
543 1 : CPLString osURL(pszURL);
544 1 : osURL.resize(pszEndURL - pszURL);
545 :
546 1 : double dfMaxX = dfMinX + dfBaseResolution * nTileWidth;
547 1 : double dfMinY = dfMaxY - dfBaseResolution * nTileHeight;
548 :
549 1 : int nTileCountX = 1;
550 1 : if (fabs(dfMinX - -180) < 1e-4 && fabs(dfMaxY - 90) < 1e-4 &&
551 : fabs(dfMinY - -90) < 1e-4)
552 : {
553 0 : nTileCountX = 2;
554 0 : dfMaxX = 180;
555 : }
556 :
557 : CPLString osXML = CPLSPrintf(
558 : "<GDAL_WMS>\n"
559 : " <Service name=\"TMS\">\n"
560 : " <ServerUrl>%s/MapServer/tile/${z}/${y}/${x}</ServerUrl>\n"
561 : " </Service>\n"
562 : " <DataWindow>\n"
563 : " <UpperLeftX>%.8f</UpperLeftX>\n"
564 : " <UpperLeftY>%.8f</UpperLeftY>\n"
565 : " <LowerRightX>%.8f</LowerRightX>\n"
566 : " <LowerRightY>%.8f</LowerRightY>\n"
567 : " <TileLevel>%d</TileLevel>\n"
568 : " <TileCountX>%d</TileCountX>\n"
569 : " <YOrigin>top</YOrigin>\n"
570 : " </DataWindow>\n"
571 : " <Projection>EPSG:%d</Projection>\n"
572 : " <BlockSizeX>%d</BlockSizeX>\n"
573 : " <BlockSizeY>%d</BlockSizeY>\n"
574 : " <Cache/>\n"
575 : "</GDAL_WMS>\n",
576 : osURL.c_str(),
577 : dfMinX, dfMaxY, dfMaxX, dfMinY,
578 : nLevelCount,
579 : nTileCountX,
580 : nWKID,
581 1 : nTileWidth, nTileHeight);
582 1 : CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str());
583 :
584 1 : return CPLParseXMLString(osXML);
585 : }
586 :
587 : /************************************************************************/
588 : /* Identify() */
589 : /************************************************************************/
590 :
591 9245 : int GDALWMSDataset::Identify(GDALOpenInfo *poOpenInfo)
592 : {
593 9245 : const char* pszFilename = poOpenInfo->pszFilename;
594 9245 : const char* pabyHeader = (const char *) poOpenInfo->pabyHeader;
595 9245 : if (poOpenInfo->nHeaderBytes == 0 &&
596 : EQUALN(pszFilename, "<GDAL_WMS>", 10))
597 : {
598 0 : return TRUE;
599 : }
600 9245 : else if (poOpenInfo->nHeaderBytes >= 10 &&
601 : EQUALN(pabyHeader, "<GDAL_WMS>", 10))
602 : {
603 0 : return TRUE;
604 : }
605 9245 : else if (poOpenInfo->nHeaderBytes == 0 &&
606 : (EQUALN(pszFilename, "WMS:", 4) ||
607 : CPLString(pszFilename).ifind("SERVICE=WMS") != std::string::npos) )
608 : {
609 0 : return TRUE;
610 : }
611 9245 : else if (poOpenInfo->nHeaderBytes != 0 &&
612 : (strstr(pabyHeader, "<WMT_MS_Capabilities") != NULL ||
613 : strstr(pabyHeader, "<WMS_Capabilities") != NULL ||
614 : strstr(pabyHeader, "<!DOCTYPE WMT_MS_Capabilities") != NULL))
615 : {
616 0 : return TRUE;
617 : }
618 9245 : else if (poOpenInfo->nHeaderBytes != 0 &&
619 : strstr(pabyHeader, "<WMS_Tile_Service") != NULL)
620 : {
621 0 : return TRUE;
622 : }
623 9245 : else if (poOpenInfo->nHeaderBytes != 0 &&
624 : strstr(pabyHeader, "<TileMap version=\"1.0.0\"") != NULL)
625 : {
626 0 : return TRUE;
627 : }
628 9245 : else if (poOpenInfo->nHeaderBytes != 0 &&
629 : strstr(pabyHeader, "<Services") != NULL &&
630 : strstr(pabyHeader, "<TileMapService version=\"1.0") != NULL)
631 : {
632 0 : return TRUE;
633 : }
634 9245 : else if (poOpenInfo->nHeaderBytes != 0 &&
635 : strstr(pabyHeader, "<TileMapService version=\"1.0.0\"") != NULL)
636 : {
637 0 : return TRUE;
638 : }
639 9245 : else if (poOpenInfo->nHeaderBytes == 0 &&
640 : EQUALN(pszFilename, "http", 4) &&
641 : strstr(pszFilename, "/MapServer?f=json") != NULL)
642 : {
643 0 : return TRUE;
644 : }
645 : else
646 9245 : return FALSE;
647 : }
648 :
649 : /************************************************************************/
650 : /* Open() */
651 : /************************************************************************/
652 :
653 2130 : GDALDataset *GDALWMSDataset::Open(GDALOpenInfo *poOpenInfo)
654 : {
655 2130 : CPLXMLNode *config = NULL;
656 2130 : CPLErr ret = CE_None;
657 :
658 2130 : const char* pszFilename = poOpenInfo->pszFilename;
659 2130 : const char* pabyHeader = (const char *) poOpenInfo->pabyHeader;
660 :
661 2135 : if (poOpenInfo->nHeaderBytes == 0 &&
662 : EQUALN(pszFilename, "<GDAL_WMS>", 10))
663 : {
664 5 : config = CPLParseXMLString(pszFilename);
665 : }
666 2126 : else if (poOpenInfo->nHeaderBytes >= 10 &&
667 : EQUALN(pabyHeader, "<GDAL_WMS>", 10))
668 : {
669 1 : config = CPLParseXMLFile(pszFilename);
670 : }
671 2125 : else if (poOpenInfo->nHeaderBytes == 0 &&
672 : (EQUALN(pszFilename, "WMS:http", 8) ||
673 : EQUALN(pszFilename, "http", 4)) &&
674 : strstr(pszFilename, "/MapServer?f=json") != NULL)
675 : {
676 1 : if (EQUALN(pszFilename, "WMS:http", 8))
677 0 : pszFilename += 4;
678 1 : CPLString osURL(pszFilename);
679 1 : if (strstr(pszFilename, "&pretty=true") == NULL)
680 0 : osURL += "&pretty=true";
681 1 : CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), NULL);
682 1 : if (psResult == NULL)
683 0 : return NULL;
684 1 : if (psResult->pabyData == NULL)
685 : {
686 0 : CPLHTTPDestroyResult(psResult);
687 0 : return NULL;
688 : }
689 : config = GDALWMSDatasetGetConfigFromArcGISJSON(osURL,
690 1 : (const char*)psResult->pabyData);
691 1 : CPLHTTPDestroyResult(psResult);
692 : }
693 :
694 2123 : else if (poOpenInfo->nHeaderBytes == 0 &&
695 : (EQUALN(pszFilename, "WMS:", 4) ||
696 : CPLString(pszFilename).ifind("SERVICE=WMS") != std::string::npos))
697 : {
698 4 : CPLString osLayers = CPLURLGetValue(pszFilename, "LAYERS");
699 4 : CPLString osRequest = CPLURLGetValue(pszFilename, "REQUEST");
700 4 : if (osLayers.size() != 0)
701 2 : config = GDALWMSDatasetGetConfigFromURL(poOpenInfo);
702 2 : else if (EQUAL(osRequest, "GetTileService"))
703 1 : return GDALWMSMetaDataset::DownloadGetTileService(poOpenInfo);
704 : else
705 1 : return GDALWMSMetaDataset::DownloadGetCapabilities(poOpenInfo);
706 : }
707 2119 : else if (poOpenInfo->nHeaderBytes != 0 &&
708 : (strstr(pabyHeader, "<WMT_MS_Capabilities") != NULL ||
709 : strstr(pabyHeader, "<WMS_Capabilities") != NULL ||
710 : strstr(pabyHeader, "<!DOCTYPE WMT_MS_Capabilities") != NULL))
711 : {
712 0 : CPLXMLNode* psXML = CPLParseXMLFile(pszFilename);
713 0 : if (psXML == NULL)
714 0 : return NULL;
715 0 : GDALDataset* poRet = GDALWMSMetaDataset::AnalyzeGetCapabilities(psXML);
716 0 : CPLDestroyXMLNode( psXML );
717 0 : return poRet;
718 : }
719 2119 : else if (poOpenInfo->nHeaderBytes != 0 &&
720 : strstr(pabyHeader, "<WMS_Tile_Service") != NULL)
721 : {
722 0 : CPLXMLNode* psXML = CPLParseXMLFile(pszFilename);
723 0 : if (psXML == NULL)
724 0 : return NULL;
725 0 : GDALDataset* poRet = GDALWMSMetaDataset::AnalyzeGetTileService(psXML);
726 0 : CPLDestroyXMLNode( psXML );
727 0 : return poRet;
728 : }
729 2119 : else if (poOpenInfo->nHeaderBytes != 0 &&
730 : strstr(pabyHeader, "<TileMap version=\"1.0.0\"") != NULL)
731 : {
732 0 : CPLXMLNode* psXML = CPLParseXMLFile(pszFilename);
733 0 : if (psXML == NULL)
734 0 : return NULL;
735 0 : config = GDALWMSDatasetGetConfigFromTileMap(psXML);
736 0 : CPLDestroyXMLNode( psXML );
737 : }
738 2119 : else if (poOpenInfo->nHeaderBytes != 0 &&
739 : strstr(pabyHeader, "<Services") != NULL &&
740 : strstr(pabyHeader, "<TileMapService version=\"1.0") != NULL)
741 : {
742 0 : CPLXMLNode* psXML = CPLParseXMLFile(pszFilename);
743 0 : if (psXML == NULL)
744 0 : return NULL;
745 0 : CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=Services" );
746 0 : GDALDataset* poRet = NULL;
747 0 : if (psRoot)
748 : {
749 0 : CPLXMLNode* psTileMapService = CPLGetXMLNode(psRoot, "TileMapService");
750 0 : if (psTileMapService)
751 : {
752 0 : const char* pszHref = CPLGetXMLValue(psTileMapService, "href", NULL);
753 0 : if (pszHref)
754 : {
755 0 : poRet = (GDALDataset*) GDALOpen(pszHref, GA_ReadOnly);
756 : }
757 : }
758 : }
759 0 : CPLDestroyXMLNode( psXML );
760 0 : return poRet;
761 : }
762 2119 : else if (poOpenInfo->nHeaderBytes != 0 &&
763 : strstr(pabyHeader, "<TileMapService version=\"1.0.0\"") != NULL)
764 : {
765 0 : CPLXMLNode* psXML = CPLParseXMLFile(pszFilename);
766 0 : if (psXML == NULL)
767 0 : return NULL;
768 0 : GDALDataset* poRet = GDALWMSMetaDataset::AnalyzeTileMapService(psXML);
769 0 : CPLDestroyXMLNode( psXML );
770 0 : return poRet;
771 : }
772 : else
773 2119 : return NULL;
774 9 : if (config == NULL) return NULL;
775 :
776 : /* -------------------------------------------------------------------- */
777 : /* Confirm the requested access is supported. */
778 : /* -------------------------------------------------------------------- */
779 9 : if( poOpenInfo->eAccess == GA_Update )
780 : {
781 0 : CPLDestroyXMLNode(config);
782 : CPLError( CE_Failure, CPLE_NotSupported,
783 : "The WMS driver does not support update access to existing"
784 0 : " datasets.\n" );
785 0 : return NULL;
786 : }
787 :
788 9 : GDALWMSDataset *ds = new GDALWMSDataset();
789 9 : ret = ds->Initialize(config);
790 9 : if (ret != CE_None) {
791 0 : delete ds;
792 0 : ds = NULL;
793 : }
794 9 : CPLDestroyXMLNode(config);
795 :
796 : /* -------------------------------------------------------------------- */
797 : /* Initialize any PAM information. */
798 : /* -------------------------------------------------------------------- */
799 9 : if (ds != NULL)
800 : {
801 9 : ds->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
802 9 : ds->SetDescription( poOpenInfo->pszFilename );
803 9 : ds->TryLoadXML();
804 : }
805 :
806 9 : return ds;
807 : }
808 : /************************************************************************/
809 : /* CreateCopy() */
810 : /************************************************************************/
811 :
812 19 : GDALDataset *GDALWMSDataset::CreateCopy( const char * pszFilename,
813 : GDALDataset *poSrcDS,
814 : int bStrict, char ** papszOptions,
815 : GDALProgressFunc pfnProgress,
816 : void * pProgressData )
817 : {
818 57 : if (poSrcDS->GetDriver() == NULL ||
819 38 : !EQUAL(poSrcDS->GetDriver()->GetDescription(), "WMS"))
820 : {
821 : CPLError(CE_Failure, CPLE_NotSupported,
822 18 : "Source dataset must be a WMS dataset");
823 18 : return NULL;
824 : }
825 :
826 1 : const char* pszXML = poSrcDS->GetMetadataItem("XML", "WMS");
827 1 : if (pszXML == NULL)
828 : {
829 : CPLError(CE_Failure, CPLE_AppDefined,
830 0 : "Cannot get XML definition of source WMS dataset");
831 0 : return NULL;
832 : }
833 :
834 1 : VSILFILE* fp = VSIFOpenL(pszFilename, "wb");
835 1 : if (fp == NULL)
836 0 : return NULL;
837 :
838 1 : VSIFWriteL(pszXML, 1, strlen(pszXML), fp);
839 1 : VSIFCloseL(fp);
840 :
841 1 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
842 1 : return Open(&oOpenInfo);
843 : }
844 :
845 : /************************************************************************/
846 : /* GDALDeregister_WMS() */
847 : /************************************************************************/
848 :
849 522 : static void GDALDeregister_WMS( GDALDriver * )
850 :
851 : {
852 522 : DestroyWMSMiniDriverManager();
853 522 : }
854 :
855 : /************************************************************************/
856 : /* GDALRegister_WMS() */
857 : /************************************************************************/
858 :
859 558 : void GDALRegister_WMS() {
860 : GDALDriver *driver;
861 558 : if (GDALGetDriverByName("WMS") == NULL) {
862 537 : driver = new GDALDriver();
863 537 : driver->SetDescription("WMS");
864 537 : driver->SetMetadataItem(GDAL_DMD_LONGNAME, "OGC Web Map Service");
865 537 : driver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_wms.html");
866 537 : driver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
867 537 : driver->pfnOpen = GDALWMSDataset::Open;
868 537 : driver->pfnIdentify = GDALWMSDataset::Identify;
869 537 : driver->pfnUnloadDriver = GDALDeregister_WMS;
870 537 : driver->pfnCreateCopy = GDALWMSDataset::CreateCopy;
871 537 : GetGDALDriverManager()->RegisterDriver(driver);
872 :
873 537 : GDALWMSMiniDriverManager *const mdm = GetGDALWMSMiniDriverManager();
874 1074 : mdm->Register(new GDALWMSMiniDriverFactory_WMS());
875 1074 : mdm->Register(new GDALWMSMiniDriverFactory_TileService());
876 1074 : mdm->Register(new GDALWMSMiniDriverFactory_WorldWind());
877 1074 : mdm->Register(new GDALWMSMiniDriverFactory_TMS());
878 1074 : mdm->Register(new GDALWMSMiniDriverFactory_TiledWMS());
879 1074 : mdm->Register(new GDALWMSMiniDriverFactory_VirtualEarth());
880 : }
881 558 : }
|