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