Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/hittest.c
Line
Count
Source
1
/*****************************************************************************
2
 *
3
 * Project:  MapServer
4
 * Purpose:  Content Dependent 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
0
void initStyleHitTests(styleObj *s, style_hittest *sh, int default_status) {
32
0
  (void)s;
33
0
  sh->status = default_status;
34
0
}
35
36
0
void initLabelHitTests(labelObj *l, label_hittest *lh, int default_status) {
37
0
  int i;
38
0
  lh->stylehits = msSmallCalloc(l->numstyles, sizeof(style_hittest));
39
0
  lh->status = default_status;
40
0
  for (i = 0; i < l->numstyles; i++) {
41
0
    initStyleHitTests(l->styles[i], &lh->stylehits[i], default_status);
42
0
  }
43
0
}
44
45
0
void initClassHitTests(classObj *c, class_hittest *ch, int default_status) {
46
0
  int i;
47
0
  ch->stylehits = msSmallCalloc(c->numstyles, sizeof(style_hittest));
48
0
  ch->labelhits = msSmallCalloc(c->numlabels, sizeof(label_hittest));
49
0
  ch->status = default_status;
50
0
  for (i = 0; i < c->numstyles; i++) {
51
0
    initStyleHitTests(c->styles[i], &ch->stylehits[i], default_status);
52
0
  }
53
0
  for (i = 0; i < c->numlabels; i++) {
54
0
    initLabelHitTests(c->labels[i], &ch->labelhits[i], default_status);
55
0
  }
56
0
}
57
58
0
void initLayerHitTests(layerObj *l, layer_hittest *lh) {
59
0
  int i, default_status;
60
0
  lh->classhits = msSmallCalloc(l->numclasses, sizeof(class_hittest));
61
62
0
  switch (l->type) {
63
0
  case MS_LAYER_POLYGON:
64
0
  case MS_LAYER_POINT:
65
0
  case MS_LAYER_LINE:
66
0
  case MS_LAYER_ANNOTATION:
67
0
    default_status = 0; /* needs testing */
68
0
    break;
69
0
  default:
70
0
    default_status = 1; /* no hittesting needed, use traditional mode */
71
0
    break;
72
0
  }
73
0
  lh->status = default_status;
74
0
  for (i = 0; i < l->numclasses; i++) {
75
0
    initClassHitTests(l->class[i], &lh -> classhits[i], default_status);
76
0
  }
77
0
}
78
0
void initMapHitTests(mapObj *map, map_hittest *mh) {
79
0
  int i;
80
0
  mh->layerhits = msSmallCalloc(map->numlayers, sizeof(layer_hittest));
81
0
  for (i = 0; i < map->numlayers; i++) {
82
0
    initLayerHitTests(GET_LAYER(map, i), &mh->layerhits[i]);
83
0
  }
84
0
}
85
86
0
void freeLabelHitTests(labelObj *l, label_hittest *lh) {
87
0
  (void)l;
88
0
  free(lh->stylehits);
89
0
}
90
91
0
void freeClassHitTests(classObj *c, class_hittest *ch) {
92
0
  int i;
93
0
  for (i = 0; i < c->numlabels; i++) {
94
0
    freeLabelHitTests(c->labels[i], &ch->labelhits[i]);
95
0
  }
96
0
  free(ch->stylehits);
97
0
  free(ch->labelhits);
98
0
}
99
0
void freeLayerHitTests(layerObj *l, layer_hittest *lh) {
100
0
  int i;
101
0
  for (i = 0; i < l->numclasses; i++) {
102
0
    freeClassHitTests(l->class[i], &lh -> classhits[i]);
103
0
  }
104
0
  free(lh->classhits);
105
0
}
106
0
void freeMapHitTests(mapObj *map, map_hittest *mh) {
107
0
  int i;
108
0
  for (i = 0; i < map->numlayers; i++) {
109
0
    freeLayerHitTests(GET_LAYER(map, i), &mh->layerhits[i]);
110
0
  }
111
0
  free(mh->layerhits);
112
0
}
113
114
int msHitTestShape(mapObj *map, layerObj *layer, shapeObj *shape, int drawmode,
115
0
                   class_hittest *hittest) {
116
0
  int i;
117
0
  classObj *cp = layer->class[shape->classindex];
118
0
  if (MS_DRAW_FEATURES(drawmode)) {
119
0
    for (i = 0; i < cp->numstyles; i++) {
120
0
      styleObj *sp = cp->styles[i];
121
0
      if (msScaleInBounds(map->scaledenom, sp->minscaledenom,
122
0
                          sp->maxscaledenom)) {
123
0
        hittest->stylehits[i].status = 1;
124
0
      }
125
0
    }
126
0
  }
127
0
  if (MS_DRAW_LABELS(drawmode)) {
128
0
    for (i = 0; i < cp->numlabels; i++) {
129
0
      labelObj *l = cp->labels[i];
130
0
      if (msGetLabelStatus(map, layer, shape, l) == MS_ON) {
131
0
        int s;
132
0
        hittest->labelhits[i].status = 1;
133
0
        for (s = 0; s < l->numstyles; s++) {
134
0
          hittest->labelhits[i].stylehits[s].status = 1;
135
0
        }
136
0
      }
137
0
    }
138
0
  }
139
0
  return MS_SUCCESS;
140
0
}
141
142
0
int msHitTestLayer(mapObj *map, layerObj *layer, layer_hittest *hittest) {
143
0
  int status;
144
#ifdef USE_GEOS
145
  shapeObj searchpoly;
146
#endif
147
0
  if (!msLayerIsVisible(map, layer)) {
148
0
    hittest->status = 0;
149
0
    return MS_SUCCESS;
150
0
  }
151
0
  if (layer->type == MS_LAYER_LINE || layer->type == MS_LAYER_POLYGON ||
152
0
      layer->type == MS_LAYER_POINT || layer->type == MS_LAYER_ANNOTATION) {
153
0
    int maxfeatures = msLayerGetMaxFeaturesToDraw(layer, NULL);
154
0
    int annotate = msEvalContext(map, layer, layer->labelrequires);
155
0
    shapeObj shape;
156
0
    int nclasses, featuresdrawn = 0;
157
0
    int *classgroup;
158
0
    rectObj searchrect;
159
0
    int minfeaturesize = -1;
160
0
    if (map->scaledenom > 0) {
161
0
      if ((layer->labelmaxscaledenom != -1) &&
162
0
          (map->scaledenom >= layer->labelmaxscaledenom))
163
0
        annotate = MS_FALSE;
164
0
      if ((layer->labelminscaledenom != -1) &&
165
0
          (map->scaledenom < layer->labelminscaledenom))
166
0
        annotate = MS_FALSE;
167
0
    }
168
169
0
    status = msLayerOpen(layer);
170
0
    if (status != MS_SUCCESS)
171
0
      return MS_FAILURE;
172
173
    /* build item list */
174
0
    status = msLayerWhichItems(layer, MS_FALSE, NULL);
175
176
0
    if (status != MS_SUCCESS) {
177
0
      msLayerClose(layer);
178
0
      return MS_FAILURE;
179
0
    }
180
181
    /* identify target shapes */
182
0
    if (layer->transform == MS_TRUE) {
183
0
      searchrect = map->extent;
184
0
      if ((map->projection.numargs > 0) && (layer->projection.numargs > 0))
185
0
        msProjectRect(
186
0
            &map->projection, &layer->projection,
187
0
            &searchrect); /* project the searchrect to source coords */
188
0
    } else {
189
0
      searchrect.minx = searchrect.miny = 0;
190
0
      searchrect.maxx = map->width - 1;
191
0
      searchrect.maxy = map->height - 1;
192
0
    }
193
#ifdef USE_GEOS
194
    msInitShape(&searchpoly);
195
    msRectToPolygon(searchrect, &searchpoly);
196
#endif
197
198
0
    status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
199
0
    if (status == MS_DONE) { /* no overlap */
200
#ifdef USE_GEOS
201
      msFreeShape(&searchpoly);
202
#endif
203
0
      msLayerClose(layer);
204
0
      hittest->status = 0;
205
0
      return MS_SUCCESS;
206
0
    } else if (status != MS_SUCCESS) {
207
#ifdef USE_GEOS
208
      msFreeShape(&searchpoly);
209
#endif
210
0
      msLayerClose(layer);
211
0
      return MS_FAILURE;
212
0
    }
213
214
    /* step through the target shapes */
215
0
    msInitShape(&shape);
216
217
0
    nclasses = 0;
218
0
    classgroup = NULL;
219
0
    if (layer->classgroup && layer->numclasses > 0)
220
0
      classgroup = msAllocateValidClassGroups(layer, &nclasses);
221
222
0
    if (layer->minfeaturesize > 0)
223
0
      minfeaturesize = Pix2LayerGeoref(map, layer, layer->minfeaturesize);
224
225
0
    while ((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
226
0
      int drawmode = MS_DRAWMODE_FEATURES;
227
#ifdef USE_GEOS
228
      if (!msGEOSIntersects(&shape, &searchpoly)) {
229
        msFreeShape(&shape);
230
        continue;
231
      }
232
#else
233
0
      if (shape.type == MS_SHAPE_POLYGON) {
234
0
        msClipPolygonRect(&shape, map->extent);
235
0
      } else {
236
0
        msClipPolylineRect(&shape, map->extent);
237
0
      }
238
0
      if (shape.numlines == 0) {
239
0
        msFreeShape(&shape);
240
0
        continue;
241
0
      }
242
0
#endif
243
      /* Check if the shape size is ok to be drawn, we need to clip */
244
0
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
245
0
          (minfeaturesize > 0)) {
246
0
        msTransformShape(&shape, map->extent, map->cellsize, NULL);
247
0
        msComputeBounds(&shape);
248
0
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
249
0
          msFreeShape(&shape);
250
0
          continue;
251
0
        }
252
0
      }
253
254
0
      shape.classindex =
255
0
          msShapeGetClass(layer, map, &shape, classgroup, nclasses);
256
0
      if ((shape.classindex == -1) ||
257
0
          (layer->class[shape.classindex] -> status == MS_OFF)) {
258
0
        msFreeShape(&shape);
259
0
        continue;
260
0
      }
261
0
      hittest->classhits[shape.classindex].status = 1;
262
0
      hittest->status = 1;
263
264
0
      if (maxfeatures >= 0 && featuresdrawn >= maxfeatures) {
265
0
        msFreeShape(&shape);
266
0
        status = MS_DONE;
267
0
        break;
268
0
      }
269
0
      featuresdrawn++;
270
271
0
      if (annotate && layer->class[shape.classindex] -> numlabels > 0) {
272
0
        drawmode |= MS_DRAWMODE_LABELS;
273
0
      }
274
275
0
      status = msHitTestShape(
276
0
          map, layer, &shape, drawmode,
277
0
          &hittest->classhits[shape.classindex]); /* all styles  */
278
0
      msFreeShape(&shape);
279
0
    }
280
281
#ifdef USE_GEOS
282
    msFreeShape(&searchpoly);
283
#endif
284
285
0
    if (classgroup)
286
0
      msFree(classgroup);
287
288
0
    if (status != MS_DONE) {
289
0
      msLayerClose(layer);
290
0
      return MS_FAILURE;
291
0
    }
292
0
    msLayerClose(layer);
293
0
    return MS_SUCCESS;
294
295
0
  } else {
296
    /* we don't hittest these layers, skip as they already have been initialized
297
     */
298
0
    return MS_SUCCESS;
299
0
  }
300
0
}
301
0
int msHitTestMap(mapObj *map, map_hittest *hittest) {
302
0
  int i, status;
303
0
  map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height);
304
0
  status = msCalculateScale(map->extent, map->units, map->width, map->height,
305
0
                            map->resolution, &map->scaledenom);
306
0
  if (status != MS_SUCCESS) {
307
0
    return MS_FAILURE;
308
0
  }
309
0
  for (i = 0; i < map->numlayers; i++) {
310
0
    layerObj *lp = map->layers[i];
311
0
    status = msHitTestLayer(map, lp, &hittest->layerhits[i]);
312
0
    if (status != MS_SUCCESS) {
313
0
      return MS_FAILURE;
314
0
    }
315
0
  }
316
0
  return MS_SUCCESS;
317
0
}