1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: High level msDrawMap() implementation and related functions.
6 * Author: Steve Lime and the MapServer team.
7 *
8 ******************************************************************************
9 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
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
22 * OR 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 <assert.h>
31 #include <math.h>
32 #include "mapserver.h"
33 #include "maptime.h"
34 #include "mapcopy.h"
35 #include "mapfile.h"
36 #include "mapows.h"
37
38
39 /* msPrepareImage()
40 *
41 * Returns a new imageObj ready for rendering the current map.
42 *
43 * If allow_nonsquare is set to MS_TRUE then the caller should call
44 * msMapRestoreRealExtent() once they are done with the image.
45 * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
46 */
msPrepareImage(mapObj * map,int allow_nonsquare)47 imageObj *msPrepareImage(mapObj *map, int allow_nonsquare)
48 {
49 int i, status;
50 imageObj *image=NULL;
51 double geo_cellsize;
52
53 if(map->width == -1 || map->height == -1) {
54 msSetError(MS_MISCERR, "Image dimensions not specified.", "msPrepareImage()");
55 return(NULL);
56 }
57
58 msFreeLabelCache(&(map->labelcache));
59 msInitLabelCache(&(map->labelcache)); /* this clears any previously allocated cache */
60
61 /* clear any previously created mask layer images */
62 for(i=0; i<map->numlayers; i++) {
63 if(GET_LAYER(map, i)->maskimage) {
64 msFreeImage(GET_LAYER(map, i)->maskimage);
65 GET_LAYER(map, i)->maskimage = NULL;
66 }
67 }
68
69 status = msValidateContexts(map); /* make sure there are no recursive REQUIRES or LABELREQUIRES expressions */
70 if(status != MS_SUCCESS) return NULL;
71
72 if(!map->outputformat) {
73 msSetError(MS_IMGERR, "Map outputformat not set!", "msPrepareImage()");
74 return(NULL);
75 } else if (MS_RENDERER_PLUGIN(map->outputformat)) {
76 rendererVTableObj *renderer = map->outputformat->vtable;
77 colorObj *bg = &map->imagecolor;
78 map->imagecolor.alpha=255;
79 if(map->transparent == MS_TRUE) {
80 /* don't set the image color */
81 bg = NULL;
82 }
83
84 image = renderer->createImage(map->width, map->height, map->outputformat,bg);
85 if (image == NULL)
86 return(NULL);
87 image->format = map->outputformat;
88 image->format->refcount++;
89 image->width = map->width;
90 image->height = map->height;
91
92 image->resolution = map->resolution;
93 image->resolutionfactor = map->resolution/map->defresolution;
94 if (map->web.imagepath)
95 image->imagepath = msStrdup(map->web.imagepath);
96 if (map->web.imageurl)
97 image->imageurl = msStrdup(map->web.imageurl);
98
99 } else if( MS_RENDERER_IMAGEMAP(map->outputformat) ) {
100 image = msImageCreateIM(map->width, map->height, map->outputformat,
101 map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution);
102 } else if( MS_RENDERER_RAWDATA(map->outputformat) ) {
103 image = msImageCreate(map->width, map->height, map->outputformat,
104 map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution, &map->imagecolor);
105 } else {
106 image = NULL;
107 }
108
109 if(!image) {
110 msSetError(MS_IMGERR, "Unable to initialize image.", "msPrepareImage()");
111 return(NULL);
112 }
113
114 image->map = map;
115
116 /*
117 * If we want to support nonsquare pixels, note that now, otherwise
118 * adjust the extent size to have square pixels.
119 *
120 * If allow_nonsquare is set to MS_TRUE then the caller should call
121 * msMapRestoreRealExtent() once they are done with the image.
122 * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
123 */
124 if( allow_nonsquare && msTestConfigOption( map, "MS_NONSQUARE", MS_FALSE ) ) {
125 double cellsize_x = (map->extent.maxx - map->extent.minx)/map->width;
126 double cellsize_y = (map->extent.maxy - map->extent.miny)/map->height;
127
128 if( cellsize_y != 0.0
129 && (fabs(cellsize_x/cellsize_y) > 1.00001
130 || fabs(cellsize_x/cellsize_y) < 0.99999) ) {
131 map->gt.need_geotransform = MS_TRUE;
132 if (map->debug)
133 msDebug( "msDrawMap(): kicking into non-square pixel preserving mode.\n" );
134 }
135 map->cellsize = (cellsize_x*0.5 + cellsize_y*0.5);
136 } else
137 map->cellsize = msAdjustExtent(&(map->extent),map->width,map->height);
138
139 status = msCalculateScale(map->extent,map->units,map->width,map->height, map->resolution, &map->scaledenom);
140 if(status != MS_SUCCESS) {
141 msFreeImage(image);
142 return(NULL);
143 }
144
145 /* update geotransform based on adjusted extent. */
146 msMapComputeGeotransform( map );
147
148 /* Do we need to fake out stuff for rotated support? */
149 if( map->gt.need_geotransform )
150 msMapSetFakedExtent( map );
151
152 /* We will need a cellsize that represents a real georeferenced */
153 /* coordinate cellsize here, so compute it from saved extents. */
154
155 geo_cellsize = map->cellsize;
156 if( map->gt.need_geotransform == MS_TRUE ) {
157 double cellsize_x = (map->saved_extent.maxx - map->saved_extent.minx)
158 / map->width;
159 double cellsize_y = (map->saved_extent.maxy - map->saved_extent.miny)
160 / map->height;
161
162 geo_cellsize = sqrt(cellsize_x*cellsize_x + cellsize_y*cellsize_y)
163 / sqrt(2.0);
164 }
165
166 /* compute layer scale factors now */
167 for(i=0; i<map->numlayers; i++) {
168 if(GET_LAYER(map, i)->sizeunits != MS_PIXELS)
169 GET_LAYER(map, i)->scalefactor = (msInchesPerUnit(GET_LAYER(map, i)->sizeunits,0)/msInchesPerUnit(map->units,0)) / geo_cellsize;
170 else if(GET_LAYER(map, i)->symbolscaledenom > 0 && map->scaledenom > 0)
171 GET_LAYER(map, i)->scalefactor = GET_LAYER(map, i)->symbolscaledenom/map->scaledenom*map->resolution/map->defresolution;
172 else
173 GET_LAYER(map, i)->scalefactor = map->resolution/map->defresolution;
174 }
175
176 image->refpt.x = MS_MAP2IMAGE_X_IC_DBL(0, map->extent.minx, 1.0/map->cellsize);
177 image->refpt.y = MS_MAP2IMAGE_Y_IC_DBL(0, map->extent.maxy, 1.0/map->cellsize);
178
179 return image;
180
181 }
182
msCompositeRasterBuffer(mapObj * map,imageObj * img,rasterBufferObj * rb,LayerCompositer * comp)183 static int msCompositeRasterBuffer(mapObj *map, imageObj *img, rasterBufferObj *rb, LayerCompositer *comp) {
184 int ret = MS_SUCCESS;
185 if(MS_IMAGE_RENDERER(img)->compositeRasterBuffer) {
186 while(comp && ret == MS_SUCCESS) {
187 rasterBufferObj *rb_ptr = rb;
188 CompositingFilter *filter = comp->filter;
189 if(filter && comp->next) {
190 /* if we have another compositor to apply, then we need to copy the rasterBufferObj. Otherwise
191 * we can work on it directly */
192 rb_ptr = (rasterBufferObj*)msSmallCalloc(sizeof(rasterBufferObj),1);
193 msCopyRasterBuffer(rb_ptr,rb);
194 }
195 while(filter && ret == MS_SUCCESS) {
196 ret = msApplyCompositingFilter(map,rb_ptr,filter);
197 filter = filter->next;
198 }
199 if(ret == MS_SUCCESS)
200 ret = MS_IMAGE_RENDERER(img)->compositeRasterBuffer(img,rb_ptr,comp->comp_op, comp->opacity);
201 if(rb_ptr != rb) {
202 msFreeRasterBuffer(rb_ptr);
203 msFree(rb_ptr);
204 }
205 comp = comp->next;
206 }
207 } else {
208 ret = MS_FAILURE;
209 }
210 return ret;
211 }
212
213 /*
214 * Generic function to render the map file.
215 * The type of the image created is based on the imagetype parameter in the map file.
216 *
217 * mapObj *map - map object loaded in MapScript or via a mapfile to use
218 * int querymap - is this map the result of a query operation, MS_TRUE|MS_FALSE
219 */
msDrawMap(mapObj * map,int querymap)220 imageObj *msDrawMap(mapObj *map, int querymap)
221 {
222 int i;
223 layerObj *lp=NULL;
224 int status = MS_FAILURE;
225 imageObj *image = NULL;
226 struct mstimeval mapstarttime = {0}, mapendtime = {0};
227 struct mstimeval starttime = {0}, endtime = {0};
228
229 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
230 enum MS_CONNECTION_TYPE lastconnectiontype;
231 httpRequestObj *pasOWSReqInfo=NULL;
232 int numOWSLayers=0;
233 int numOWSRequests=0;
234 wmsParamsObj sLastWMSParams;
235 #endif
236
237 if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&mapstarttime, NULL);
238
239 if(querymap) { /* use queryMapObj image dimensions */
240 if(map->querymap.width != -1) map->width = map->querymap.width;
241 if(map->querymap.height != -1) map->height = map->querymap.height;
242 }
243
244 msApplyMapConfigOptions(map);
245 image = msPrepareImage(map, MS_TRUE);
246
247 if(!image) {
248 msSetError(MS_IMGERR, "Unable to initialize image.", "msDrawMap()");
249 return(NULL);
250 }
251
252 if( map->debug >= MS_DEBUGLEVEL_DEBUG )
253 msDebug( "msDrawMap(): rendering using outputformat named %s (%s).\n",
254 map->outputformat->name,
255 map->outputformat->driver );
256
257 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
258
259 /* Time the OWS query phase */
260 if(map->debug >= MS_DEBUGLEVEL_TUNING ) msGettimeofday(&starttime, NULL);
261
262 /* How many OWS (WMS/WFS) layers do we have to draw?
263 * Note: numOWSLayers is the number of actual layers and numOWSRequests is
264 * the number of HTTP requests which could be lower if multiple layers
265 * are merged into the same request.
266 */
267 numOWSLayers=0;
268 for(i=0; i<map->numlayers; i++) {
269 if(map->layerorder[i] == -1 )
270 continue;
271
272 lp = GET_LAYER(map,map->layerorder[i]);
273 if( lp->connectiontype != MS_WMS &&
274 lp->connectiontype != MS_WFS ) {
275 continue;
276 }
277 numOWSLayers++;
278 }
279
280 if (numOWSLayers > 0) {
281
282 /* Alloc and init pasOWSReqInfo...
283 */
284 pasOWSReqInfo = (httpRequestObj *)malloc(numOWSLayers*sizeof(httpRequestObj));
285 if (pasOWSReqInfo == NULL) {
286 msSetError(MS_MEMERR, "Allocation of httpRequestObj failed.", "msDrawMap()");
287 return NULL;
288 }
289 msHTTPInitRequestObj(pasOWSReqInfo, numOWSLayers);
290 msInitWmsParamsObj(&sLastWMSParams);
291
292 /* Pre-download all WMS/WFS layers in parallel before starting to draw map */
293 lastconnectiontype = MS_SHAPEFILE;
294 for(i=0; i<map->numlayers; i++) {
295 if(map->layerorder[i] == -1 )
296 continue;
297
298 lp = GET_LAYER(map,map->layerorder[i]);
299 if( lp->connectiontype != MS_WMS &&
300 lp->connectiontype != MS_WFS ) {
301 continue;
302 }
303
304 if( !msLayerIsVisible(map, lp) )
305 continue;
306
307 #ifdef USE_WMS_LYR
308 if(lp->connectiontype == MS_WMS) {
309 if(msPrepareWMSLayerRequest(map->layerorder[i], map, lp, 1, lastconnectiontype, &sLastWMSParams, 0, 0, 0, NULL, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
310 msFreeWmsParamsObj(&sLastWMSParams);
311 msFreeImage(image);
312 msFree(pasOWSReqInfo);
313 return NULL;
314 }
315 }
316 #endif
317
318 #ifdef USE_WFS_LYR
319 if(lp->connectiontype == MS_WFS) {
320 if(msPrepareWFSLayerRequest(map->layerorder[i], map, lp, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
321 msFreeWmsParamsObj(&sLastWMSParams);
322 msFreeImage(image);
323 msFree(pasOWSReqInfo);
324 return NULL;
325 }
326 }
327 #endif
328
329 lastconnectiontype = lp->connectiontype;
330 }
331
332 msFreeWmsParamsObj(&sLastWMSParams);
333 } /* if numOWSLayers > 0 */
334
335 if(numOWSRequests && msOWSExecuteRequests(pasOWSReqInfo, numOWSRequests, map, MS_TRUE) == MS_FAILURE) {
336 msFreeImage(image);
337 msFree(pasOWSReqInfo);
338 return NULL;
339 }
340
341 if(map->debug >= MS_DEBUGLEVEL_TUNING) {
342 msGettimeofday(&endtime, NULL);
343 msDebug("msDrawMap(): WMS/WFS set-up and query, %.3fs\n",
344 (endtime.tv_sec+endtime.tv_usec/1.0e6)-
345 (starttime.tv_sec+starttime.tv_usec/1.0e6) );
346 }
347
348 #endif /* USE_WMS_LYR || USE_WFS_LYR */
349
350 /* OK, now we can start drawing */
351 for(i=0; i<map->numlayers; i++) {
352
353 if(map->layerorder[i] != -1) {
354 char *force_draw_label_cache = NULL;
355
356 lp = (GET_LAYER(map, map->layerorder[i]));
357
358 if(lp->postlabelcache) /* wait to draw */
359 continue;
360
361 if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING ) msGettimeofday(&starttime, NULL);
362
363 if(!msLayerIsVisible(map, lp)) continue;
364
365 if(lp->connectiontype == MS_WMS) {
366 #ifdef USE_WMS_LYR
367 if(MS_RENDERER_PLUGIN(image->format) || MS_RENDERER_RAWDATA(image->format))
368 status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
369 else {
370 msSetError(MS_WMSCONNERR, "Output format '%s' doesn't support WMS layers.", "msDrawMap()", image->format->name);
371 status = MS_FAILURE;
372 }
373
374 if(status == MS_FAILURE) {
375 msSetError(MS_WMSCONNERR,
376 "Failed to draw WMS layer named '%s'. This most likely happened because "
377 "the remote WMS server returned an invalid image, and XML exception "
378 "or another unexpected result in response to the GetMap request. Also check "
379 "and make sure that the layer's connection URL is valid.",
380 "msDrawMap()", lp->name);
381 msFreeImage(image);
382 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
383 msFree(pasOWSReqInfo);
384 return(NULL);
385 }
386
387
388 #else /* ndef USE_WMS_LYR */
389 msSetError(MS_WMSCONNERR, "MapServer not built with WMS Client support, unable to render layer '%s'.", "msDrawMap()", lp->name);
390 msFreeImage(image);
391 return(NULL);
392 #endif
393 } else { /* Default case: anything but WMS layers */
394 if(querymap)
395 status = msDrawQueryLayer(map, lp, image);
396 else
397 status = msDrawLayer(map, lp, image);
398 if(status == MS_FAILURE) {
399 msSetError(MS_IMGERR, "Failed to draw layer named '%s'.", "msDrawMap()", lp->name);
400 msFreeImage(image);
401 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
402 if (pasOWSReqInfo) {
403 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
404 msFree(pasOWSReqInfo);
405 }
406 #endif /* USE_WMS_LYR || USE_WFS_LYR */
407 return(NULL);
408 }
409 }
410 if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
411 msGettimeofday(&endtime, NULL);
412 msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
413 map->layerorder[i], lp->name?lp->name:"(null)",
414 (endtime.tv_sec+endtime.tv_usec/1.0e6)-
415 (starttime.tv_sec+starttime.tv_usec/1.0e6) );
416 }
417
418 /* Flush layer cache in-between layers if requested by PROCESSING directive*/
419 force_draw_label_cache = msLayerGetProcessingKey(lp, "FORCE_DRAW_LABEL_CACHE");
420 if (force_draw_label_cache &&
421 strncasecmp(force_draw_label_cache,"FLUSH",5)==0) {
422 if(map->debug >= MS_DEBUGLEVEL_V)
423 msDebug("msDrawMap(): PROCESSING FORCE_DRAW_LABEL_CACHE=FLUSH found.\n");
424 if(msDrawLabelCache(map, image) != MS_SUCCESS) {
425 msFreeImage(image);
426 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
427 if (pasOWSReqInfo) {
428 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
429 msFree(pasOWSReqInfo);
430 }
431 #endif /* USE_WMS_LYR || USE_WFS_LYR */
432 return(NULL);
433 }
434 msFreeLabelCache(&(map->labelcache));
435 msInitLabelCache(&(map->labelcache));
436 } /* PROCESSING FORCE_DRAW_LABEL_CACHE */
437
438 }
439 }
440
441 if(map->scalebar.status == MS_EMBED && !map->scalebar.postlabelcache) {
442
443 /* We need to temporarily restore the original extent for drawing */
444 /* the scalebar as it uses the extent to recompute cellsize. */
445 if(map->gt.need_geotransform)
446 msMapRestoreRealExtent(map);
447
448
449 if(MS_SUCCESS != msEmbedScalebar(map, image)) {
450 msFreeImage( image );
451 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
452 /* Cleanup WMS/WFS Request stuff */
453 if (pasOWSReqInfo) {
454 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
455 msFree(pasOWSReqInfo);
456 }
457 #endif
458 return NULL;
459 }
460
461
462 if(map->gt.need_geotransform)
463 msMapSetFakedExtent(map);
464 }
465
466 if(map->legend.status == MS_EMBED && !map->legend.postlabelcache) {
467 if( msEmbedLegend(map, image) != MS_SUCCESS ) {
468 msFreeImage( image );
469 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
470 /* Cleanup WMS/WFS Request stuff */
471 if (pasOWSReqInfo) {
472 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
473 msFree(pasOWSReqInfo);
474 }
475 #endif
476 return NULL;
477 }
478 }
479
480 if(msDrawLabelCache(map, image) != MS_SUCCESS) {
481 msFreeImage(image);
482 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
483 if (pasOWSReqInfo) {
484 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
485 msFree(pasOWSReqInfo);
486 }
487 #endif /* USE_WMS_LYR || USE_WFS_LYR */
488 return(NULL);
489 }
490
491
492 for(i=0; i<map->numlayers; i++) { /* for each layer, check for postlabelcache layers */
493
494 lp = (GET_LAYER(map, map->layerorder[i]));
495
496 if(!lp->postlabelcache) continue;
497 if(!msLayerIsVisible(map, lp)) continue;
498
499 if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
500
501 if(lp->connectiontype == MS_WMS) {
502 #ifdef USE_WMS_LYR
503 if(MS_RENDERER_PLUGIN(image->format) || MS_RENDERER_RAWDATA(image->format))
504 status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
505
506 #else
507 status = MS_FAILURE;
508 #endif
509 } else {
510 if(querymap)
511 status = msDrawQueryLayer(map, lp, image);
512 else
513 status = msDrawLayer(map, lp, image);
514 }
515
516 if(status == MS_FAILURE) {
517 msFreeImage(image);
518 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
519 if (pasOWSReqInfo) {
520 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
521 msFree(pasOWSReqInfo);
522 }
523 #endif /* USE_WMS_LYR || USE_WFS_LYR */
524 return(NULL);
525 }
526
527 if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
528 msGettimeofday(&endtime, NULL);
529 msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
530 map->layerorder[i], lp->name?lp->name:"(null)",
531 (endtime.tv_sec+endtime.tv_usec/1.0e6)-
532 (starttime.tv_sec+starttime.tv_usec/1.0e6) );
533 }
534
535 }
536
537 /* Do we need to fake out stuff for rotated support? */
538 /* This really needs to be done on every preceeding exit point too... */
539 if(map->gt.need_geotransform)
540 msMapRestoreRealExtent(map);
541
542 if(map->legend.status == MS_EMBED && map->legend.postlabelcache)
543 if(UNLIKELY(MS_FAILURE == msEmbedLegend(map, image))) {
544 msFreeImage( image );
545 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
546 /* Cleanup WMS/WFS Request stuff */
547 if (pasOWSReqInfo) {
548 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
549 msFree(pasOWSReqInfo);
550 }
551 #endif
552 return NULL;
553 }
554
555 if(map->scalebar.status == MS_EMBED && map->scalebar.postlabelcache) {
556
557 /* We need to temporarily restore the original extent for drawing */
558 /* the scalebar as it uses the extent to recompute cellsize. */
559 if(map->gt.need_geotransform)
560 msMapRestoreRealExtent(map);
561
562 if(MS_SUCCESS != msEmbedScalebar(map, image)) {
563 msFreeImage( image );
564 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
565 /* Cleanup WMS/WFS Request stuff */
566 if (pasOWSReqInfo) {
567 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
568 msFree(pasOWSReqInfo);
569 }
570 #endif
571 return NULL;
572 }
573
574
575 if(map->gt.need_geotransform)
576 msMapSetFakedExtent(map);
577 }
578
579 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
580 /* Cleanup WMS/WFS Request stuff */
581 if (pasOWSReqInfo) {
582 msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
583 msFree(pasOWSReqInfo);
584 }
585 #endif
586
587 if(map->debug >= MS_DEBUGLEVEL_TUNING) {
588 msGettimeofday(&mapendtime, NULL);
589 msDebug("msDrawMap() total time: %.3fs\n",
590 (mapendtime.tv_sec+mapendtime.tv_usec/1.0e6)-
591 (mapstarttime.tv_sec+mapstarttime.tv_usec/1.0e6) );
592 }
593
594 return(image);
595 }
596
597 /*
598 * Test whether a layer should be drawn or not in the current map view and
599 * at the current scale.
600 * Returns TRUE if layer is visible, FALSE if not.
601 */
msLayerIsVisible(mapObj * map,layerObj * layer)602 int msLayerIsVisible(mapObj *map, layerObj *layer)
603 {
604 int i;
605
606 if(!layer->data && !layer->tileindex && !layer->connection && !layer->features && !layer->grid)
607 return(MS_FALSE); /* no data associated with this layer, not an error since layer may be used as a template from MapScript */
608
609 if(layer->type == MS_LAYER_QUERY || layer->type == MS_LAYER_TILEINDEX) return(MS_FALSE);
610 if((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) return(MS_FALSE);
611
612 /* Do comparisons of layer scale vs map scale now, since msExtentsOverlap() */
613 /* can be slow */
614 if(map->scaledenom > 0) {
615
616 /* layer scale boundaries should be checked first */
617 if((layer->maxscaledenom > 0) && (map->scaledenom > layer->maxscaledenom)) {
618 if( layer->debug >= MS_DEBUGLEVEL_V ) {
619 msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.MAXSCALE is too small for this MAP scale\n", layer->name);
620 }
621 return(MS_FALSE);
622 }
623 if(/*(layer->minscaledenom > 0) &&*/ (map->scaledenom <= layer->minscaledenom)) {
624 if( layer->debug >= MS_DEBUGLEVEL_V ) {
625 msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.MINSCALE is too large for this MAP scale\n", layer->name);
626 }
627 return(MS_FALSE);
628 }
629 }
630
631 /* Only return MS_FALSE if it is definitely false. Sometimes it will return MS_UNKNOWN, which we
632 ** consider true, for this use case (it might be visible, try and draw it, see what happens). */
633 if ( msExtentsOverlap(map, layer) == MS_FALSE ) {
634 if( layer->debug >= MS_DEBUGLEVEL_V ) {
635 msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.EXTENT does not intersect MAP.EXTENT\n", layer->name);
636 }
637 return(MS_FALSE);
638 }
639
640 if(msEvalContext(map, layer, layer->requires) == MS_FALSE) return(MS_FALSE);
641
642 if(map->scaledenom > 0) {
643
644 /* now check class scale boundaries (all layers *must* pass these tests) */
645 if(layer->numclasses > 0) {
646 for(i=0; i<layer->numclasses; i++) {
647 if((layer->class[i]->maxscaledenom > 0) && (map->scaledenom > layer->class[i]->maxscaledenom))
648 continue; /* can skip this one, next class */
649 if((layer->class[i]->minscaledenom > 0) && (map->scaledenom <= layer->class[i]->minscaledenom))
650 continue; /* can skip this one, next class */
651
652 break; /* can't skip this class (or layer for that matter) */
653 }
654 if(i == layer->numclasses) {
655 if( layer->debug >= MS_DEBUGLEVEL_V ) {
656 msDebug("msLayerIsVisible(): Skipping layer (%s) because no CLASS in the layer is in-scale for this MAP scale\n", layer->name);
657 }
658 return(MS_FALSE);
659 }
660 }
661
662 }
663
664 if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
665 if((layer->maxgeowidth > 0) && ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {
666 if( layer->debug >= MS_DEBUGLEVEL_V ) {
667 msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER width is much smaller than map width\n", layer->name);
668 }
669 return(MS_FALSE);
670 }
671 if((layer->mingeowidth > 0) && ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) {
672 if( layer->debug >= MS_DEBUGLEVEL_V ) {
673 msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER width is much larger than map width\n", layer->name);
674 }
675 return(MS_FALSE);
676 }
677 }
678
679 return MS_TRUE; /* All tests passed. Layer is visible. */
680 }
681
682
683 #define LAYER_NEEDS_COMPOSITING(layer) (((layer)->compositer != NULL) && ((layer)->compositer->next || (layer)->compositor->opacity < 100 || (layer)->compositor->compop != MS_COMPOP_SRC_OVER || (layer)->compositer->filter ))
684 /*
685 * Generic function to render a layer object.
686 */
msDrawLayer(mapObj * map,layerObj * layer,imageObj * image)687 int msDrawLayer(mapObj *map, layerObj *layer, imageObj *image)
688 {
689 imageObj *image_draw = image;
690 outputFormatObj *altFormat=NULL;
691 int retcode=MS_SUCCESS;
692 const char *alternativeFomatString = NULL;
693 layerObj *maskLayer = NULL;
694
695 if(!msLayerIsVisible(map, layer))
696 return MS_SUCCESS;
697
698 if(layer->compositer && !layer->compositer->next && layer->compositer->opacity == 0) return MS_SUCCESS; /* layer is completely transparent, skip it */
699
700
701 /* conditions may have changed since this layer last drawn, so retest
702 layer->project (Bug #673) */
703 layer->project = msProjectionsDiffer(&(layer->projection),&(map->projection));
704
705 /* make sure labelcache setting is set correctly if postlabelcache is set. This is done by the parser but
706 may have been altered by a mapscript. see #5142 */
707 if(layer->postlabelcache) {
708 layer->labelcache = MS_FALSE;
709 }
710
711 if(layer->mask) {
712 int maskLayerIdx;
713 /* render the mask layer in its own imageObj */
714 if(!MS_IMAGE_RENDERER(image)->supports_pixel_buffer) {
715 msSetError(MS_MISCERR, "Layer (%s) references references a mask layer, but the selected renderer does not support them", "msDrawLayer()",
716 layer->name);
717 return (MS_FAILURE);
718 }
719 maskLayerIdx = msGetLayerIndex(map,layer->mask);
720 if(maskLayerIdx == -1) {
721 msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)", "msDrawLayer()",
722 layer->name,layer->mask);
723 return (MS_FAILURE);
724 }
725 maskLayer = GET_LAYER(map, maskLayerIdx);
726 if(!maskLayer->maskimage) {
727 int i;
728 int origstatus, origlabelcache;
729 altFormat = msSelectOutputFormat(map, "png24");
730 msInitializeRendererVTable(altFormat);
731 /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg */
732 maskLayer->maskimage= msImageCreate(image->width, image->height,altFormat,
733 image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL);
734 if (!maskLayer->maskimage) {
735 msSetError(MS_MISCERR, "Unable to initialize mask image.", "msDrawLayer()");
736 return (MS_FAILURE);
737 }
738
739 /*
740 * force the masked layer to status on, and turn off the labelcache so that
741 * eventual labels are added to the temporary image instead of being added
742 * to the labelcache
743 */
744 origstatus = maskLayer->status;
745 origlabelcache = maskLayer->labelcache;
746 maskLayer->status = MS_ON;
747 maskLayer->labelcache = MS_OFF;
748
749 /* draw the mask layer in the temporary image */
750 retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
751 maskLayer->status = origstatus;
752 maskLayer->labelcache = origlabelcache;
753 if(retcode != MS_SUCCESS) {
754 return MS_FAILURE;
755 }
756 /*
757 * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain
758 * symbols that reference it. We want to remove those references before the altFormat is destroyed
759 * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking
760 * it's for him.
761 */
762 for(i=0; i<map->symbolset.numsymbols; i++) {
763 if (map->symbolset.symbol[i]!=NULL) {
764 symbolObj *s = map->symbolset.symbol[i];
765 if(s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
766 MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
767 s->renderer = NULL;
768 }
769 }
770 }
771 /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
772 msFree(map->imagetype);
773 map->imagetype = msStrdup(image->format->name);
774 }
775
776 }
777 altFormat = NULL;
778 /* inform the rendering device that layer draw is starting. */
779 msImageStartLayer(map, layer, image);
780
781
782 /*check if an alternative renderer should be used for this layer*/
783 alternativeFomatString = msLayerGetProcessingKey( layer, "RENDERER");
784 if (MS_RENDERER_PLUGIN(image_draw->format) && alternativeFomatString!=NULL &&
785 (altFormat= msSelectOutputFormat(map, alternativeFomatString))) {
786 rendererVTableObj *renderer=NULL;
787 msInitializeRendererVTable(altFormat);
788
789 image_draw = msImageCreate(image->width, image->height,
790 altFormat, image->imagepath, image->imageurl, map->resolution, map->defresolution, &map->imagecolor);
791 renderer = MS_IMAGE_RENDERER(image_draw);
792 renderer->startLayer(image_draw,map,layer);
793 } else if (MS_RENDERER_PLUGIN(image_draw->format)) {
794 rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw);
795 if ((layer->mask && layer->connectiontype!=MS_WMS && layer->type != MS_LAYER_RASTER) || layer->compositer) {
796 /* masking occurs at the pixel/layer level for raster images, so we don't need to create a temporary image
797 in these cases
798 */
799 if (layer->mask || renderer->compositeRasterBuffer) {
800 image_draw = msImageCreate(image->width, image->height,
801 image->format, image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL);
802 if (!image_draw) {
803 msSetError(MS_MISCERR, "Unable to initialize temporary transparent image.",
804 "msDrawLayer()");
805 return (MS_FAILURE);
806 }
807 image_draw->map = map;
808 renderer->startLayer(image_draw,map,layer);
809 }
810 }
811 }
812 /*
813 ** redirect procesing of some layer types.
814 */
815 if(layer->connectiontype == MS_WMS) {
816 #ifdef USE_WMS_LYR
817 retcode = msDrawWMSLayer(map, layer, image_draw);
818 #else
819 retcode = MS_FAILURE;
820 #endif
821 } else if(layer->type == MS_LAYER_RASTER) {
822 retcode = msDrawRasterLayer(map, layer, image_draw);
823 } else if(layer->type == MS_LAYER_CHART) {
824 retcode = msDrawChartLayer(map, layer, image_draw);
825 } else { /* must be a Vector layer */
826 retcode = msDrawVectorLayer(map, layer, image_draw);
827 }
828
829 if (altFormat) {
830 rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
831 rendererVTableObj *altrenderer = MS_IMAGE_RENDERER(image_draw);
832 rasterBufferObj rb;
833 int i;
834 memset(&rb,0,sizeof(rasterBufferObj));
835
836 altrenderer->endLayer(image_draw,map,layer);
837
838 retcode = altrenderer->getRasterBufferHandle(image_draw,&rb);
839 if(UNLIKELY(retcode == MS_FAILURE)) {
840 goto altformat_cleanup;
841 }
842 retcode = renderer->mergeRasterBuffer(image,&rb,((layer->compositer)?(layer->compositer->opacity*0.01):(1.0)),0,0,0,0,rb.width,rb.height);
843 if(UNLIKELY(retcode == MS_FAILURE)) {
844 goto altformat_cleanup;
845 }
846
847 altformat_cleanup:
848 /*
849 * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain
850 * symbols that reference it. We want to remove those references before the altFormat is destroyed
851 * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking
852 * it's for him.
853 */
854 for(i=0; i<map->symbolset.numsymbols; i++) {
855 if (map->symbolset.symbol[i]!=NULL) {
856 symbolObj *s = map->symbolset.symbol[i];
857 if(s->renderer == altrenderer) {
858 altrenderer->freeSymbol(s);
859 s->renderer = NULL;
860 }
861 }
862 }
863 msFreeImage(image_draw);
864 /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
865 msFree(map->imagetype);
866 map->imagetype = msStrdup(image->format->name);
867 } else if( image != image_draw) {
868 rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw);
869 rasterBufferObj rb;
870 memset(&rb,0,sizeof(rasterBufferObj));
871
872 renderer->endLayer(image_draw,map,layer);
873
874 retcode = renderer->getRasterBufferHandle(image_draw,&rb);
875 if(UNLIKELY(retcode == MS_FAILURE)) {
876 goto imagedraw_cleanup;
877 }
878 if(maskLayer && maskLayer->maskimage) {
879 rasterBufferObj mask;
880 unsigned int row,col;
881 memset(&mask,0,sizeof(rasterBufferObj));
882 retcode = MS_IMAGE_RENDERER(maskLayer->maskimage)->getRasterBufferHandle(maskLayer->maskimage,&mask);
883 if(UNLIKELY(retcode == MS_FAILURE)) {
884 goto imagedraw_cleanup;
885 }
886 /* modify the pixels of the overlay */
887
888 if(rb.type == MS_BUFFER_BYTE_RGBA) {
889 for(row=0; row<rb.height; row++) {
890 unsigned char *ma,*a,*r,*g,*b;
891 r=rb.data.rgba.r+row*rb.data.rgba.row_step;
892 g=rb.data.rgba.g+row*rb.data.rgba.row_step;
893 b=rb.data.rgba.b+row*rb.data.rgba.row_step;
894 a=rb.data.rgba.a+row*rb.data.rgba.row_step;
895 ma=mask.data.rgba.a+row*mask.data.rgba.row_step;
896 for(col=0; col<rb.width; col++) {
897 if(*ma == 0) {
898 *a = *r = *g = *b = 0;
899 }
900 a+=rb.data.rgba.pixel_step;
901 r+=rb.data.rgba.pixel_step;
902 g+=rb.data.rgba.pixel_step;
903 b+=rb.data.rgba.pixel_step;
904 ma+=mask.data.rgba.pixel_step;
905 }
906 }
907 }
908 }
909 if(!layer->compositer) {
910 /*we have a mask layer with no composition configured, do a nomral blend */
911 retcode = renderer->mergeRasterBuffer(image,&rb,1.0,0,0,0,0,rb.width,rb.height);
912 } else {
913 retcode = msCompositeRasterBuffer(map,image,&rb,layer->compositer);
914 }
915 if(UNLIKELY(retcode == MS_FAILURE)) {
916 goto imagedraw_cleanup;
917 }
918
919 imagedraw_cleanup:
920 msFreeImage(image_draw);
921 }
922
923 msImageEndLayer(map,layer,image);
924 return(retcode);
925 }
926
msDrawVectorLayer(mapObj * map,layerObj * layer,imageObj * image)927 int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
928 {
929 int status, retcode=MS_SUCCESS;
930 int drawmode=MS_DRAWMODE_FEATURES;
931 char annotate=MS_TRUE;
932 shapeObj shape;
933 shapeObj savedShape;
934 rectObj searchrect;
935 char cache=MS_FALSE;
936 int maxnumstyles=1;
937 featureListNodeObjPtr shpcache=NULL, current=NULL;
938 int nclasses = 0;
939 int *classgroup = NULL;
940 double minfeaturesize = -1;
941 int maxfeatures=-1;
942 int featuresdrawn=0;
943
944 if (image)
945 maxfeatures=msLayerGetMaxFeaturesToDraw(layer, image->format);
946
947 /* TODO TBT: draw as raster layer in vector renderers */
948
949 annotate = msEvalContext(map, layer, layer->labelrequires);
950 if(map->scaledenom > 0) {
951 if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
952 if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
953 }
954
955 /* open this layer */
956 status = msLayerOpen(layer);
957 if(status != MS_SUCCESS) return MS_FAILURE;
958
959 /* build item list. STYLEITEM javascript needs the shape attributes */
960 if (layer->styleitem && (strncasecmp(layer->styleitem, "javascript://", 13) == 0)) {
961 status = msLayerWhichItems(layer, MS_TRUE, NULL);
962 } else {
963 status = msLayerWhichItems(layer, MS_FALSE, NULL);
964 }
965
966 if(status != MS_SUCCESS) {
967 msLayerClose(layer);
968 return MS_FAILURE;
969 }
970
971 /* identify target shapes */
972 if(layer->transform == MS_TRUE) {
973 searchrect = map->extent;
974
975 if((map->projection.numargs > 0) && (layer->projection.numargs > 0)) {
976 int bDone = MS_FALSE;
977
978 if( layer->connectiontype == MS_UVRASTER )
979 {
980 /* Nasty hack to make msUVRASTERLayerWhichShapes() aware that the */
981 /* original area of interest is (map->extent, map->projection)... */
982 /* Useful when dealin with UVRASTER that extend beyond 180 deg */
983 msUVRASTERLayerUseMapExtentAndProjectionForNextWhichShapes( layer, map );
984
985 searchrect = msUVRASTERGetSearchRect( layer, map );
986 bDone = MS_TRUE;
987 }
988
989 if( !bDone )
990 msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
991 }
992
993 } else {
994 searchrect.minx = searchrect.miny = 0;
995 searchrect.maxx = map->width-1;
996 searchrect.maxy = map->height-1;
997 }
998
999 status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
1000
1001 if( layer->connectiontype == MS_UVRASTER )
1002 {
1003 msUVRASTERLayerUseMapExtentAndProjectionForNextWhichShapes( layer, NULL );
1004 }
1005
1006 if(status == MS_DONE) { /* no overlap */
1007 msLayerClose(layer);
1008 return MS_SUCCESS;
1009 } else if(status != MS_SUCCESS) {
1010 msLayerClose(layer);
1011 return MS_FAILURE;
1012 }
1013
1014 nclasses = 0;
1015 classgroup = NULL;
1016 if(layer->classgroup && layer->numclasses > 0)
1017 classgroup = msAllocateValidClassGroups(layer, &nclasses);
1018
1019 if(layer->minfeaturesize > 0)
1020 minfeaturesize = Pix2LayerGeoref(map, layer, layer->minfeaturesize);
1021
1022 // Select how to render classes
1023 // MS_FIRST_MATCHING_CLASS: Default and historic MapServer behavior
1024 // MS_ALL_MATCHING_CLASSES: SLD behavior
1025 int ref_rendermode;
1026 char * rendermodestr = msLayerGetProcessingKey(layer, "RENDERMODE");
1027 if (layer->rendermode == MS_ALL_MATCHING_CLASSES)
1028 {
1029 // SLD takes precedence
1030 ref_rendermode = MS_ALL_MATCHING_CLASSES;
1031 }
1032 else if (!rendermodestr)
1033 {
1034 // Default Mapfile
1035 ref_rendermode = MS_FIRST_MATCHING_CLASS;
1036 }
1037 else if (!strcmp(rendermodestr,"FIRST_MATCHING_CLASS"))
1038 {
1039 // Explicit default Mapfile
1040 ref_rendermode = MS_FIRST_MATCHING_CLASS;
1041 }
1042 else if (!strcmp(rendermodestr,"ALL_MATCHING_CLASSES"))
1043 {
1044 // SLD-like Mapfile
1045 ref_rendermode = MS_ALL_MATCHING_CLASSES;
1046 }
1047 else
1048 {
1049 msLayerClose(layer);
1050 msSetError(MS_MISCERR,
1051 "Unknown RENDERMODE: %s, should be one of: FIRST_MATCHING_CLASS, ALL_MATCHING_CLASSES.",
1052 "msDrawVectorLayer()",
1053 rendermodestr);
1054 return MS_FAILURE;
1055 }
1056
1057 /* step through the target shapes and their classes */
1058 msInitShape(&shape);
1059 int classindex = -1;
1060 int classcount = 0;
1061 for (;;) {
1062 int rendermode;
1063 if (classindex == -1) {
1064 msFreeShape(&shape);
1065 status = msLayerNextShape(layer, &shape);
1066 if (status != MS_SUCCESS) {
1067 break;
1068 }
1069
1070 /* Check if the shape size is ok to be drawn */
1071 if((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) && (minfeaturesize > 0) && (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE)) {
1072 if(layer->debug >= MS_DEBUGLEVEL_V)
1073 msDebug("msDrawVectorLayer(): Skipping shape (%ld) because LAYER::MINFEATURESIZE is bigger than shape size\n", shape.index);
1074 continue;
1075 }
1076 classcount = 0;
1077 }
1078
1079 classindex = msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses);
1080 if((classindex == -1) || (layer->class[classindex]->status == MS_OFF)) {
1081 continue;
1082 }
1083 shape.classindex = classindex;
1084
1085 // When only one class is applicable, rendering mode is forced to its default,
1086 // i.e. only the first applicable class is actually applied. As a consequence,
1087 // cache can be enabled when relevant.
1088 classcount++;
1089 rendermode = ref_rendermode;
1090 if ((classcount == 1) && (msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses) == -1))
1091 {
1092 rendermode = MS_FIRST_MATCHING_CLASS;
1093 }
1094
1095 if (rendermode == MS_FIRST_MATCHING_CLASS)
1096 {
1097 classindex = -1;
1098 }
1099
1100 if(maxfeatures >=0 && featuresdrawn >= maxfeatures) {
1101 status = MS_DONE;
1102 break;
1103 }
1104 featuresdrawn++;
1105
1106 cache = MS_FALSE;
1107 if(layer->type == MS_LAYER_LINE && (layer->class[shape.classindex]->numstyles > 1 || (layer->class[shape.classindex]->numstyles == 1 && layer->class[shape.classindex]->styles[0]->outlinewidth > 0))) {
1108 int i;
1109 cache = MS_TRUE; /* only line layers with multiple styles need be cached (I don't think POLYLINE layers need caching - SDL) */
1110
1111 /* we can't handle caching with attribute binding other than for the first style (#3976) */
1112 for(i=1; i<layer->class[shape.classindex]->numstyles; i++) {
1113 if(layer->class[shape.classindex]->styles[i]->numbindings > 0) cache = MS_FALSE;
1114 }
1115 }
1116
1117 /* With 'STYLEITEM AUTO', we will have the datasource fill the class' */
1118 /* style parameters for this shape. */
1119 if(layer->styleitem) {
1120 if(strcasecmp(layer->styleitem, "AUTO") == 0) {
1121 if(msLayerGetAutoStyle(map, layer, layer->class[shape.classindex], &shape) != MS_SUCCESS) {
1122 retcode = MS_FAILURE;
1123 break;
1124 }
1125 } else {
1126 /* Generic feature style handling as per RFC-61 */
1127 if(msLayerGetFeatureStyle(map, layer, layer->class[shape.classindex], &shape) != MS_SUCCESS) {
1128 retcode = MS_FAILURE;
1129 break;
1130 }
1131 }
1132
1133 /* __TODO__ For now, we can't cache features with 'AUTO' style */
1134 cache = MS_FALSE;
1135 }
1136
1137 if (rendermode == MS_ALL_MATCHING_CLASSES)
1138 {
1139 // Cache is designed to handle only one class. Therefore it is
1140 // disabled when using SLD "painters model" rendering mode.
1141 cache = MS_FALSE;
1142 }
1143
1144 /* RFC77 TODO: check return value, may need a more sophisticated if-then test. */
1145 if(annotate && layer->class[shape.classindex]->numlabels > 0) {
1146 drawmode |= MS_DRAWMODE_LABELS;
1147 if (msLayerGetProcessingKey(layer, "LABEL_NO_CLIP")) {
1148 drawmode |= MS_DRAWMODE_UNCLIPPEDLABELS;
1149 }
1150 }
1151
1152 if (layer->type == MS_LAYER_LINE && msLayerGetProcessingKey(layer, "POLYLINE_NO_CLIP")) {
1153 drawmode |= MS_DRAWMODE_UNCLIPPEDLINES;
1154 }
1155
1156 if (rendermode == MS_ALL_MATCHING_CLASSES)
1157 {
1158 // In SLD "painters model" rendering mode, all applicable classes are actually applied.
1159 // Coordinates stored in the shape must keep their original values for
1160 // the shape to be drawn multiple times.
1161 // Here the original shape is saved.
1162 msInitShape(&savedShape);
1163 msCopyShape(&shape, &savedShape);
1164 }
1165
1166 if (cache) {
1167 styleObj *pStyle = layer->class[shape.classindex]->styles[0];
1168 if (pStyle->outlinewidth > 0) {
1169 /*
1170 * RFC 49 implementation
1171 * if an outlinewidth is used:
1172 * - augment the style's width to account for the outline width
1173 * - swap the style color and outlinecolor
1174 * - draw the shape (the outline) in the first pass of the
1175 * caching mechanism
1176 */
1177 msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1178 }
1179 status = msDrawShape(map, layer, &shape, image, 0, drawmode|MS_DRAWMODE_SINGLESTYLE); /* draw a single style */
1180 if (pStyle->outlinewidth > 0) {
1181 /*
1182 * RFC 49 implementation: switch back the styleobj to its
1183 * original state, so the line fill will be drawn in the
1184 * second pass of the caching mechanism
1185 */
1186 msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1187 }
1188 }
1189
1190 else
1191 status = msDrawShape(map, layer, &shape, image, -1, drawmode); /* all styles */
1192
1193 if (rendermode == MS_ALL_MATCHING_CLASSES)
1194 {
1195 // In SLD "painters model" rendering mode, all applicable classes are actually applied.
1196 // Coordinates stored in the shape must keep their original values for
1197 // the shape to be drawn multiple times.
1198 // Here the original shape is restored.
1199 msFreeShape(&shape);
1200 msCopyShape(&savedShape, &shape);
1201 msFreeShape(&savedShape);
1202 }
1203
1204 if(status != MS_SUCCESS) {
1205 retcode = MS_FAILURE;
1206 break;
1207 }
1208
1209 if(shape.numlines == 0) { /* once clipped the shape didn't need to be drawn */
1210 continue;
1211 }
1212
1213 if(cache) {
1214 if(insertFeatureList(&shpcache, &shape) == NULL) {
1215 retcode = MS_FAILURE; /* problem adding to the cache */
1216 break;
1217 }
1218 }
1219
1220 maxnumstyles = MS_MAX(maxnumstyles, layer->class[shape.classindex]->numstyles);
1221
1222 }
1223 msFreeShape(&shape);
1224
1225 if (classgroup)
1226 msFree(classgroup);
1227
1228 if(status != MS_DONE || retcode == MS_FAILURE) {
1229 msLayerClose(layer);
1230 if(shpcache) {
1231 freeFeatureList(shpcache);
1232 shpcache = NULL;
1233 }
1234 return MS_FAILURE;
1235 }
1236
1237 if(shpcache && MS_DRAW_FEATURES(drawmode)) {
1238 int s;
1239 for(s=0; s<maxnumstyles; s++) {
1240 for(current=shpcache; current; current=current->next) {
1241 if(layer->class[current->shape.classindex]->numstyles > s) {
1242 styleObj *pStyle = layer->class[current->shape.classindex]->styles[s];
1243 if(pStyle->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
1244 continue; /*skip this as it has already been rendered*/
1245 if(map->scaledenom > 0) {
1246 if((pStyle->maxscaledenom != -1) && (map->scaledenom >= pStyle->maxscaledenom))
1247 continue;
1248 if((pStyle->minscaledenom != -1) && (map->scaledenom < pStyle->minscaledenom))
1249 continue;
1250 }
1251 if(s==0 && pStyle->outlinewidth>0 && MS_VALID_COLOR(pStyle->color)) {
1252 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor))) {
1253 return MS_FAILURE;
1254 }
1255 } else if(s>0) {
1256 if (pStyle->outlinewidth > 0 && MS_VALID_COLOR(pStyle->outlinecolor)) {
1257 /*
1258 * RFC 49 implementation
1259 * if an outlinewidth is used:
1260 * - augment the style's width to account for the outline width
1261 * - swap the style color and outlinecolor
1262 * - draw the shape (the outline) in the first pass of the
1263 * caching mechanism
1264 */
1265 msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1266 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor))) {
1267 return MS_FAILURE;
1268 }
1269 /*
1270 * RFC 49 implementation: switch back the styleobj to its
1271 * original state, so the line fill will be drawn in the
1272 * second pass of the caching mechanism
1273 */
1274 msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1275 }
1276 /* draw a valid line, i.e. one with a color defined or of type pixmap*/
1277 if(MS_VALID_COLOR(pStyle->color) ||
1278 (
1279 pStyle->symbol<map->symbolset.numsymbols &&
1280 (
1281 map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_PIXMAP ||
1282 map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_SVG
1283 )
1284 )
1285 ) {
1286 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor)))
1287 return MS_FAILURE;
1288 }
1289 }
1290 }
1291 }
1292 }
1293
1294 freeFeatureList(shpcache);
1295 shpcache = NULL;
1296 }
1297
1298 msLayerClose(layer);
1299 return MS_SUCCESS;
1300
1301 }
1302
1303 /*
1304 ** Function to draw a layer IF it already has a result cache associated with it. Called by msDrawMap and via MapScript.
1305 */
msDrawQueryLayer(mapObj * map,layerObj * layer,imageObj * image)1306 int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image)
1307 {
1308 int i, status;
1309 char annotate=MS_TRUE, cache=MS_FALSE;
1310 int drawmode = MS_DRAWMODE_FEATURES;
1311 shapeObj shape;
1312 int maxnumstyles=1;
1313
1314 featureListNodeObjPtr shpcache=NULL, current=NULL;
1315
1316 colorObj *colorbuffer = NULL;
1317 int *mindistancebuffer = NULL;
1318
1319 if(!layer->resultcache) return(msDrawLayer(map, layer, image));
1320
1321 if(!msLayerIsVisible(map, layer)) return(MS_SUCCESS); /* not an error, just nothing to do */
1322
1323 /* conditions may have changed since this layer last drawn, so reset
1324 layer->project to recheck projection needs (Bug #673) */
1325 layer->project = msProjectionsDiffer(&(layer->projection),&(map->projection));
1326
1327 /* set annotation status */
1328 annotate = msEvalContext(map, layer, layer->labelrequires);
1329 if(map->scaledenom > 0) {
1330 if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
1331 if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
1332 }
1333
1334 /*
1335 ** Certain query map styles require us to render all features only (MS_NORMAL) or first (MS_HILITE). With
1336 ** single-pass queries we have to make a copy of the layer and work from it instead.
1337 */
1338 if(map->querymap.style == MS_NORMAL || map->querymap.style == MS_HILITE) {
1339 layerObj tmp_layer;
1340
1341 if(initLayer(&tmp_layer, map) == -1)
1342 return(MS_FAILURE);
1343
1344 if (msCopyLayer(&tmp_layer, layer) != MS_SUCCESS)
1345 return(MS_FAILURE);
1346
1347 /* disable the connection pool for this layer */
1348 msLayerSetProcessingKey(&tmp_layer, "CLOSE_CONNECTION", "ALWAYS");
1349
1350 status = msDrawLayer(map, &tmp_layer, image);
1351
1352 freeLayer(&tmp_layer);
1353
1354 if(map->querymap.style == MS_NORMAL || status != MS_SUCCESS) return(status);
1355 }
1356
1357 /* if MS_HILITE, alter the one style (always at least 1 style), and set a MINDISTANCE for the labelObj to avoid duplicates */
1358 if(map->querymap.style == MS_HILITE) {
1359
1360 drawmode |= MS_DRAWMODE_QUERY;
1361
1362 if (layer->numclasses > 0) {
1363 colorbuffer = (colorObj*)msSmallMalloc(layer->numclasses*sizeof(colorObj));
1364 mindistancebuffer = (int*)msSmallMalloc(layer->numclasses*sizeof(int));
1365 }
1366
1367 for(i=0; i<layer->numclasses; i++) {
1368 if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) { /* alter BOTTOM style since that's almost always the fill */
1369 if (layer->class[i]->styles == NULL) {
1370 msSetError(MS_MISCERR, "Don't know how to draw class %s of layer %s without a style definition.", "msDrawQueryLayer()", layer->class[i]->name, layer->name);
1371 msFree(colorbuffer);
1372 msFree(mindistancebuffer);
1373 return(MS_FAILURE);
1374 }
1375 if(MS_VALID_COLOR(layer->class[i]->styles[0]->color)) {
1376 colorbuffer[i] = layer->class[i]->styles[0]->color; /* save the color from the BOTTOM style */
1377 layer->class[i]->styles[0]->color = map->querymap.color;
1378 } else if(MS_VALID_COLOR(layer->class[i]->styles[0]->outlinecolor)) {
1379 colorbuffer[i] = layer->class[i]->styles[0]->outlinecolor; /* if no color, save the outlinecolor from the BOTTOM style */
1380 layer->class[i]->styles[0]->outlinecolor = map->querymap.color;
1381 }
1382 } else if (layer->type == MS_LAYER_LINE && layer->class[i]->numstyles > 0 && layer->class[i]->styles[0]->outlinewidth > 0) { /* alter BOTTOM style for lines with outlines */
1383 if(MS_VALID_COLOR(layer->class[i]->styles[0]->color)) {
1384 colorbuffer[i] = layer->class[i]->styles[0]->color; /* save the color from the BOTTOM style */
1385 layer->class[i]->styles[0]->color = map->querymap.color;
1386 } /* else ??? */
1387 } else if (layer->class[i]->numstyles > 0) {
1388 if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->color)) {
1389 colorbuffer[i] = layer->class[i]->styles[layer->class[i]->numstyles-1]->color; /* save the color from the TOP style */
1390 layer->class[i]->styles[layer->class[i]->numstyles-1]->color = map->querymap.color;
1391 } else if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor)) {
1392 colorbuffer[i] = layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor; /* if no color, save the outlinecolor from the TOP style */
1393 layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor = map->querymap.color;
1394 }
1395 } else if (layer->class[i]->numlabels > 0) {
1396 colorbuffer[i] = layer->class[i]->labels[0]->color;
1397 layer->class[i]->labels[0]->color = map->querymap.color;
1398 } /* else ??? */
1399
1400 mindistancebuffer[i] = -1; /* RFC77 TODO: only using the first label, is that cool? */
1401 if(layer->class[i]->numlabels > 0) {
1402 mindistancebuffer[i] = layer->class[i]->labels[0]->mindistance;
1403 layer->class[i]->labels[0]->mindistance = MS_MAX(0, layer->class[i]->labels[0]->mindistance);
1404 }
1405 }
1406 }
1407
1408 /*
1409 ** Layer was opened as part of the query process, msLayerWhichItems() has also been run, shapes have been classified - start processing!
1410 */
1411
1412 msInitShape(&shape);
1413
1414 for(i=0; i<layer->resultcache->numresults; i++) {
1415 status = msLayerGetShape(layer, &shape, &(layer->resultcache->results[i]));
1416 if(status != MS_SUCCESS) {
1417 msFree(colorbuffer);
1418 msFree(mindistancebuffer);
1419 return(MS_FAILURE);
1420 }
1421
1422 shape.classindex = layer->resultcache->results[i].classindex;
1423 /* classindex may be -1 here if there was a template at the top level
1424 * in this layer and the current shape selected even if it didn't
1425 * match any class
1426 *
1427 * FrankW: classindex is also sometimes 0 even if there are no classes, so
1428 * we are careful not to use out of range class values as an index.
1429 */
1430 if(shape.classindex==-1
1431 || shape.classindex >= layer->numclasses
1432 || layer->class[shape.classindex]->status == MS_OFF) {
1433 msFreeShape(&shape);
1434 continue;
1435 }
1436
1437 cache = MS_FALSE;
1438 if(layer->type == MS_LAYER_LINE && (layer->class[shape.classindex]->numstyles > 1 || (layer->class[shape.classindex]->numstyles == 1 && layer->class[shape.classindex]->styles[0]->outlinewidth > 0))) {
1439 int i;
1440 cache = MS_TRUE; /* only line layers with multiple styles need be cached (I don't think POLYLINE layers need caching - SDL) */
1441
1442 /* we can't handle caching with attribute binding other than for the first style (#3976) */
1443 for(i=1; i<layer->class[shape.classindex]->numstyles; i++) {
1444 if(layer->class[shape.classindex]->styles[i]->numbindings > 0) cache = MS_FALSE;
1445 }
1446 }
1447
1448 if(annotate && layer->class[shape.classindex]->numlabels > 0) {
1449 drawmode |= MS_DRAWMODE_LABELS;
1450 }
1451
1452 if(cache) {
1453 styleObj *pStyle = layer->class[shape.classindex]->styles[0];
1454 if (pStyle->outlinewidth > 0) msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1455 status = msDrawShape(map, layer, &shape, image, 0, drawmode|MS_DRAWMODE_SINGLESTYLE); /* draw only the first style */
1456 if (pStyle->outlinewidth > 0) msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1457 } else {
1458 status = msDrawShape(map, layer, &shape, image, -1, drawmode); /* all styles */
1459 }
1460 if(status != MS_SUCCESS) {
1461 msLayerClose(layer);
1462 msFree(colorbuffer);
1463 msFree(mindistancebuffer);
1464 return(MS_FAILURE);
1465 }
1466
1467 if(shape.numlines == 0) { /* once clipped the shape didn't need to be drawn */
1468 msFreeShape(&shape);
1469 continue;
1470 }
1471
1472 if(cache) {
1473 if(insertFeatureList(&shpcache, &shape) == NULL) {
1474 msFree(colorbuffer);
1475 msFree(mindistancebuffer);
1476 return(MS_FAILURE); /* problem adding to the cache */
1477 }
1478 }
1479
1480 maxnumstyles = MS_MAX(maxnumstyles, layer->class[shape.classindex]->numstyles);
1481 msFreeShape(&shape);
1482 }
1483
1484 if(shpcache) {
1485 int s;
1486 for(s=0; s<maxnumstyles; s++) {
1487 for(current=shpcache; current; current=current->next) {
1488 if(layer->class[current->shape.classindex]->numstyles > s) {
1489 styleObj *pStyle = layer->class[current->shape.classindex]->styles[s];
1490 if(pStyle->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
1491 continue; /* skip this as it has already been rendered */
1492 if(map->scaledenom > 0) {
1493 if((pStyle->maxscaledenom != -1) && (map->scaledenom >= pStyle->maxscaledenom))
1494 continue;
1495 if((pStyle->minscaledenom != -1) && (map->scaledenom < pStyle->minscaledenom))
1496 continue;
1497 }
1498 if(s==0 && pStyle->outlinewidth>0 && MS_VALID_COLOR(pStyle->color)) {
1499 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor))) {
1500 return MS_FAILURE;
1501 }
1502 } else if(s>0) {
1503 if (pStyle->outlinewidth > 0 && MS_VALID_COLOR(pStyle->outlinecolor)) {
1504 msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1505 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor))) {
1506 return MS_FAILURE;
1507 }
1508 msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1509 }
1510 /* draw a valid line, i.e. one with a color defined or of type pixmap */
1511 if(MS_VALID_COLOR(pStyle->color) || (pStyle->symbol<map->symbolset.numsymbols && (map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_PIXMAP || map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_SVG))) {
1512 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, ¤t->shape, pStyle, layer->scalefactor)))
1513 return MS_FAILURE;
1514 }
1515 }
1516 }
1517 }
1518 }
1519
1520 freeFeatureList(shpcache);
1521 shpcache = NULL;
1522 }
1523
1524 /* if MS_HILITE, restore color and mindistance values */
1525 if(map->querymap.style == MS_HILITE) {
1526 for(i=0; i<layer->numclasses; i++) {
1527 if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) {
1528 if(MS_VALID_COLOR(layer->class[i]->styles[0]->color))
1529 layer->class[i]->styles[0]->color = colorbuffer[i];
1530 else if(MS_VALID_COLOR(layer->class[i]->styles[0]->outlinecolor))
1531 layer->class[i]->styles[0]->outlinecolor = colorbuffer[i]; /* if no color, restore outlinecolor for the BOTTOM style */
1532 } else if (layer->type == MS_LAYER_LINE && layer->class[i]->numstyles > 0 && layer->class[i]->styles[0]->outlinewidth > 0) {
1533 if(MS_VALID_COLOR(layer->class[i]->styles[0]->color))
1534 layer->class[i]->styles[0]->color = colorbuffer[i];
1535 } else if (layer->class[i]->numstyles > 0) {
1536 if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->color))
1537 layer->class[i]->styles[layer->class[i]->numstyles-1]->color = colorbuffer[i];
1538 else if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor))
1539 layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor = colorbuffer[i]; /* if no color, restore outlinecolor for the TOP style */
1540 } else if (layer->class[i]->numlabels > 0) {
1541 if(MS_VALID_COLOR(layer->class[i]->labels[0]->color))
1542 layer->class[i]->labels[0]->color = colorbuffer[i];
1543 }
1544
1545 if(layer->class[i]->numlabels > 0)
1546 layer->class[i]->labels[0]->mindistance = mindistancebuffer[i]; /* RFC77 TODO: again, only using the first label, is that cool? */
1547 }
1548
1549 msFree(colorbuffer);
1550 msFree(mindistancebuffer);
1551 }
1552
1553 return(MS_SUCCESS);
1554 }
1555
1556 /**
1557 * msDrawRasterLayerPlugin()
1558 */
1559
1560 static int
msDrawRasterLayerPlugin(mapObj * map,layerObj * layer,imageObj * image)1561 msDrawRasterLayerPlugin( mapObj *map, layerObj *layer, imageObj *image)
1562
1563 {
1564 rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
1565 rasterBufferObj *rb = msSmallCalloc(1,sizeof(rasterBufferObj));
1566 int ret;
1567 if( renderer->supports_pixel_buffer ) {
1568 if (MS_SUCCESS != renderer->getRasterBufferHandle( image, rb )) {
1569 msSetError(MS_MISCERR,"renderer failed to extract raster buffer","msDrawRasterLayer()");
1570 return MS_FAILURE;
1571 }
1572 ret = msDrawRasterLayerLow( map, layer, image, rb );
1573 } else {
1574 if (MS_SUCCESS != renderer->initializeRasterBuffer( rb, image->width, image->height, MS_IMAGEMODE_RGBA )) {
1575 msSetError(MS_MISCERR,"renderer failed to create raster buffer","msDrawRasterLayer()");
1576 return MS_FAILURE;
1577 }
1578
1579 ret = msDrawRasterLayerLow( map, layer, image, rb );
1580
1581 if( ret == 0 ) {
1582 ret = renderer->mergeRasterBuffer( image, rb, 1.0, 0, 0, 0, 0, rb->width, rb->height );
1583 }
1584
1585 msFreeRasterBuffer(rb);
1586 }
1587 #define RB_GET_R(rb,x,y) *((rb)->data.rgba.r + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1588 #define RB_GET_G(rb,x,y) *((rb)->data.rgba.g + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1589 #define RB_GET_B(rb,x,y) *((rb)->data.rgba.b + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1590 #define RB_GET_A(rb,x,y) *((rb)->data.rgba.a + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1591
1592 free(rb);
1593
1594 return ret;
1595 }
1596
1597 /**
1598 * Generic function to render raster layers.
1599 */
msDrawRasterLayer(mapObj * map,layerObj * layer,imageObj * image)1600 int msDrawRasterLayer(mapObj *map, layerObj *layer, imageObj *image)
1601 {
1602
1603 int rv = MS_FAILURE;
1604 if (!image || !map || !layer) {
1605 return rv;
1606 }
1607
1608 /* RFC-86 Scale dependant token replacements*/
1609 rv = msLayerApplyScaletokens(layer,(layer->map)?layer->map->scaledenom:-1);
1610 if (rv != MS_SUCCESS) return rv;
1611 if( MS_RENDERER_PLUGIN(image->format) )
1612 rv = msDrawRasterLayerPlugin(map, layer, image);
1613 else if( MS_RENDERER_RAWDATA(image->format) )
1614 rv = msDrawRasterLayerLow(map, layer, image, NULL);
1615 msLayerRestoreFromScaletokens(layer);
1616 return rv;
1617 }
1618
1619 /**
1620 * msDrawWMSLayer()
1621 *
1622 * Draw a single WMS layer.
1623 * Multiple WMS layers in a map are preloaded and then drawn using
1624 * msDrawWMSLayerLow()
1625 */
1626
1627 #ifdef USE_WMS_LYR
msDrawWMSLayer(mapObj * map,layerObj * layer,imageObj * image)1628 int msDrawWMSLayer(mapObj *map, layerObj *layer, imageObj *image)
1629 {
1630 int nStatus = MS_FAILURE;
1631
1632 if (image && map && layer) {
1633 /* ------------------------------------------------------------------
1634 * Start by downloading the layer
1635 * ------------------------------------------------------------------ */
1636 httpRequestObj asReqInfo[2];
1637 int numReq = 0;
1638
1639 msHTTPInitRequestObj(asReqInfo, 2);
1640
1641 if ( msPrepareWMSLayerRequest(1, map, layer, 1,
1642 0, NULL, 0, 0, 0, NULL,
1643 asReqInfo, &numReq) == MS_FAILURE ||
1644 msOWSExecuteRequests(asReqInfo, numReq, map, MS_TRUE) == MS_FAILURE ) {
1645 return MS_FAILURE;
1646 }
1647
1648 /* ------------------------------------------------------------------
1649 * Then draw layer based on output format
1650 * ------------------------------------------------------------------ */
1651 if( MS_RENDERER_PLUGIN(image->format) )
1652 nStatus = msDrawWMSLayerLow(1, asReqInfo, numReq,
1653 map, layer, image) ;
1654 else if( MS_RENDERER_RAWDATA(image->format) )
1655 nStatus = msDrawWMSLayerLow(1, asReqInfo, numReq,
1656 map, layer, image) ;
1657
1658 else {
1659 msSetError(MS_WMSCONNERR,
1660 "Output format '%s' doesn't support WMS layers.",
1661 "msDrawWMSLayer()", image->format->name);
1662 nStatus = MS_SUCCESS; /* Should we fail if output doesn't support WMS? */
1663 }
1664 /* Cleanup */
1665 msHTTPFreeRequestObj(asReqInfo, numReq);
1666 }
1667
1668 return nStatus;
1669 }
1670 #endif
1671
circleLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape)1672 int circleLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape)
1673 {
1674 pointObj center;
1675 double r;
1676 int s;
1677 int c = shape->classindex;
1678
1679 if (shape->numlines != 1) return (MS_SUCCESS); /* invalid shape */
1680 if (shape->line[0].numpoints != 2) return (MS_SUCCESS); /* invalid shape */
1681
1682 center.x = (shape->line[0].point[0].x + shape->line[0].point[1].x) / 2.0;
1683 center.y = (shape->line[0].point[0].y + shape->line[0].point[1].y) / 2.0;
1684 r = MS_ABS(center.x - shape->line[0].point[0].x);
1685 if (r == 0)
1686 r = MS_ABS(center.y - shape->line[0].point[0].y);
1687 if (r == 0)
1688 return (MS_SUCCESS);
1689
1690 if (layer->transform == MS_TRUE) {
1691
1692 if (layer->project)
1693 msProjectPoint(&layer->projection, &map->projection, ¢er);
1694
1695 center.x = MS_MAP2IMAGE_X(center.x, map->extent.minx, map->cellsize);
1696 center.y = MS_MAP2IMAGE_Y(center.y, map->extent.maxy, map->cellsize);
1697 r /= map->cellsize;
1698 } else
1699 msOffsetPointRelativeTo(¢er, layer);
1700
1701 for (s = 0; s < layer->class[c]->numstyles; s++) {
1702 if (msScaleInBounds(map->scaledenom,
1703 layer->class[c]->styles[s]->minscaledenom,
1704 layer->class[c]->styles[s]->maxscaledenom))
1705 if(UNLIKELY(MS_FAILURE == msCircleDrawShadeSymbol(map, image, ¢er, r,
1706 layer->class[c]->styles[s], layer->scalefactor))) {
1707 return MS_FAILURE;
1708 }
1709 }
1710 return MS_SUCCESS;
1711 /* TODO: need to handle circle annotation */
1712 }
1713
1714 static
pointLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,int drawmode)1715 int pointLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape, int drawmode)
1716 {
1717 int l, c = shape->classindex, j, i, s;
1718 pointObj *point;
1719 int ret = MS_FAILURE;
1720
1721 if (layer->project && layer->transform == MS_TRUE)
1722 {
1723 if( layer->reprojectorLayerToMap == NULL )
1724 {
1725 layer->reprojectorLayerToMap = msProjectCreateReprojector(
1726 &layer->projection, &map->projection);
1727 if( layer->reprojectorLayerToMap == NULL )
1728 {
1729 return MS_FAILURE;
1730 }
1731 }
1732 msProjectShapeEx(layer->reprojectorLayerToMap, shape);
1733 }
1734
1735 // Only take into account map rotation if the label and style angles are
1736 // non-zero.
1737 if( map->gt.rotation_angle )
1738 {
1739 for (l = 0; l < layer->class[c]->numlabels; l++)
1740 {
1741 if( layer->class[c]->labels[l]->angle != 0 )
1742 layer->class[c]->labels[l]->angle -= map->gt.rotation_angle;
1743 }
1744
1745 for (s = 0; s < layer->class[c]->numstyles; s++)
1746 {
1747 if( layer->class[c]->styles[s]->angle != 0 )
1748 layer->class[c]->styles[s]->angle -= map->gt.rotation_angle;
1749 }
1750 }
1751
1752 for (j = 0; j < shape->numlines; j++) {
1753 for (i = 0; i < shape->line[j].numpoints; i++) {
1754 point = &(shape->line[j].point[i]);
1755 if (layer->transform == MS_TRUE) {
1756 if (!msPointInRect(point, &map->extent)) continue; /* next point */
1757 msTransformPoint(point, &map->extent, map->cellsize, image);
1758 } else
1759 msOffsetPointRelativeTo(point, layer);
1760
1761 if(MS_DRAW_FEATURES(drawmode)) {
1762 for (s = 0; s < layer->class[c]->numstyles; s++) {
1763 if (msScaleInBounds(map->scaledenom,
1764 layer->class[c]->styles[s]->minscaledenom,
1765 layer->class[c]->styles[s]->maxscaledenom))
1766 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, point, layer->class[c]->styles[s], layer->scalefactor))) {
1767 goto end;
1768 }
1769 }
1770 }
1771 if(MS_DRAW_LABELS(drawmode)) {
1772 if (layer->labelcache) {
1773 if (msAddLabelGroup(map, image, layer, c, shape, point, -1) != MS_SUCCESS) goto end;
1774 } else {
1775 for (l = 0; l < layer->class[c]->numlabels; l++)
1776 if(msGetLabelStatus(map,layer,shape,layer->class[c]->labels[l]) == MS_ON) {
1777 char *annotext = msShapeGetLabelAnnotation(layer,shape,layer->class[c]->labels[l]);
1778 if(UNLIKELY(MS_FAILURE == msDrawLabel(map, image, *point, annotext, layer->class[c]->labels[l], layer->scalefactor))) {
1779 goto end;
1780 }
1781 }
1782 }
1783 }
1784 }
1785 }
1786 ret = MS_SUCCESS;
1787
1788 end:
1789 if( map->gt.rotation_angle )
1790 {
1791 for (l = 0; l < layer->class[c]->numlabels; l++)
1792 {
1793 if( layer->class[c]->labels[l]->angle != 0 )
1794 layer->class[c]->labels[l]->angle += map->gt.rotation_angle;
1795 }
1796
1797 for (s = 0; s < layer->class[c]->numstyles; s++)
1798 {
1799 if( layer->class[c]->styles[s]->angle != 0 )
1800 layer->class[c]->styles[s]->angle += map->gt.rotation_angle;
1801 }
1802 }
1803
1804 return ret;
1805 }
1806
lineLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,shapeObj * anno_shape,shapeObj * unclipped_shape,int style,int drawmode)1807 int lineLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape,
1808 shapeObj *anno_shape, shapeObj *unclipped_shape, int style, int drawmode)
1809 {
1810 int c = shape->classindex;
1811 int ret = MS_SUCCESS;
1812 int i, s, l = 0;
1813
1814 /* RFC48: loop through the styles, and pass off to the type-specific
1815 function if the style has an appropriate type */
1816 if(MS_DRAW_FEATURES(drawmode)) {
1817 for (s = 0; s < layer->class[c]->numstyles; s++) {
1818 if (msScaleInBounds(map->scaledenom,
1819 layer->class[c]->styles[s]->minscaledenom,
1820 layer->class[c]->styles[s]->maxscaledenom)) {
1821 if (layer->class[c]->styles[s]->_geomtransform.type != MS_GEOMTRANSFORM_NONE) {
1822 if(UNLIKELY(MS_FAILURE == msDrawTransformedShape(map, image, unclipped_shape, layer->class[c]->styles[s], layer->scalefactor))) {
1823 return MS_FAILURE;
1824 }
1825 }
1826 else if (!MS_DRAW_SINGLESTYLE(drawmode) || s == style) {
1827 if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, shape, layer->class[c]->styles[s], layer->scalefactor))) {
1828 return MS_FAILURE;
1829 }
1830 }
1831 }
1832 }
1833 }
1834
1835 if(MS_DRAW_LABELS(drawmode)) {
1836 for (l = 0; l < layer->class[c]->numlabels; l++) {
1837 labelObj *label = layer->class[c]->labels[l];
1838 textSymbolObj ts;
1839 char *annotext;
1840 if(!msGetLabelStatus(map,layer,shape,label)) {
1841 continue;
1842 }
1843
1844 annotext = msShapeGetLabelAnnotation(layer,anno_shape,label);
1845 if(!annotext) continue;
1846 initTextSymbol(&ts);
1847 msPopulateTextSymbolForLabelAndString(&ts,label,annotext,layer->scalefactor,image->resolutionfactor, layer->labelcache);
1848
1849
1850 if (label->anglemode == MS_FOLLOW) { /* bug #1620 implementation */
1851 struct label_follow_result lfr;
1852
1853 if (!layer->labelcache) {
1854 msSetError(MS_MISCERR, "Angle mode 'FOLLOW' is supported only with labelcache on", "msDrawShape()");
1855 ret = MS_FAILURE;
1856 goto line_cleanup;
1857 }
1858
1859 memset(&lfr,0,sizeof(lfr));
1860 msPolylineLabelPath(map, image, anno_shape, &ts, label, &lfr);
1861
1862 for (i = 0; i < lfr.num_follow_labels; i++) {
1863 if (msAddLabel(map, image, label, layer->index, c, anno_shape, NULL, -1, lfr.follow_labels[i]) != MS_SUCCESS) {
1864 ret = MS_FAILURE;
1865 goto line_cleanup;
1866 }
1867 }
1868 free(lfr.follow_labels);
1869 for(i=0; i<lfr.lar.num_label_points; i++) {
1870 textSymbolObj *ts_auto = msSmallMalloc(sizeof(textSymbolObj));
1871 initTextSymbol(ts_auto);
1872 msCopyTextSymbol(ts_auto,&ts);
1873 ts_auto->rotation = lfr.lar.angles[i];
1874 if (layer->labelcache) {
1875 if (msAddLabel(map, image, label, layer->index, c, anno_shape, &lfr.lar.label_points[i], -1, ts_auto) != MS_SUCCESS) {
1876 ret = MS_FAILURE;
1877 free(lfr.lar.angles);
1878 free(lfr.lar.label_points);
1879 goto line_cleanup;
1880 }
1881 } else {
1882 ret = msDrawTextSymbol(map,image,lfr.lar.label_points[i],ts_auto);
1883 freeTextSymbol(ts_auto);
1884 free(ts_auto); /* TODO RFC98: could we not re-use the original ts instead of duplicating into ts_auto ?
1885 * we cannot for now, as the rendering code will modify the glyph positions to apply
1886 * the labelpoint and rotation offsets */
1887 if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1888 }
1889
1890 }
1891 free(lfr.lar.angles);
1892 free(lfr.lar.label_points);
1893 } else {
1894 struct label_auto_result lar;
1895 memset(&lar,0,sizeof(struct label_auto_result));
1896 ret = msPolylineLabelPoint(map, anno_shape, &ts, label, &lar, image->resolutionfactor);
1897 if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1898
1899 if (label->angle != 0)
1900 label->angle -= map->gt.rotation_angle; /* apply rotation angle */
1901
1902 for(i=0; i<lar.num_label_points; i++) {
1903 textSymbolObj *ts_auto = msSmallMalloc(sizeof(textSymbolObj));
1904 initTextSymbol(ts_auto);
1905 msCopyTextSymbol(ts_auto,&ts);
1906 ts_auto->rotation = lar.angles[i];
1907 if (layer->labelcache) {
1908 if (msAddLabel(map, image, label, layer->index, c, anno_shape, &lar.label_points[i], -1, ts_auto) != MS_SUCCESS) {
1909 ret = MS_FAILURE;
1910 free(lar.angles);
1911 free(lar.label_points);
1912 freeTextSymbol(ts_auto);
1913 free(ts_auto);
1914 goto line_cleanup;
1915 }
1916 } else {
1917 if(!ts_auto->textpath) {
1918 if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts_auto))) {
1919 ret = MS_FAILURE;
1920 free(lar.angles);
1921 free(lar.label_points);
1922 freeTextSymbol(ts_auto);
1923 free(ts_auto);
1924 goto line_cleanup;
1925 }
1926 }
1927 ret = msDrawTextSymbol(map,image,lar.label_points[i],ts_auto);
1928 freeTextSymbol(ts_auto);
1929 free(ts_auto); /* TODO RFC98: could we not re-use the original ts instead of duplicating into ts_auto ?
1930 * we cannot for now, as the rendering code will modify the glyph positions to apply
1931 * the labelpoint and rotation offsets */
1932 ts_auto = NULL;
1933 if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1934 }
1935
1936 }
1937 free(lar.angles);
1938 free(lar.label_points);
1939 }
1940
1941 line_cleanup:
1942 /* clean up and reset */
1943 if (ret == MS_FAILURE) {
1944 break; /* from the label looping */
1945 }
1946 freeTextSymbol(&ts);
1947 } /* next label */
1948 }
1949
1950 return ret;
1951
1952 }
1953
polygonLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,shapeObj * anno_shape,shapeObj * unclipped_shape,int drawmode)1954 int polygonLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer,
1955 shapeObj *shape, shapeObj *anno_shape, shapeObj *unclipped_shape, int drawmode)
1956 {
1957
1958 int c = shape->classindex;
1959 pointObj annopnt = {0,0,0,0}; // initialize
1960 int i;
1961
1962 if(MS_DRAW_FEATURES(drawmode)) {
1963 for (i = 0; i < layer->class[c]->numstyles; i++) {
1964 if (msScaleInBounds(map->scaledenom, layer->class[c]->styles[i]->minscaledenom,
1965 layer->class[c]->styles[i]->maxscaledenom)) {
1966 if (layer->class[c]->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
1967 if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, shape, layer->class[c]->styles[i], layer->scalefactor))) {
1968 return MS_FAILURE;
1969 }
1970 }
1971 else {
1972 if(UNLIKELY(MS_FAILURE == msDrawTransformedShape(map, image, unclipped_shape,
1973 layer->class[c]->styles[i], layer->scalefactor))) {
1974 return MS_FAILURE;
1975 }
1976 }
1977 }
1978 }
1979 }
1980
1981 if(MS_DRAW_LABELS(drawmode)) {
1982 if (layer->class[c]->numlabels > 0) {
1983 double minfeaturesize = layer->class[c]->labels[0]->minfeaturesize * image->resolutionfactor;
1984 if (msPolygonLabelPoint(anno_shape, &annopnt, minfeaturesize) == MS_SUCCESS) {
1985 for (i = 0; i < layer->class[c]->numlabels; i++)
1986 if (layer->class[c]->labels[i]->angle != 0) layer->class[c]->labels[i]->angle -= map->gt.rotation_angle; /* TODO: is this correct ??? */
1987 if (layer->labelcache) {
1988 if (msAddLabelGroup(map, image, layer, c, anno_shape, &annopnt,
1989 MS_MIN(shape->bounds.maxx - shape->bounds.minx, shape->bounds.maxy - shape->bounds.miny)) != MS_SUCCESS) {
1990 return MS_FAILURE;
1991 }
1992 } else {
1993 for (i = 0; i < layer->class[c]->numlabels; i++)
1994 if(msGetLabelStatus(map,layer,shape,layer->class[c]->labels[i]) == MS_ON) {
1995 char *annotext = msShapeGetLabelAnnotation(layer,shape,layer->class[c]->labels[i]); /*ownership taken by msDrawLabel, no need to free */
1996 if(UNLIKELY(MS_FAILURE == msDrawLabel(map, image, annopnt, annotext, layer->class[c]->labels[i], layer->scalefactor))) {
1997 return MS_FAILURE;
1998 }
1999 }
2000 }
2001 }
2002 }
2003 }
2004 return MS_SUCCESS;
2005 }
2006
2007 /*
2008 ** Function to render an individual shape, the style variable enables/disables the drawing of a single style
2009 ** versus a single style. This is necessary when drawing entire layers as proper overlay can only be achived
2010 ** through caching. "querymapMode" parameter is used to tell msBindLayerToShape to not override the
2011 ** QUERYMAP HILITE color.
2012 */
msDrawShape(mapObj * map,layerObj * layer,shapeObj * shape,imageObj * image,int style,int drawmode)2013 int msDrawShape(mapObj *map, layerObj *layer, shapeObj *shape, imageObj *image, int style, int drawmode)
2014 {
2015 int c,s,ret=MS_SUCCESS;
2016 shapeObj *anno_shape, *unclipped_shape = shape;
2017 int bNeedUnclippedShape = MS_FALSE;
2018 int bNeedUnclippedAnnoShape = MS_FALSE;
2019 int bShapeNeedsClipping = MS_TRUE;
2020
2021 if(shape->numlines == 0 || shape->type == MS_SHAPE_NULL) return MS_SUCCESS;
2022
2023 c = shape->classindex;
2024
2025 /* Before we do anything else, we will check for a rangeitem.
2026 If its there, we need to change the style's color to map
2027 the range to the shape */
2028 for(s=0; s<layer->class[c]->numstyles; s++) {
2029 styleObj *style = layer->class[c]->styles[s];
2030 if(style->rangeitem != NULL)
2031 msShapeToRange((layer->class[c]->styles[s]), shape);
2032 }
2033
2034 /* circle and point layers go through their own treatment */
2035 if(layer->type == MS_LAYER_CIRCLE) {
2036 if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) return MS_FAILURE;
2037 msDrawStartShape(map, layer, image, shape);
2038 ret = circleLayerDrawShape(map,image,layer,shape);
2039 msDrawEndShape(map,layer,image,shape);
2040 return ret;
2041 } else if(layer->type == MS_LAYER_POINT || layer->type == MS_LAYER_RASTER) {
2042 if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) return MS_FAILURE;
2043 msDrawStartShape(map, layer, image, shape);
2044 ret = pointLayerDrawShape(map,image,layer,shape,drawmode);
2045 msDrawEndShape(map,layer,image,shape);
2046 return ret;
2047 }
2048
2049 if (layer->type == MS_LAYER_POLYGON && shape->type != MS_SHAPE_POLYGON) {
2050 msSetError(MS_MISCERR, "Only polygon shapes can be drawn using a polygon layer definition.", "polygonLayerDrawShape()");
2051 return (MS_FAILURE);
2052 }
2053 if (layer->type == MS_LAYER_LINE && shape->type != MS_SHAPE_POLYGON && shape->type != MS_SHAPE_LINE) {
2054 msSetError(MS_MISCERR, "Only polygon or line shapes can be drawn using a line layer definition.", "msDrawShape()");
2055 return (MS_FAILURE);
2056 }
2057
2058 if (layer->project && layer->transform == MS_TRUE)
2059 {
2060 if( layer->reprojectorLayerToMap == NULL )
2061 {
2062 layer->reprojectorLayerToMap = msProjectCreateReprojector(
2063 &layer->projection, &map->projection);
2064 if( layer->reprojectorLayerToMap == NULL )
2065 {
2066 return MS_FAILURE;
2067 }
2068 }
2069 msProjectShapeEx(layer->reprojectorLayerToMap, shape);
2070 }
2071
2072 /* check if we'll need the unclipped shape */
2073 if (shape->type != MS_SHAPE_POINT) {
2074 if(MS_DRAW_FEATURES(drawmode)) {
2075 for (s = 0; s < layer->class[c]->numstyles; s++) {
2076 styleObj *style = layer->class[c]->styles[s];
2077 if (style->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
2078 bNeedUnclippedShape = MS_TRUE;
2079 }
2080 }
2081 /* check if we need to clip the shape */
2082 if (shape->bounds.minx < map->extent.minx ||
2083 shape->bounds.miny < map->extent.miny ||
2084 shape->bounds.maxx > map->extent.maxx ||
2085 shape->bounds.maxy > map->extent.maxy) {
2086 bShapeNeedsClipping = MS_TRUE;
2087 }
2088
2089 if(MS_DRAW_LABELS(drawmode) && MS_DRAW_UNCLIPPED_LABELS(drawmode)) {
2090 bNeedUnclippedAnnoShape = MS_TRUE;
2091 bNeedUnclippedShape = MS_TRUE;
2092 }
2093
2094 if(MS_DRAW_UNCLIPPED_LINES(drawmode)) {
2095 bShapeNeedsClipping = MS_FALSE;
2096 }
2097 } else {
2098 bShapeNeedsClipping = MS_FALSE;
2099 }
2100
2101 if(layer->transform == MS_TRUE && bShapeNeedsClipping) {
2102 /* compute the size of the clipping buffer, in pixels. This buffer must account
2103 for the size of symbols drawn to avoid artifacts around the image edges */
2104 int clip_buf = 0;
2105 int s;
2106 rectObj cliprect;
2107 for (s=0;s<layer->class[c]->numstyles;s++) {
2108 double maxsize, maxunscaledsize;
2109 symbolObj *symbol;
2110 styleObj *style = layer->class[c]->styles[s];
2111 if (!MS_IS_VALID_ARRAY_INDEX(style->symbol, map->symbolset.numsymbols)) {
2112 msSetError(MS_SYMERR, "Invalid symbol index: %d", "msDrawShape()", style->symbol);
2113 return MS_FAILURE;
2114 }
2115 symbol = map->symbolset.symbol[style->symbol];
2116 if (symbol->type == MS_SYMBOL_PIXMAP) {
2117 if (MS_SUCCESS != msPreloadImageSymbol(MS_MAP_RENDERER(map), symbol))
2118 return MS_FAILURE;
2119 } else if (symbol->type == MS_SYMBOL_SVG) {
2120 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
2121 if (MS_SUCCESS != msPreloadSVGSymbol(symbol))
2122 return MS_FAILURE;
2123 #else
2124 msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msDrawShape()");
2125 return MS_FAILURE;
2126 #endif
2127 }
2128 maxsize = MS_MAX(msSymbolGetDefaultSize(symbol), MS_MAX(style->size, style->width));
2129 maxunscaledsize = MS_MAX(style->minsize*image->resolutionfactor, style->minwidth*image->resolutionfactor);
2130 if(shape->type == MS_SHAPE_POLYGON && !IS_PARALLEL_OFFSET(style->offsety)) {
2131 maxsize += MS_MAX(fabs(style->offsety),fabs(style->offsetx));
2132 }
2133 clip_buf = MS_MAX(clip_buf,MS_NINT(MS_MAX(maxsize * layer->scalefactor, maxunscaledsize) + 1));
2134 }
2135
2136
2137 /* if we need a copy of the unclipped shape, transform first, then clip to avoid transforming twice */
2138 if(bNeedUnclippedShape) {
2139 msTransformShape(shape, map->extent, map->cellsize, image);
2140 if(shape->numlines == 0) return MS_SUCCESS;
2141 msComputeBounds(shape);
2142
2143 /* TODO: there's an optimization here that can be implemented:
2144 - no need to allocate unclipped_shape for each call to this function
2145 - the calls to msClipXXXRect will discard the original lineObjs, whereas
2146 we have just copied them because they where needed. These two functions
2147 could be changed so they are instructed not to free the original lineObjs. */
2148 unclipped_shape = (shapeObj *) msSmallMalloc(sizeof (shapeObj));
2149 msInitShape(unclipped_shape);
2150 msCopyShape(shape, unclipped_shape);
2151 if(shape->type == MS_SHAPE_POLYGON) {
2152 /* #179: additional buffer for polygons */
2153 clip_buf += 2;
2154 }
2155
2156 cliprect.minx = cliprect.miny = -clip_buf;
2157 cliprect.maxx = image->width + clip_buf;
2158 cliprect.maxy = image->height + clip_buf;
2159 if(shape->type == MS_SHAPE_POLYGON) {
2160 msClipPolygonRect(shape, cliprect);
2161 } else {
2162 assert(shape->type == MS_SHAPE_LINE);
2163 msClipPolylineRect(shape, cliprect);
2164 }
2165 if(bNeedUnclippedAnnoShape) {
2166 anno_shape = unclipped_shape;
2167 } else {
2168 anno_shape = shape;
2169 }
2170 } else {
2171 /* clip first, then transform. This means we are clipping in geographical space */
2172 double clip_buf_d;
2173 if(shape->type == MS_SHAPE_POLYGON) {
2174 /*
2175 * add a small buffer around the cliping rectangle to
2176 * avoid lines around the edges : #179
2177 */
2178 clip_buf += 2;
2179 }
2180 clip_buf_d = clip_buf * map->cellsize;
2181 cliprect.minx = map->extent.minx - clip_buf_d;
2182 cliprect.miny = map->extent.miny - clip_buf_d;
2183 cliprect.maxx = map->extent.maxx + clip_buf_d;
2184 cliprect.maxy = map->extent.maxy + clip_buf_d;
2185 if(shape->type == MS_SHAPE_POLYGON) {
2186 msClipPolygonRect(shape, cliprect);
2187 } else {
2188 assert(shape->type == MS_SHAPE_LINE);
2189 msClipPolylineRect(shape, cliprect);
2190 }
2191 msTransformShape(shape, map->extent, map->cellsize, image);
2192 msComputeBounds(shape);
2193 anno_shape = shape;
2194 }
2195
2196 } else {
2197 /* the shape is fully in the map extent,
2198 * or is a point type layer where out of bounds points are treated differently*/
2199 if (layer->transform == MS_TRUE) {
2200 msTransformShape(shape, map->extent, map->cellsize, image);
2201 msComputeBounds(shape);
2202 } else {
2203 msOffsetShapeRelativeTo(shape, layer);
2204 }
2205 anno_shape = shape;
2206 }
2207 if(shape->numlines == 0) {
2208 ret = MS_SUCCESS; /* error message is set in msBindLayerToShape() */
2209 goto draw_shape_cleanup;
2210 }
2211
2212 if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) {
2213 ret = MS_FAILURE; /* error message is set in msBindLayerToShape() */
2214 goto draw_shape_cleanup;
2215 }
2216
2217 switch(layer->type) {
2218 case MS_LAYER_LINE:
2219 msDrawStartShape(map, layer, image, shape);
2220 ret = lineLayerDrawShape(map, image, layer, shape, anno_shape, unclipped_shape, style, drawmode);
2221 break;
2222 case MS_LAYER_POLYGON:
2223 msDrawStartShape(map, layer, image, shape);
2224 ret = polygonLayerDrawShape(map, image, layer, shape, anno_shape, unclipped_shape, drawmode);
2225 break;
2226 case MS_LAYER_POINT:
2227 case MS_LAYER_RASTER:
2228 assert(0); //bug !
2229 default:
2230 msSetError(MS_MISCERR, "Unknown layer type.", "msDrawShape()");
2231 ret = MS_FAILURE;
2232 }
2233
2234 draw_shape_cleanup:
2235 msDrawEndShape(map,layer,image,shape);
2236 if(unclipped_shape != shape) {
2237 msFreeShape(unclipped_shape);
2238 msFree(unclipped_shape);
2239 }
2240 return ret;
2241 }
2242
2243 /*
2244 ** Function to render an individual point, used as a helper function for mapscript only. Since a point
2245 ** can't carry attributes you can't do attribute based font size or angle.
2246 */
msDrawPoint(mapObj * map,layerObj * layer,pointObj * point,imageObj * image,int classindex,char * labeltext)2247 int msDrawPoint(mapObj *map, layerObj *layer, pointObj *point, imageObj *image, int classindex, char *labeltext)
2248 {
2249 int s,ret;
2250 classObj *theclass=NULL;
2251 labelObj *label=NULL;
2252
2253 if(layer->transform == MS_TRUE && layer->project && msProjectionsDiffer(&(layer->projection), &(map->projection))) {
2254 msProjectPoint(&(layer->projection), &(map->projection), point);
2255 }
2256
2257 if(classindex > layer->numclasses) {
2258 msSetError(MS_MISCERR, "Invalid classindex (%d)", "msDrawPoint()", classindex);
2259 return MS_FAILURE;
2260 }
2261 theclass = layer->class[classindex];
2262
2263 if(labeltext && theclass->numlabels > 0) {
2264 label = theclass->labels[0];
2265 }
2266
2267 switch(layer->type) {
2268 case MS_LAYER_POINT:
2269 if(layer->transform == MS_TRUE) {
2270 if(!msPointInRect(point, &map->extent)) return(0);
2271 point->x = MS_MAP2IMAGE_X(point->x, map->extent.minx, map->cellsize);
2272 point->y = MS_MAP2IMAGE_Y(point->y, map->extent.maxy, map->cellsize);
2273 } else
2274 msOffsetPointRelativeTo(point, layer);
2275
2276 for(s=0; s<theclass->numstyles; s++) {
2277 if(msScaleInBounds(map->scaledenom, theclass->styles[s]->minscaledenom, theclass->styles[s]->maxscaledenom))
2278 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, point, theclass->styles[s], layer->scalefactor))) {
2279 return MS_FAILURE;
2280 }
2281 }
2282 if(label && labeltext && *labeltext) {
2283 textSymbolObj *ts = msSmallMalloc(sizeof(textSymbolObj));
2284 initTextSymbol(ts);
2285 msPopulateTextSymbolForLabelAndString(ts, label, msStrdup(labeltext), layer->scalefactor, image->resolutionfactor, layer->labelcache);
2286 if(layer->labelcache) {
2287 if(msAddLabel(map, image, label, layer->index, classindex, NULL, point, -1, ts) != MS_SUCCESS) {
2288 return(MS_FAILURE);
2289 }
2290 } else {
2291 if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts))) {
2292 freeTextSymbol(ts);
2293 free(ts);
2294 return MS_FAILURE;
2295 }
2296 ret = msDrawTextSymbol(map,image,*point,ts);
2297 freeTextSymbol(ts);
2298 free(ts);
2299 if(UNLIKELY(ret == MS_FAILURE)) return MS_FAILURE;
2300 }
2301 }
2302 break;
2303 default:
2304 break; /* don't do anything with layer of other types */
2305 }
2306
2307 return(MS_SUCCESS); /* all done, no cleanup */
2308 }
2309
2310 /*
2311 ** Draws a single label independently of the label cache. No collision avoidance is performed.
2312 */
msDrawLabel(mapObj * map,imageObj * image,pointObj labelPnt,char * string,labelObj * label,double scalefactor)2313 int msDrawLabel(mapObj *map, imageObj *image, pointObj labelPnt, char *string, labelObj *label, double scalefactor)
2314 {
2315 shapeObj labelPoly;
2316 label_bounds lbounds = {0};
2317 lineObj labelPolyLine;
2318 pointObj labelPolyPoints[5];
2319 textSymbolObj ts = {0};
2320 int needLabelPoly=MS_TRUE;
2321 int needLabelPoint=MS_TRUE;
2322 int haveLabelText=MS_TRUE;
2323
2324 if(!string || !*string)
2325 haveLabelText = MS_FALSE;
2326
2327 if(haveLabelText) {
2328 initTextSymbol(&ts);
2329 msPopulateTextSymbolForLabelAndString(&ts, label, string, scalefactor, image->resolutionfactor, 0);
2330 if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,&ts))) {
2331 freeTextSymbol(&ts);
2332 return MS_FAILURE;
2333 }
2334 }
2335
2336 labelPoly.line = &labelPolyLine; /* setup the label polygon structure */
2337 labelPoly.numlines = 1;
2338 lbounds.poly = &labelPolyLine; /* setup the label polygon structure */
2339 labelPoly.line->point = labelPolyPoints;
2340 labelPoly.line->numpoints = 5;
2341
2342 if(label->position != MS_XY) {
2343 pointObj p = {0};
2344
2345 if(label->numstyles > 0) {
2346 int i;
2347
2348 for(i=0; i<label->numstyles; i++) {
2349 if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
2350 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelPnt, label->styles[i], scalefactor))) {
2351 if(haveLabelText)
2352 freeTextSymbol(&ts);
2353 return MS_FAILURE;
2354 }
2355 } else if(haveLabelText && (label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER)) {
2356 if(needLabelPoly) {
2357 p = get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2358 label->offsety * ts.scalefactor, ts.rotation, 1, &lbounds);
2359 if(!lbounds.poly) {
2360 /* we need the full shape to draw the label background */
2361 labelPolyPoints[0].x = labelPolyPoints[4].x = lbounds.bbox.minx;
2362 labelPolyPoints[0].y = labelPolyPoints[4].y = lbounds.bbox.miny;
2363 labelPolyPoints[1].x = lbounds.bbox.minx;
2364 labelPolyPoints[1].y = lbounds.bbox.maxy;
2365 labelPolyPoints[2].x = lbounds.bbox.maxx;
2366 labelPolyPoints[2].y = lbounds.bbox.maxy;
2367 labelPolyPoints[3].x = lbounds.bbox.maxx;
2368 labelPolyPoints[3].y = lbounds.bbox.miny;
2369 }
2370 needLabelPoint = MS_FALSE; /* don't re-compute */
2371 needLabelPoly = MS_FALSE;
2372 }
2373 if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2374 if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, &labelPoly, label->styles[i], ts.scalefactor))) {
2375 freeTextSymbol(&ts);
2376 return MS_FAILURE;
2377 }
2378 } else {
2379 pointObj labelCenter;
2380 labelCenter.x = (lbounds.bbox.maxx + lbounds.bbox.minx)/2;
2381 labelCenter.y = (lbounds.bbox.maxy + lbounds.bbox.miny)/2;
2382 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelCenter, label->styles[i], scalefactor))) {
2383 freeTextSymbol(&ts);
2384 return MS_FAILURE;
2385 }
2386 }
2387 } else {
2388 msSetError(MS_MISCERR,"Unknown label geomtransform %s", "msDrawLabel()",label->styles[i]->_geomtransform.string);
2389 if(haveLabelText)
2390 freeTextSymbol(&ts);
2391 return MS_FAILURE;
2392 }
2393 }
2394 }
2395
2396 if(haveLabelText) {
2397 if(needLabelPoint)
2398 p = get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2399 label->offsety * ts.scalefactor, ts.rotation, 0, &lbounds);
2400
2401 /* draw the label text */
2402 if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,p,&ts))) {
2403 freeTextSymbol(&ts);
2404 return MS_FAILURE;
2405 }
2406 }
2407 } else {
2408 labelPnt.x += label->offsetx * ts.scalefactor;
2409 labelPnt.y += label->offsety * ts.scalefactor;
2410
2411 if(label->numstyles > 0) {
2412 int i;
2413
2414 for(i=0; i<label->numstyles; i++) {
2415 if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT ||
2416 label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
2417 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelPnt, label->styles[i], scalefactor))) {
2418 freeTextSymbol(&ts);
2419 return MS_FAILURE;
2420 }
2421 } else if(haveLabelText && (label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER)) {
2422 if(needLabelPoly) {
2423 get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2424 label->offsety * ts.scalefactor, ts.rotation, 1, &lbounds);
2425 needLabelPoly = MS_FALSE; /* don't re-compute */
2426 if(!lbounds.poly) {
2427 /* we need the full shape to draw the label background */
2428 labelPolyPoints[0].x = labelPolyPoints[4].x = lbounds.bbox.minx;
2429 labelPolyPoints[0].y = labelPolyPoints[4].y = lbounds.bbox.miny;
2430 labelPolyPoints[1].x = lbounds.bbox.minx;
2431 labelPolyPoints[1].y = lbounds.bbox.maxy;
2432 labelPolyPoints[2].x = lbounds.bbox.maxx;
2433 labelPolyPoints[2].y = lbounds.bbox.maxy;
2434 labelPolyPoints[3].x = lbounds.bbox.maxx;
2435 labelPolyPoints[3].y = lbounds.bbox.miny;
2436 }
2437 }
2438 if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2439 if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, &labelPoly, label->styles[i], scalefactor))) {
2440 freeTextSymbol(&ts);
2441 return MS_FAILURE;
2442 }
2443 } else {
2444 pointObj labelCenter;
2445 labelCenter.x = (lbounds.bbox.maxx + lbounds.bbox.minx)/2;
2446 labelCenter.y = (lbounds.bbox.maxy + lbounds.bbox.miny)/2;
2447 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelCenter, label->styles[i], scalefactor))) {
2448 freeTextSymbol(&ts);
2449 return MS_FAILURE;
2450 }
2451 }
2452 } else {
2453 msSetError(MS_MISCERR,"Unknown label geomtransform %s", "msDrawLabel()",label->styles[i]->_geomtransform.string);
2454 if(haveLabelText)
2455 freeTextSymbol(&ts);
2456 return MS_FAILURE;
2457 }
2458 }
2459 }
2460
2461 if(haveLabelText) {
2462 /* draw the label text */
2463 if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,labelPnt,&ts))) {
2464 freeTextSymbol(&ts);
2465 return MS_FAILURE;
2466 }
2467 }
2468 }
2469 if(haveLabelText)
2470 freeTextSymbol(&ts);
2471
2472 return MS_SUCCESS;
2473 }
2474
offset_bbox(const rectObj * from,rectObj * to,double ox,double oy)2475 static inline void offset_bbox(const rectObj *from, rectObj *to, double ox, double oy) {
2476 to->minx = from->minx + ox;
2477 to->miny = from->miny + oy;
2478 to->maxx = from->maxx + ox;
2479 to->maxy = from->maxy + oy;
2480 }
2481
offset_label_bounds(const label_bounds * from,label_bounds * to,double ox,double oy)2482 static inline void offset_label_bounds(const label_bounds *from, label_bounds *to, double ox, double oy) {
2483 if(from->poly) {
2484 int i;
2485 for(i=0; i<from->poly->numpoints; i++) {
2486 to->poly->point[i].x = from->poly->point[i].x + ox;
2487 to->poly->point[i].y = from->poly->point[i].y + oy;
2488 }
2489 to->poly->numpoints = from->poly->numpoints;
2490 } else {
2491 to->poly = NULL;
2492 }
2493 offset_bbox(&from->bbox, &to->bbox, ox, oy);
2494 }
2495
2496 /* private shortcut function to try a leader offsetted label
2497 * the caller must ensure that scratch->poly->points has been sufficiently allocated
2498 * to hold the points from the cachePtr's label_bounds */
offsetAndTest(mapObj * map,labelCacheMemberObj * cachePtr,double ox,double oy,int priority,int label_idx,label_bounds * scratch)2499 void offsetAndTest(mapObj *map, labelCacheMemberObj *cachePtr, double ox, double oy,
2500 int priority, int label_idx, label_bounds *scratch)
2501 {
2502 int i,j,status;
2503 pointObj leaderpt;
2504 lineObj *scratch_line = scratch->poly;
2505
2506 for(i=0; i<cachePtr->numtextsymbols; i++) {
2507 textSymbolObj *ts = cachePtr->textsymbols[i];
2508 if(ts->textpath) {
2509 offset_label_bounds(&ts->textpath->bounds, scratch, ox, oy);
2510 status = msTestLabelCacheCollisions(map, cachePtr, scratch, priority, label_idx);
2511 if(!status) {
2512 return;
2513 }
2514 }
2515 for(j=0; j<ts->label->numstyles; j++) {
2516 if(ts->label->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
2517 scratch->poly = scratch_line;
2518 offset_label_bounds(ts->style_bounds[j], scratch, ox, oy);
2519 status = msTestLabelCacheCollisions(map, cachePtr, scratch, priority, label_idx);
2520 if(!status) {
2521 return;
2522 }
2523 }
2524 }
2525 }
2526
2527 leaderpt.x = cachePtr->point.x + ox;
2528 leaderpt.y = cachePtr->point.y + oy;
2529
2530 status = msTestLabelCacheLeaderCollision(map, &cachePtr->point, &leaderpt);
2531 if(!status) {
2532 return;
2533 }
2534
2535
2536 /* the current offset is ok */
2537 cachePtr->leaderbbox = msSmallMalloc(sizeof(rectObj));
2538 cachePtr->leaderline = msSmallMalloc(sizeof(lineObj));
2539 cachePtr->leaderline->point = msSmallMalloc(2 * sizeof(pointObj));
2540 cachePtr->leaderline->numpoints = 2;
2541 cachePtr->leaderline->point[0] = cachePtr->point;
2542 cachePtr->leaderline->point[1] = leaderpt;
2543 cachePtr->leaderbbox->minx = MS_MIN(leaderpt.x,cachePtr->point.x);
2544 cachePtr->leaderbbox->maxx = MS_MAX(leaderpt.x,cachePtr->point.x);
2545 cachePtr->leaderbbox->miny = MS_MIN(leaderpt.y,cachePtr->point.y);
2546 cachePtr->leaderbbox->maxy = MS_MAX(leaderpt.y,cachePtr->point.y);
2547 cachePtr->status = MS_ON;
2548
2549 offset_bbox(&cachePtr->bbox,&cachePtr->bbox,ox,oy);
2550
2551 for(i=0; i<cachePtr->numtextsymbols; i++) {
2552 textSymbolObj *ts = cachePtr->textsymbols[i];
2553 if(ts->textpath) {
2554 offset_label_bounds(&ts->textpath->bounds, &ts->textpath->bounds, ox, oy);
2555 ts->annopoint.x += ox;
2556 ts->annopoint.y += oy;
2557 }
2558 if(ts->style_bounds) {
2559 for(j=0; j<ts->label->numstyles; j++) {
2560 if(ts->label->styles[j]->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
2561 offset_label_bounds(ts->style_bounds[j], ts->style_bounds[j], ox, oy);
2562 }
2563 }
2564 }
2565 }
2566
msDrawOffsettedLabels(imageObj * image,mapObj * map,int priority)2567 int msDrawOffsettedLabels(imageObj *image, mapObj *map, int priority)
2568 {
2569 int retval = MS_SUCCESS;
2570 int l;
2571 labelCacheObj *labelcache = &(map->labelcache);
2572 labelCacheSlotObj *cacheslot;
2573 labelCacheMemberObj *cachePtr;
2574 label_bounds scratch;
2575 lineObj scratch_line;
2576 pointObj *scratch_points = NULL;
2577 int num_allocated_scratch_points = 0;
2578 assert(MS_RENDERER_PLUGIN(image->format));
2579 cacheslot = &(labelcache->slots[priority]);
2580 scratch.poly = &scratch_line;
2581
2582 for(l=cacheslot->numlabels-1; l>=0; l--) {
2583 cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
2584 if(cachePtr->status == MS_OFF) {
2585 /* only test regular labels that have had their bounding box computed
2586 and that haven't been rendered */
2587 classObj *classPtr = (GET_CLASS(map,cachePtr->layerindex,cachePtr->classindex));
2588 layerObj *layerPtr = (GET_LAYER(map,cachePtr->layerindex));
2589 int steps,i,num_scratch_points_to_allocate = 0;
2590
2591 assert(classPtr->leader); /* cachePtrs that don't need to be tested have been marked as status on or delete */
2592
2593 if(cachePtr->point.x < labelcache->gutter ||
2594 cachePtr->point.y < labelcache->gutter ||
2595 cachePtr->point.x >= image->width - labelcache->gutter ||
2596 cachePtr->point.y >= image->height - labelcache->gutter) {
2597 /* don't look for leaders if point is in edge buffer as the leader line would end up chopped off */
2598 continue;
2599 }
2600
2601 for(i=0; i<cachePtr->numtextsymbols; i++) {
2602 int j;
2603 textSymbolObj *ts = cachePtr->textsymbols[i];
2604 if(ts->textpath && ts->textpath->bounds.poly) {
2605 num_scratch_points_to_allocate = MS_MAX(num_scratch_points_to_allocate, ts->textpath->bounds.poly->numpoints);
2606 }
2607 if(ts->style_bounds) {
2608 for(j=0;j<ts->label->numstyles; j++) {
2609 if(ts->label->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT &&
2610 ts->style_bounds[j]->poly) {
2611 num_scratch_points_to_allocate = MS_MAX(num_scratch_points_to_allocate, ts->style_bounds[j]->poly->numpoints);
2612 }
2613 }
2614 }
2615 }
2616 if(num_scratch_points_to_allocate > num_allocated_scratch_points) {
2617 scratch_points = msSmallRealloc(scratch_points, num_scratch_points_to_allocate * sizeof(pointObj));
2618 num_allocated_scratch_points = num_scratch_points_to_allocate;
2619 }
2620
2621
2622 steps = classPtr->leader->maxdistance / classPtr->leader->gridstep;
2623
2624 #define x0 (cachePtr->point.x)
2625 #define y0 (cachePtr->point.y)
2626 #define gridstepsc (classPtr->leader->gridstep)
2627
2628
2629 #define otest(ox,oy) if((x0+(ox)) >= labelcache->gutter &&\
2630 (y0+(oy)) >= labelcache->gutter &&\
2631 (x0+(ox)) < image->width - labelcache->gutter &&\
2632 (y0+(oy)) < image->height - labelcache->gutter) {\
2633 scratch_line.point = scratch_points;\
2634 scratch.poly = &scratch_line; \
2635 offsetAndTest(map,cachePtr,(ox),(oy),priority,l,&scratch); \
2636 if(cachePtr->status == MS_ON) break;\
2637 }
2638
2639 /* loop through possible offsetted positions */
2640 for(i=1; i<=steps; i++) {
2641
2642
2643
2644
2645 /* test the intermediate points on the ring */
2646
2647 /* (points marked "0" are the ones being tested)
2648
2649 X00X00X
2650 0XXXXX0
2651 0XXXXX0
2652 XXX.XXX
2653 0XXXXX0
2654 0XXXXX0
2655 X00X00X
2656 */
2657 int j;
2658 for(j=1; j<i-1; j++) {
2659 /* test the right positions */
2660 otest(i*gridstepsc,j * gridstepsc);
2661 otest(i*gridstepsc,- j * gridstepsc);
2662 /* test the left positions */
2663 otest(- i*gridstepsc,j * gridstepsc);
2664 otest(- i*gridstepsc,- j * gridstepsc);
2665 /* test the top positions */
2666 otest(j*gridstepsc,- i * gridstepsc);
2667 otest(- j *gridstepsc,- i * gridstepsc);
2668 /* test the bottom positions */
2669 otest(j*gridstepsc,i * gridstepsc);
2670 otest(- j *gridstepsc,i * gridstepsc);
2671 }
2672 if(j<(i-1)) break;
2673
2674 otest(i*gridstepsc,i*gridstepsc);
2675 otest(-i*gridstepsc,-i*gridstepsc);
2676 otest(i*gridstepsc,-i*gridstepsc);
2677 otest(-i*gridstepsc,i*gridstepsc);
2678
2679
2680 /* test the intermediate points on the ring */
2681
2682 /* (points marked "0" are the ones being tested)
2683
2684 X00X00X
2685 0XXXXX0
2686 0XXXXX0
2687 XXX.XXX
2688 0XXXXX0
2689 0XXXXX0
2690 X00X00X
2691
2692 */
2693
2694 /* test the extreme diagonal points */
2695
2696 /* (points marked "0" are the ones being tested)
2697
2698 0XXXXX0
2699 XXXXXXX
2700 XXXXXXX
2701 XXX.XXX
2702 XXXXXXX
2703 XXXXXXX
2704 0XXXXX0
2705
2706 (x0+i*gridstep, y0+i*gridstep), pos lr
2707 (x0-i*gridstep, y0-i*gridstep), pos ul
2708 (x0+i*gridstep, y0-i*gridstep), pos ur
2709 (x0-i*gridstep, y0+i*gridstep), pos ll
2710
2711 */
2712
2713 /* test the 4 cardinal points on the ring */
2714
2715 /* (points marked "0" are the ones being tested)
2716
2717 XXX0XXX
2718 XXXXXXX
2719 XXXXXXX
2720 0XX.XX0
2721 XXXXXXX
2722 XXXXXXX
2723 XXX0XXX
2724
2725 * (x0+i*gridtep,y0), pos cr
2726
2727 * (x0-i*gridstep,y0), pos cl
2728 * (x0,y0-i*gridstep), pos uc
2729 * (x0,y0+i*gridstep), pos lc
2730 */
2731 otest(i*gridstepsc,0);
2732 otest(-i*gridstepsc,0);
2733 otest(0,-i*gridstepsc);
2734 otest(0,i*gridstepsc);
2735 }
2736 if(cachePtr->status == MS_ON) {
2737 int ll;
2738 shapeObj labelLeader; /* label polygon (bounding box, possibly rotated) */
2739 labelLeader.line = cachePtr->leaderline; /* setup the label polygon structure */
2740 labelLeader.numlines = 1;
2741 insertRenderedLabelMember(map, cachePtr);
2742
2743 for(ll=0; ll<classPtr->leader->numstyles; ll++) {
2744 retval = msDrawLineSymbol(map, image,&labelLeader , classPtr->leader->styles[ll], layerPtr->scalefactor);
2745 if(UNLIKELY(retval == MS_FAILURE)) {
2746 goto offset_cleanup;
2747 }
2748 }
2749 for(ll=0; ll<cachePtr->numtextsymbols; ll++) {
2750 textSymbolObj *ts = cachePtr->textsymbols[ll];
2751
2752 if(ts->style_bounds) {
2753 /* here's where we draw the label styles */
2754 for(i=0; i<ts->label->numstyles; i++) {
2755 if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
2756 retval = msDrawMarkerSymbol(map, image, &(labelLeader.line->point[1]), ts->label->styles[i], layerPtr->scalefactor);
2757 if(UNLIKELY(retval == MS_FAILURE)) {
2758 goto offset_cleanup;
2759 }
2760 } else if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2761 retval = msDrawLabelBounds(map,image,ts->style_bounds[i],ts->label->styles[i], ts->scalefactor);
2762 if(UNLIKELY(retval == MS_FAILURE)) {
2763 goto offset_cleanup;
2764 }
2765 } else if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
2766 pointObj labelCenter;
2767 labelCenter.x = (ts->style_bounds[i]->bbox.maxx + ts->style_bounds[i]->bbox.minx)/2;
2768 labelCenter.y = (ts->style_bounds[i]->bbox.maxy + ts->style_bounds[i]->bbox.miny)/2;
2769 retval = msDrawMarkerSymbol(map, image, &labelCenter, ts->label->styles[i], layerPtr->scalefactor);
2770 if(UNLIKELY(retval == MS_FAILURE)) {
2771 goto offset_cleanup;
2772 }
2773 } else {
2774 msSetError(MS_MISCERR,"Labels only support LABELPNT, LABELPOLY and LABELCENTER GEOMTRANSFORMS", "msDrawOffsettedLabels()");
2775 retval = MS_FAILURE;
2776 }
2777 }
2778 }
2779 if(ts->annotext) {
2780 retval = msDrawTextSymbol(map,image,ts->annopoint,ts);
2781 if(UNLIKELY(retval == MS_FAILURE)) {
2782 goto offset_cleanup;
2783 }
2784 }
2785 }
2786 /* TODO: draw cachePtr->marker, but where ? */
2787
2788 /*
2789 styleObj tstyle;
2790 static int foo =0;
2791 if(!foo) {
2792 srand(time(NULL));
2793 foo = 1;
2794 initStyle(&tstyle);
2795 tstyle.width = 1;
2796 tstyle.color.alpha = 255;
2797 }
2798 tstyle.color.red = random()%255;
2799 tstyle.color.green = random()%255;
2800 tstyle.color.blue =random()%255;
2801 msDrawLineSymbol(&map->symbolset, image, cachePtr->poly, &tstyle, layerPtr->scalefactor);
2802 */
2803
2804 }
2805 }
2806 }
2807
2808 offset_cleanup:
2809
2810 free(scratch_points);
2811
2812
2813 return retval;
2814 }
2815
fastComputeBounds(lineObj * line,rectObj * bounds)2816 void fastComputeBounds(lineObj *line, rectObj *bounds)
2817 {
2818 int j;
2819 bounds->minx = bounds->maxx = line->point[0].x;
2820 bounds->miny = bounds->maxy = line->point[0].y;
2821
2822
2823 for( j=0; j<line->numpoints; j++ ) {
2824 bounds->minx = MS_MIN(bounds->minx, line->point[j].x);
2825 bounds->maxx = MS_MAX(bounds->maxx, line->point[j].x);
2826 bounds->miny = MS_MIN(bounds->miny, line->point[j].y);
2827 bounds->maxy = MS_MAX(bounds->maxy, line->point[j].y);
2828 }
2829 }
2830
computeMarkerBounds(mapObj * map,pointObj * annopoint,textSymbolObj * ts,label_bounds * poly)2831 int computeMarkerBounds(mapObj *map, pointObj *annopoint, textSymbolObj *ts, label_bounds *poly)
2832 {
2833 int i;
2834 for (i=0; i<ts->label->numstyles; i++) {
2835 styleObj *style = ts->label->styles[i];
2836 if(style->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT &&
2837 style->symbol < map->symbolset.numsymbols && style->symbol >= 0) {
2838 double sx,sy;
2839 int p;
2840 double aox,aoy;
2841 symbolObj *symbol = map->symbolset.symbol[style->symbol];
2842 if(msGetMarkerSize(map, style, &sx, &sy, ts->scalefactor) != MS_SUCCESS)
2843 return -1; /* real error, different from MS_FALSE, return -1 so we can trap it */
2844 if(style->angle) {
2845 pointObj *point = poly->poly->point;
2846 point[0].x = sx / 2.0;
2847 point[0].y = sy / 2.0;
2848 point[1].x = point[0].x;
2849 point[1].y = -point[0].y;
2850 point[2].x = -point[0].x;
2851 point[2].y = -point[0].y;
2852 point[3].x = -point[0].x;
2853 point[3].y = point[0].y;
2854 point[4].x = point[0].x;
2855 point[4].y = point[0].y;
2856 if(symbol->anchorpoint_x != 0.5 || symbol->anchorpoint_y != 0.5) {
2857 aox = (0.5 - symbol->anchorpoint_x) * sx;
2858 aoy = (0.5 - symbol->anchorpoint_y) * sy;
2859 for(p=0; p<5; p++) {
2860 point[p].x += aox;
2861 point[p].y += aoy;
2862 }
2863 }
2864 if(style->angle) {
2865 double rot = -style->angle * MS_DEG_TO_RAD;
2866 double sina = sin(rot);
2867 double cosa = cos(rot);
2868 for(p=0; p<5; p++) {
2869 double tmpx = point[p].x;
2870 point[p].x = point[p].x * cosa - point[p].y * sina;
2871 point[p].y = tmpx * sina + point[p].y * cosa;
2872 }
2873 }
2874 aox = annopoint->x + style->offsetx * ts->scalefactor;
2875 aoy = annopoint->y + style->offsety * ts->scalefactor;
2876 for(p=0; p<5; p++) {
2877 point[p].x += aox;
2878 point[p].y += aoy;
2879 }
2880 fastComputeBounds(poly->poly,&poly->bbox);
2881 } else {
2882 double aox = (0.5 - symbol->anchorpoint_x)*sx + annopoint->x + style->offsetx * ts->scalefactor;
2883 double aoy = (0.5 - symbol->anchorpoint_y)*sy + annopoint->y + style->offsety * ts->scalefactor;
2884 poly->poly = NULL;
2885 poly->bbox.maxx = sx/2.0 + aox;
2886 poly->bbox.minx = -sx/2.0 + aox;
2887 poly->bbox.maxy = sy/2.0 + aoy;
2888 poly->bbox.miny = -sy/2.0 + aoy;
2889 }
2890 break;
2891 }
2892 }
2893 if(i == ts->label->numstyles)
2894 return MS_FALSE; /* the label has no marker styles */
2895 else
2896 return MS_TRUE;
2897 }
2898
2899
2900 /* check that the current entry does not fall close to a label with identical text, if configured so.
2901 * Currently only checks the first label/text */
2902
msCheckLabelMinDistance(mapObj * map,labelCacheMemberObj * lc)2903 int msCheckLabelMinDistance(mapObj *map, labelCacheMemberObj *lc)
2904 {
2905 int i;
2906 textSymbolObj *s; /* shortcut */
2907 textSymbolObj *ts;
2908 rectObj buffered;
2909 if (lc->numtextsymbols == 0)
2910 return MS_FALSE; /* no label with text */
2911 s = lc->textsymbols[0];
2912
2913 if (!s->annotext || s->label->mindistance <= 0.0 || s->label->force == MS_TRUE)
2914 return MS_FALSE; /* min distance is not checked */
2915
2916 /* we buffer the label and check for intersection instead of calculating
2917 the distance of two textpaths. we also buffer only the bbox of lc for
2918 faster computation (it is still compared to the full textpath
2919 of the label cache members).
2920 */
2921 buffered = lc->bbox;
2922 buffered.minx -= s->label->mindistance * s->resolutionfactor;
2923 buffered.miny -= s->label->mindistance * s->resolutionfactor;
2924 buffered.maxx += s->label->mindistance * s->resolutionfactor;
2925 buffered.maxy += s->label->mindistance * s->resolutionfactor;
2926
2927 for (i = 0; i < map->labelcache.num_rendered_members; i++) {
2928 labelCacheMemberObj *ilc = map->labelcache.rendered_text_symbols[i];
2929 if (ilc->numtextsymbols == 0 || !ilc->textsymbols[0]->annotext)
2930 continue;
2931
2932 ts = ilc->textsymbols[0];
2933 if (strcmp(s->annotext, ts->annotext) != 0) {
2934 /* only check min distance against same label */
2935 continue;
2936 }
2937
2938 if (msPointInRect(&ilc->point, &buffered) == MS_TRUE) {
2939 return MS_TRUE;
2940 }
2941
2942 if(ts->textpath && ts->textpath->absolute) {
2943 if (intersectLabelPolygons(ts->textpath->bounds.poly, &ilc->bbox, NULL, &buffered) == MS_TRUE) {
2944 return MS_TRUE;
2945 }
2946 continue;
2947 }
2948
2949
2950 if (intersectLabelPolygons(NULL, &ilc->bbox, NULL, &buffered) == MS_TRUE) {
2951 return MS_TRUE;
2952 }
2953
2954 }
2955 return MS_FALSE;
2956 }
2957
copyLabelBounds(label_bounds * dst,label_bounds * src)2958 void copyLabelBounds(label_bounds *dst, label_bounds *src) {
2959 *dst = *src;
2960 if(src->poly) {
2961 int i;
2962 dst->poly = msSmallMalloc(sizeof(lineObj));
2963 dst->poly->numpoints = src->poly->numpoints;
2964 dst->poly->point = msSmallMalloc(dst->poly->numpoints * sizeof(pointObj));
2965 for(i=0; i<dst->poly->numpoints; i++) {
2966 dst->poly->point[i] = src->poly->point[i];
2967 }
2968 }
2969 }
2970
getLabelPositionFromString(char * pszString)2971 static int getLabelPositionFromString(char *pszString) {
2972 if (strcasecmp(pszString, "UL")==0) return MS_UL;
2973 else if (strcasecmp(pszString, "LR")==0) return MS_LR;
2974 else if (strcasecmp(pszString, "UR")==0) return MS_UR;
2975 else if (strcasecmp(pszString, "LL")==0) return MS_LL;
2976 else if (strcasecmp(pszString, "CR")==0) return MS_CR;
2977 else if (strcasecmp(pszString, "CL")==0) return MS_CL;
2978 else if (strcasecmp(pszString, "UC")==0) return MS_UC;
2979 else if (strcasecmp(pszString, "LC")==0) return MS_LC;
2980 else return MS_CC;
2981 }
2982
msDrawLabelCache(mapObj * map,imageObj * image)2983 int msDrawLabelCache(mapObj *map, imageObj *image)
2984 {
2985 int nReturnVal = MS_SUCCESS;
2986 struct mstimeval starttime={0}, endtime={0};
2987
2988 if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
2989
2990 if(image) {
2991 if(MS_RENDERER_PLUGIN(image->format)) {
2992 int i, l, ll, priority, its;
2993
2994 double marker_offset_x, marker_offset_y;
2995 int label_offset_x, label_offset_y;
2996 const char *value;
2997
2998 labelCacheMemberObj *cachePtr=NULL;
2999 layerObj *layerPtr=NULL;
3000 classObj *classPtr=NULL;
3001 textSymbolObj *textSymbolPtr=NULL;
3002
3003 /*
3004 * some statically allocated containers for storing label bounds before
3005 * copying them into the cachePtr: we avoid allocating these structures
3006 * at runtime, except for the labels that are actually rendered.
3007 */
3008 lineObj labelpoly_line;
3009 pointObj labelpoly_points[5];
3010 label_bounds labelpoly_bounds = {0};
3011 lineObj label_marker_line;
3012 pointObj label_marker_points[5];
3013 label_bounds label_marker_bounds;
3014 lineObj metrics_line;
3015 pointObj metrics_points[5];
3016 label_bounds metrics_bounds;
3017
3018 label_marker_line.point = label_marker_points;
3019 label_marker_line.numpoints = 5;
3020 metrics_line.point = metrics_points;
3021 metrics_line.numpoints = 5;
3022 labelpoly_line.point = labelpoly_points;
3023 labelpoly_line.numpoints = 5;
3024
3025 /* Look for labelcache_map_edge_buffer map metadata
3026 * If set then the value defines a buffer (in pixels) along the edge of the
3027 * map image where labels can't fall
3028 */
3029 if((value = msLookupHashTable(&(map->web.metadata), "labelcache_map_edge_buffer")) != NULL) {
3030 map->labelcache.gutter = MS_ABS(atoi(value));
3031 if(map->debug) msDebug("msDrawLabelCache(): labelcache_map_edge_buffer = %d\n", map->labelcache.gutter);
3032 }
3033
3034 for(priority=MS_MAX_LABEL_PRIORITY-1; priority>=0; priority--) {
3035 labelCacheSlotObj *cacheslot;
3036 cacheslot = &(map->labelcache.slots[priority]);
3037
3038 for(l=cacheslot->numlabels-1; l>=0; l--) {
3039 cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
3040
3041 layerPtr = (GET_LAYER(map, cachePtr->layerindex)); /* set a couple of other pointers, avoids nasty references */
3042 classPtr = (GET_CLASS(map, cachePtr->layerindex, cachePtr->classindex));
3043
3044 if(cachePtr->textsymbols[0]->textpath && cachePtr->textsymbols[0]->textpath->absolute) {
3045 /* we have an angle follow label */
3046 cachePtr->bbox = cachePtr->textsymbols[0]->textpath->bounds.bbox;
3047
3048 /* before going any futher, check that mindistance is respected */
3049 if (cachePtr->numtextsymbols && cachePtr->textsymbols[0]->label->mindistance > 0.0 && cachePtr->textsymbols[0]->annotext) {
3050 if (msCheckLabelMinDistance(map, cachePtr) == MS_TRUE) {
3051 cachePtr->status = MS_DELETE;
3052 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3053 "Skipping labelgroup %d \"%s\" in layer \"%s\": too close to an identical label (mindistance)\n",
3054 l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3055 continue; /* move on to next entry, this one is too close to an already placed one */
3056 }
3057 }
3058
3059 if(!cachePtr->textsymbols[0]->label->force)
3060 cachePtr->status = msTestLabelCacheCollisions(map,cachePtr,&cachePtr->textsymbols[0]->textpath->bounds, priority, l);
3061 else
3062 cachePtr->status = MS_ON;
3063 if(cachePtr->status) {
3064
3065
3066 if (UNLIKELY(MS_FAILURE == msDrawTextSymbol(map, image, cachePtr->textsymbols[0]->annopoint /*not used*/, cachePtr->textsymbols[0])))
3067 {
3068 return MS_FAILURE;
3069 }
3070 insertRenderedLabelMember(map, cachePtr);
3071 } else {
3072 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3073 "Skipping follow labelgroup %d \"%s\" in layer \"%s\": text collided\n",
3074 l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3075 }
3076 cachePtr->status = MS_DELETE; /* we're done with this label, it won't even have a second chance in the leader phase */
3077 } else {
3078 marker_offset_x = marker_offset_y = 0; /* assume no marker */
3079
3080 if (layerPtr->type == MS_LAYER_POINT && cachePtr->markerid!=-1) { /* there is a marker already in the image that we need to account for */
3081 markerCacheMemberObj *markerPtr = &(cacheslot->markers[cachePtr->markerid]); /* point to the right spot in the marker cache*/
3082 marker_offset_x = (markerPtr->bounds.maxx-markerPtr->bounds.minx)/2.0;
3083 marker_offset_y = (markerPtr->bounds.maxy-markerPtr->bounds.miny)/2.0;
3084 }
3085
3086
3087 /*
3088 ** all other cases *could* have multiple labels defined
3089 */
3090 cachePtr->status = MS_ON; /* assume this cache element can be placed */
3091 for(ll=0; ll<cachePtr->numtextsymbols; ll++) { /* RFC 77 TODO: Still may want to step through backwards... */
3092 int label_marker_status = MS_ON, have_label_marker, metrics_status = MS_ON;
3093 int need_labelpoly = 0;
3094
3095 /* reset the lineObj which may have been unset by a previous call to get_metrics() */
3096 label_marker_bounds.poly = &label_marker_line;
3097 labelpoly_bounds.poly = &labelpoly_line;
3098
3099 textSymbolPtr = cachePtr->textsymbols[ll];
3100 for(i=0; i<textSymbolPtr->label->numstyles; i++) {
3101 if(textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY ||
3102 textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3103 need_labelpoly = 1;
3104 break;
3105 }
3106 }
3107
3108 /* compute the poly of the label styles */
3109 if((have_label_marker = computeMarkerBounds(map,&cachePtr->point,textSymbolPtr, &label_marker_bounds)) == MS_TRUE) {
3110 if(cachePtr->numtextsymbols > 1) { /* FIXME this test doesn't seem right, should probably check if we have an annotation layer with a regular style defined */
3111 marker_offset_x = (label_marker_bounds.bbox.maxx-label_marker_bounds.bbox.minx)/2.0;
3112 marker_offset_y = (label_marker_bounds.bbox.maxy-label_marker_bounds.bbox.miny)/2.0;
3113 } else {
3114 /* we might be using an old style behavior with a markerPtr */
3115 marker_offset_x = MS_MAX(marker_offset_x,(label_marker_bounds.bbox.maxx-label_marker_bounds.bbox.minx)/2.0);
3116 marker_offset_y = MS_MAX(marker_offset_y,(label_marker_bounds.bbox.maxy-label_marker_bounds.bbox.miny)/2.0);
3117 }
3118 /* add marker to cachePtr->poly */
3119 if(textSymbolPtr->label->force != MS_TRUE) {
3120 label_marker_status = msTestLabelCacheCollisions(map, cachePtr, &label_marker_bounds ,priority, l);
3121 }
3122 if(label_marker_status == MS_OFF &&
3123 !(textSymbolPtr->label->force==MS_ON || classPtr->leader)) {
3124 cachePtr->status = MS_DELETE;
3125 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3126 "Skipping label %d of labelgroup %d of class %d in layer \"%s\": marker collided\n",
3127 ll, l, cachePtr->classindex, layerPtr->name);
3128 break; /* the marker collided, break from multi-label loop */
3129 }
3130 }
3131 if(have_label_marker == -1) return MS_FAILURE; /* error occured (symbol not found, etc...) */
3132
3133 if(textSymbolPtr->annotext) {
3134 /*
3135 * if we don't have an offset defined, first check that the labelpoint itself does not collide
3136 * this helps speed things up in dense labelling, as if the labelpoint collides there's
3137 * no use in computing the labeltext bounds (i.e. going into shaping+freetype).
3138 * We do however skip collision testing against the marker cache, as we want to allow rendering
3139 * a label for points that overlap each other: this is done by setting priority to MAX_PRIORITY
3140 * in the call to msTestLabelCacheCollisions (which is a hack!!)
3141 */
3142 if(!have_label_marker && textSymbolPtr->label->force != MS_TRUE && !classPtr->leader &&
3143 !textSymbolPtr->label->offsetx && !textSymbolPtr->label->offsety) {
3144 label_bounds labelpoint_bounds;
3145 labelpoint_bounds.poly = NULL;
3146 labelpoint_bounds.bbox.minx = cachePtr->point.x - 0.1;
3147 labelpoint_bounds.bbox.maxx = cachePtr->point.x + 0.1;
3148 labelpoint_bounds.bbox.miny = cachePtr->point.y - 0.1;
3149 labelpoint_bounds.bbox.maxy = cachePtr->point.y + 0.1;
3150 if(MS_OFF == msTestLabelCacheCollisions(map, cachePtr, &labelpoint_bounds, MS_MAX_LABEL_PRIORITY, l)) {
3151 cachePtr->status = MS_DELETE; /* we won't check for leader offseted positions, as the anchor point colided */
3152 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3153 "Skipping label %d \"%s\" of labelgroup %d of class %d in layer \"%s\": labelpoint collided\n",
3154 ll, textSymbolPtr->annotext, l, cachePtr->classindex, layerPtr->name);
3155 break;
3156 }
3157 }
3158
3159 /* compute label size */
3160 if(!textSymbolPtr->textpath) {
3161 if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,textSymbolPtr))) {
3162 return MS_FAILURE;
3163 }
3164 }
3165
3166 /* if our label has an outline, adjust the marker offset so the outlinecolor does
3167 * not bleed into the marker */
3168 if(marker_offset_x && MS_VALID_COLOR(textSymbolPtr->label->outlinecolor)) {
3169 marker_offset_x += textSymbolPtr->label->outlinewidth/2.0 * textSymbolPtr->scalefactor;
3170 marker_offset_y += textSymbolPtr->label->outlinewidth/2.0 * textSymbolPtr->scalefactor;
3171 }
3172
3173 /* apply offset and buffer settings */
3174 if(textSymbolPtr->label->anglemode != MS_FOLLOW) {
3175 label_offset_x = textSymbolPtr->label->offsetx * textSymbolPtr->scalefactor;
3176 label_offset_y = textSymbolPtr->label->offsety * textSymbolPtr-> scalefactor;
3177 } else {
3178 label_offset_x = 0;
3179 label_offset_y = 0;
3180 }
3181
3182 if(textSymbolPtr->label->position == MS_AUTO) {
3183 /* no point in using auto positionning if the marker cannot be placed */
3184 int positions[MS_POSITIONS_LENGTH], npositions=0;
3185
3186 /*
3187 ** (Note: might be able to re-order this for more speed.)
3188 */
3189 if(msLayerGetProcessingKey(layerPtr, "LABEL_POSITIONS")) {
3190 int p, ncustom_positions=0;
3191 char **custom_positions = msStringSplitComplex(msLayerGetProcessingKey(layerPtr, "LABEL_POSITIONS"), ",", &ncustom_positions, MS_STRIPLEADSPACES|MS_STRIPENDSPACES);
3192 for(p=0; p<MS_MIN(9,ncustom_positions); p++)
3193 positions[p] = getLabelPositionFromString(custom_positions[p]);
3194 npositions = p;
3195 msFree(custom_positions);
3196 } else if(layerPtr->type == MS_LAYER_POLYGON && marker_offset_x==0 ) {
3197 positions[0]=MS_CC;
3198 positions[1]=MS_UC;
3199 positions[2]=MS_LC;
3200 positions[3]=MS_CL;
3201 positions[4]=MS_CR;
3202 npositions = 5;
3203 } else if(layerPtr->type == MS_LAYER_LINE && marker_offset_x == 0) {
3204 positions[0]=MS_UC;
3205 positions[1]=MS_LC;
3206 positions[2]=MS_CC;
3207 npositions = 3;
3208 } else {
3209 positions[0]=MS_UL;
3210 positions[1]=MS_LR;
3211 positions[2]=MS_UR;
3212 positions[3]=MS_LL;
3213 positions[4]=MS_CR;
3214 positions[5]=MS_CL;
3215 positions[6]=MS_UC;
3216 positions[7]=MS_LC;
3217 npositions = 8;
3218 }
3219
3220 for(i=0; i<npositions; i++) {
3221 // RFC 77 TODO: take label_marker_offset_x/y into account
3222 metrics_bounds.poly = &metrics_line;
3223 textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), positions[i], textSymbolPtr->textpath,
3224 marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3225 textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor, &metrics_bounds);
3226 if(textSymbolPtr->label->force == MS_OFF) {
3227 for(its=0;its<ll;its++) {
3228 /* check for collisions inside the label group */
3229 if(intersectTextSymbol(cachePtr->textsymbols[its],&metrics_bounds) == MS_TRUE) {
3230 /* there was a self intersection */
3231 break; /* next position, will break out to next position in containing loop*/
3232 }
3233 if(its != ll)
3234 continue; /* goto next position, this one had an intersection with our own label group */
3235 }
3236 }
3237
3238 metrics_status = msTestLabelCacheCollisions(map, cachePtr,&metrics_bounds, priority, l);
3239
3240 /* found a suitable place for this label */
3241 if(metrics_status == MS_TRUE || (i==(npositions-1) && textSymbolPtr->label->force == MS_ON)) {
3242 metrics_status = MS_TRUE; /* set to true in case we are forcing it */
3243 /* compute anno poly for label background if needed */
3244 if(need_labelpoly) get_metrics(&(cachePtr->point), positions[i], textSymbolPtr->textpath,
3245 marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3246 textSymbolPtr->rotation, 1, &labelpoly_bounds);
3247
3248 break; /* ...out of position loop */
3249 }
3250 } /* next position */
3251
3252 /* if position auto didn't manage to find a position, but we have leader configured
3253 * for the class, then we want to compute the label poly anyway, placed as MS_CC */
3254 if(classPtr->leader && metrics_status == MS_FALSE) {
3255 metrics_bounds.poly = &metrics_line;
3256 textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath, label_offset_x, label_offset_y,
3257 textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor,
3258 &metrics_bounds);
3259 if(need_labelpoly) get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath,
3260 label_offset_x, label_offset_y,
3261 textSymbolPtr->rotation, 1, &labelpoly_bounds);
3262 }
3263 } else { /* explicit position */
3264 if(textSymbolPtr->label->position == MS_CC) { /* don't need the marker_offset */
3265 metrics_bounds.poly = &metrics_line;
3266 textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath, label_offset_x, label_offset_y,
3267 textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor, &metrics_bounds);
3268 if(need_labelpoly) get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath,
3269 label_offset_x, label_offset_y, textSymbolPtr->rotation, 1, &labelpoly_bounds);
3270 } else {
3271 metrics_bounds.poly = &metrics_line;
3272 textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), textSymbolPtr->label->position, textSymbolPtr->textpath,
3273 marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3274 textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor,
3275 &metrics_bounds);
3276 if(need_labelpoly) get_metrics(&(cachePtr->point), textSymbolPtr->label->position, textSymbolPtr->textpath,
3277 marker_offset_x + label_offset_x, marker_offset_y + label_offset_y, textSymbolPtr->rotation, 1, &labelpoly_bounds);
3278 }
3279
3280 if(textSymbolPtr->label->force == MS_ON) {
3281 metrics_status = MS_ON;
3282 } else {
3283 if(textSymbolPtr->label->force == MS_OFF) {
3284 /* check for collisions inside the label group unless the label is FORCE GROUP */
3285 for(its=0;its<ll;its++) {
3286 /* check for collisions inside the label group */
3287 if(intersectTextSymbol(cachePtr->textsymbols[its],&metrics_bounds) == MS_TRUE) {
3288 /* there was a self intersection */
3289 break; /* will break out to next position in containing loop*/
3290 }
3291 if(its != ll) {
3292 cachePtr->status = MS_DELETE; /* TODO RFC98: check if this is correct */
3293 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3294 "Skipping label %d (\"%s\") of labelgroup %d of class %d in layer \"%s\": intercollision with label text inside labelgroup\n",
3295 ll, textSymbolPtr->annotext, l,cachePtr->classindex, layerPtr->name);
3296 break; /* collision within the group, break out of textSymbol loop */
3297 }
3298 }
3299 }
3300 /* TODO: in case we have leader lines and multiple labels, there's no use in testing for labelcache collisions
3301 * once a first collision has been found. we only need to know that the label group has collided, and the
3302 * poly of the whole label group: if(label_group) testLabelCacheCollisions */
3303 metrics_status = msTestLabelCacheCollisions(map, cachePtr,&metrics_bounds, priority, l);
3304 }
3305 } /* end POSITION AUTO vs Fixed POSITION */
3306
3307 if(!metrics_status && classPtr->leader == 0) {
3308 cachePtr->status = MS_DELETE;
3309 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3310 "Skipping label %d \"%s\" of labelgroup %d of class %d in layer \"%s\": text collided\n",
3311 ll, textSymbolPtr->annotext, l, cachePtr->classindex, layerPtr->name);
3312 break; /* no point looking at more labels, unless their is a leader defined */
3313 }
3314 }
3315
3316 /* if we're here, we can either fit the label directly, or we need to put it in the leader queue */
3317 assert((metrics_status && label_marker_status) || classPtr->leader);
3318
3319 if(textSymbolPtr->annotext) {
3320 copyLabelBounds(&textSymbolPtr->textpath->bounds, &metrics_bounds);
3321 }
3322 if(have_label_marker) {
3323 if(!textSymbolPtr->style_bounds)
3324 textSymbolPtr->style_bounds = msSmallCalloc(textSymbolPtr->label->numstyles, sizeof(label_bounds*));
3325 for(its=0;its<textSymbolPtr->label->numstyles; its++) {
3326 if(textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3327 textSymbolPtr->style_bounds[its] = msSmallMalloc(sizeof(label_bounds));
3328 copyLabelBounds(textSymbolPtr->style_bounds[its], &label_marker_bounds);
3329 }
3330 }
3331 }
3332 if(!label_marker_status || ! metrics_status) {
3333 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3334 "Putting label %d of labelgroup %d of class %d , layer \"%s\" in leader queue\n",
3335 ll, l, cachePtr->classindex, layerPtr->name);
3336 cachePtr->status = MS_OFF; /* we have a collision, but this entry is a candidate for leader testing */
3337 }
3338
3339 /* do we need to copy the labelpoly, or can we use the static one ?*/
3340 if(cachePtr->numtextsymbols > 1 || (cachePtr->status == MS_OFF && classPtr->leader)) {
3341 /*
3342 * if we have more than one label, or if we have a single one which didn't fit but needs
3343 * to go through leader offset testing
3344 */
3345 if(!textSymbolPtr->style_bounds)
3346 textSymbolPtr->style_bounds = msSmallCalloc(textSymbolPtr->label->numstyles, sizeof(label_bounds*));
3347 for(its=0;its<textSymbolPtr->label->numstyles; its++) {
3348 if(textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY ||
3349 textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3350 textSymbolPtr->style_bounds[its] = msSmallMalloc(sizeof(label_bounds));
3351 copyLabelBounds(textSymbolPtr->style_bounds[its], &labelpoly_bounds);
3352 }
3353 }
3354
3355 } /* else: we'll use labelpoly_bounds directly below */
3356 } /* next label in the group */
3357
3358 if(cachePtr->status != MS_DELETE) {
3359 /* compute the global label bbox */
3360 int inited = 0,s;
3361 for(its=0;its<cachePtr->numtextsymbols;its++) {
3362 if(cachePtr->textsymbols[its]->annotext) {
3363 if(inited == 0) {
3364 cachePtr->bbox = cachePtr->textsymbols[its]->textpath->bounds.bbox;
3365 inited = 1;
3366 } else {
3367 cachePtr->bbox.minx = MS_MIN(cachePtr->bbox.minx, cachePtr->textsymbols[its]->textpath->bounds.bbox.minx);
3368 cachePtr->bbox.miny = MS_MIN(cachePtr->bbox.miny, cachePtr->textsymbols[its]->textpath->bounds.bbox.miny);
3369 cachePtr->bbox.maxx = MS_MAX(cachePtr->bbox.maxx, cachePtr->textsymbols[its]->textpath->bounds.bbox.maxx);
3370 cachePtr->bbox.maxy = MS_MAX(cachePtr->bbox.maxy, cachePtr->textsymbols[its]->textpath->bounds.bbox.maxy);
3371 }
3372 }
3373 for(s=0; s<cachePtr->textsymbols[its]->label->numstyles; s++) {
3374 if(cachePtr->textsymbols[its]->label->styles[s]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3375 if(inited == 0) {
3376 cachePtr->bbox = cachePtr->textsymbols[its]->style_bounds[s]->bbox;
3377 inited = 1;
3378 break;
3379 } else {
3380 cachePtr->bbox.minx = MS_MIN(cachePtr->bbox.minx, cachePtr->textsymbols[its]->style_bounds[s]->bbox.minx);
3381 cachePtr->bbox.miny = MS_MIN(cachePtr->bbox.miny, cachePtr->textsymbols[its]->style_bounds[s]->bbox.miny);
3382 cachePtr->bbox.maxx = MS_MAX(cachePtr->bbox.maxx, cachePtr->textsymbols[its]->style_bounds[s]->bbox.maxx);
3383 cachePtr->bbox.maxy = MS_MAX(cachePtr->bbox.maxy, cachePtr->textsymbols[its]->style_bounds[s]->bbox.maxy);
3384 break;
3385 }
3386 }
3387 }
3388 }
3389 }
3390
3391 /* check that mindistance is respected */
3392 if (cachePtr->numtextsymbols && cachePtr->textsymbols[0]->label->mindistance > 0.0 && cachePtr->textsymbols[0]->annotext) {
3393 if (msCheckLabelMinDistance(map, cachePtr) == MS_TRUE) {
3394 cachePtr->status = MS_DELETE;
3395 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3396 "Skipping labelgroup %d \"%s\" in layer \"%s\": too close to an identical label (mindistance)\n",
3397 l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3398 }
3399 }
3400
3401 if(cachePtr->status == MS_OFF || cachePtr->status == MS_DELETE)
3402 continue; /* next labelCacheMemberObj, as we had a collision */
3403
3404 /* insert the rendered label */
3405 insertRenderedLabelMember(map, cachePtr);
3406
3407 for(ll=0; ll<cachePtr->numtextsymbols; ll++) {
3408 textSymbolPtr = cachePtr->textsymbols[ll];
3409
3410 /* here's where we draw the label styles */
3411 for(i=0; i<textSymbolPtr->label->numstyles; i++) {
3412 if(textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3413 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &(cachePtr->point), textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3414 return MS_FAILURE;
3415 }
3416 } else if(textSymbolPtr->annotext && textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
3417 if(textSymbolPtr->style_bounds && textSymbolPtr->style_bounds[i]) {
3418 if(UNLIKELY(MS_FAILURE == msDrawLabelBounds(map,image,textSymbolPtr->style_bounds[i],textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3419 return MS_FAILURE;
3420 }
3421 } else {
3422 if(UNLIKELY(MS_FAILURE == msDrawLabelBounds(map,image,&labelpoly_bounds,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3423 return MS_FAILURE;
3424 }
3425 }
3426 } else if(textSymbolPtr->annotext && textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3427 pointObj labelCenter;
3428
3429 if(textSymbolPtr->style_bounds && textSymbolPtr->style_bounds[i]) {
3430 labelCenter.x = (textSymbolPtr->style_bounds[i]->bbox.maxx + textSymbolPtr->style_bounds[i]->bbox.minx)/2;
3431 labelCenter.y = (textSymbolPtr->style_bounds[i]->bbox.maxy + textSymbolPtr->style_bounds[i]->bbox.miny)/2;
3432 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map,image,&labelCenter,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3433 return MS_FAILURE;
3434 }
3435 } else {
3436 labelCenter.x = (labelpoly_bounds.bbox.maxx + labelpoly_bounds.bbox.minx)/2;
3437 labelCenter.y = (labelpoly_bounds.bbox.maxy + labelpoly_bounds.bbox.miny)/2;
3438 if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map,image,&labelCenter,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3439 return MS_FAILURE;
3440 }
3441 }
3442
3443 } else {
3444 msSetError(MS_MISCERR,"Labels only support LABELPNT, LABELPOLY and LABELCENTER GEOMTRANSFORMS", "msDrawLabelCache()");
3445 return MS_FAILURE;
3446 }
3447 }
3448
3449 if(textSymbolPtr->annotext) {
3450 if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,textSymbolPtr->annopoint,textSymbolPtr))) {
3451 return MS_FAILURE;
3452 }
3453 }
3454 }
3455 }
3456 } /* next label(group) from cacheslot */
3457 if(UNLIKELY(MS_FAILURE == msDrawOffsettedLabels(image, map, priority))) {
3458 return MS_FAILURE;
3459 }
3460 } /* next priority */
3461 #ifdef TBDEBUG
3462 styleObj tstyle;
3463 initStyle(&tstyle);
3464 tstyle.width = 1;
3465 tstyle.color.alpha = 255;
3466 tstyle.color.red = 255;
3467 tstyle.color.green = 0;
3468 tstyle.color.blue = 0;
3469 for(priority=MS_MAX_LABEL_PRIORITY-1; priority>=0; priority--) {
3470 labelCacheSlotObj *cacheslot;
3471 cacheslot = &(map->labelcache.slots[priority]);
3472
3473 for(l=cacheslot->numlabels-1; l>=0; l--) {
3474 cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
3475 /*
3476 assert((cachePtr->poly == NULL && cachePtr->status == MS_OFF) ||
3477 (cachePtr->poly && (cachePtr->status == MS_ON));
3478 */
3479 if(cachePtr->status) {
3480 msDrawLineSymbol(map, image, cachePtr->poly, &tstyle, layerPtr->scalefactor);
3481 }
3482 }
3483 }
3484 #endif
3485
3486 nReturnVal = MS_SUCCESS; /* necessary? */
3487 }
3488 }
3489
3490 if(map->debug >= MS_DEBUGLEVEL_TUNING) {
3491 msGettimeofday(&endtime, NULL);
3492 msDebug("msDrawMap(): Drawing Label Cache, %.3fs\n",
3493 (endtime.tv_sec+endtime.tv_usec/1.0e6)-
3494 (starttime.tv_sec+starttime.tv_usec/1.0e6) );
3495 }
3496
3497 return nReturnVal;
3498 }
3499
3500
3501 /**
3502 * Generic function to tell the underline device that layer
3503 * drawing is stating
3504 */
3505
msImageStartLayer(mapObj * map,layerObj * layer,imageObj * image)3506 void msImageStartLayer(mapObj *map, layerObj *layer, imageObj *image)
3507 {
3508 if (image) {
3509 if( MS_RENDERER_PLUGIN(image->format) ) {
3510 char *approximation_scale = msLayerGetProcessingKey( layer, "APPROXIMATION_SCALE" );
3511 if(approximation_scale) {
3512 if(!strncasecmp(approximation_scale,"ROUND",5)) {
3513 MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_ROUND;
3514 } else if(!strncasecmp(approximation_scale,"FULL",4)) {
3515 MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_FULLRESOLUTION;
3516 } else if(!strncasecmp(approximation_scale,"SIMPLIFY",8)) {
3517 MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_SIMPLIFY;
3518 } else {
3519 MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_SNAPTOGRID;
3520 MS_IMAGE_RENDERER(image)->approximation_scale = atof(approximation_scale);
3521 }
3522 } else {
3523 MS_IMAGE_RENDERER(image)->transform_mode = MS_IMAGE_RENDERER(image)->default_transform_mode;
3524 MS_IMAGE_RENDERER(image)->approximation_scale = MS_IMAGE_RENDERER(image)->default_approximation_scale;
3525 }
3526 MS_IMAGE_RENDERER(image)->startLayer(image, map, layer);
3527 } else if( MS_RENDERER_IMAGEMAP(image->format) )
3528 msImageStartLayerIM(map, layer, image);
3529
3530 }
3531 }
3532
3533
3534 /**
3535 * Generic function to tell the underline device that layer
3536 * drawing is ending
3537 */
3538
msImageEndLayer(mapObj * map,layerObj * layer,imageObj * image)3539 void msImageEndLayer(mapObj *map, layerObj *layer, imageObj *image)
3540 {
3541 if(image) {
3542 if( MS_RENDERER_PLUGIN(image->format) ) {
3543 MS_IMAGE_RENDERER(image)->endLayer(image,map,layer);
3544 }
3545 }
3546 }
3547
3548 /**
3549 * Generic function to tell the underline device that shape
3550 * drawing is starting
3551 */
3552
msDrawStartShape(mapObj * map,layerObj * layer,imageObj * image,shapeObj * shape)3553 void msDrawStartShape(mapObj *map, layerObj *layer, imageObj *image,
3554 shapeObj *shape)
3555 {
3556 if (image) {
3557 if(MS_RENDERER_PLUGIN(image->format)) {
3558 if (image->format->vtable->startShape)
3559 image->format->vtable->startShape(image, shape);
3560 }
3561
3562
3563 }
3564 }
3565
3566
3567 /**
3568 * Generic function to tell the underline device that shape
3569 * drawing is ending
3570 */
3571
msDrawEndShape(mapObj * map,layerObj * layer,imageObj * image,shapeObj * shape)3572 void msDrawEndShape(mapObj *map, layerObj *layer, imageObj *image,
3573 shapeObj *shape)
3574 {
3575 if(MS_RENDERER_PLUGIN(image->format)) {
3576 if (image->format->vtable->endShape)
3577 image->format->vtable->endShape(image, shape);
3578 }
3579 }
3580 /**
3581 * take the value from the shape and use it to change the
3582 * color in the style to match the range map
3583 */
msShapeToRange(styleObj * style,shapeObj * shape)3584 int msShapeToRange(styleObj *style, shapeObj *shape)
3585 {
3586 double fieldVal;
3587 char* fieldStr;
3588
3589 /*first, get the value of the rangeitem, which should*/
3590 /*evaluate to a double*/
3591 fieldStr = shape->values[style->rangeitemindex];
3592 if (fieldStr == NULL) { /*if there's not value, bail*/
3593 return MS_FAILURE;
3594 }
3595 fieldVal = 0.0;
3596 fieldVal = atof(fieldStr); /*faith that it's ok -- */
3597 /*should switch to strtod*/
3598 return msValueToRange(style, fieldVal, MS_COLORSPACE_RGB);
3599 }
3600
3601 /**
3602 * Allow direct mapping of a value so that mapscript can use the
3603 * Ranges. The styls passed in is updated to reflect the right color
3604 * based on the fieldVal
3605 */
msValueToRange(styleObj * style,double fieldVal,colorspace cs)3606 int msValueToRange(styleObj *style, double fieldVal, colorspace cs)
3607 {
3608 double range;
3609 double scaledVal;
3610
3611 range = style->maxvalue - style->minvalue;
3612 scaledVal = (fieldVal - style->minvalue)/range;
3613
3614 if(cs == MS_COLORSPACE_RGB) {
3615 /*At this point, we know where on the range we need to be*/
3616 /*However, we don't know how to map it yet, since RGB(A) can */
3617 /*Go up or down*/
3618 style->color.red = (int)(MS_MAX(0,(MS_MIN(255, (style->mincolor.red + ((style->maxcolor.red - style->mincolor.red) * scaledVal))))));
3619 style->color.green = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.green + ((style->maxcolor.green - style->mincolor.green) * scaledVal))))));
3620 style->color.blue = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.blue + ((style->maxcolor.blue - style->mincolor.blue) * scaledVal))))));
3621 style->color.alpha = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.alpha + ((style->maxcolor.alpha - style->mincolor.alpha) * scaledVal))))));
3622 } else {
3623 /* HSL */
3624 assert(cs == MS_COLORSPACE_HSL);
3625 if(fieldVal <= style->minvalue) style->color = style->mincolor;
3626 else if(fieldVal >= style->maxvalue) style->color = style->maxcolor;
3627 else {
3628 double mh,ms,ml,Mh,Ms,Ml;
3629 msRGBtoHSL(&style->mincolor,&mh,&ms,&ml);
3630 msRGBtoHSL(&style->maxcolor,&Mh,&Ms,&Ml);
3631 mh+=(Mh-mh)*scaledVal;
3632 ms+=(Ms-ms)*scaledVal;
3633 ml+=(Ml-ml)*scaledVal;
3634 msHSLtoRGB(mh,ms,ml,&style->color);
3635 style->color.alpha = style->mincolor.alpha + (style->maxcolor.alpha - style->mincolor.alpha)*scaledVal;
3636 }
3637 }
3638 /*( "msMapRange(): %i %i %i", style->color.red , style->color.green, style->color.blue);*/
3639
3640 #if ALPHACOLOR_ENABLED
3641 /*NO ALPHA RANGE YET style->color.alpha = style->mincolor.alpha + ((style->maxcolor.alpha - style->mincolor.alpha) * scaledVal);*/
3642 #endif
3643
3644 return MS_SUCCESS;
3645 }
3646