root/tags/rel-5-0-2/mapserver/mapdraw.c

Revision 7019, 74.1 kB (checked in by warmerdam, 9 months ago)

reset layer->project flag before query/draw to force reconsideration (#673)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  High level msDrawMap() implementation and related functions.
5  * Author:   Steve Lime and the MapServer team.
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2005 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *****************************************************************************/
28
29 #include <assert.h>
30 #include "mapserver.h"
31 #include "maptime.h"
32
33 MS_CVSID("$Id$")
34
35 /*
36  * Functions to reset any pen (color index) values previously set. Used primarily to reset things when
37  * using MapScript to create multiple images. How the pen values are set is irrelevant (definitely output
38  * format type specific) which is why this function is here instead of the GD, PDF or SWF source files.
39 */
40 void msClearLayerPenValues(layerObj *layer) {
41   int i, j; 
42
43   for(i=0; i<layer->numclasses; i++) {
44     layer->class[i]->label.backgroundcolor.pen = MS_PEN_UNSET; /* set in billboardXX function */
45     layer->class[i]->label.backgroundshadowcolor.pen = MS_PEN_UNSET;
46     layer->class[i]->label.color.pen = MS_PEN_UNSET; /* set in MSXXDrawText function */
47     layer->class[i]->label.outlinecolor.pen = MS_PEN_UNSET;
48     layer->class[i]->label.shadowcolor.pen = MS_PEN_UNSET;     
49
50     for(j=0; j<layer->class[i]->numstyles; j++) {
51       layer->class[i]->styles[j]->backgroundcolor.pen = MS_PEN_UNSET; /* set in various symbol drawing functions */
52           layer->class[i]->styles[j]->color.pen = MS_PEN_UNSET;
53       layer->class[i]->styles[j]->outlinecolor.pen = MS_PEN_UNSET;
54     }
55   }
56 }
57
58 void msClearScalebarPenValues(scalebarObj *scalebar)
59 {
60     if (scalebar)
61     {
62        scalebar->color.pen = MS_PEN_UNSET;
63        scalebar->backgroundcolor.pen = MS_PEN_UNSET;
64        scalebar->outlinecolor.pen = MS_PEN_UNSET;
65        scalebar->imagecolor.pen = MS_PEN_UNSET;
66
67        scalebar->label.color.pen = MS_PEN_UNSET;
68        scalebar->label.outlinecolor.pen = MS_PEN_UNSET;
69        scalebar->label.shadowcolor.pen = MS_PEN_UNSET;
70        scalebar->label.backgroundcolor.pen = MS_PEN_UNSET;
71        scalebar->label.backgroundshadowcolor.pen = MS_PEN_UNSET;
72     }
73 }
74
75 void msClearLegendPenValues(legendObj *legend)
76 {
77     if (legend)
78     {
79        legend->outlinecolor.pen = MS_PEN_UNSET;
80        legend->imagecolor.pen = MS_PEN_UNSET;
81
82        legend->label.color.pen = MS_PEN_UNSET;
83        legend->label.outlinecolor.pen = MS_PEN_UNSET;
84        legend->label.shadowcolor.pen = MS_PEN_UNSET;
85        legend->label.backgroundcolor.pen = MS_PEN_UNSET;
86        legend->label.backgroundshadowcolor.pen = MS_PEN_UNSET;
87     }
88 }
89
90 void msClearReferenceMapPenValues(referenceMapObj *referencemap)
91 {
92     if (referencemap)
93     {
94         referencemap->outlinecolor.pen = MS_PEN_UNSET;
95         referencemap->color.pen = MS_PEN_UNSET;
96     }
97 }
98
99
100 void msClearQueryMapPenValues(queryMapObj *querymap)
101 {
102     if (querymap)
103       querymap->color.pen = MS_PEN_UNSET;
104 }
105
106
107 void msClearPenValues(mapObj *map) {
108   int i;
109
110   for(i=0; i<map->numlayers; i++)
111     msClearLayerPenValues((GET_LAYER(map, i)));
112
113   msClearLegendPenValues(&(map->legend));
114   msClearScalebarPenValues(&(map->scalebar));
115   msClearReferenceMapPenValues(&(map->reference));
116   msClearQueryMapPenValues(&(map->querymap));
117  
118 }
119
120 /* msPrepareImage()
121  *
122  * Returns a new imageObj ready for rendering the current map.
123  *
124  * If allow_nonsquare is set to MS_TRUE then the caller should call
125  * msMapRestoreRealExtent() once they are done with the image.
126  * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
127  */
128 imageObj *msPrepareImage(mapObj *map, int allow_nonsquare)
129 {
130     int i, status;
131     imageObj *image=NULL;
132     double geo_cellsize;
133
134     if(map->width == -1 || map->height == -1) {
135         msSetError(MS_MISCERR, "Image dimensions not specified.", "msPrepareImage()");
136         return(NULL);
137     }
138
139     msInitLabelCache(&(map->labelcache)); /* this clears any previously allocated cache */
140
141     status = msValidateContexts(map); /* make sure there are no recursive REQUIRES or LABELREQUIRES expressions */
142     if(status != MS_SUCCESS) return NULL;
143
144     if(!map->outputformat) {
145         msSetError(MS_GDERR, "Map outputformat not set!", "msPrepareImage()");
146         return(NULL);
147     }
148     else if( MS_RENDERER_GD(map->outputformat) )
149     {
150         image = msImageCreateGD(map->width, map->height, map->outputformat,
151                                 map->web.imagepath, map->web.imageurl);       
152         if( image != NULL ) msImageInitGD( image, &map->imagecolor );
153         msPreAllocateColorsGD(image, map);
154     }
155 #ifdef USE_AGG
156     else if( MS_RENDERER_AGG(map->outputformat) )
157     {
158         image = msImageCreateAGG(map->width, map->height, map->outputformat,
159                                 map->web.imagepath, map->web.imageurl);       
160         if( image != NULL ) msImageInitAGG( image, &map->imagecolor );
161     }
162 #endif
163     else if( MS_RENDERER_IMAGEMAP(map->outputformat) )
164     {
165         image = msImageCreateIM(map->width, map->height, map->outputformat,
166                                 map->web.imagepath, map->web.imageurl);       
167         if( image != NULL ) msImageInitIM( image );
168     }
169     else if( MS_RENDERER_RAWDATA(map->outputformat) )
170     {
171         image = msImageCreate(map->width, map->height, map->outputformat,
172                               map->web.imagepath, map->web.imageurl, map);
173     }
174 #ifdef USE_MING_FLASH
175     else if( MS_RENDERER_SWF(map->outputformat) )
176     {
177         image = msImageCreateSWF(map->width, map->height, map->outputformat,
178                                  map->web.imagepath, map->web.imageurl,
179                                  map);
180     }
181 #endif
182 #ifdef USE_PDF
183     else if( MS_RENDERER_PDF(map->outputformat) )
184     {
185         image = msImageCreatePDF(map->width, map->height, map->outputformat,
186                                  map->web.imagepath, map->web.imageurl,
187                                  map);
188         }
189 #endif
190     else if( MS_RENDERER_SVG(map->outputformat) )
191     {
192         image = msImageCreateSVG(map->width, map->height, map->outputformat,
193                                  map->web.imagepath, map->web.imageurl,
194                                  map);
195     }
196     else
197     {
198         image = NULL;
199     }
200  
201     if(!image) {
202         msSetError(MS_GDERR, "Unable to initialize image.", "msPrepareImage()");
203         return(NULL);
204     }
205
206     /*
207      * If we want to support nonsquare pixels, note that now, otherwise
208      * adjust the extent size to have square pixels.
209      *
210      * If allow_nonsquare is set to MS_TRUE then the caller should call
211      * msMapRestoreRealExtent() once they are done with the image.
212      * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
213      */
214     if( allow_nonsquare && msTestConfigOption( map, "MS_NONSQUARE", MS_FALSE ) )
215     {
216         double cellsize_x = (map->extent.maxx - map->extent.minx)/map->width;
217         double cellsize_y = (map->extent.maxy - map->extent.miny)/map->height;
218
219         if( cellsize_y != 0.0
220             && (fabs(cellsize_x/cellsize_y) > 1.00001
221                 || fabs(cellsize_x/cellsize_y) < 0.99999) )
222         {
223             map->gt.need_geotransform = MS_TRUE;
224             if (map->debug)
225                 msDebug( "msDrawMap(): kicking into non-square pixel preserving mode." );
226         }
227         map->cellsize = (cellsize_x*0.5 + cellsize_y*0.5);
228     }
229     else
230         map->cellsize = msAdjustExtent(&(map->extent),map->width,map->height);
231
232     status = msCalculateScale(map->extent,map->units,map->width,map->height, map->resolution, &map->scaledenom);
233     if(status != MS_SUCCESS) {
234         msFreeImage(image);
235         return(NULL);
236     }
237
238    /* update geotransform based on adjusted extent. */
239     msMapComputeGeotransform( map );
240
241     /* Do we need to fake out stuff for rotated support? */
242     if( map->gt.need_geotransform )
243         msMapSetFakedExtent( map );
244
245     /* We will need a cellsize that represents a real georeferenced */
246     /* coordinate cellsize here, so compute it from saved extents.   */
247
248     geo_cellsize = map->cellsize;
249     if( map->gt.need_geotransform == MS_TRUE ) {
250         double cellsize_x = (map->saved_extent.maxx - map->saved_extent.minx)
251             / map->width;
252         double cellsize_y = (map->saved_extent.maxy - map->saved_extent.miny)
253             / map->height;
254
255         geo_cellsize = sqrt(cellsize_x*cellsize_x + cellsize_y*cellsize_y)
256             / sqrt(2.0);
257     }
258
259     /* compute layer scale factors now */
260     for(i=0;i<map->numlayers; i++) {
261       if(GET_LAYER(map, i)->sizeunits != MS_PIXELS)
262         GET_LAYER(map, i)->scalefactor = (msInchesPerUnit(GET_LAYER(map, i)->sizeunits,0)/msInchesPerUnit(map->units,0)) / geo_cellsize;
263       else if(GET_LAYER(map, i)->symbolscaledenom > 0 && map->scaledenom > 0)
264         GET_LAYER(map, i)->scalefactor = GET_LAYER(map, i)->symbolscaledenom/map->scaledenom;
265       else
266         GET_LAYER(map, i)->scalefactor = 1;
267     }
268
269     return image;
270 }
271
272
273 /*
274  * Generic function to render the map file.
275  * The type of the image created is based on the imagetype parameter in the map file.
276  *
277  * mapObj *map - map object loaded in MapScript or via a mapfile to use
278  * int querymap - is this map the result of a query operation, MS_TRUE|MS_FALSE
279 */
280 imageObj *msDrawMap(mapObj *map, int querymap)
281 {
282   int i;
283   layerObj *lp=NULL;
284   int status = MS_FAILURE;
285   imageObj *image = NULL;
286   struct mstimeval mapstarttime, mapendtime;
287   struct mstimeval starttime, endtime;
288   int oldAlphaBlending;  /* allows toggling of gd alpha blending (bug 490) */
289
290 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
291   enum MS_CONNECTION_TYPE lastconnectiontype;
292   httpRequestObj *pasOWSReqInfo;
293   int numOWSRequests=0;
294   wmsParamsObj sLastWMSParams;
295
296   /* Alloc and init pasOWSReqInfo... for now we alloc numlayers+1 entries
297    * but this could definitely be optimized
298    */
299   pasOWSReqInfo = (httpRequestObj *)malloc((map->numlayers+1)*sizeof(httpRequestObj));
300   if (pasOWSReqInfo == NULL) {
301     msSetError(MS_MEMERR, "Allocation of httpRequestObj failed.", "msDrawMap()");
302     return NULL;
303   }
304
305   msHTTPInitRequestObj(pasOWSReqInfo, map->numlayers+1);
306   msInitWmsParamsObj(&sLastWMSParams);
307 #endif
308
309   if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&mapstarttime, NULL);
310
311   if(querymap) { /* use queryMapObj image dimensions */
312     if(map->querymap.width != -1) map->width = map->querymap.width;
313     if(map->querymap.height != -1) map->height = map->querymap.height;
314     if(map->querymap.style == MS_NORMAL) querymap = MS_FALSE; /* draw as normal */
315   }
316
317   msApplyMapConfigOptions(map);
318   image = msPrepareImage(map, MS_TRUE);
319
320   if(!image) {
321     msSetError(MS_IMGERR, "Unable to initialize image.", "msDrawMap()");
322 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
323     msFreeWmsParamsObj(&sLastWMSParams);
324     msFree(pasOWSReqInfo);
325 #endif
326     return(NULL);
327   }
328
329 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
330   /* Pre-download all WMS/WFS layers in parallel before starting to draw map */
331   lastconnectiontype = MS_SHAPEFILE;
332   for(i=0; i<map->numlayers; i++) {
333     //if(map->layerorder[i] == -1 || !msLayerIsVisible(map, &(map->layers[map->layerorder[i]])))
334     if(map->layerorder[i] == -1 || !msLayerIsVisible(map, GET_LAYER(map,map->layerorder[i])))
335       continue;
336
337     lp = GET_LAYER(map,map->layerorder[i]);
338
339 #ifdef USE_WMS_LYR
340     if(lp->connectiontype == MS_WMS) {
341       if(msPrepareWMSLayerRequest(map->layerorder[i], map, lp, lastconnectiontype, &sLastWMSParams, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
342         msFreeWmsParamsObj(&sLastWMSParams);
343         msFreeImage(image);
344         msFree(pasOWSReqInfo);
345         return NULL;
346       }
347     }
348 #endif
349
350 #ifdef USE_WFS_LYR
351     if(lp->connectiontype == MS_WFS) {
352       if(msPrepareWFSLayerRequest(map->layerorder[i], map, lp, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
353         msFreeWmsParamsObj(&sLastWMSParams);
354         msFreeImage(image);
355         msFree(pasOWSReqInfo);
356         return NULL;
357       }
358     }
359 #endif
360
361     lastconnectiontype = lp->connectiontype;
362   }
363
364 #ifdef USE_WMS_LYR
365   msFreeWmsParamsObj(&sLastWMSParams);
366 #endif
367
368   if(numOWSRequests && msOWSExecuteRequests(pasOWSReqInfo, numOWSRequests, map, MS_TRUE) == MS_FAILURE) {
369     msFreeImage(image);
370     msFree(pasOWSReqInfo);
371     return NULL;
372   }
373 #endif /* USE_WMS_LYR || USE_WFS_LYR */
374
375   /* OK, now we can start drawing */
376   for(i=0; i<map->numlayers; i++) {
377
378     if(map->layerorder[i] != -1) {
379       lp = (GET_LAYER(map,  map->layerorder[i]));
380
381       if(lp->postlabelcache) /* wait to draw */
382         continue;
383
384       if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING ) msGettimeofday(&starttime, NULL);
385
386       if(!msLayerIsVisible(map, lp)) continue;
387
388       if(lp->connectiontype == MS_WMS) {
389 #ifdef USE_WMS_LYR
390         if(MS_RENDERER_GD(image->format) || MS_RENDERER_RAWDATA(image->format))
391           status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests,  map, lp, image);
392 #ifdef USE_AGG
393         else if(MS_RENDERER_AGG(image->format))
394           status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
395 #endif
396 #ifdef USE_MING_FLASH               
397         else if(MS_RENDERER_SWF(image->format))
398           status = msDrawWMSLayerSWF(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
399 #endif
400 #ifdef USE_PDF
401         else if(MS_RENDERER_PDF(image->format))
402           status = msDrawWMSLayerPDF(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
403 #endif
404         else {
405           msSetError(MS_WMSCONNERR, "Output format '%s' doesn't support WMS layers.", "msDrawMap()", image->format->name);
406           status = MS_FAILURE;
407         }
408                
409         if(status == MS_FAILURE) {
410           msSetError(MS_WMSCONNERR,
411                      "Failed to draw WMS layer named '%s'. This most likely happened because "
412                      "the remote WMS server returned an invalid image, and XML exception "
413                      "or another unexpected result in response to the GetMap request. Also check "
414                      "and make sure that the layer's connection URL is valid.",
415                      "msDrawMap()", lp->name);
416           msFreeImage(image);
417           msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
418           msFree(pasOWSReqInfo);
419           return(NULL);
420         }
421
422
423 #else /* ndef USE_WMS_LYR */
424         msSetError(MS_WMSCONNERR, "MapServer not built with WMS Client support, unable to render layer '%s'.", "msDrawMap()", lp->name);
425         msFreeImage(image);
426         return(NULL);
427 #endif
428       } else { /* Default case: anything but WMS layers */
429         if(querymap)
430           status = msDrawQueryLayer(map, lp, image);
431         else
432           status = msDrawLayer(map, lp, image);
433         if(status == MS_FAILURE) {
434           msSetError(MS_IMGERR, "Failed to draw layer named '%s'.", "msDrawMap()", lp->name);
435           msFreeImage(image);
436 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
437           msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
438           msFree(pasOWSReqInfo);
439 #endif /* USE_WMS_LYR || USE_WFS_LYR */
440           return(NULL);
441         }
442       }
443     }
444
445     if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
446       msGettimeofday(&endtime, NULL);
447       msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
448               map->layerorder[i], lp->name?lp->name:"(null)",
449               (endtime.tv_sec+endtime.tv_usec/1.0e6)-
450               (starttime.tv_sec+starttime.tv_usec/1.0e6) );
451     }
452   }
453
454   if(map->scalebar.status == MS_EMBED && !map->scalebar.postlabelcache) {
455
456     /* We need to temporarily restore the original extent for drawing */
457     /* the scalebar as it uses the extent to recompute cellsize. */
458     if(map->gt.need_geotransform)
459       msMapRestoreRealExtent(map);
460
461     /* fix for bug 490 - turn on alpha blending for embedded scalebar */
462     oldAlphaBlending = (image->img.gd)->alphaBlendingFlag;
463     gdImageAlphaBlending(image->img.gd, 1);
464
465     msEmbedScalebar(map, image->img.gd); /* TODO   */
466
467     /* restore original alpha blending */
468     gdImageAlphaBlending(image->img.gd, oldAlphaBlending);
469
470     if(map->gt.need_geotransform)
471       msMapSetFakedExtent(map);
472   }
473
474   if(map->legend.status == MS_EMBED && !map->legend.postlabelcache)
475     msEmbedLegend(map, image->img.gd);
476
477   if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
478
479   if(msDrawLabelCache(image, map) == -1) {
480     msFreeImage(image);
481 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
482     msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
483     msFree(pasOWSReqInfo);
484 #endif /* USE_WMS_LYR || USE_WFS_LYR */
485     return(NULL);
486   }
487
488   if(map->debug >= MS_DEBUGLEVEL_TUNING) {
489     msGettimeofday(&endtime, NULL);
490     msDebug("msDrawMap(): Drawing Label Cache, %.3fs\n",
491             (endtime.tv_sec+endtime.tv_usec/1.0e6)-
492             (starttime.tv_sec+starttime.tv_usec/1.0e6) );
493   }
494
495   for(i=0; i<map->numlayers; i++) { /* for each layer, check for postlabelcache layers */
496
497     lp = (GET_LAYER(map, map->layerorder[i]));
498
499     if(!lp->postlabelcache) continue;
500     if(!msLayerIsVisible(map, lp)) continue;
501
502     if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
503
504     if(lp->connectiontype == MS_WMS) {
505 #ifdef USE_WMS_LYR
506       if(MS_RENDERER_GD(image->format) || MS_RENDERER_RAWDATA(image->format))
507         status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
508 #ifdef USE_AGG               
509       else if(MS_RENDERER_AGG(image->format))
510         status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
511 #endif
512 #ifdef USE_MING_FLASH
513       else if(MS_RENDERER_SWF(image->format) )
514         status = msDrawWMSLayerSWF(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
515 #endif
516 #ifdef USE_PDF
517       else if(MS_RENDERER_PDF(image->format) )
518         status = msDrawWMSLayerPDF(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
519 #endif
520
521 #else
522       status = MS_FAILURE;
523 #endif
524     } else {
525       if(querymap)
526         status = msDrawQueryLayer(map, lp, image);
527       else
528         status = msDrawLayer(map, lp, image);
529     }
530
531     if(status == MS_FAILURE) {
532       msFreeImage(image);
533 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
534       msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
535       msFree(pasOWSReqInfo);
536 #endif /* USE_WMS_LYR || USE_WFS_LYR */
537       return(NULL);
538     }
539
540     if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
541       msGettimeofday(&endtime, NULL);
542       msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
543               map->layerorder[i], lp->name?lp->name:"(null)",
544               (endtime.tv_sec+endtime.tv_usec/1.0e6)-
545               (starttime.tv_sec+starttime.tv_usec/1.0e6) );
546     }
547
548   }
549  
550   /* Do we need to fake out stuff for rotated support? */
551   /* This really needs to be done on every preceeding exit point too... */
552   if(map->gt.need_geotransform)
553     msMapRestoreRealExtent(map);
554
555   if(map->scalebar.status == MS_EMBED && map->scalebar.postlabelcache) {
556
557     /* fix for bug 490 - turn on alpha blending for embedded scalebar */
558     oldAlphaBlending = (image->img.gd)->alphaBlendingFlag;
559     gdImageAlphaBlending(image->img.gd, 1);
560
561     msEmbedScalebar(map, image->img.gd); /* TODO   */
562
563     /* restore original alpha blending */
564     gdImageAlphaBlending(image->img.gd, oldAlphaBlending);
565   }
566
567   if(map->legend.status == MS_EMBED && map->legend.postlabelcache)
568     msEmbedLegend(map, image->img.gd); /* TODO */
569
570 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
571   /* Cleanup WMS/WFS Request stuff */
572   msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
573   msFree(pasOWSReqInfo);
574 #endif
575
576   if(map->debug >= MS_DEBUGLEVEL_TUNING) {
577     msGettimeofday(&mapendtime, NULL);
578     msDebug("msDrawMap() total time: %.3fs\n",
579             (mapendtime.tv_sec+mapendtime.tv_usec/1.0e6)-
580             (mapstarttime.tv_sec+mapstarttime.tv_usec/1.0e6) );
581   }
582
583   return(image);
584 }
585
586 /*
587  * Test whether a layer should be drawn or not in the current map view and
588  * at the current scale. 
589  * Returns TRUE if layer is visible, FALSE if not.
590 */
591 int msLayerIsVisible(mapObj *map, layerObj *layer)
592 {
593   int i;
594
595   if(!layer->data && !layer->tileindex && !layer->connection && !layer->features && !layer->layerinfo)
596     return(MS_FALSE); /* no data associated with this layer, not an error since layer may be used as a template from MapScript */
597
598   if(layer->type == MS_LAYER_QUERY || layer->type == MS_LAYER_TILEINDEX) return(MS_FALSE);
599   if((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) return(MS_FALSE);
600   if(msEvalContext(map, layer, layer->requires) == MS_FALSE) return(MS_FALSE);
601
602   if(map->scaledenom > 0) {
603    
604     /* layer scale boundaries should be checked first */
605     if((layer->maxscaledenom > 0) && (map->scaledenom > layer->maxscaledenom)) return(MS_FALSE);
606     if((layer->minscaledenom > 0) && (map->scaledenom <= layer->minscaledenom)) return(MS_FALSE);
607
608     /* now check class scale boundaries (all layers *must* pass these tests) */
609     if(layer->numclasses > 0) {
610       for(i=0; i<layer->numclasses; i++) {
611         if((layer->class[i]->maxscaledenom > 0) && (map->scaledenom > layer->class[i]->maxscaledenom))
612           continue; /* can skip this one, next class */
613         if((layer->class[i]->minscaledenom > 0) && (map->scaledenom <= layer->class[i]->minscaledenom))
614           continue; /* can skip this one, next class */
615
616         break; /* can't skip this class (or layer for that matter) */
617       }
618       if(i == layer->numclasses) return(MS_FALSE);
619     }
620
621   }
622
623   return MS_TRUE;  /* All tests passed.  Layer is visible. */
624 }
625 /*
626  * Generic function to render a layer object.
627 */
628 int msDrawLayer(mapObj *map, layerObj *layer, imageObj *image)
629 {
630   imageObj *image_draw = image;
631   outputFormatObj *transFormat = NULL;
632   int retcode=MS_SUCCESS;
633   int oldAlphaBlending=0;  /* allow toggling of gd alpha blending (bug 490) */
634
635   if(!msLayerIsVisible(map, layer))
636     return MS_SUCCESS; 
637
638   if(layer->opacity == 0) return MS_SUCCESS; /* layer is completely transparent, skip it */
639
640   /* conditions may have changed since this layer last drawn, so set
641      layer->project true to recheck projection needs (Bug #673) */
642   layer->project = MS_TRUE;
643
644   /* inform the rendering device that layer draw is starting. */
645   msImageStartLayer(map, layer, image);
646
647   if(MS_RENDERER_GD(image_draw->format)) {
648
649     /*
650     ** for layer-level opacity we render to a temp image
651     */
652     if(layer->opacity > 0 && layer->opacity < 100) {
653       msApplyOutputFormat(&transFormat, image->format, MS_TRUE, MS_NOOVERRIDE, MS_NOOVERRIDE);
654      
655       image_draw = msImageCreateGD( image->width, image->height, transFormat, image->imagepath, image->imageurl );
656       if(!image_draw) {
657         msSetError(MS_GDERR, "Unable to initialize image.", "msDrawLayer()");
658         return(MS_FAILURE);
659       }
660       msImageInitGD(image_draw, &map->imagecolor);
661      
662       if(image_draw->format->imagemode == MS_IMAGEMODE_PC256) /* gdImageCopyMerge() needs this later */
663         gdImageColorTransparent(image_draw->img.gd, 0);
664     }
665
666     /* Bug 490 - switch alpha blending on for a layer that requires it */
667     else if (layer->opacity == MS_GD_ALPHA) {
668         oldAlphaBlending = (image->img.gd)->alphaBlendingFlag;
669         gdImageAlphaBlending(image->img.gd, 1);
670     }
671   }
672 #ifdef USE_AGG
673   else if(MS_RENDERER_AGG(image_draw->format)) {
674    
675     /*
676     ** for layer-level opacity we render to a temp image
677     */
678     if(layer->opacity > 0 && layer->opacity < 100) {
679       msApplyOutputFormat(&transFormat, image->format, MS_TRUE, MS_NOOVERRIDE, MS_NOOVERRIDE);
680
681       image_draw = msImageCreateAGG(image->width, image->height, transFormat, image->imagepath, image->imageurl);
682       if(!image_draw) {
683         msSetError(MS_GDERR, "Unable to initialize image.", "msDrawLayer()");
684         return(MS_FAILURE);
685       }
686       msImageInitAGG(image_draw, &map->imagecolor);
687     }
688
689     /* keeping this for the time being. will go when full switch to AGG */
690     else if (layer->opacity == MS_GD_ALPHA) {
691       oldAlphaBlending = (image->img.gd)->alphaBlendingFlag;
692     }
693   }
694 #endif
695
696   /*
697   ** redirect procesing of some layer types.
698   */
699   if(layer->connectiontype == MS_WMS) {
700 #ifdef USE_WMS_LYR
701     retcode = msDrawWMSLayer(map, layer, image_draw);
702 #else 
703     retcode = MS_FAILURE;
704 #endif
705   } else if(layer->type == MS_LAYER_RASTER) {
706     retcode = msDrawRasterLayer(map, layer, image_draw);
707   } else if(layer->type == MS_LAYER_CHART) {
708 #ifdef USE_AGG
709       if( MS_RENDERER_AGG(image_draw->format))
710           msAlphaGD2AGG(image_draw);
711 #endif
712     retcode = msDrawChartLayer(map, layer, image_draw);
713 #ifdef USE_AGG
714       if( MS_RENDERER_AGG(image_draw->format))
715           msAlphaAGG2GD(image_draw);
716 #endif
717   } else {   /* must be a Vector layer */
718 #ifdef USE_AGG
719       if( MS_RENDERER_AGG(image_draw->format))
720           msAlphaGD2AGG(image_draw);
721 #endif
722     retcode = msDrawVectorLayer(map, layer, image_draw);
723 #ifdef USE_AGG
724       if( MS_RENDERER_AGG(image_draw->format))
725           msAlphaAGG2GD(image_draw);
726 #endif
727   }
728
729   /* Destroy the temp image for this layer tranparency */
730   if( MS_RENDERER_GD(image_draw->format) && layer->opacity > 0 && layer->opacity < 100 ) {
731
732     if(layer->type == MS_LAYER_RASTER)
733       msImageCopyMerge(image->img.gd, image_draw->img.gd, 0, 0, 0, 0, image->img.gd->sx, image->img.gd->sy, layer->opacity);
734     else
735       msImageCopyMergeNoAlpha(image->img.gd, image_draw->img.gd, 0, 0, 0, 0, image->img.gd->sx, image->img.gd->sy, layer->opacity, &map->imagecolor);
736     msFreeImage(image_draw);
737
738     /* deref and possibly free temporary transparent output format.  */
739     msApplyOutputFormat( &transFormat, NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE );
740   }
741 #ifdef USE_AGG
742   else if( MS_RENDERER_AGG(image_draw->format) && layer->opacity > 0 && layer->opacity < 100 ) {
743
744     if(layer->type == MS_LAYER_RASTER)
745       msImageCopyMerge(image->img.gd, image_draw->img.gd, 0, 0, 0, 0, image->img.gd->sx, image->img.gd->sy, layer->opacity);
746     else
747       msImageCopyMergeNoAlpha(image->img.gd, image_draw->img.gd, 0, 0, 0, 0, image->img.gd->sx, image->img.gd->sy, layer->opacity, &map->imagecolor);
748     msFreeImage( image_draw );
749
750     /* deref and possibly free temporary transparent output format.  */
751     msApplyOutputFormat( &transFormat, NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE );
752   }
753 #endif
754
755   /* restore original alpha blending */
756   else if (layer->opacity == MS_GD_ALPHA) {
757     gdImageAlphaBlending(image->img.gd, oldAlphaBlending);
758   } else {
759     assert( image == image_draw );
760   }
761
762   return(retcode);
763 }
764
765 int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
766 {
767   int         status, retcode=MS_SUCCESS;
768   char        annotate=MS_TRUE;
769   shapeObj    shape;
770   rectObj     searchrect;
771   char        cache=MS_FALSE;
772   int         maxnumstyles=1;
773   featureListNodeObjPtr shpcache=NULL, current=NULL;
774
775 /* ==================================================================== */
776 /*      For Flash, we use a metadata called SWFOUTPUT that              */
777 /*      is used to render vector layers as rasters. It is also true     */
778 /*      if  the output movie  format indicated in the map file is       */
779 /*      SINGLE.                                                         */
780 /* ==================================================================== */
781 #ifdef USE_MING_FLASH
782   if(image &&  MS_RENDERER_SWF(image->format))
783   {
784     if ((msLookupHashTable(&(layer->metadata), "SWFOUTPUT") &&
785         strcasecmp(msLookupHashTable(&(layer->metadata), "SWFOUTPUT"),"RASTER")==0) ||
786         strcasecmp(msGetOutputFormatOption(image->format,"OUTPUT_MOVIE", ""), 
787                    "MULTIPLE") != 0)
788     return msDrawVectorLayerAsRasterSWF(map, layer, image);
789   }
790 #endif
791  
792
793 /* ==================================================================== */
794 /*      For PDF if the output_type is set to raster, draw the vector    */
795 /*      into a gd image.                                                */
796 /* ==================================================================== */
797 #ifdef USE_PDF
798   if(image &&  MS_RENDERER_PDF(image->format))
799   {
800     if (strcasecmp(msGetOutputFormatOption(image->format,"OUTPUT_TYPE", ""), 
801                    "RASTER") == 0)
802     return msDrawVectorLayerAsRasterPDF(map, layer, image);
803   }
804 #endif
805
806   annotate = msEvalContext(map, layer, layer->labelrequires);
807   if(map->scaledenom > 0) {
808     if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
809     if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
810   }
811
812   /* reset layer pen values just in case the map has been previously processed */
813   msClearLayerPenValues(layer);
814  
815   /* open this layer */
816   status = msLayerOpen(layer);
817   if(status != MS_SUCCESS) return MS_FAILURE;
818  
819   /* build item list */
820 /* ==================================================================== */
821 /*      For Flash, we use a metadata called SWFDUMPATTRIBUTES that      */
822 /*      contains a list of attributes that we want to write to the      */
823 /*      flash movie for query purpose.                                  */
824 /* ==================================================================== */
825   if(image && MS_RENDERER_SWF(image->format))
826     status = msLayerWhichItems(layer, MS_TRUE, annotate, msLookupHashTable(&(layer->metadata), "SWFDUMPATTRIBUTES"));                               
827   else       
828     status = msLayerWhichItems(layer, MS_TRUE, annotate, NULL);
829   if(status != MS_SUCCESS) {
830     msLayerClose(layer);
831     return MS_FAILURE;
832   }
833  
834   /* identify target shapes */
835   if(layer->transform == MS_TRUE)
836     searchrect = map->extent;
837   else {
838     searchrect.minx = searchrect.miny = 0;
839     searchrect.maxx = map->width-1;
840     searchrect.maxy = map->height-1;
841   }
842  
843 #ifdef USE_PROJ
844   if((map->projection.numargs > 0) && (layer->projection.numargs > 0))
845     msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
846 #endif
847    
848   status = msLayerWhichShapes(layer, searchrect);
849   if(status == MS_DONE) { /* no overlap */
850     msLayerClose(layer);
851     return MS_SUCCESS;
852   } else if(status != MS_SUCCESS) {
853     msLayerClose(layer);
854     return MS_FAILURE;
855   }
856  
857   /* step through the target shapes */
858   msInitShape(&shape);
859  
860   while((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
861
862     shape.classindex = msShapeGetClass(layer, &shape, map->scaledenom);
863     if((shape.classindex == -1) || (layer->class[shape.classindex]->status == MS_OFF)) {
864        msFreeShape(&shape);
865        continue;
866     }
867  
868     cache = MS_FALSE;
869     if(layer->type == MS_LAYER_LINE && layer->class[shape.classindex]->numstyles > 1)
870       cache = MS_TRUE; /* only line layers with multiple styles need be cached (I don't think POLYLINE layers need caching - SDL) */
871          
872     /* With 'STYLEITEM AUTO', we will have the datasource fill the class' */
873     /* style parameters for this shape. */
874     if(layer->styleitem && strcasecmp(layer->styleitem, "AUTO") == 0) {
875       if(msLayerGetAutoStyle(map, layer, layer->class[shape.classindex], shape.tileindex, shape.index) != MS_SUCCESS) {
876         retcode = MS_FAILURE;
877         break;
878       }
879                  
880       /* __TODO__ For now, we can't cache features with 'AUTO' style */
881       cache = MS_FALSE;
882     }
883  
884     if(annotate && (layer->class[shape.classindex]->text.string || layer->labelitem) && layer->class[shape.classindex]->label.size != -1)
885       shape.text = msShapeGetAnnotation(layer, &shape);
886
887     if(cache)
888       status = msDrawShape(map, layer, &shape, image, 0); /* draw only the first style */
889     else
890       status = msDrawShape(map, layer, &shape, image, -1); /* all styles  */
891     if(status != MS_SUCCESS) {
892       msFreeShape(&shape);
893       retcode = MS_FAILURE;
894       break;
895     }
896
897     if(shape.numlines == 0) { /* once clipped the shape didn't need to be drawn */
898       msFreeShape(&shape);
899       continue;
900     }
901  
902     if(cache) {
903       if(insertFeatureList(&shpcache, &shape) == NULL) {
904         retcode = MS_FAILURE; /* problem adding to the cache */
905         break;
906       }
907     } 
908
909     maxnumstyles = MS_MAX(maxnumstyles, layer->class[shape.classindex]->numstyles);
910     msFreeShape(&shape);
911   }
912    
913   if(status != MS_DONE) {
914     msLayerClose(layer);
915     return MS_FAILURE;
916   }
917  
918   if(shpcache) {
919     int s;
920      
921     for(s=1; s<maxnumstyles; s++) {
922       for(current=shpcache; current; current=current->next) {       
923         if(layer->class[current->shape.classindex]->numstyles > s)
924           msDrawLineSymbol(&map->symbolset, image, &current->shape, layer->class[current->shape.classindex]->styles[s], l