1 /*****************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: Implementation of WMS CONNECTIONTYPE - client to WMS servers
6 * Author: Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7 *
8 *****************************************************************************
9 * Copyright (c) 2001-2004, Daniel Morissette, DM Solutions Group Inc
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 *****************************************************************************/
29
30 #include "mapserver.h"
31 #include "maperror.h"
32 #include "mapogcsld.h"
33 #include "mapows.h"
34
35 #include <time.h>
36 #include <ctype.h>
37
38 #if defined(_WIN32) && !defined(__CYGWIN__)
39 #include <process.h>
40 #include <stdio.h>
41 #endif
42
43 #include "cpl_vsi.h"
44
45 /**********************************************************************
46 * msInitWmsParamsObj()
47 *
48 **********************************************************************/
msInitWmsParamsObj(wmsParamsObj * wmsparams)49 int msInitWmsParamsObj(wmsParamsObj *wmsparams)
50 {
51 wmsparams->onlineresource = NULL;
52 wmsparams->params = msCreateHashTable();
53 wmsparams->numparams=0;
54 wmsparams->httpcookiedata = NULL;
55
56 return MS_SUCCESS;
57 }
58
59 /**********************************************************************
60 * msFreeWmsParamsObj()
61 *
62 * Frees the contents of the object, but not the object itself.
63 **********************************************************************/
msFreeWmsParamsObj(wmsParamsObj * wmsparams)64 void msFreeWmsParamsObj(wmsParamsObj *wmsparams)
65 {
66 msFree(wmsparams->onlineresource);
67 wmsparams->onlineresource = NULL;
68
69 msFreeHashTable(wmsparams->params);
70 wmsparams->params = NULL;
71
72 msFree(wmsparams->httpcookiedata);
73
74 wmsparams->numparams=0;
75 }
76
77 /**********************************************************************
78 * msSetWMSParamString()
79 *
80 **********************************************************************/
81
82 #ifdef USE_WMS_LYR
msSetWMSParamString(wmsParamsObj * psWMSParams,const char * name,const char * value,int urlencode,int nVersion)83 static int msSetWMSParamString(wmsParamsObj *psWMSParams,
84 const char *name, const char * value,
85 int urlencode, int nVersion)
86 {
87 if (urlencode) {
88 char *pszTmp;
89
90 /*
91 * Special case handling for characters the WMS specification
92 * says should not be encoded, when they occur in certain
93 * parameters.
94 *
95 * Note: WMS 1.3 removes SRS and FORMAT from the set of
96 * exceptional cases, but renames SRS as CRS in any case.
97 */
98 if( strcmp(name,"LAYERS") == 0 ||
99 strcmp(name,"STYLES") == 0 ||
100 strcmp(name,"BBOX") == 0 ) {
101 pszTmp = msEncodeUrlExcept(value,',');
102 } else if ( strcmp(name,"SRS") == 0 ) {
103 pszTmp = msEncodeUrlExcept(value,':');
104 } else if ( nVersion < OWS_1_3_0 && strcmp(name,"FORMAT") == 0 ) {
105 pszTmp = msEncodeUrlExcept(value,'/');
106 } else {
107 pszTmp = msEncodeUrl(value);
108 }
109
110 msInsertHashTable(psWMSParams->params, name, pszTmp);
111 msFree(pszTmp);
112 } else {
113 msInsertHashTable(psWMSParams->params, name, value);
114 }
115 psWMSParams->numparams++;
116
117 return MS_SUCCESS;
118 }
119 #endif /* def USE_WMS_LYR */
120
121 /**********************************************************************
122 * msSetWMSParamInt()
123 *
124 **********************************************************************/
125
126 #ifdef USE_WMS_LYR
msSetWMSParamInt(wmsParamsObj * wmsparams,const char * name,int value)127 static int msSetWMSParamInt(wmsParamsObj *wmsparams,
128 const char *name, int value)
129 {
130 char szBuf[100];
131
132 snprintf(szBuf, sizeof(szBuf), "%d", value);
133 msInsertHashTable(wmsparams->params, name, szBuf);
134 wmsparams->numparams++;
135
136 return MS_SUCCESS;
137 }
138 #endif /* def USE_WMS_LYR */
139
140 /**********************************************************************
141 * msBuildWMSParamsUrl()
142 *
143 **********************************************************************/
msBuildURLFromWMSParams(wmsParamsObj * wmsparams)144 static char *msBuildURLFromWMSParams(wmsParamsObj *wmsparams)
145 {
146 const char *key, *value;
147 size_t bufferSize = 0;
148 int nLen;
149 char *pszURL;
150
151 /* Compute size required for URL buffer
152 */
153 nLen = strlen(wmsparams->onlineresource) + 3;
154
155 key = msFirstKeyFromHashTable(wmsparams->params);
156 while (key != NULL) {
157 value = msLookupHashTable(wmsparams->params, key);
158 nLen += strlen(key) + strlen(value) + 2;
159
160 key = msNextKeyFromHashTable(wmsparams->params, key);
161 }
162
163 bufferSize = nLen+1;
164 pszURL = (char*)msSmallMalloc(bufferSize);
165
166 /* Start with the onlineresource value and append trailing '?' or '&'
167 * if missing.
168 */
169 strcpy(pszURL, wmsparams->onlineresource);
170 if (strchr(pszURL, '?') == NULL)
171 strcat(pszURL, "?");
172 else {
173 char *c;
174 c = pszURL+strlen(pszURL)-1;
175 if (*c != '?' && *c != '&')
176 strcpy(c+1, "&");
177 }
178
179 /* Now add all the parameters
180 */
181 nLen = strlen(pszURL);
182 key = msFirstKeyFromHashTable(wmsparams->params);
183 while (key != NULL) {
184 value = msLookupHashTable(wmsparams->params, key);
185 snprintf(pszURL+nLen, bufferSize-nLen, "%s=%s&", key, value);
186 nLen += strlen(key) + strlen(value) + 2;
187 key = msNextKeyFromHashTable(wmsparams->params, key);
188 }
189
190 /* Get rid of trailing '&'*/
191 pszURL[nLen-1] = '\0';
192
193
194 return pszURL;
195 }
196
197
198
199
200 #ifdef USE_WMS_LYR
201 /**********************************************************************
202 * msBuildWMSLayerURLBase()
203 *
204 * Build the base of a GetMap or GetFeatureInfo URL using metadata.
205 * The parameters to set are:
206 * VERSION
207 * LAYERS
208 * FORMAT
209 * TRANSPARENT
210 * STYLES
211 * QUERY_LAYERS (for queriable layers only)
212 *
213 * Returns a reference to a newly allocated string that should be freed
214 * by the caller.
215 **********************************************************************/
msBuildWMSLayerURLBase(mapObj * map,layerObj * lp,wmsParamsObj * psWMSParams,int nRequestType)216 static int msBuildWMSLayerURLBase(mapObj *map, layerObj *lp,
217 wmsParamsObj *psWMSParams, int nRequestType)
218 {
219 const char *pszOnlineResource, *pszVersion, *pszName, *pszFormat;
220 const char *pszFormatList, *pszStyle, /* *pszStyleList,*/ *pszTime;
221 const char *pszBgColor, *pszTransparent;
222 const char *pszSLD=NULL, *pszStyleSLDBody=NULL, *pszVersionKeyword=NULL;
223 const char *pszSLDBody=NULL, *pszSLDURL = NULL;
224 char *pszSLDGenerated = NULL;
225 int nVersion=OWS_VERSION_NOTSET;
226
227 /* If lp->connection is not set then use wms_onlineresource metadata */
228 pszOnlineResource = lp->connection;
229 if (pszOnlineResource == NULL)
230 pszOnlineResource = msOWSLookupMetadata(&(lp->metadata),
231 "MO", "onlineresource");
232
233 pszVersion = msOWSLookupMetadata(&(lp->metadata), "MO", "server_version");
234 pszName = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
235 pszFormat = msOWSLookupMetadata(&(lp->metadata), "MO", "format");
236 pszFormatList = msOWSLookupMetadata(&(lp->metadata), "MO", "formatlist");
237 pszStyle = msOWSLookupMetadata(&(lp->metadata), "MO", "style");
238 /*pszStyleList = msOWSLookupMetadata(&(lp->metadata), "MO", "stylelist");*/
239 pszTime = msOWSLookupMetadata(&(lp->metadata), "MO", "time");
240 pszSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body");
241 pszSLDURL = msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url");
242 pszBgColor = msOWSLookupMetadata(&(lp->metadata), "MO", "bgcolor");
243 pszTransparent = msOWSLookupMetadata(&(lp->metadata), "MO", "transparent");
244
245 if (pszOnlineResource==NULL || pszVersion==NULL || pszName==NULL) {
246 msSetError(MS_WMSCONNERR,
247 "One of wms_onlineresource, wms_server_version, wms_name "
248 "metadata is missing in layer %s. "
249 "Please either provide a valid CONNECTION URL, or provide "
250 "those values in the layer's metadata.\n",
251 "msBuildWMSLayerURLBase()", lp->name);
252 return MS_FAILURE;
253 }
254
255 psWMSParams->onlineresource = msStrdup(pszOnlineResource);
256
257 if (strncmp(pszVersion, "1.0.7", 5) < 0)
258 pszVersionKeyword = "WMTVER";
259 else
260 pszVersionKeyword = "VERSION";
261
262 nVersion = msOWSParseVersionString(pszVersion);
263 /* WMS 1.0.8 is really just 1.1.0 */
264 if (nVersion == OWS_1_0_8) nVersion = OWS_1_1_0;
265
266 msSetWMSParamString(psWMSParams, pszVersionKeyword, pszVersion, MS_FALSE, nVersion);
267 msSetWMSParamString(psWMSParams, "SERVICE", "WMS", MS_FALSE, nVersion);
268 msSetWMSParamString(psWMSParams, "LAYERS", pszName, MS_TRUE, nVersion);
269
270 if (pszFormat==NULL && pszFormatList==NULL) {
271 msSetError(MS_WMSCONNERR,
272 "At least wms_format or wms_formatlist is required for "
273 "layer %s. "
274 "Please either provide a valid CONNECTION URL, or provide "
275 "those values in the layer's metadata.\n",
276 "msBuildWMSLayerURLBase()", lp->name);
277 return MS_FAILURE;
278 }
279
280 if (pszFormat != NULL) {
281 msSetWMSParamString(psWMSParams, "FORMAT", pszFormat, MS_TRUE, nVersion);
282 } else {
283 /* Look for the first format in list that matches */
284 char **papszTok;
285 int i, n;
286 papszTok = msStringSplit(pszFormatList, ',', &n);
287
288 for(i=0; pszFormat==NULL && i<n; i++) {
289 if (0
290 #if defined USE_PNG
291 || strcasecmp(papszTok[i], "PNG")
292 || strcasecmp(papszTok[i], "image/png")
293 #endif
294 #if defined USE_JPEG
295 || strcasecmp(papszTok[i], "JPEG")
296 || strcasecmp(papszTok[i], "image/jpeg")
297 #endif
298 ) {
299 pszFormat = papszTok[i];
300 }
301 }
302
303 if (pszFormat) {
304 msSetWMSParamString(psWMSParams, "FORMAT", pszFormat, MS_TRUE, nVersion);
305 msFreeCharArray(papszTok, n);
306 } else {
307 msSetError(MS_WMSCONNERR,
308 "Could not find a format that matches supported input "
309 "formats in wms_formatlist metdata in layer %s. "
310 "Please either provide a valid CONNECTION URL, or "
311 "provide the required layer metadata.\n",
312 "msBuildWMSLayerURLBase()", lp->name);
313 msFreeCharArray(papszTok, n);
314 return MS_FAILURE;
315 }
316 }
317
318 if (pszStyle==NULL) {
319 /* When no style is selected, use "" which is a valid default. */
320 pszStyle = "";
321 } else {
322 /* Was a wms_style_..._sld URL provided? */
323 char szBuf[100];
324 snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld", pszStyle);
325 pszSLD = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
326 snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld_body", pszStyle);
327 pszStyleSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
328
329 if (pszSLD || pszStyleSLDBody) {
330 /* SLD URL is set. If this defn. came from a map context then */
331 /* the style name may just be an internal name: "Style{%d}" if */
332 /* that's the case then we should not pass this name via the URL */
333 if (strncmp(pszStyle, "Style{", 6) == 0)
334 pszStyle = "";
335 }
336 }
337
338 /* set STYLE parameter no matter what, even if it's empty (i.e. "STYLES=")
339 * GetLegendGraphic doesn't support multiple styles and is named STYLE
340 */
341 if (nRequestType == WMS_GETLEGENDGRAPHIC) {
342 msSetWMSParamString(psWMSParams, "STYLE", pszStyle, MS_TRUE, nVersion);
343 } else {
344 msSetWMSParamString(psWMSParams, "STYLES", pszStyle, MS_TRUE, nVersion);
345 }
346
347 if (pszSLD != NULL) {
348 /* Only SLD is set */
349 msSetWMSParamString(psWMSParams, "SLD", pszSLD, MS_TRUE, nVersion);
350 } else if (pszStyleSLDBody != NULL) {
351 /* SLDBODY are set */
352 msSetWMSParamString(psWMSParams, "SLD_BODY", pszStyleSLDBody, MS_TRUE, nVersion);
353 }
354
355 if (msIsLayerQueryable(lp)) {
356 msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszName, MS_TRUE, nVersion);
357 }
358 if (pszTime && strlen(pszTime) > 0) {
359 msSetWMSParamString(psWMSParams, "TIME", pszTime, MS_TRUE, nVersion);
360 }
361
362 /* if the metadata wms_sld_body is set to AUTO, we generate
363 * the sld based on classes found in the map file and send
364 * it in the URL. If diffrent from AUTO, we are assuming that
365 * it is a valid sld.
366 */
367 if (pszSLDBody) {
368 if (strcasecmp(pszSLDBody, "AUTO") == 0) {
369 if (pszVersion && strncmp(pszVersion, "1.3.0", 5) == 0)
370 pszSLDGenerated = msSLDGenerateSLD(map, lp->index, "1.1.0");
371 else
372 pszSLDGenerated = msSLDGenerateSLD(map, lp->index, NULL);
373
374 if (pszSLDGenerated) {
375 msSetWMSParamString(psWMSParams, "SLD_BODY",
376 pszSLDGenerated, MS_TRUE, nVersion);
377 free(pszSLDGenerated);
378 }
379 } else {
380 msSetWMSParamString(psWMSParams, "SLD_BODY", pszSLDBody, MS_TRUE, nVersion);
381 }
382
383 }
384
385 if (pszSLDURL) {
386 msSetWMSParamString(psWMSParams, "SLD", pszSLDURL, MS_TRUE, nVersion);
387 }
388
389 if (pszBgColor) {
390 msSetWMSParamString(psWMSParams, "BGCOLOR", pszBgColor, MS_TRUE, nVersion);
391 }
392
393 if (pszTransparent) {
394 msSetWMSParamString(psWMSParams, "TRANSPARENT", pszTransparent, MS_TRUE, nVersion);
395 } else {
396 msSetWMSParamString(psWMSParams, "TRANSPARENT", "TRUE", MS_TRUE, nVersion);
397 }
398
399 return MS_SUCCESS;
400 }
401
402 #endif /* USE_WMS_LYR */
403
404
405 /**********************************************************************
406 * msBuildWMSLayerURL()
407 *
408 * Build a GetMap or GetFeatureInfo URL.
409 *
410 * Returns a reference to a newly allocated string that should be freed
411 * by the caller.
412 **********************************************************************/
413
414 static int
msBuildWMSLayerURL(mapObj * map,layerObj * lp,int nRequestType,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,rectObj * bbox_ret,int * width_ret,int * height_ret,wmsParamsObj * psWMSParams)415 msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType,
416 int nClickX, int nClickY, int nFeatureCount,
417 const char *pszInfoFormat, rectObj *bbox_ret,
418 int *width_ret, int *height_ret,
419 wmsParamsObj *psWMSParams)
420 {
421 #ifdef USE_WMS_LYR
422 char *pszEPSG = NULL;
423 const char *pszVersion, *pszRequestParam, *pszExceptionsParam,
424 *pszSrsParamName="SRS", *pszLayer=NULL, *pszQueryLayers=NULL,
425 *pszUseStrictAxisOrder;
426 rectObj bbox;
427 int bbox_width = map->width, bbox_height = map->height;
428 int nVersion=OWS_VERSION_NOTSET;
429 int bUseStrictAxisOrder = MS_FALSE; /* this is the assumption up to 1.1.0 */
430 int bFlipAxisOrder = MS_FALSE;
431 const char *pszTmp;
432 int bIsEssential = MS_FALSE;
433
434 if (lp->connectiontype != MS_WMS) {
435 msSetError(MS_WMSCONNERR, "Call supported only for CONNECTIONTYPE WMS",
436 "msBuildWMSLayerURL()");
437 return MS_FAILURE;
438 }
439
440 /* ------------------------------------------------------------------
441 * Find out request version
442 * ------------------------------------------------------------------ */
443 if (lp->connection == NULL ||
444 ((pszVersion = strstr(lp->connection, "VERSION=")) == NULL &&
445 (pszVersion = strstr(lp->connection, "version=")) == NULL &&
446 (pszVersion = strstr(lp->connection, "WMTVER=")) == NULL &&
447 (pszVersion = strstr(lp->connection, "wmtver=")) == NULL ) ) {
448 /* CONNECTION missing or seems incomplete... try to build from metadata */
449 if (msBuildWMSLayerURLBase(map, lp, psWMSParams, nRequestType) != MS_SUCCESS)
450 return MS_FAILURE; /* An error already produced. */
451
452 /* If we received MS_SUCCESS then version must have been set */
453 pszVersion = msLookupHashTable(psWMSParams->params, "VERSION");
454 if (pszVersion ==NULL)
455 pszVersion = msLookupHashTable(psWMSParams->params, "WMTVER");
456
457 nVersion = msOWSParseVersionString(pszVersion);
458 } else {
459 /* CONNECTION string seems complete, start with that. */
460 char *pszDelimiter;
461 psWMSParams->onlineresource = msStrdup(lp->connection);
462
463 /* Fetch version info */
464 pszVersion = strchr(pszVersion, '=')+1;
465 pszDelimiter = strchr(pszVersion, '&');
466 if (pszDelimiter != NULL)
467 *pszDelimiter = '\0';
468 nVersion = msOWSParseVersionString(pszVersion);
469 if (pszDelimiter != NULL)
470 *pszDelimiter = '&';
471 }
472
473 switch (nVersion) {
474 case OWS_1_0_8:
475 nVersion = OWS_1_1_0; /* 1.0.8 == 1.1.0 */
476 break;
477 case OWS_1_0_0:
478 case OWS_1_0_1:
479 case OWS_1_0_7:
480 case OWS_1_1_0:
481 case OWS_1_1_1:
482 /* All is good, this is a supported version. */
483 break;
484 case OWS_1_3_0:
485 /* 1.3.0 introduces a few changes... */
486 pszSrsParamName = "CRS";
487 bUseStrictAxisOrder = MS_TRUE; /* this is the assumption for 1.3.0 */
488 break;
489 default:
490 /* Not a supported version */
491 msSetError(MS_WMSCONNERR, "MapServer supports only WMS 1.0.0 to 1.3.0 (please verify the VERSION parameter in the connection string).", "msBuildWMSLayerURL()");
492 return MS_FAILURE;
493 }
494
495 /* ------------------------------------------------------------------
496 * For GetFeatureInfo requests, make sure QUERY_LAYERS is included
497 * ------------------------------------------------------------------ */
498 if (nRequestType == WMS_GETFEATUREINFO &&
499 strstr(psWMSParams->onlineresource, "QUERY_LAYERS=") == NULL &&
500 strstr(psWMSParams->onlineresource, "query_layers=") == NULL &&
501 msLookupHashTable(psWMSParams->params, "QUERY_LAYERS") == NULL) {
502 pszQueryLayers = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
503
504 if (pszQueryLayers == NULL) {
505 msSetError(MS_WMSCONNERR, "wms_name not set or WMS Connection String must contain the QUERY_LAYERS parameter to support GetFeatureInfo requests (with name in uppercase).", "msBuildWMSLayerURL()");
506 return MS_FAILURE;
507 }
508 }
509
510 /* ------------------------------------------------------------------
511 * For GetLegendGraphic requests, make sure LAYER is included
512 * ------------------------------------------------------------------ */
513 if (nRequestType == WMS_GETLEGENDGRAPHIC &&
514 strstr(psWMSParams->onlineresource, "LAYER=") == NULL &&
515 strstr(psWMSParams->onlineresource, "layer=") == NULL &&
516 msLookupHashTable(psWMSParams->params, "LAYER") == NULL) {
517 pszLayer = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
518
519 if (pszLayer == NULL) {
520 msSetError(MS_WMSCONNERR, "wms_name not set or WMS Connection String must contain the LAYER parameter to support GetLegendGraphic requests (with name in uppercase).", "msBuildWMSLayerURL()");
521 return MS_FAILURE;
522 }
523 }
524
525 /* ------------------------------------------------------------------
526 * Figure the SRS we'll use for the request.
527 * - Fetch the map SRS (if it's EPSG)
528 * - Check if map SRS is listed in layer wms_srs metadata
529 * - If map SRS is valid for this layer then use it
530 * - Otherwise request layer in its default SRS and we'll reproject later
531 * ------------------------------------------------------------------ */
532 msOWSGetEPSGProj(&(map->projection),NULL, NULL, MS_TRUE, &pszEPSG);
533 if ( pszEPSG &&
534 (strncasecmp(pszEPSG, "EPSG:", 5) == 0 ||
535 strncasecmp(pszEPSG, "AUTO:", 5) == 0) ) {
536 const char *pszFound;
537 char *pszLyrEPSG;
538 int nLen;
539 char *pszPtr = NULL;
540
541 /* If it's an AUTO projection then keep only id and strip off */
542 /* the parameters for now (we'll restore them at the end) */
543 if (strncasecmp(pszEPSG, "AUTO:", 5) == 0) {
544 if ((pszPtr = strchr(pszEPSG, ',')))
545 *pszPtr = '\0';
546 }
547
548 nLen = strlen(pszEPSG);
549
550 msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE, &pszLyrEPSG);
551
552 if (pszLyrEPSG == NULL ||
553 (pszFound = strstr(pszLyrEPSG, pszEPSG)) == NULL ||
554 ! ((*(pszFound+nLen) == '\0') || isspace(*(pszFound+nLen))) ) {
555 /* Not found in Layer's list of SRS (including projection object) */
556 free(pszEPSG);
557 pszEPSG = NULL;
558 }
559 msFree(pszLyrEPSG);
560 if (pszEPSG && pszPtr)
561 *pszPtr = ','; /* Restore full AUTO:... definition */
562 }
563
564 if (pszEPSG == NULL) {
565 msOWSGetEPSGProj(&(lp->projection), &(lp->metadata),"MO", MS_TRUE, &pszEPSG);
566 if( pszEPSG == NULL || (strncasecmp(pszEPSG, "EPSG:", 5) != 0 && strncasecmp(pszEPSG, "AUTO:", 5) != 0) ) {
567 msSetError(MS_WMSCONNERR, "Layer must have an EPSG or AUTO projection code (in its PROJECTION object or wms_srs metadata)", "msBuildWMSLayerURL()");
568 msFree(pszEPSG);
569 return MS_FAILURE;
570 }
571 }
572
573 /* ------------------------------------------------------------------
574 * For an AUTO projection, set the Units,lon0,lat0 if not already set
575 * ------------------------------------------------------------------ */
576 if (strncasecmp(pszEPSG, "AUTO:", 5) == 0 &&
577 strchr(pszEPSG, ',') == NULL) {
578 pointObj oPoint;
579 char *pszNewEPSG;
580
581 /* Use center of map view for lon0,lat0 */
582 oPoint.x = (map->extent.minx + map->extent.maxx)/2.0;
583 oPoint.y = (map->extent.miny + map->extent.maxy)/2.0;
584 msProjectPoint(&(map->projection), &(map->latlon), &oPoint);
585
586 pszNewEPSG = (char*)msSmallMalloc(101*sizeof(char));
587
588 snprintf(pszNewEPSG, 100, "%s,9001,%.16g,%.16g",
589 pszEPSG, oPoint.x, oPoint.y);
590 pszNewEPSG[100]='\0';
591 free(pszEPSG);
592 pszEPSG=pszNewEPSG;
593 }
594
595 /*
596 * Work out whether we'll be wanting to flip the axis order for the request
597 */
598 pszUseStrictAxisOrder = msOWSLookupMetadata(&(lp->metadata), "MO", "strict_axis_order");
599 if (pszUseStrictAxisOrder != NULL) {
600 if (strncasecmp(pszUseStrictAxisOrder, "1", 1) == 0 ||
601 strncasecmp(pszUseStrictAxisOrder, "true", 4) == 0) {
602 bUseStrictAxisOrder = MS_TRUE;
603 } else if (strncasecmp(pszUseStrictAxisOrder, "0", 1) == 0 ||
604 strncasecmp(pszUseStrictAxisOrder, "false", 5) == 0) {
605 bUseStrictAxisOrder = MS_FALSE;
606 }
607 }
608 if (bUseStrictAxisOrder == MS_TRUE && pszEPSG &&
609 strncasecmp(pszEPSG, "EPSG:", 5) == 0 &&
610 msIsAxisInverted(atoi(pszEPSG + 5))) {
611 bFlipAxisOrder = MS_TRUE;
612 }
613
614 /* ------------------------------------------------------------------
615 * Set layer SRS.
616 * ------------------------------------------------------------------ */
617 /* No need to set lp->proj if it's already set to the right EPSG code */
618 {
619 char* pszEPSGCodeFromLayer = NULL;
620 msOWSGetEPSGProj(&(lp->projection), NULL, "MO", MS_TRUE, &pszEPSGCodeFromLayer);
621 if (pszEPSGCodeFromLayer == NULL || strcasecmp(pszEPSG, pszEPSGCodeFromLayer) != 0) {
622 char *ows_srs = NULL;
623 msOWSGetEPSGProj(NULL, &(lp->metadata), "MO", MS_FALSE, &ows_srs);
624 /* no need to set lp->proj if it is already set and there is only
625 one item in the _srs metadata for this layer - we will assume
626 the projection block matches the _srs metadata (the search for ' '
627 in ows_srs is a test to see if there are multiple EPSG: codes) */
628 if( lp->projection.numargs == 0 || ows_srs == NULL || (strchr(ows_srs,' ') != NULL) ) {
629 if (strncasecmp(pszEPSG, "EPSG:", 5) == 0) {
630 char szProj[20];
631 snprintf(szProj, sizeof(szProj), "init=epsg:%s", pszEPSG+5);
632 if (msLoadProjectionString(&(lp->projection), szProj) != 0) {
633 msFree(pszEPSGCodeFromLayer);
634 msFree(ows_srs);
635 return MS_FAILURE;
636 }
637 } else {
638 if (msLoadProjectionString(&(lp->projection), pszEPSG) != 0) {
639 msFree(pszEPSGCodeFromLayer);
640 msFree(ows_srs);
641 return MS_FAILURE;
642 }
643 }
644 }
645 msFree(ows_srs);
646 }
647 msFree(pszEPSGCodeFromLayer);
648 }
649
650 /* ------------------------------------------------------------------
651 * Adjust for MapServer EXTENT being center of pixel and WMS BBOX being
652 * edge of pixel (#2843).
653 * ------------------------------------------------------------------ */
654 bbox = map->extent;
655
656 bbox.minx -= map->cellsize * 0.5;
657 bbox.maxx += map->cellsize * 0.5;
658 bbox.miny -= map->cellsize * 0.5;
659 bbox.maxy += map->cellsize * 0.5;
660
661 /* -------------------------------------------------------------------- */
662 /* Reproject if needed. */
663 /* -------------------------------------------------------------------- */
664 if (msProjectionsDiffer(&(map->projection), &(lp->projection))) {
665 msProjectRect(&(map->projection), &(lp->projection), &bbox);
666
667 /* -------------------------------------------------------------------- */
668 /* Sometimes our remote WMS only accepts square pixel */
669 /* requests. If this is the case adjust adjust the number of */
670 /* pixels or lines in the request so that the pixels are */
671 /* square. */
672 /* -------------------------------------------------------------------- */
673 {
674 const char *nonsquare_ok =
675 msOWSLookupMetadata(&(lp->metadata),
676 "MO", "nonsquare_ok");
677
678 /* assume nonsquare_ok is false */
679 if( nonsquare_ok != NULL
680 && (strcasecmp(nonsquare_ok,"no") == 0
681 || strcasecmp(nonsquare_ok,"false") == 0) ) {
682 double cellsize_x = (bbox.maxx-bbox.minx) / bbox_width;
683 double cellsize_y = (bbox.maxy-bbox.miny) / bbox_height;
684
685 if( cellsize_x < cellsize_y * 0.999999 ) {
686 int new_bbox_height =
687 ceil((cellsize_y/cellsize_x) * bbox_height);
688
689 if (lp->debug)
690 msDebug("NONSQUARE_OK=%s, adjusted HEIGHT from %d to %d to equalize cellsize at %g.\n",
691 nonsquare_ok,
692 bbox_height, new_bbox_height, cellsize_x );
693 bbox_height = new_bbox_height;
694 } else if( cellsize_y < cellsize_x * 0.999999 ) {
695 int new_bbox_width =
696 ceil((cellsize_x/cellsize_y) * bbox_width);
697
698 if (lp->debug)
699 msDebug("NONSQUARE_OK=%s, adjusted WIDTH from %d to %d to equalize cellsize at %g.\n",
700 nonsquare_ok,
701 bbox_width, new_bbox_width, cellsize_y );
702 bbox_width = new_bbox_width;
703 } else {
704 if (lp->debug)
705 msDebug("NONSQUARE_OK=%s, but cellsize was already square - no change.\n",
706 nonsquare_ok );
707 }
708 }
709 }
710 }
711
712 /* -------------------------------------------------------------------- */
713 /* If the layer has predefined extents, and a predefined */
714 /* projection that matches the request projection, then */
715 /* consider restricting the BBOX to match the limits. */
716 /* -------------------------------------------------------------------- */
717 if( bbox_width != 0 ) {
718 char *ows_srs;
719 rectObj layer_rect;
720
721 msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE, &ows_srs);
722
723 if( ows_srs && strchr(ows_srs,' ') == NULL
724 && msOWSGetLayerExtent( map, lp, "MO", &layer_rect) == MS_SUCCESS ) {
725 /* fulloverlap */
726 if( msRectContained( &bbox, &layer_rect ) ) {
727 /* no changes */
728 }
729
730 /* no overlap */
731 else if( !msRectOverlap( &layer_rect, &bbox ) ) {
732 bbox_width = 0;
733 bbox_height = 0;
734 }
735
736 else {
737 double cellsize_x = (bbox.maxx-bbox.minx) / bbox_width;
738 double cellsize_y = (bbox.maxy-bbox.miny) / bbox_height;
739 double cellsize = MS_MIN(cellsize_x,cellsize_y);
740
741 msRectIntersect( &bbox, &layer_rect );
742
743 bbox_width = round((bbox.maxx - bbox.minx) / cellsize);
744 bbox_height = round((bbox.maxy - bbox.miny) / cellsize);
745
746 /* Force going through the resampler if we're going to receive a clipped BBOX (#4931) */
747 if(msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) {
748 msLayerSetProcessingKey(lp, "RESAMPLE", "nearest");
749 }
750 }
751 }
752 msFree(ows_srs);
753 }
754
755 /* -------------------------------------------------------------------- */
756 /* Potentially return the bbox. */
757 /* -------------------------------------------------------------------- */
758 if (bbox_ret != NULL)
759 *bbox_ret = bbox;
760
761 if( width_ret != NULL )
762 *width_ret = bbox_width;
763
764 if( height_ret != NULL )
765 *height_ret = bbox_height;
766
767 /* ------------------------------------------------------------------
768 * Build the request URL.
769 * At this point we set only the following parameters for GetMap:
770 * REQUEST
771 * SRS (or CRS)
772 * BBOX
773 *
774 * And for GetFeatureInfo:
775 * X (I for 1.3.0)
776 * Y (J for 1.3.0)
777 * INFO_FORMAT
778 * FEATURE_COUNT (only if nFeatureCount > 0)
779 *
780 * The connection string should contain all other required params,
781 * including:
782 * VERSION
783 * LAYERS
784 * FORMAT
785 * TRANSPARENT
786 * STYLES
787 * QUERY_LAYERS (for queryable layers only)
788 * ------------------------------------------------------------------ */
789
790 /* ------------------------------------------------------------------
791 * Sometimes a requested layer is essential for the map, so if the
792 * request fails or an error is delivered, the map has not to be drawn
793 * ------------------------------------------------------------------ */
794 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
795 "MO", "essential")) != NULL) {
796 if( strcasecmp(pszTmp,"true") == 0
797 || strcasecmp(pszTmp,"on") == 0
798 || strcasecmp(pszTmp,"yes") == 0 )
799 bIsEssential = MS_TRUE;
800 else
801 bIsEssential = atoi(pszTmp);
802 }
803
804 if (nRequestType == WMS_GETFEATUREINFO) {
805 char szBuf[100] = "";
806
807 if (nVersion >= OWS_1_0_7)
808 pszRequestParam = "GetFeatureInfo";
809 else
810 pszRequestParam = "feature_info";
811
812 if (nVersion >= OWS_1_3_0)
813 pszExceptionsParam = "XML";
814 else if (nVersion >= OWS_1_1_0) /* 1.1.0 to 1.1.0 */
815 pszExceptionsParam = "application/vnd.ogc.se_xml";
816 else if (nVersion > OWS_1_0_0) /* 1.0.1 to 1.0.7 */
817 pszExceptionsParam = "SE_XML";
818 else
819 pszExceptionsParam = "WMS_XML";
820
821 msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
822 msSetWMSParamInt( psWMSParams, "WIDTH", bbox_width);
823 msSetWMSParamInt( psWMSParams, "HEIGHT", bbox_height);
824
825 msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
826
827 if (bFlipAxisOrder == MS_TRUE) {
828 snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
829 bbox.miny, bbox.minx, bbox.maxy, bbox.maxx);
830 } else {
831 snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
832 bbox.minx, bbox.miny, bbox.maxx, bbox.maxy);
833 }
834 msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
835
836 if (nVersion >= OWS_1_3_0) {
837 msSetWMSParamInt( psWMSParams, "I", nClickX);
838 msSetWMSParamInt( psWMSParams, "J", nClickY);
839 } else {
840 msSetWMSParamInt( psWMSParams, "X", nClickX);
841 msSetWMSParamInt( psWMSParams, "Y", nClickY);
842 }
843
844 msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam, MS_FALSE, nVersion);
845 msSetWMSParamString(psWMSParams, "INFO_FORMAT", pszInfoFormat, MS_TRUE, nVersion);
846
847 if (pszQueryLayers) { /* not set in CONNECTION string */
848 msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszQueryLayers, MS_FALSE, nVersion);
849 }
850
851 /* If FEATURE_COUNT <= 0 then don't pass this parameter */
852 /* The spec states that FEATURE_COUNT must be greater than zero */
853 /* and if not passed then the behavior is up to the server */
854 if (nFeatureCount > 0) {
855 msSetWMSParamInt(psWMSParams, "FEATURE_COUNT", nFeatureCount);
856 }
857
858 } else if (nRequestType == WMS_GETLEGENDGRAPHIC) {
859 if(map->extent.maxx > map->extent.minx && map->width > 0 && map->height > 0) {
860 char szBuf[20] = "";
861 double scaledenom;
862 msCalculateScale(map->extent, map->units, map->width, map->height,
863 map->resolution, &scaledenom);
864 snprintf(szBuf, 20, "%g",scaledenom);
865 msSetWMSParamString(psWMSParams, "SCALE", szBuf, MS_FALSE, nVersion);
866 }
867 pszRequestParam = "GetLegendGraphic";
868
869 pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
870 "MO", "exceptions_format");
871 if (pszExceptionsParam == NULL) {
872 if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
873 pszExceptionsParam = "application/vnd.ogc.se_inimage";
874 else
875 pszExceptionsParam = "INIMAGE";
876 }
877
878 if (pszLayer) { /* not set in CONNECTION string */
879 msSetWMSParamString(psWMSParams, "LAYER", pszLayer, MS_FALSE, nVersion);
880 }
881
882 msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
883 msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
884
885 if (nVersion >= OWS_1_3_0) {
886 msSetWMSParamString(psWMSParams, "SLD_VERSION", "1.1.0", MS_FALSE, nVersion);
887 }
888
889 } else { /* if (nRequestType == WMS_GETMAP) */
890 char szBuf[100] = "";
891
892 if (nVersion >= OWS_1_0_7)
893 pszRequestParam = "GetMap";
894 else
895 pszRequestParam = "map";
896
897 pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
898 "MO", "exceptions_format");
899
900 if (!bIsEssential) {
901 if (pszExceptionsParam == NULL) {
902 if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
903 pszExceptionsParam = "application/vnd.ogc.se_inimage";
904 else
905 pszExceptionsParam = "INIMAGE";
906 }
907 } else {
908 /* if layer is essential, do not emit EXCEPTIONS parameter (defaults to XML) */
909 pszExceptionsParam = NULL;
910 }
911
912 msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
913 msSetWMSParamInt( psWMSParams, "WIDTH", bbox_width);
914 msSetWMSParamInt( psWMSParams, "HEIGHT", bbox_height);
915 msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
916
917 if (bFlipAxisOrder == MS_TRUE) {
918 snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
919 bbox.miny, bbox.minx, bbox.maxy, bbox.maxx);
920 } else {
921 snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
922 bbox.minx, bbox.miny, bbox.maxx, bbox.maxy);
923 }
924 msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
925 if( pszExceptionsParam ) {
926 msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam, MS_FALSE, nVersion);
927 }
928 }
929
930 free(pszEPSG);
931
932 return MS_SUCCESS;
933
934 #else
935 /* ------------------------------------------------------------------
936 * WMS CONNECTION Support not included...
937 * ------------------------------------------------------------------ */
938 msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
939 "msBuildWMSLayerURL()");
940 return MS_FAILURE;
941
942 #endif /* USE_WMS_LYR */
943
944 }
945
946
947 /**********************************************************************
948 * msWMSGetFeatureInfoURL()
949 *
950 * Build a GetFeatureInfo URL for this layer.
951 *
952 * Returns a reference to a newly allocated string that should be freed
953 * by the caller.
954 **********************************************************************/
msWMSGetFeatureInfoURL(mapObj * map,layerObj * lp,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat)955 char *msWMSGetFeatureInfoURL(mapObj *map, layerObj *lp,
956 int nClickX, int nClickY, int nFeatureCount,
957 const char *pszInfoFormat)
958 {
959 wmsParamsObj sThisWMSParams;
960 char *pszURL;
961
962 msInitWmsParamsObj(&sThisWMSParams);
963
964 if (msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO,
965 nClickX, nClickY, nFeatureCount,
966 pszInfoFormat, NULL, NULL, NULL,
967 &sThisWMSParams)!= MS_SUCCESS) {
968 return NULL;
969 }
970
971 pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
972 msFreeWmsParamsObj(&sThisWMSParams);
973
974 return pszURL;
975 }
976
977 /**********************************************************************
978 * msPrepareWMSLayerRequest()
979 *
980 **********************************************************************/
981
msPrepareWMSLayerRequest(int nLayerId,mapObj * map,layerObj * lp,int nRequestType,enum MS_CONNECTION_TYPE lastconnectiontype,wmsParamsObj * psLastWMSParams,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,httpRequestObj * pasReqInfo,int * numRequests)982 int msPrepareWMSLayerRequest(int nLayerId, mapObj *map, layerObj *lp,
983 int nRequestType, enum MS_CONNECTION_TYPE lastconnectiontype,
984 wmsParamsObj *psLastWMSParams,
985 int nClickX, int nClickY, int nFeatureCount, const char *pszInfoFormat,
986 httpRequestObj *pasReqInfo, int *numRequests)
987 {
988 #ifdef USE_WMS_LYR
989 char *pszURL = NULL, *pszHTTPCookieData = NULL;
990 const char *pszTmp;
991 rectObj bbox = { 0 };
992 int bbox_width = 0, bbox_height = 0;
993 int nTimeout, bOkToMerge, bForceSeparateRequest, bCacheToDisk;
994 wmsParamsObj sThisWMSParams;
995
996 if (lp->connectiontype != MS_WMS)
997 return MS_FAILURE;
998
999 msInitWmsParamsObj(&sThisWMSParams);
1000
1001 /* ------------------------------------------------------------------
1002 * Build the request URL, this will also set layer projection and
1003 * compute BBOX in that projection.
1004 * ------------------------------------------------------------------ */
1005
1006
1007 if (nRequestType == WMS_GETMAP &&
1008 ( msBuildWMSLayerURL(map, lp, WMS_GETMAP,
1009 0, 0, 0, NULL, &bbox, &bbox_width, &bbox_height,
1010 &sThisWMSParams) != MS_SUCCESS) ) {
1011 /* an error was already reported. */
1012 msFreeWmsParamsObj(&sThisWMSParams);
1013 return MS_FAILURE;
1014 }
1015
1016 else if (nRequestType == WMS_GETFEATUREINFO &&
1017 msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO,
1018 nClickX, nClickY, nFeatureCount, pszInfoFormat,
1019 NULL, NULL, NULL,
1020 &sThisWMSParams) != MS_SUCCESS ) {
1021 /* an error was already reported. */
1022 msFreeWmsParamsObj(&sThisWMSParams);
1023 return MS_FAILURE;
1024 } else if (nRequestType == WMS_GETLEGENDGRAPHIC &&
1025 msBuildWMSLayerURL(map, lp, WMS_GETLEGENDGRAPHIC,
1026 0, 0, 0, NULL,
1027 NULL, NULL, NULL,
1028 &sThisWMSParams) != MS_SUCCESS ) {
1029 /* an error was already reported. */
1030 msFreeWmsParamsObj(&sThisWMSParams);
1031 return MS_FAILURE;
1032 }
1033
1034
1035 /* ------------------------------------------------------------------
1036 * Check if the request is empty, perhaps due to reprojection problems
1037 * or wms_extents restrictions.
1038 * ------------------------------------------------------------------ */
1039 if ((nRequestType == WMS_GETMAP) && (bbox_width == 0 || bbox_height == 0) ) {
1040 msFreeWmsParamsObj(&sThisWMSParams);
1041 return MS_SUCCESS; /* No overlap. */
1042 }
1043
1044 /* ------------------------------------------------------------------
1045 * Check if layer overlaps current view window (using wms_latlonboundingbox)
1046 * ------------------------------------------------------------------ */
1047 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1048 "MO", "latlonboundingbox")) != NULL) {
1049 char **tokens;
1050 int n;
1051 rectObj ext;
1052
1053 tokens = msStringSplit(pszTmp, ' ', &n);
1054 if (tokens==NULL || n != 4) {
1055 msSetError(MS_WMSCONNERR, "Wrong number of arguments for 'wms_latlonboundingbox' metadata.",
1056 "msDrawWMSLayer()");
1057 msFreeWmsParamsObj(&sThisWMSParams);
1058 return MS_FAILURE;
1059 }
1060
1061 ext.minx = atof(tokens[0]);
1062 ext.miny = atof(tokens[1]);
1063 ext.maxx = atof(tokens[2]);
1064 ext.maxy = atof(tokens[3]);
1065
1066 msFreeCharArray(tokens, n);
1067
1068 /* Reproject latlonboundingbox to the selected SRS for the layer and */
1069 /* check if it overlaps the bbox that we calculated for the request */
1070
1071 msProjectRect(&(map->latlon), &(lp->projection), &ext);
1072 if (!msRectOverlap(&bbox, &ext)) {
1073 /* No overlap... nothing to do */
1074
1075 msFreeWmsParamsObj(&sThisWMSParams);
1076 return MS_SUCCESS; /* No overlap. */
1077 }
1078 }
1079
1080 /* ------------------------------------------------------------------
1081 * check to see if a the metadata wms_connectiontimeout is set. If it is
1082 * the case we will use it, else we use the default which is 30 seconds.
1083 * First check the metadata in the layer object and then in the map object.
1084 * ------------------------------------------------------------------ */
1085 nTimeout = 30; /* Default is 30 seconds */
1086 if ((pszTmp = msOWSLookupMetadata2(&(lp->metadata), &(map->web.metadata),
1087 "MO", "connectiontimeout")) != NULL) {
1088 nTimeout = atoi(pszTmp);
1089 }
1090
1091 /* ------------------------------------------------------------------
1092 * Check if we want to use in memory images instead of writing to disk.
1093 * ------------------------------------------------------------------ */
1094 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1095 "MO", "cache_to_disk")) != NULL) {
1096 if( strcasecmp(pszTmp,"true") == 0
1097 || strcasecmp(pszTmp,"on") == 0
1098 || strcasecmp(pszTmp,"yes") == 0 )
1099 bCacheToDisk = MS_TRUE;
1100 else
1101 bCacheToDisk = atoi(pszTmp);
1102 } else
1103 bCacheToDisk = MS_FALSE;
1104
1105 if( bCacheToDisk ) {
1106 /* We'll store the remote server's response to a tmp file. */
1107 if (map->web.imagepath == NULL || strlen(map->web.imagepath) == 0) {
1108 msSetError(MS_WMSERR,
1109 "WEB.IMAGEPATH must be set to use WMS client connections.",
1110 "msPrepareWMSLayerRequest()");
1111 return MS_FAILURE;
1112 }
1113 }
1114
1115 /* ------------------------------------------------------------------
1116 * Check if layer can be merged with previous WMS layer requests
1117 * Metadata wms_force_separate_request can be set to 1 to prevent this
1118 * this layer from being combined with any other layer.
1119 * ------------------------------------------------------------------ */
1120 bForceSeparateRequest = MS_FALSE;
1121 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1122 "MO", "force_separate_request")) != NULL) {
1123 bForceSeparateRequest = atoi(pszTmp);
1124 }
1125 bOkToMerge = MS_FALSE;
1126 if (!bForceSeparateRequest &&
1127 lastconnectiontype == MS_WMS &&
1128 psLastWMSParams != NULL &&
1129 sThisWMSParams.numparams == psLastWMSParams->numparams &&
1130 strcmp(sThisWMSParams.onlineresource,
1131 psLastWMSParams->onlineresource) == 0) {
1132 const char *key, *value1, *value2;
1133 bOkToMerge = MS_TRUE;
1134
1135 key = msFirstKeyFromHashTable(sThisWMSParams.params);
1136 while (key != NULL && bOkToMerge == MS_TRUE) {
1137 /* Skip parameters whose values can be different */
1138 if (!(strcmp(key, "LAYERS") == 0 ||
1139 strcmp(key, "QUERY_LAYERS") == 0 ||
1140 strcmp(key, "STYLES") == 0) ) {
1141 value1 = msLookupHashTable(psLastWMSParams->params, key);
1142 value2 = msLookupHashTable(sThisWMSParams.params, key);
1143
1144 if (value1==NULL || value2==NULL ||
1145 strcmp(value1, value2) != 0) {
1146 bOkToMerge = MS_FALSE;
1147 break;
1148 }
1149 }
1150 key = msNextKeyFromHashTable(sThisWMSParams.params, key);
1151 }
1152 }
1153
1154 /*------------------------------------------------------------------
1155 * Check to see if there's a HTTP Cookie to forward
1156 * If Cookie differ between the two connection, it's NOT OK to merge
1157 * the connection
1158 * ------------------------------------------------------------------ */
1159 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1160 "MO", "http_cookie")) != NULL) {
1161 if(strcasecmp(pszTmp, "forward") == 0) {
1162 pszTmp= msLookupHashTable(&(map->web.metadata),"http_cookie_data");
1163 if(pszTmp != NULL) {
1164 pszHTTPCookieData = msStrdup(pszTmp);
1165 }
1166 } else {
1167 pszHTTPCookieData = msStrdup(pszTmp);
1168 }
1169 } else if ((pszTmp = msOWSLookupMetadata(&(map->web.metadata),
1170 "MO", "http_cookie")) != NULL) {
1171 if(strcasecmp(pszTmp, "forward") == 0) {
1172 pszTmp= msLookupHashTable(&(map->web.metadata),"http_cookie_data");
1173 if(pszTmp != NULL) {
1174 pszHTTPCookieData = msStrdup(pszTmp);
1175 }
1176 } else {
1177 pszHTTPCookieData = msStrdup(pszTmp);
1178 }
1179 }
1180
1181 if(bOkToMerge && pszHTTPCookieData != sThisWMSParams.httpcookiedata) {
1182 if(pszHTTPCookieData == NULL || sThisWMSParams.httpcookiedata == NULL) {
1183 bOkToMerge = MS_FALSE;
1184 }
1185 if(strcmp(pszHTTPCookieData, sThisWMSParams.httpcookiedata) != 0) {
1186 bOkToMerge = MS_FALSE;
1187 }
1188 }
1189
1190 if (bOkToMerge) {
1191 /* Merge both requests into sThisWMSParams
1192 */
1193 const char *value1, *value2;
1194 char *keys[] = {"LAYERS", "QUERY_LAYERS", "STYLES"};
1195 int i;
1196
1197 for(i=0; i<3; i++) {
1198 value1 = msLookupHashTable(psLastWMSParams->params, keys[i]);
1199 value2 = msLookupHashTable(sThisWMSParams.params, keys[i]);
1200 if (value1 && value2) {
1201 char *pszBuf;
1202 int nLen;
1203
1204 nLen = strlen(value1) + strlen(value2) +2;
1205 pszBuf = malloc(nLen);
1206 MS_CHECK_ALLOC(pszBuf, nLen, MS_FAILURE);
1207
1208 snprintf(pszBuf, nLen, "%s,%s", value1, value2);
1209 /* TODO should really send the server request version here */
1210 msSetWMSParamString(&sThisWMSParams, keys[i], pszBuf,MS_FALSE, OWS_VERSION_NOTSET);
1211
1212 /* This key existed already, we don't want it counted twice */
1213 sThisWMSParams.numparams--;
1214
1215 msFree(pszBuf);
1216 }
1217 }
1218 }
1219
1220
1221 /* ------------------------------------------------------------------
1222 * Build new request URL
1223 * ------------------------------------------------------------------ */
1224 pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
1225
1226 if (bOkToMerge && (*numRequests)>0) {
1227 /* ------------------------------------------------------------------
1228 * Update the last request in the array: (*numRequests)-1
1229 * ------------------------------------------------------------------ */
1230 msFree(pasReqInfo[(*numRequests)-1].pszGetUrl);
1231 pasReqInfo[(*numRequests)-1].pszGetUrl = pszURL;
1232 pszURL = NULL;
1233 pasReqInfo[(*numRequests)-1].debug |= lp->debug;
1234 if (nTimeout > pasReqInfo[(*numRequests)-1].nTimeout)
1235 pasReqInfo[(*numRequests)-1].nTimeout = nTimeout;
1236 } else {
1237 /* ------------------------------------------------------------------
1238 * Add a request to the array (already preallocated)
1239 * ------------------------------------------------------------------ */
1240 pasReqInfo[(*numRequests)].nLayerId = nLayerId;
1241 pasReqInfo[(*numRequests)].pszGetUrl = pszURL;
1242
1243 pszURL = NULL;
1244 pasReqInfo[(*numRequests)].pszHTTPCookieData = pszHTTPCookieData;
1245 pszHTTPCookieData = NULL;
1246 if( bCacheToDisk ) {
1247 pasReqInfo[(*numRequests)].pszOutputFile =
1248 msTmpFile(map, map->mappath, NULL, "wms.tmp");
1249 } else
1250 pasReqInfo[(*numRequests)].pszOutputFile = NULL;
1251 pasReqInfo[(*numRequests)].nStatus = 0;
1252 pasReqInfo[(*numRequests)].nTimeout = nTimeout;
1253 pasReqInfo[(*numRequests)].bbox = bbox;
1254 pasReqInfo[(*numRequests)].width = bbox_width;
1255 pasReqInfo[(*numRequests)].height = bbox_height;
1256 pasReqInfo[(*numRequests)].debug = lp->debug;
1257
1258 if (msHTTPAuthProxySetup(&(map->web.metadata), &(lp->metadata),
1259 pasReqInfo, *numRequests, map, "MO") != MS_SUCCESS)
1260 return MS_FAILURE;
1261
1262 (*numRequests)++;
1263 }
1264
1265
1266 /* ------------------------------------------------------------------
1267 * Replace contents of psLastWMSParams with sThisWMSParams
1268 * unless bForceSeparateRequest is set in which case we make it empty
1269 * ------------------------------------------------------------------ */
1270 if (psLastWMSParams) {
1271 msFreeWmsParamsObj(psLastWMSParams);
1272 if (!bForceSeparateRequest)
1273 *psLastWMSParams = sThisWMSParams;
1274 else
1275 msInitWmsParamsObj(psLastWMSParams);
1276 } else {
1277 /* Can't copy it, so we just free it */
1278 msFreeWmsParamsObj(&sThisWMSParams);
1279 }
1280
1281 return MS_SUCCESS;
1282
1283 #else
1284 /* ------------------------------------------------------------------
1285 * WMS CONNECTION Support not included...
1286 * ------------------------------------------------------------------ */
1287 msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1288 "msDrawWMSLayer()");
1289 return(MS_FAILURE);
1290
1291 #endif /* USE_WMS_LYR */
1292
1293 }
1294
1295 /**********************************************************************
1296 * msDrawWMSLayerLow()
1297 *
1298 **********************************************************************/
1299
msDrawWMSLayerLow(int nLayerId,httpRequestObj * pasReqInfo,int numRequests,mapObj * map,layerObj * lp,imageObj * img)1300 int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo,
1301 int numRequests, mapObj *map, layerObj *lp, imageObj *img)
1302 {
1303 #ifdef USE_WMS_LYR
1304 int status = MS_SUCCESS;
1305 int iReq = -1;
1306 char szPath[MS_MAXPATHLEN];
1307 int currenttype;
1308 int currentconnectiontype;
1309 int numclasses;
1310 char *mem_filename = NULL;
1311 const char *pszTmp;
1312 int bIsEssential = MS_FALSE;
1313
1314 /* ------------------------------------------------------------------
1315 * Sometimes a requested layer is essential for the map, so if the
1316 * request fails or an error is delivered, the map has not to be drawn
1317 * ------------------------------------------------------------------ */
1318 if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1319 "MO", "essential")) != NULL) {
1320 if( strcasecmp(pszTmp,"true") == 0
1321 || strcasecmp(pszTmp,"on") == 0
1322 || strcasecmp(pszTmp,"yes") == 0 )
1323 bIsEssential = MS_TRUE;
1324 else
1325 bIsEssential = atoi(pszTmp);
1326 }
1327
1328 /* ------------------------------------------------------------------
1329 * Find the request info for this layer in the array, based on nLayerId
1330 * ------------------------------------------------------------------ */
1331 for(iReq=0; iReq<numRequests; iReq++) {
1332 if (pasReqInfo[iReq].nLayerId == nLayerId)
1333 break;
1334 }
1335
1336 if (iReq == numRequests) {
1337 /* This layer was skipped or was included in a multi-layers
1338 * request ... nothing to do.
1339 */
1340 return MS_SUCCESS;
1341 }
1342
1343 if ( !MS_HTTP_SUCCESS( pasReqInfo[iReq].nStatus ) ) {
1344 /* ====================================================================
1345 Failed downloading layer... we log an error but we still return
1346 SUCCESS here so that the layer is only skipped instead of aborting
1347 the whole draw map.
1348 If the layer is essential the map is not to be drawn.
1349 ==================================================================== */
1350 msSetError(MS_WMSERR,
1351 "WMS GetMap request failed for layer '%s' (Status %d: %s).",
1352 "msDrawWMSLayerLow()",
1353 (lp->name?lp->name:"(null)"),
1354 pasReqInfo[iReq].nStatus, pasReqInfo[iReq].pszErrBuf );
1355
1356 if (!bIsEssential)
1357 return MS_SUCCESS;
1358 else
1359 return MS_FAILURE;
1360 }
1361
1362 /* ------------------------------------------------------------------
1363 * Check the Content-Type of the response to see if we got an exception,
1364 * if yes then try to parse it and pass the info to msSetError().
1365 * We log an error but we still return SUCCESS here so that the layer
1366 * is only skipped instead of aborting the whole draw map.
1367 * If the layer is essential the map is not to be drawn.
1368 * ------------------------------------------------------------------ */
1369 if (pasReqInfo[iReq].pszContentType &&
1370 (strcmp(pasReqInfo[iReq].pszContentType, "text/xml") == 0 ||
1371 strcmp(pasReqInfo[iReq].pszContentType, "application/vnd.ogc.se_xml") == 0)) {
1372 FILE *fp;
1373 char szBuf[MS_BUFFER_LENGTH];
1374
1375 if( pasReqInfo[iReq].pszOutputFile ) {
1376 fp = fopen(pasReqInfo[iReq].pszOutputFile, "r");
1377 if (fp) {
1378 /* TODO: For now we'll only read the first chunk and return it
1379 * via msSetError()... we should really try to parse the XML
1380 * and extract the exception code/message though
1381 */
1382 size_t nSize;
1383
1384 nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH-1, fp);
1385 if (nSize < MS_BUFFER_LENGTH)
1386 szBuf[nSize] = '\0';
1387 else {
1388 strlcpy(szBuf, "(!!!)", sizeof(szBuf)); /* This should never happen */
1389 }
1390
1391 fclose(fp);
1392
1393 /* We're done with the remote server's response... delete it. */
1394 if (!lp->debug)
1395 unlink(pasReqInfo[iReq].pszOutputFile);
1396 } else {
1397 strlcpy(szBuf, "(Failed to open exception response)", sizeof(szBuf));
1398 }
1399 } else {
1400 strlcpy( szBuf, pasReqInfo[iReq].result_data, MS_BUFFER_LENGTH );
1401 }
1402
1403 if (lp->debug)
1404 msDebug("WMS GetMap request got XML exception for layer '%s': %s.",
1405 (lp->name?lp->name:"(null)"), szBuf );
1406
1407 msSetError(MS_WMSERR,
1408 "WMS GetMap request got XML exception for layer '%s': %s.",
1409 "msDrawWMSLayerLow()",
1410 (lp->name?lp->name:"(null)"), szBuf );
1411
1412 if (!bIsEssential)
1413 return MS_SUCCESS;
1414 else
1415 return MS_FAILURE;
1416 }
1417
1418 /* ------------------------------------------------------------------
1419 * If the output was written to a memory buffer, then we will need
1420 * to attach a "VSI" name to this buffer.
1421 * ------------------------------------------------------------------ */
1422 if( pasReqInfo[iReq].pszOutputFile == NULL ) {
1423 mem_filename = msTmpFile(map, NULL, "/vsimem/msout/", "img.tmp" );
1424
1425 VSIFCloseL(
1426 VSIFileFromMemBuffer( mem_filename,
1427 (GByte*) pasReqInfo[iReq].result_data,
1428 (vsi_l_offset) pasReqInfo[iReq].result_size,
1429 FALSE ) );
1430 }
1431
1432 /* ------------------------------------------------------------------
1433 * Prepare layer for drawing, reprojecting the image received from the
1434 * server if needed...
1435 * ------------------------------------------------------------------ */
1436 /* keep the current type that will be restored at the end of this */
1437 /* function. */
1438 currenttype = lp->type;
1439 currentconnectiontype = lp->connectiontype;
1440 lp->type = MS_LAYER_RASTER;
1441 lp->connectiontype = MS_SHAPEFILE;
1442
1443 /* set the classes to 0 so that It won't do client side */
1444 /* classification if an sld was set. */
1445 numclasses = lp->numclasses;
1446
1447 /* ensure the file connection is closed right away after the layer */
1448 /* is rendered */
1449 msLayerSetProcessingKey( lp, "CLOSE_CONNECTION", "NORMAL");
1450
1451 if (msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body") ||
1452 msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url"))
1453 lp->numclasses = 0;
1454
1455 if (lp->data) free(lp->data);
1456 if( mem_filename != NULL )
1457 lp->data = mem_filename;
1458 else
1459 lp->data = msStrdup(pasReqInfo[iReq].pszOutputFile);
1460
1461 /* #3138 If PROCESSING "RESAMPLE=..." is set we cannot use the simple case */
1462 if (!msProjectionsDiffer(&(map->projection), &(lp->projection)) &&
1463 (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) ) {
1464 /* The simple case... no reprojection needed... render layer directly. */
1465 lp->transform = MS_FALSE;
1466 /* if (msDrawRasterLayerLow(map, lp, img) != 0) */
1467 /* status = MS_FAILURE; */
1468 if (msDrawLayer(map, lp, img) != 0)
1469 status = MS_FAILURE;
1470 } else {
1471 FILE *fp;
1472 char *wldfile;
1473 /* OK, we have to resample the raster to map projection... */
1474 lp->transform = MS_TRUE;
1475 msLayerSetProcessingKey( lp, "LOAD_WHOLE_IMAGE", "YES" );
1476
1477 /* Create a world file with raster extents */
1478 /* One line per value, in this order: cx, 0, 0, cy, ulx, uly */
1479 wldfile = msBuildPath(szPath, lp->map->mappath, lp->data);
1480 if (wldfile && (strlen(wldfile)>=3))
1481 strcpy(wldfile+strlen(wldfile)-3, "wld");
1482 if (wldfile && (fp = VSIFOpenL(wldfile, "wt")) != NULL) {
1483 double dfCellSizeX = MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.minx,
1484 pasReqInfo[iReq].bbox.maxx,
1485 pasReqInfo[iReq].width);
1486 double dfCellSizeY = MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.maxy,
1487 pasReqInfo[iReq].bbox.miny,
1488 pasReqInfo[iReq].height);
1489 char world_text[5000];
1490
1491 sprintf( world_text, "%.12f\n0\n0\n%.12f\n%.12f\n%.12f\n",
1492 dfCellSizeX,
1493 dfCellSizeY,
1494 pasReqInfo[iReq].bbox.minx + dfCellSizeX * 0.5,
1495 pasReqInfo[iReq].bbox.maxy + dfCellSizeY * 0.5 );
1496
1497 VSIFWriteL( world_text, 1, strlen(world_text), fp );
1498 VSIFCloseL( fp );
1499
1500 /* GDAL should be called to reproject automatically. */
1501 if (msDrawLayer(map, lp, img) != 0)
1502 status = MS_FAILURE;
1503
1504 if (!lp->debug || mem_filename != NULL)
1505 VSIUnlink( wldfile );
1506 } else {
1507 msSetError(MS_WMSCONNERR,
1508 "Unable to create wld file for WMS slide.",
1509 "msDrawWMSLayer()");
1510 status = MS_FAILURE;
1511 }
1512
1513 }
1514
1515 /* We're done with the remote server's response... delete it. */
1516 if (!lp->debug || mem_filename != NULL)
1517 VSIUnlink(lp->data);
1518
1519 /* restore prveious type */
1520 lp->type = currenttype;
1521 lp->connectiontype = currentconnectiontype;
1522
1523 /* restore previous numclasses */
1524 lp->numclasses = numclasses;
1525
1526 free(lp->data);
1527 lp->data = NULL;
1528
1529 return status;
1530
1531 #else
1532 /* ------------------------------------------------------------------
1533 * WMS CONNECTION Support not included...
1534 * ------------------------------------------------------------------ */
1535 msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1536 "msDrawWMSLayer()");
1537 return(MS_FAILURE);
1538
1539 #endif /* USE_WMS_LYR */
1540
1541 }
1542
1543
msWMSLayerExecuteRequest(mapObj * map,int nOWSLayers,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,int type)1544 int msWMSLayerExecuteRequest(mapObj *map, int nOWSLayers, int nClickX, int nClickY,
1545 int nFeatureCount, const char *pszInfoFormat, int type)
1546 {
1547 #ifdef USE_WMS_LYR
1548
1549 msIOContext *context;
1550
1551 httpRequestObj *pasReqInfo;
1552 wmsParamsObj sLastWMSParams;
1553 int i, numReq = 0;
1554
1555 pasReqInfo = (httpRequestObj *)msSmallMalloc((nOWSLayers+1)*sizeof(httpRequestObj));
1556 msHTTPInitRequestObj(pasReqInfo, nOWSLayers+1);
1557 msInitWmsParamsObj(&sLastWMSParams);
1558
1559 /* Generate the http request */
1560 for (i=0; i<map->numlayers; i++) {
1561 if (GET_LAYER(map,map->layerorder[i])->status == MS_ON) {
1562 if (type == WMS_GETFEATUREINFO ) {
1563 if( msPrepareWMSLayerRequest(map->layerorder[i], map, GET_LAYER(map,map->layerorder[i]),
1564 WMS_GETFEATUREINFO,
1565 MS_WMS, &sLastWMSParams,
1566 nClickX, nClickY, nFeatureCount, pszInfoFormat,
1567 pasReqInfo, &numReq) == MS_FAILURE) {
1568 msFreeWmsParamsObj(&sLastWMSParams);
1569 msFree(pasReqInfo);
1570 return MS_FAILURE;
1571 }
1572 } else if (msPrepareWMSLayerRequest(map->layerorder[i], map, GET_LAYER(map,map->layerorder[i]),
1573 WMS_GETLEGENDGRAPHIC,
1574 MS_WMS, &sLastWMSParams,
1575 0, 0, 0, NULL,
1576 pasReqInfo, &numReq) == MS_FAILURE) {
1577 msFreeWmsParamsObj(&sLastWMSParams);
1578 msFree(pasReqInfo);
1579 return MS_FAILURE;
1580 }
1581 }
1582 }
1583
1584 if (msOWSExecuteRequests(pasReqInfo, numReq, map, MS_FALSE) == MS_FAILURE) {
1585 msHTTPFreeRequestObj(pasReqInfo, numReq);
1586 msFree(pasReqInfo);
1587 msFreeWmsParamsObj(&sLastWMSParams);
1588 return MS_FAILURE;
1589 }
1590
1591 context = msIO_getHandler( stdout );
1592 if( context == NULL ) {
1593 msHTTPFreeRequestObj(pasReqInfo, numReq);
1594 msFree(pasReqInfo);
1595 msFreeWmsParamsObj(&sLastWMSParams);
1596 return MS_FAILURE;
1597 }
1598
1599 msIO_printf("Content-Type: %s%c%c",pasReqInfo[0].pszContentType, 10,10);
1600
1601 if( pasReqInfo[0].pszOutputFile ) {
1602 FILE *fp;
1603 char szBuf[MS_BUFFER_LENGTH];
1604
1605 fp = fopen(pasReqInfo[0].pszOutputFile, "r");
1606 if (fp) {
1607 while(1) {
1608 size_t nSize;
1609 nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH-1, fp);
1610 if (nSize > 0)
1611 msIO_contextWrite( context,
1612 szBuf,
1613 nSize);
1614 if (nSize != MS_BUFFER_LENGTH-1)
1615 break;
1616 }
1617 fclose(fp);
1618 if (!map->debug)
1619 unlink(pasReqInfo[0].pszOutputFile);
1620 } else {
1621 msSetError(MS_IOERR, "'%s'.",
1622 "msWMSLayerExecuteRequest()", pasReqInfo[0].pszOutputFile);
1623 return MS_FAILURE;
1624 }
1625 } else {
1626 msIO_contextWrite( context,
1627 pasReqInfo[0].result_data,
1628 pasReqInfo[0].result_size );
1629 }
1630
1631 msHTTPFreeRequestObj(pasReqInfo, numReq);
1632 msFree(pasReqInfo);
1633 msFreeWmsParamsObj(&sLastWMSParams);
1634
1635 return MS_SUCCESS;
1636 #else
1637 /* ------------------------------------------------------------------
1638 * WMS CONNECTION Support not included...
1639 * ------------------------------------------------------------------ */
1640 msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1641 "msWMSLayerExecuteRequest()");
1642 return(MS_FAILURE);
1643
1644 #endif /* USE_WMS_LYR */
1645
1646 }
1647