Coverage Report

Created: 2025-06-13 06:18

/src/MapServer/src/interpolation.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  MapServer
4
 * Purpose:  KernelDensity layer implementation and related functions.
5
 * Author:   Hermes L. Herrera and the MapServer team.
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2014 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
#include <float.h>
31
32
#include "gdal.h"
33
#include "cpl_string.h"
34
35
/******************************************************************************
36
 * kernel density.
37
 ******************************************************************************/
38
void msKernelDensity(imageObj *image, float *values, int width, int height,
39
                     int npoints, interpolationProcessingParams *interpParams,
40
                     unsigned char *iValues);
41
void msKernelDensityProcessing(layerObj *layer,
42
                               interpolationProcessingParams *interpParams);
43
44
/******************************************************************************
45
 * kernel density.
46
 ******************************************************************************/
47
void msIdw(float *xyz, int width, int height, int npoints,
48
           interpolationProcessingParams *interpParams, unsigned char *iValues);
49
void msIdwProcessing(layerObj *layer,
50
                     interpolationProcessingParams *interpParams);
51
52
//---------------------------------------------------------------------------//
53
int msInterpolationDataset(mapObj *map, imageObj *image,
54
                           layerObj *interpolation_layer, void **hDSvoid,
55
0
                           void **cleanup_ptr) {
56
57
0
  int status, layer_idx, i, npoints = 0, length = 0;
58
0
  rectObj searchrect;
59
0
  shapeObj shape;
60
0
  layerObj *layer = NULL;
61
0
  float *values = NULL, *xyz_values = NULL;
62
0
  int im_width = image->width, im_height = image->height;
63
0
  double invcellsize = 1.0 / map->cellsize, georadius = 0;
64
0
  unsigned char *iValues;
65
0
  GDALDatasetH hDS;
66
0
  interpolationProcessingParams interpParams;
67
68
0
  memset(&interpParams, 0, sizeof(interpParams));
69
70
0
  assert(interpolation_layer->connectiontype == MS_KERNELDENSITY ||
71
0
         interpolation_layer->connectiontype == MS_IDW);
72
0
  *cleanup_ptr = NULL;
73
74
0
  if (!interpolation_layer->connection || !*interpolation_layer->connection) {
75
0
    msSetError(MS_MISCERR, "msInterpolationDataset()",
76
0
               "Interpolation layer has no CONNECTION defined");
77
0
    return MS_FAILURE;
78
0
  }
79
80
0
  if (interpolation_layer->connectiontype == MS_KERNELDENSITY) {
81
0
    msKernelDensityProcessing(interpolation_layer, &interpParams);
82
0
  } else if (interpolation_layer->connectiontype == MS_IDW) {
83
0
    msIdwProcessing(interpolation_layer, &interpParams);
84
0
  }
85
86
0
  layer_idx = msGetLayerIndex(map, interpolation_layer->connection);
87
0
  if (layer_idx == -1) {
88
0
    int nLayers, *aLayers;
89
0
    aLayers =
90
0
        msGetLayersIndexByGroup(map, interpolation_layer->connection, &nLayers);
91
0
    if (!aLayers || !nLayers) {
92
0
      msSetError(MS_MISCERR,
93
0
                 "Interpolation layer (%s) references unknown layer (%s)",
94
0
                 "msInterpolationDataset()", interpolation_layer->name,
95
0
                 interpolation_layer->connection);
96
0
      return (MS_FAILURE);
97
0
    }
98
0
    for (i = 0; i < nLayers; i++) {
99
0
      layer_idx = aLayers[i];
100
0
      layer = GET_LAYER(map, layer_idx);
101
0
      if (msScaleInBounds(map->scaledenom, layer->minscaledenom,
102
0
                          layer->maxscaledenom))
103
0
        break;
104
0
    }
105
0
    free(aLayers);
106
0
    if (i == nLayers) {
107
0
      msSetError(
108
0
          MS_MISCERR,
109
0
          "Interpolation layer (%s) references no layer for current scale",
110
0
          "msInterpolationDataset()", interpolation_layer->name);
111
0
      return (MS_FAILURE);
112
0
    }
113
0
  } else {
114
0
    layer = GET_LAYER(map, layer_idx);
115
0
  }
116
  /* open the linked layer */
117
0
  status = msLayerOpen(layer);
118
0
  if (status != MS_SUCCESS)
119
0
    return MS_FAILURE;
120
121
0
  status = msLayerWhichItems(layer, MS_FALSE, NULL);
122
0
  if (status != MS_SUCCESS) {
123
0
    msLayerClose(layer);
124
0
    return MS_FAILURE;
125
0
  }
126
127
  /* identify target shapes */
128
0
  if (layer->transform == MS_TRUE) {
129
0
    searchrect = map->extent;
130
0
    if (interpParams.expand_searchrect) {
131
0
      georadius = interpParams.radius * map->cellsize;
132
0
      searchrect.minx -= georadius;
133
0
      searchrect.miny -= georadius;
134
0
      searchrect.maxx += georadius;
135
0
      searchrect.maxy += georadius;
136
0
      im_width += 2 * interpParams.radius;
137
0
      im_height += 2 * interpParams.radius;
138
0
    }
139
0
  } else {
140
0
    searchrect.minx = searchrect.miny = 0;
141
0
    searchrect.maxx = map->width - 1;
142
0
    searchrect.maxy = map->height - 1;
143
0
  }
144
145
0
  layer->project =
146
0
      msProjectionsDiffer(&(layer->projection), &(map->projection));
147
0
  if (layer->project)
148
0
    msProjectRect(&map->projection, &layer->projection,
149
0
                  &searchrect); /* project the searchrect to source coords */
150
151
0
  status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
152
  /* nothing to do */
153
0
  if (status == MS_SUCCESS) { /* at least one sample may have overlapped */
154
155
0
    int nclasses = 0;
156
0
    int *classgroup = NULL;
157
0
    if (layer->classgroup && layer->numclasses > 0)
158
0
      classgroup = msAllocateValidClassGroups(layer, &nclasses);
159
160
0
    msInitShape(&shape);
161
0
    while ((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
162
0
      int l, p, s, c;
163
0
      double weight = 1.0;
164
0
      if (!values) { /* defer allocation until we effectively have a feature */
165
0
        values = (float *)msSmallCalloc(((size_t)im_width) * im_height,
166
0
                                        sizeof(float));
167
0
        xyz_values = (float *)msSmallCalloc(((size_t)im_width) * im_height,
168
0
                                            sizeof(float));
169
0
      }
170
0
      if (layer->project)
171
0
        msProjectShape(&layer->projection, &map->projection, &shape);
172
173
      /* the weight for the sample is set to 1.0 by default. If the
174
       * layer has some classes defined, we will read the weight from
175
       * the class->style->size (which can be binded to an attribute)
176
       */
177
0
      if (layer->numclasses > 0) {
178
0
        c = msShapeGetClass(layer, map, &shape, classgroup, nclasses);
179
0
        if ((c == -1) || (layer->class[c] -> status == MS_OFF)) {
180
0
          goto nextshape; /* no class matched, skip */
181
0
        }
182
0
        for (s = 0; s < layer->class[c] -> numstyles; s++) {
183
0
          if (msScaleInBounds(
184
0
                  map->scaledenom,
185
0
                  layer->class[c] -> styles[s] -> minscaledenom,
186
0
                  layer -> class[c] -> styles[s] -> maxscaledenom)) {
187
0
            if (layer->class[c] -> styles[s]
188
0
                -> bindings[MS_STYLE_BINDING_SIZE].index != -1) {
189
0
              weight =
190
0
                  atof(shape.values[layer->class[c] -> styles[s]
191
0
                                    -> bindings[MS_STYLE_BINDING_SIZE].index]);
192
0
            } else {
193
0
              weight = layer->class[c]->styles[s]->size;
194
0
            }
195
0
            break;
196
0
          }
197
0
        }
198
0
        if (s == layer->class[c] -> numstyles) {
199
          /* no style in scale bounds */
200
0
          goto nextshape;
201
0
        }
202
0
      }
203
0
      for (l = 0; l < shape.numlines; l++) {
204
0
        for (p = 0; p < shape.line[l].numpoints; p++) {
205
0
          int x =
206
0
              MS_MAP2IMAGE_XCELL_IC(shape.line[l].point[p].x,
207
0
                                    map->extent.minx - georadius, invcellsize);
208
0
          int y =
209
0
              MS_MAP2IMAGE_YCELL_IC(shape.line[l].point[p].y,
210
0
                                    map->extent.maxy + georadius, invcellsize);
211
0
          if (x >= 0 && y >= 0 && x < im_width && y < im_height) {
212
0
            float *value = values + y * im_width + x;
213
0
            (*value) += weight;
214
0
            xyz_values[length++] = x;
215
0
            xyz_values[length++] = y;
216
0
            xyz_values[length++] = (*value);
217
0
          }
218
0
        }
219
0
      }
220
221
0
    nextshape:
222
0
      msFreeShape(&shape);
223
0
    }
224
225
0
    msFree(classgroup);
226
227
    // number of layer points.
228
0
    npoints = length / 3;
229
0
  } else if (status != MS_DONE) {
230
0
    msLayerClose(layer);
231
0
    return MS_FAILURE;
232
0
  }
233
234
  /* status == MS_DONE */
235
0
  msLayerClose(layer);
236
0
  status = MS_SUCCESS;
237
238
0
  if (npoints > 0 && interpParams.expand_searchrect) {
239
0
    iValues =
240
0
        msSmallMalloc(sizeof(unsigned char) * image->width * image->height);
241
0
  } else {
242
0
    iValues =
243
0
        msSmallCalloc(1, sizeof(unsigned char) * image->width * image->height);
244
0
  }
245
246
0
  if (npoints >
247
0
      0) { /* no use applying the filtering kernel if we have no samples */
248
0
    if (interpolation_layer->connectiontype == MS_KERNELDENSITY) {
249
0
      msKernelDensity(image, values, im_width, im_height, npoints,
250
0
                      &interpParams, iValues);
251
0
    } else if (interpolation_layer->connectiontype == MS_IDW) {
252
0
      msIdw(xyz_values, image->width, image->height, npoints, &interpParams,
253
0
            iValues);
254
0
    }
255
0
  }
256
257
0
  free(values);
258
0
  free(xyz_values);
259
260
0
  GDALDriverH hMemDRV = GDALGetDriverByName("MEM");
261
0
  if (!hMemDRV) {
262
0
    msSetError(MS_IOERR, "GDAL MEM driver not available",
263
0
               "msInterpolationDataset()");
264
0
    free(iValues);
265
0
    return MS_FAILURE;
266
0
  }
267
268
0
  hDS = GDALCreate(hMemDRV, "", image->width, image->height, 0, 0, NULL);
269
0
  if (hDS == NULL) {
270
0
    msSetError(MS_IMGERR, "Unable to create GDAL Memory dataset.",
271
0
               "msInterpolationDataset()");
272
0
    free(iValues);
273
0
    return MS_FAILURE;
274
0
  }
275
276
0
  char pointer[64];
277
0
  memset(pointer, 0, sizeof(pointer));
278
0
  CPLPrintPointer(pointer, iValues, sizeof(pointer));
279
280
0
  char **papszOptions = CSLSetNameValue(NULL, "DATAPOINTER", pointer);
281
0
  CPLErr eErr = GDALAddBand(hDS, GDT_Byte, papszOptions);
282
0
  CSLDestroy(papszOptions);
283
0
  if (eErr != CE_None) {
284
0
    msSetError(MS_IMGERR, "Unable to add band to GDAL Memory dataset.",
285
0
               "msInterpolationDataset()");
286
0
    free(iValues);
287
0
    GDALClose(hDS);
288
0
    return MS_FAILURE;
289
0
  }
290
291
0
  double adfGeoTransform[6];
292
0
  adfGeoTransform[0] = map->extent.minx - map->cellsize * 0.5; /* top left x */
293
0
  adfGeoTransform[1] = map->cellsize; /* w-e pixel resolution */
294
0
  adfGeoTransform[2] = 0;             /* 0 */
295
0
  adfGeoTransform[3] = map->extent.maxy + map->cellsize * 0.5; /* top left y */
296
0
  adfGeoTransform[4] = 0;                                      /* 0 */
297
0
  adfGeoTransform[5] =
298
0
      -map->cellsize; /* n-s pixel resolution (negative value) */
299
0
  GDALSetGeoTransform(hDS, adfGeoTransform);
300
0
  *hDSvoid = hDS;
301
0
  *cleanup_ptr = (void *)iValues;
302
303
0
  return status;
304
0
}
305
306
int msCleanupInterpolationDataset(mapObj *map, imageObj *image, layerObj *layer,
307
0
                                  void *cleanup_ptr) {
308
0
  (void)map;
309
0
  (void)image;
310
0
  (void)layer;
311
0
  free(cleanup_ptr);
312
0
  return MS_SUCCESS;
313
0
}