1 : /******************************************************************************
2 : *
3 : * Project: WMS Client Driver
4 : * Purpose: Implementation of the OnEarth Tiled WMS minidriver.
5 : * http://onearth.jpl.nasa.gov/tiled.html
6 : * Author: Lucian Plesea (Lucian dot Plesea at jpl.nasa.gov)
7 : * Adam Nowacki
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 :
33 1106 : CPP_GDALWMSMiniDriverFactory(TiledWMS)
34 :
35 : static char SIG[]="GDAL_WMS TiledWMS: ";
36 :
37 : /*
38 : *\brief Read a number from an xml element
39 : */
40 :
41 36 : static double getXMLNum(CPLXMLNode *poRoot, const char *pszPath, const char *pszDefault)
42 : {
43 36 : return CPLAtof(CPLGetXMLValue(poRoot,pszPath,pszDefault));
44 : }
45 :
46 : /*
47 : *\brief Read a ColorEntry XML node, return a GDALColorEntry structure
48 : *
49 : */
50 :
51 7 : static GDALColorEntry GetXMLColorEntry(CPLXMLNode *p)
52 : {
53 : GDALColorEntry ce;
54 7 : ce.c1= static_cast<short>(getXMLNum(p,"c1","0"));
55 7 : ce.c2= static_cast<short>(getXMLNum(p,"c2","0"));
56 7 : ce.c3= static_cast<short>(getXMLNum(p,"c3","0"));
57 7 : ce.c4= static_cast<short>(getXMLNum(p,"c4","255"));
58 7 : return ce;
59 : }
60 :
61 :
62 : /************************************************************************/
63 : /* SearchXMLSiblings() */
64 : /************************************************************************/
65 :
66 : /*
67 : * \brief Search for a sibling of the root node with a given name.
68 : *
69 : * Searches only the next siblings of the node passed in for the named element or attribute.
70 : * If the first character of the pszElement is '=', the search includes the psRoot node
71 : *
72 : * @param psRoot the root node to search. This should be a node of type
73 : * CXT_Element. NULL is safe.
74 : *
75 : * @param pszElement the name of the element or attribute to search for.
76 : *
77 : *
78 : * @return The first matching node or NULL on failure.
79 : */
80 :
81 228 : static CPLXMLNode *SearchXMLSiblings( CPLXMLNode *psRoot, const char *pszElement )
82 :
83 : {
84 228 : if( psRoot == NULL || pszElement == NULL )
85 0 : return NULL;
86 :
87 : // If the strings starts with '=', skip it and test the root
88 : // If not, start testing with the next sibling
89 228 : if (pszElement[0]=='=')
90 39 : pszElement++;
91 : else
92 189 : psRoot=psRoot->psNext;
93 :
94 3460 : for (;psRoot!=NULL;psRoot=psRoot->psNext)
95 : {
96 3281 : if ((psRoot->eType == CXT_Element ||
97 : psRoot->eType == CXT_Attribute)
98 : && EQUAL(pszElement,psRoot->pszValue))
99 49 : return psRoot;
100 : }
101 179 : return NULL;
102 : }
103 :
104 : /************************************************************************/
105 : /* SearchLeafGroupName() */
106 : /************************************************************************/
107 :
108 : /*
109 : * \brief Search for a leaf TileGroup node by name.
110 : *
111 : * @param psRoot the root node to search. This should be a node of type
112 : * CXT_Element. NULL is safe.
113 : *
114 : * @param pszElement the name of the TileGroup to search for.
115 : *
116 : * @return The XML node of the matching TileGroup or NULL on failure.
117 : */
118 :
119 186 : static CPLXMLNode *SearchLeafGroupName( CPLXMLNode *psRoot, const char *name )
120 :
121 : {
122 186 : CPLXMLNode *ret=NULL;
123 :
124 186 : if( psRoot == NULL || name == NULL ) return NULL;
125 :
126 : // Has to be a leaf TileGroup with the right name
127 182 : if (NULL==SearchXMLSiblings(psRoot->psChild,"TiledGroup"))
128 : {
129 178 : if (EQUAL(name,CPLGetXMLValue(psRoot,"Name","")))
130 : {
131 3 : return psRoot;
132 : }
133 : else
134 : { // Try a sibling
135 175 : return SearchLeafGroupName(psRoot->psNext,name);
136 : }
137 : }
138 : else
139 : { // Is metagroup, try children then siblings
140 4 : ret=SearchLeafGroupName(psRoot->psChild,name);
141 4 : if (NULL!=ret) return ret;
142 4 : return SearchLeafGroupName(psRoot->psNext,name);
143 : }
144 : }
145 :
146 : /************************************************************************/
147 : /* BandInterp() */
148 : /************************************************************************/
149 :
150 : /*
151 : * \brief Utility function to calculate color band interpretation.
152 : * Only handles Gray, GrayAlpha, RGB and RGBA, based on total band count
153 : *
154 : * @param nbands is the total number of bands in the image
155 : *
156 : * @param band is the band number, starting with 1
157 : *
158 : * @return GDALColorInterp of the band
159 : */
160 :
161 4 : static GDALColorInterp BandInterp(int nbands, int band) {
162 4 : switch (nbands) {
163 1 : case 1: return GCI_GrayIndex;
164 0 : case 2: return ((band==1)?GCI_GrayIndex:GCI_AlphaBand);
165 : case 3: // RGB
166 : case 4: // RBGA
167 3 : if (band<3)
168 2 : return ((band==1)?GCI_RedBand:GCI_GreenBand);
169 1 : return ((band==3)?GCI_BlueBand:GCI_AlphaBand);
170 : default:
171 0 : return GCI_Undefined;
172 : }
173 : }
174 :
175 : /************************************************************************/
176 : /* FindBbox() */
177 : /************************************************************************/
178 :
179 : /*
180 : * \brief Utility function to find the position of the bbox parameter value
181 : * within a request string. The search for the bbox is case insensitive
182 : *
183 : * @param in, the string to search into
184 : *
185 : * @return The position from the begining of the string or -1 if not found
186 : */
187 :
188 355 : static int FindBbox(CPLString in) {
189 :
190 355 : size_t pos = in.ifind("&bbox=");
191 355 : if (pos == std::string::npos)
192 0 : return -1;
193 : else
194 355 : return (int)pos + 6;
195 : }
196 :
197 : /************************************************************************/
198 : /* FindChangePattern() */
199 : /************************************************************************/
200 :
201 : /*
202 : * \brief Build the right request pattern based on the change request list
203 : * It only gets called on initialization
204 : * @param cdata, possible request strings, white space separated
205 : * @param substs, the list of substitutions to be applied
206 : * @param keys, the list of available substitution keys
207 : * @param ret The return value, a matching request or an empty string
208 : */
209 :
210 39 : static void FindChangePattern( char *cdata,char **substs, char **keys, CPLString &ret)
211 : {
212 : char **papszTokens=CSLTokenizeString2(cdata," \t\n\r",
213 39 : CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES);
214 39 : ret.clear();
215 :
216 39 : int matchcount=CSLCount(substs);
217 39 : int keycount=CSLCount(keys);
218 39 : if (keycount<matchcount)
219 : {
220 0 : CSLDestroy(papszTokens);
221 0 : return;
222 : }
223 :
224 : // A valid string has only the keys in the substs list and none other
225 39 : for (int j=0;j<CSLCount(papszTokens);j++)
226 : {
227 39 : ret=papszTokens[j]; // The target string
228 39 : bool matches=true;
229 :
230 39 : for (int k=0;k<keycount;k++)
231 : {
232 0 : const char *key=keys[k];
233 0 : int sub_number=CSLPartialFindString(substs,key);
234 0 : if (sub_number!=-1)
235 : { // It is a listed match
236 : // But is the match for the key position?
237 0 : char *found_key=NULL;
238 0 : const char *found_value=CPLParseNameValue(substs[sub_number],&found_key);
239 0 : if (found_key!=NULL && EQUAL(found_key,key))
240 : { // Should exits in the request
241 0 : if (std::string::npos==ret.find(key))
242 : {
243 0 : matches=false;
244 0 : break;
245 : }
246 : // Execute the substitution on the "ret" string
247 0 : URLSearchAndReplace(&ret,key,found_value);
248 : }
249 0 : if (found_key!=NULL) CPLFree(found_key);
250 : }
251 : else
252 : { // Key not in the subst list, should not match
253 0 : if (std::string::npos!=ret.find(key))
254 : {
255 0 : matches=false;
256 0 : break;
257 : }
258 : }
259 : } // Key loop
260 39 : if (matches)
261 : {
262 39 : CSLDestroy(papszTokens);
263 39 : return; // We got the string ready, all keys accounted for and substs applied
264 : }
265 : }
266 0 : ret.clear();
267 0 : CSLDestroy(papszTokens);
268 : }
269 :
270 3 : GDALWMSMiniDriver_TiledWMS::GDALWMSMiniDriver_TiledWMS() {
271 3 : m_requests = NULL;
272 3 : }
273 :
274 3 : GDALWMSMiniDriver_TiledWMS::~GDALWMSMiniDriver_TiledWMS() {
275 3 : CSLDestroy(m_requests);
276 3 : }
277 :
278 :
279 : // Returns the scale of a WMS request as compared to the base resolution
280 316 : double GDALWMSMiniDriver_TiledWMS::Scale(const char *request) {
281 316 : int bbox=FindBbox(request);
282 316 : if (bbox<0) return 0;
283 : double x,y,X,Y;
284 316 : sscanf(request+bbox,"%lf,%lf,%lf,%lf",&x,&y,&X,&Y);
285 316 : return (m_data_window.m_x1-m_data_window.m_x0)/(X-x)*m_bsx/m_data_window.m_sx;
286 : }
287 :
288 :
289 : // Finds, extracts, and returns the highest resolution request string from a list, starting at item i
290 39 : CPLString GDALWMSMiniDriver_TiledWMS::GetLowestScale(char **& list,int i)
291 : {
292 39 : CPLString req;
293 39 : double scale=-1;
294 39 : int position=-1;
295 355 : while (NULL!=list[i])
296 : {
297 277 : double tscale=Scale(list[i]);
298 277 : if (tscale>=scale)
299 : {
300 117 : scale=tscale;
301 117 : position=i;
302 : }
303 277 : i++;
304 : }
305 39 : if (position>-1)
306 : {
307 39 : req=list[position];
308 39 : list = CSLRemoveStrings(list,position,1,NULL);
309 : }
310 0 : return req;
311 : }
312 :
313 : /*
314 : *\Brief Initialize minidriver with info from the server
315 : */
316 :
317 3 : CPLErr GDALWMSMiniDriver_TiledWMS::Initialize(CPLXMLNode *config)
318 : {
319 3 : CPLErr ret = CE_None;
320 3 : CPLXMLNode *tileServiceConfig=NULL;
321 3 : CPLHTTPResult *psResult=NULL;
322 3 : CPLXMLNode *TG=NULL;
323 :
324 3 : char **requests=NULL;
325 3 : char **substs=NULL;
326 3 : char **keys=NULL;
327 :
328 3 : for (int once=1;once;once--) { // Something to break out of
329 : // Parse info from the service
330 :
331 3 : m_end_url = CPLGetXMLValue(config,"AdditionalArgs","");
332 6 : m_base_url = CPLGetXMLValue(config, "ServerURL", "");
333 3 : if (m_base_url.empty()) {
334 0 : CPLError(ret=CE_Failure, CPLE_AppDefined, "%s ServerURL missing.",SIG);
335 0 : break;
336 : }
337 :
338 3 : CPLString tiledGroupName (CPLGetXMLValue(config, "TiledGroupName", ""));
339 3 : if (tiledGroupName.empty()) {
340 0 : CPLError(ret=CE_Failure, CPLE_AppDefined, "%s TiledGroupName missing.",SIG);
341 : break;
342 : }
343 :
344 : // Change strings, key is an attribute, value is the value of the Change node
345 : // Multiple substitutions are possible
346 3 : TG=CPLSearchXMLNode(config, "Change");
347 3 : while(TG!=NULL) {
348 0 : CPLString name=CPLGetXMLValue(TG,"key","");
349 0 : if (name.empty()) {
350 : CPLError(ret=CE_Failure, CPLE_AppDefined,
351 0 : "%s Change element needs a non-empty \"key\" attribute",SIG);
352 : break;
353 : }
354 0 : substs=CSLSetNameValue(substs,name,CPLGetXMLValue(TG,"",""));
355 0 : TG=SearchXMLSiblings(TG,"Change");
356 : }
357 3 : if (ret!=CE_None) break;
358 :
359 3 : CPLString getTileServiceUrl = m_base_url + "request=GetTileService";
360 3 : psResult = CPLHTTPFetch(getTileServiceUrl, NULL);
361 :
362 3 : if (NULL==psResult) {
363 0 : CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Can't use HTTP", SIG);
364 : break;
365 : }
366 :
367 3 : if ((psResult->nStatus!=0)||(NULL==psResult->pabyData)||('\0'==psResult->pabyData[0])) {
368 0 : CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Server response error on GetTileService.",SIG);
369 : break;
370 : }
371 :
372 3 : if (NULL==(tileServiceConfig=CPLParseXMLString((const char*)psResult->pabyData))) {
373 0 : CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Error parsing the GetTileService response.",SIG);
374 : break;
375 : }
376 :
377 3 : if (NULL==(TG=CPLSearchXMLNode(tileServiceConfig, "TiledPatterns"))) {
378 : CPLError(ret=CE_Failure,CPLE_AppDefined,
379 0 : "%s Can't locate TiledPatterns in server response.",SIG);
380 : break;
381 : }
382 :
383 : // Get the global base_url and bounding box, these can be overwritten at the tileGroup level
384 : // They are just pointers into existing structures, cleanup is not required
385 3 : const char *global_base_url=CPLGetXMLValue(tileServiceConfig,"TiledPatterns.OnlineResource.xlink:href","");
386 3 : CPLXMLNode *global_latlonbbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.LatLonBoundingBox");
387 3 : CPLXMLNode *global_bbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.BoundingBox");
388 :
389 3 : if (NULL==(TG=SearchLeafGroupName(TG->psChild,tiledGroupName))) {
390 : CPLError(ret=CE_Failure,CPLE_AppDefined,
391 : "%s Can't locate TiledGroup ""%s"" in server response.",SIG,
392 0 : tiledGroupName.c_str());
393 : break;
394 : }
395 :
396 3 : int band_count=atoi(CPLGetXMLValue(TG, "Bands", "3"));
397 :
398 3 : if (!GDALCheckBandCount(band_count, FALSE)) {
399 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG,
400 0 : "Invalid number of bands in server response");
401 : break;
402 : }
403 :
404 : // Collect all keys defined by this tileset
405 3 : if (NULL!=CPLGetXMLNode(TG,"Key")) {
406 0 : CPLXMLNode *node=CPLGetXMLNode(TG,"Key");
407 0 : while (NULL!=node) {
408 0 : const char *val=CPLGetXMLValue(node,NULL,NULL);
409 0 : if (val) keys=CSLAddString(keys,val);
410 0 : node=SearchXMLSiblings(node,"Key");
411 : }
412 : }
413 :
414 : // Data values are attributes, they include NoData Min and Max
415 3 : if (0!=CPLGetXMLNode(TG,"DataValues")) {
416 1 : const char *nodata=CPLGetXMLValue(TG,"DataValues.NoData",NULL);
417 1 : if (nodata!=NULL) m_parent_dataset->WMSSetNoDataValue(nodata);
418 1 : const char *min=CPLGetXMLValue(TG,"DataValues.min",NULL);
419 1 : if (min!=NULL) m_parent_dataset->WMSSetMinValue(min);
420 1 : const char *max=CPLGetXMLValue(TG,"DataValues.max",NULL);
421 1 : if (max!=NULL) m_parent_dataset->WMSSetMaxValue(max);
422 : }
423 :
424 3 : m_parent_dataset->WMSSetBandsCount(band_count);
425 3 : m_parent_dataset->WMSSetDataType(GDALGetDataTypeByName(CPLGetXMLValue(TG, "DataType", "Byte")));
426 3 : m_projection_wkt=CPLGetXMLValue(TG, "Projection","");
427 :
428 3 : m_base_url=CPLGetXMLValue(TG,"OnlineResource.xlink:href",global_base_url);
429 3 : if (m_base_url[0]=='\0') {
430 : CPLError(ret=CE_Failure,CPLE_AppDefined, "%s%s",SIG,
431 0 : "Can't locate OnlineResource in the server response.");
432 : break;
433 : }
434 :
435 : // Bounding box, local, global, local lat-lon, global lat-lon, in this order
436 3 : CPLXMLNode *bbox = CPLGetXMLNode(TG, "BoundingBox");
437 3 : if (NULL==bbox) bbox=global_bbox;
438 3 : if (NULL==bbox) bbox=CPLGetXMLNode(TG, "LatLonBoundingBox");
439 3 : if (NULL==bbox) bbox=global_latlonbbox;
440 :
441 3 : if (NULL==bbox) {
442 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG,
443 0 : "Can't locate the LatLonBoundingBox in server response.");
444 : break;
445 : }
446 :
447 3 : m_data_window.m_x0=atof(CPLGetXMLValue(bbox,"minx","0"));
448 3 : m_data_window.m_x1=atof(CPLGetXMLValue(bbox,"maxx","-1"));
449 3 : m_data_window.m_y0=atof(CPLGetXMLValue(bbox,"maxy","0"));
450 3 : m_data_window.m_y1=atof(CPLGetXMLValue(bbox,"miny","-1"));
451 :
452 3 : if ((m_data_window.m_x1-m_data_window.m_x0)<0) {
453 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s", SIG,
454 0 : "Coordinate order in BBox, problem in server response");
455 : break;
456 : }
457 :
458 : // Is there a palette?
459 : //
460 : // Format is
461 : // <Palette>
462 : // <Size>N</Size> : Optional
463 : // <Model>RGBA|RGB|CMYK|HSV|HLS|L</Model> :mandatory
464 : // <Entry idx=i c1=v1 c2=v2 c3=v3 c4=v4/> :Optional
465 : // <Entry .../>
466 : // </Palette>
467 : // the idx attribute is optional, it autoincrements
468 : // The entries are actually vertices, interpolation takes place inside
469 : // The palette starts initialized with zeros
470 : // HSV and HLS are the similar, with c2 and c3 swapped
471 : // RGB or RGBA are same
472 : //
473 :
474 3 : GDALColorTable *poColorTable=NULL;
475 :
476 3 : if ((band_count==1) && CPLGetXMLNode(TG,"Palette")) {
477 :
478 1 : CPLXMLNode *node=CPLGetXMLNode(TG,"Palette");
479 :
480 1 : int entries=static_cast<int>(getXMLNum(node,"Size","255"));
481 1 : GDALPaletteInterp eInterp=GPI_RGB;
482 :
483 1 : CPLString pModel=CPLGetXMLValue(node,"Model","RGB");
484 1 : if (!pModel.empty() && pModel.find("RGB")!=std::string::npos)
485 1 : eInterp=GPI_RGB;
486 : else {
487 : CPLError(CE_Failure, CPLE_AppDefined,
488 : "%s Palette Model %s is unknown, use RGB or RGBA",
489 0 : SIG, pModel.c_str());
490 0 : return CE_Failure;
491 : }
492 :
493 2 : if ((entries>0)&&(entries<257)) {
494 : int start_idx, end_idx;
495 1 : GDALColorEntry ce_start={0,0,0,255},ce_end={0,0,0,255};
496 :
497 : // Create it and initialize it to nothing
498 1 : poColorTable = new GDALColorTable(eInterp);
499 1 : poColorTable->CreateColorRamp(0,&ce_start,entries-1,&ce_end);
500 : // Read the values
501 1 : CPLXMLNode *p=CPLGetXMLNode(node,"Entry");
502 1 : if (p) {
503 : // Initialize the first entry
504 1 : start_idx=static_cast<int>(getXMLNum(p,"idx","0"));
505 1 : ce_start=GetXMLColorEntry(p);
506 1 : if (start_idx<0) {
507 : CPLError(CE_Failure, CPLE_AppDefined,
508 0 : "%s Palette index %d not allowed",SIG,start_idx);
509 0 : delete poColorTable;
510 0 : return CE_Failure;
511 : }
512 1 : poColorTable->SetColorEntry(start_idx,&ce_start);
513 8 : while (NULL!=(p=SearchXMLSiblings(p,"Entry"))) {
514 : // For every entry, create a ramp
515 6 : ce_end=GetXMLColorEntry(p);
516 6 : end_idx=static_cast<int>(getXMLNum(p,"idx",CPLString().FormatC(start_idx+1).c_str()));
517 6 : if ((end_idx<=start_idx)||(start_idx>=entries)) {
518 : CPLError(CE_Failure, CPLE_AppDefined,
519 0 : "%s Index Error at index %d",SIG,end_idx);
520 0 : delete poColorTable;
521 0 : return CE_Failure;
522 : }
523 : poColorTable->CreateColorRamp(start_idx,&ce_start,
524 6 : end_idx,&ce_end);
525 6 : ce_start=ce_end;
526 6 : start_idx=end_idx;
527 : }
528 : }
529 1 : m_parent_dataset->SetColorTable(poColorTable);
530 : } else {
531 0 : CPLError(CE_Failure, CPLE_AppDefined,"%s Palette definition error",SIG);
532 0 : return CE_Failure;
533 0 : }
534 : }
535 :
536 3 : int overview_count=0;
537 3 : CPLXMLNode *Pattern=TG->psChild;
538 :
539 3 : m_bsx=m_bsy=-1;
540 3 : m_data_window.m_sx=m_data_window.m_sy=0;
541 :
542 6 : for (int once=1;once;once--) { // Something to break out of
543 3 : while ((NULL!=Pattern)&&(NULL!=(Pattern=SearchXMLSiblings(Pattern,"=TilePattern")))) {
544 : int mbsx,mbsy;
545 :
546 39 : CPLString request;
547 39 : FindChangePattern(Pattern->psChild->pszValue,substs,keys,request);
548 :
549 39 : char **papszTokens=CSLTokenizeString2(request,"&",0);
550 :
551 39 : const char* pszWIDTH = CSLFetchNameValue(papszTokens,"WIDTH");
552 39 : const char* pszHEIGHT = CSLFetchNameValue(papszTokens,"HEIGHT");
553 39 : if (pszWIDTH == NULL || pszHEIGHT == NULL)
554 : {
555 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG,
556 0 : "Cannot find width and/or height parameters.");
557 0 : overview_count=0;
558 0 : CSLDestroy(papszTokens);
559 : break;
560 : }
561 :
562 39 : mbsx=atoi(pszWIDTH);
563 39 : mbsy=atoi(pszHEIGHT);
564 39 : if (m_projection_wkt.empty()) {
565 2 : m_projection_wkt = CSLFetchNameValueDef(papszTokens,"SRS", "");
566 2 : if (!m_projection_wkt.empty())
567 2 : m_projection_wkt=ProjToWKT(m_projection_wkt);
568 : }
569 :
570 39 : if (-1==m_bsx) m_bsx=mbsx;
571 39 : if (-1==m_bsy) m_bsy=mbsy;
572 39 : if ((m_bsy!=mbsy)||(m_bsy!=mbsy)) {
573 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG,
574 0 : "Tileset uses different block sizes.");
575 0 : overview_count=0;
576 0 : CSLDestroy(papszTokens);
577 : break;
578 : }
579 :
580 : double x,y,X,Y;
581 39 : if (sscanf(CSLFetchNameValueDef(papszTokens,"BBOX", ""),"%lf,%lf,%lf,%lf",&x,&y,&X,&Y)!=4)
582 : {
583 : CPLError(ret=CE_Failure,CPLE_AppDefined,
584 0 : "%s Error parsing BBOX, pattern %d\n",SIG,overview_count+1);
585 0 : CSLDestroy(papszTokens);
586 : break;
587 : }
588 : // Pick the largest size
589 39 : int sx=static_cast<int>((m_data_window.m_x1-m_data_window.m_x0)/(X-x)*m_bsx);
590 39 : int sy=static_cast<int>(fabs((m_data_window.m_y1-m_data_window.m_y0)/(Y-y)*m_bsy));
591 39 : if (sx>m_data_window.m_sx) m_data_window.m_sx=sx;
592 39 : if (sy>m_data_window.m_sy) m_data_window.m_sy=sy;
593 39 : CSLDestroy(papszTokens);
594 :
595 : // Only use overlays where the top coordinate is within a pixel from the top of coverage
596 : double pix_off,temp;
597 39 : pix_off=m_bsy*modf(fabs((Y-m_data_window.m_y0)/(Y-y)),&temp);
598 78 : if ((pix_off<1)||((m_bsy-pix_off)<1)) {
599 39 : requests=CSLAddString(requests,request);
600 39 : overview_count++;
601 : } else
602 : CPLError(CE_Warning,CPLE_AppDefined,
603 0 : "%s Overlay size %dX%d can't be used due to alignment",SIG,sx,sy);
604 :
605 39 : Pattern=Pattern->psNext;
606 :
607 : }
608 :
609 : // The tlevel is needed, the tx and ty are not used by this minidriver
610 3 : m_data_window.m_tlevel = 0;
611 3 : m_data_window.m_tx = 0;
612 3 : m_data_window.m_ty = 0;
613 :
614 : // Make sure the parent_dataset values are set before creating the bands
615 3 : m_parent_dataset->WMSSetBlockSize(m_bsx,m_bsy);
616 3 : m_parent_dataset->WMSSetRasterSize(m_data_window.m_sx,m_data_window.m_sy);
617 :
618 3 : m_parent_dataset->WMSSetDataWindow(m_data_window);
619 : //m_parent_dataset->WMSSetOverviewCount(overview_count);
620 3 : m_parent_dataset->WMSSetClamp(false);
621 :
622 : // Ready for the Rasterband creation
623 3 : for (int i=0;i<overview_count;i++) {
624 39 : CPLString request=GetLowestScale(requests,i);
625 39 : double scale=Scale(request);
626 :
627 : // Base scale should be very close to 1
628 39 : if ((0==i)&&(fabs(scale-1) > 1e-6)) {
629 : CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG,
630 0 : "Base resolution pattern missing.");
631 : break;
632 : }
633 :
634 : // Prepare the request and insert it back into the list
635 : // Find returns an answer relative to the original string start!
636 39 : size_t startBbox=FindBbox(request);
637 39 : size_t endBbox=request.find('&',startBbox);
638 39 : if (endBbox==std::string::npos) endBbox=request.size();
639 39 : request.replace(startBbox,endBbox-startBbox,"${GDAL_BBOX}");
640 39 : requests = CSLInsertString(requests,i,request);
641 :
642 : // Create the Rasterband or overview
643 104 : for (int j = 1; j <= band_count; j++) {
644 65 : if (i!=0)
645 60 : m_parent_dataset->mGetBand(j)->AddOverview(scale);
646 : else { // Base resolution
647 : GDALWMSRasterBand *band=new
648 5 : GDALWMSRasterBand(m_parent_dataset,j,1);
649 6 : if (poColorTable!=NULL) band->SetColorInterpretation(GCI_PaletteIndex);
650 4 : else band->SetColorInterpretation(BandInterp(band_count,j));
651 5 : m_parent_dataset->mSetBand(j, band);
652 : };
653 : }
654 : }
655 3 : if ((overview_count==0)||(m_bsx<1)||(m_bsy<1)) {
656 : CPLError(ret=CE_Failure,CPLE_AppDefined,
657 0 : "%s No usable TilePattern elements found",SIG);
658 0 : break;
659 : }
660 : }
661 : }
662 :
663 3 : CSLDestroy(keys);
664 3 : CSLDestroy(substs);
665 3 : if (tileServiceConfig) CPLDestroyXMLNode(tileServiceConfig);
666 3 : if (psResult) CPLHTTPDestroyResult(psResult);
667 :
668 3 : m_requests=requests;
669 3 : return ret;
670 : }
671 :
672 3 : void GDALWMSMiniDriver_TiledWMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) {
673 3 : caps->m_capabilities_version = 1;
674 3 : caps->m_has_arb_overviews = 0;
675 3 : caps->m_has_image_request = 1;
676 3 : caps->m_has_tiled_image_requeset = 1;
677 3 : caps->m_max_overview_count = 32;
678 3 : }
679 :
680 :
681 : // not called
682 0 : void GDALWMSMiniDriver_TiledWMS::ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri) {
683 0 : }
684 :
685 2 : void GDALWMSMiniDriver_TiledWMS::TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri) {
686 2 : *url = m_base_url;
687 2 : URLAppend(url,CSLGetField(m_requests,-tiri.m_level));
688 : URLSearchAndReplace(url,"${GDAL_BBOX}","%013.8f,%013.8f,%013.8f,%013.8f",
689 2 : iri.m_x0,iri.m_y1,iri.m_x1,iri.m_y0);
690 2 : URLAppend(url,m_end_url);
691 2 : }
692 :
693 3 : const char *GDALWMSMiniDriver_TiledWMS::GetProjectionInWKT() {
694 3 : return m_projection_wkt.c_str();
695 : }
696 :
|