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