1 /*****************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  Content Dependant Legend rendering support
5  * Author:   Thomas Bonfort (tbonfort@terriscope.fr)
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2013 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "mapserver.h"
30 
31 
initStyleHitTests(styleObj * s,style_hittest * sh,int default_status)32 void initStyleHitTests(styleObj *s, style_hittest *sh, int default_status) {
33   sh->status = default_status;
34 }
35 
initLabelHitTests(labelObj * l,label_hittest * lh,int default_status)36 void initLabelHitTests(labelObj *l, label_hittest *lh, int default_status) {
37   int i;
38   lh->stylehits = msSmallCalloc(l->numstyles,sizeof(style_hittest));
39   lh->status = default_status;
40   for(i=0; i<l->numstyles; i++) {
41     initStyleHitTests(l->styles[i],&lh->stylehits[i],default_status);
42   }
43 }
44 
initClassHitTests(classObj * c,class_hittest * ch,int default_status)45 void initClassHitTests(classObj *c, class_hittest *ch, int default_status) {
46   int i;
47   ch->stylehits = msSmallCalloc(c->numstyles,sizeof(style_hittest));
48   ch->labelhits = msSmallCalloc(c->numlabels,sizeof(label_hittest));
49   ch->status = default_status;
50   for(i=0; i<c->numstyles; i++) {
51     initStyleHitTests(c->styles[i],&ch->stylehits[i],default_status);
52   }
53   for(i=0; i<c->numlabels; i++) {
54     initLabelHitTests(c->labels[i],&ch->labelhits[i],default_status);
55   }
56 }
57 
initLayerHitTests(layerObj * l,layer_hittest * lh)58 void initLayerHitTests(layerObj *l, layer_hittest *lh) {
59   int i,default_status;
60   lh->classhits = msSmallCalloc(l->numclasses,sizeof(class_hittest));
61 
62   switch(l->type) {
63     case MS_LAYER_POLYGON:
64     case MS_LAYER_POINT:
65     case MS_LAYER_LINE:
66     case MS_LAYER_ANNOTATION:
67       default_status = 0; /* needs testing */
68       break;
69     default:
70       default_status = 1; /* no hittesting needed, use traditional mode */
71       break;
72   }
73   lh->status = default_status;
74   for(i=0; i<l->numclasses; i++) {
75     initClassHitTests(l->class[i],&lh->classhits[i], default_status);
76   }
77 }
initMapHitTests(mapObj * map,map_hittest * mh)78 void initMapHitTests(mapObj *map, map_hittest *mh) {
79   int i;
80   mh->layerhits = msSmallCalloc(map->numlayers,sizeof(layer_hittest));
81   for(i=0; i<map->numlayers; i++) {
82     initLayerHitTests(GET_LAYER(map,i),&mh->layerhits[i]);
83   }
84 }
85 
freeLabelHitTests(labelObj * l,label_hittest * lh)86 void freeLabelHitTests(labelObj *l, label_hittest *lh) {
87   free(lh->stylehits);
88 }
89 
freeClassHitTests(classObj * c,class_hittest * ch)90 void freeClassHitTests(classObj *c, class_hittest *ch) {
91   int i;
92   for(i=0; i<c->numlabels; i++) {
93     freeLabelHitTests(c->labels[i],&ch->labelhits[i]);
94   }
95   free(ch->stylehits);
96   free(ch->labelhits);
97 }
freeLayerHitTests(layerObj * l,layer_hittest * lh)98 void freeLayerHitTests(layerObj *l, layer_hittest *lh) {
99   int i;
100   for(i=0; i<l->numclasses; i++) {
101     freeClassHitTests(l->class[i],&lh->classhits[i]);
102   }
103   free(lh->classhits);
104 }
freeMapHitTests(mapObj * map,map_hittest * mh)105 void freeMapHitTests(mapObj *map, map_hittest *mh) {
106   int i;
107   for(i=0; i<map->numlayers; i++) {
108     freeLayerHitTests(GET_LAYER(map,i),&mh->layerhits[i]);
109   }
110   free(mh->layerhits);
111 }
112 
msHitTestShape(mapObj * map,layerObj * layer,shapeObj * shape,int drawmode,class_hittest * hittest)113 int msHitTestShape(mapObj *map, layerObj *layer, shapeObj *shape, int drawmode, class_hittest *hittest) {
114   int i;
115   classObj *cp = layer->class[shape->classindex];
116   if(MS_DRAW_FEATURES(drawmode)) {
117     for(i=0;i<cp->numstyles;i++) {
118       styleObj *sp = cp->styles[i];
119       if(msScaleInBounds(map->scaledenom,sp->minscaledenom,sp->maxscaledenom)) {
120         hittest->stylehits[i].status = 1;
121       }
122     }
123   }
124   if(MS_DRAW_LABELS(drawmode)) {
125     for(i=0;i<cp->numlabels;i++) {
126       labelObj *l = cp->labels[i];
127       if(msGetLabelStatus(map,layer,shape,l) == MS_ON) {
128         int s;
129         hittest->labelhits[i].status = 1;
130         for(s=0; s<l->numstyles;s++) {
131           hittest->labelhits[i].stylehits[s].status = 1;
132         }
133       }
134     }
135   }
136   return MS_SUCCESS;
137 }
138 
msHitTestLayer(mapObj * map,layerObj * layer,layer_hittest * hittest)139 int msHitTestLayer(mapObj *map, layerObj *layer, layer_hittest *hittest) {
140   int status;
141 #ifdef USE_GEOS
142   shapeObj searchpoly;
143 #endif
144   if(!msLayerIsVisible(map,layer)) {
145     hittest->status = 0;
146     return MS_SUCCESS;
147   }
148   if(layer->type == MS_LAYER_LINE || layer->type == MS_LAYER_POLYGON || layer->type == MS_LAYER_POINT || layer->type == MS_LAYER_ANNOTATION) {
149     int maxfeatures=msLayerGetMaxFeaturesToDraw(layer, NULL);
150     int annotate = msEvalContext(map, layer, layer->labelrequires);
151     shapeObj shape;
152     int nclasses,featuresdrawn = 0;
153     int *classgroup;
154     rectObj searchrect;
155     int minfeaturesize=-1;
156     if(map->scaledenom > 0) {
157       if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
158       if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
159     }
160 
161     status = msLayerOpen(layer);
162     if(status != MS_SUCCESS) return MS_FAILURE;
163 
164     /* build item list */
165     status = msLayerWhichItems(layer, MS_FALSE, NULL);
166 
167     if(status != MS_SUCCESS) {
168       msLayerClose(layer);
169       return MS_FAILURE;
170     }
171 
172     /* identify target shapes */
173     if(layer->transform == MS_TRUE) {
174       searchrect = map->extent;
175       if((map->projection.numargs > 0) && (layer->projection.numargs > 0))
176         msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
177     }
178     else {
179       searchrect.minx = searchrect.miny = 0;
180       searchrect.maxx = map->width-1;
181       searchrect.maxy = map->height-1;
182     }
183 #ifdef USE_GEOS
184     msInitShape(&searchpoly);
185     msRectToPolygon(searchrect,&searchpoly);
186 #endif
187 
188     status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
189     if(status == MS_DONE) { /* no overlap */
190 #ifdef USE_GEOS
191       msFreeShape(&searchpoly);
192 #endif
193       msLayerClose(layer);
194       hittest->status = 0;
195       return MS_SUCCESS;
196     } else if(status != MS_SUCCESS) {
197 #ifdef USE_GEOS
198       msFreeShape(&searchpoly);
199 #endif
200       msLayerClose(layer);
201       return MS_FAILURE;
202     }
203 
204     /* step through the target shapes */
205     msInitShape(&shape);
206 
207     nclasses = 0;
208     classgroup = NULL;
209     if(layer->classgroup && layer->numclasses > 0)
210       classgroup = msAllocateValidClassGroups(layer, &nclasses);
211 
212     if(layer->minfeaturesize > 0)
213       minfeaturesize = Pix2LayerGeoref(map, layer, layer->minfeaturesize);
214 
215     while((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
216       int drawmode = MS_DRAWMODE_FEATURES;
217 #ifdef USE_GEOS
218       if(!msGEOSIntersects(&shape,&searchpoly)) {
219         msFreeShape(&shape);
220         continue;
221       }
222 #else
223       if(shape.type == MS_SHAPE_POLYGON) {
224         msClipPolygonRect(&shape, map->extent);
225       } else {
226         msClipPolylineRect(&shape, map->extent);
227       }
228       if(shape.numlines == 0) {
229         msFreeShape(&shape);
230         continue;
231       }
232 #endif
233       /* Check if the shape size is ok to be drawn, we need to clip */
234       if((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) && (minfeaturesize > 0)) {
235         msTransformShape(&shape, map->extent, map->cellsize, NULL);
236         msComputeBounds(&shape);
237         if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
238           msFreeShape(&shape);
239           continue;
240         }
241       }
242 
243       shape.classindex = msShapeGetClass(layer, map, &shape, classgroup, nclasses);
244       if((shape.classindex == -1) || (layer->class[shape.classindex]->status == MS_OFF)) {
245         msFreeShape(&shape);
246         continue;
247       }
248       hittest->classhits[shape.classindex].status = 1;
249       hittest->status = 1;
250 
251       if(maxfeatures >=0 && featuresdrawn >= maxfeatures) {
252         msFreeShape(&shape);
253         status = MS_DONE;
254         break;
255       }
256       featuresdrawn++;
257 
258       if(annotate && layer->class[shape.classindex]->numlabels > 0) {
259         drawmode |= MS_DRAWMODE_LABELS;
260       }
261 
262       status = msHitTestShape(map, layer, &shape, drawmode, &hittest->classhits[shape.classindex]); /* all styles  */
263       msFreeShape(&shape);
264     }
265 
266 #ifdef USE_GEOS
267     msFreeShape(&searchpoly);
268 #endif
269 
270     if (classgroup)
271       msFree(classgroup);
272 
273     if(status != MS_DONE) {
274       msLayerClose(layer);
275       return MS_FAILURE;
276     }
277     msLayerClose(layer);
278     return MS_SUCCESS;
279 
280   } else {
281     /* we don't hittest these layers, skip as they already have been initialised */
282     return MS_SUCCESS;
283   }
284 }
msHitTestMap(mapObj * map,map_hittest * hittest)285 int msHitTestMap(mapObj *map, map_hittest *hittest) {
286   int i,status;
287   map->cellsize = msAdjustExtent(&(map->extent),map->width,map->height);
288   status = msCalculateScale(map->extent,map->units,map->width,map->height, map->resolution, &map->scaledenom);
289   if(status != MS_SUCCESS) {
290     return MS_FAILURE;
291   }
292   for(i=0; i<map->numlayers; i++) {
293     layerObj *lp = map->layers[i];
294     status = msHitTestLayer(map,lp,&hittest->layerhits[i]);
295     if(status != MS_SUCCESS) {
296       return MS_FAILURE;
297     }
298   }
299   return MS_SUCCESS;
300 }
301