• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 17652514813

11 Sep 2025 05:33PM UTC coverage: 41.443% (+0.001%) from 41.442%
17652514813

push

github

geographika
Add GML type

62021 of 149654 relevant lines covered (41.44%)

24998.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.13
/src/maprasterquery.c
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Implementation of query operations on rasters.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
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 "mapserver.h"
32
#include "mapresample.h"
33
#include "mapthread.h"
34
#include "mapraster.h"
35

36
int msRASTERLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record);
37
int msRASTERLayerGetItems(layerObj *layer);
38

39
/* ==================================================================== */
40
/*      For now the rasterLayerInfo lives here since it is just used    */
41
/*      to hold information related to queries.                         */
42
/* ==================================================================== */
43
typedef struct {
44

45
  /* query cache results */
46
  int query_results;
47
  int query_alloc_max;
48
  int query_request_max;
49
  int query_result_hard_max;
50
  int raster_query_mode;
51
  int band_count;
52

53
  int refcount;
54

55
  rectObj which_rect;
56
  int next_shape;
57

58
  double *qc_x;
59
  double *qc_y;
60
  double *qc_x_reproj;
61
  double *qc_y_reproj;
62
  double *qc_values;
63
  int *qc_class;
64
  int *qc_red;
65
  int *qc_green;
66
  int *qc_blue;
67
  int *qc_count;
68
  int *qc_tileindex;
69

70
  /* query bound in force */
71
  shapeObj *searchshape;
72

73
  /* Only nearest result to this point. */
74
  int range_mode; /* MS_QUERY_SINGLE, MS_QUERY_MULTIPLE or -1 (skip test) */
75
  double range_dist;
76
  pointObj target_point;
77

78
  GDALColorTableH hCT;
79
  GDALDataType eDataType;
80
  double shape_tolerance;
81

82
} rasterLayerInfo;
83

84
#define RQM_UNKNOWN 0
85
#define RQM_ENTRY_PER_PIXEL 1
86
#define RQM_HIST_ON_CLASS 2
87
#define RQM_HIST_ON_VALUE 3
88

89
extern int InvGeoTransform(double *gt_in, double *gt_out);
90
#define GEO_TRANS(tr, x, y) ((tr)[0] + (tr)[1] * (x) + (tr)[2] * (y))
91

92
/************************************************************************/
93
/*                             addResult()                              */
94
/*                                                                      */
95
/*      this is a copy of the code in mapquery.c.  Should we prepare    */
96
/*      a small public API for managing results caches?                 */
97
/************************************************************************/
98

99
static int addResult(resultCacheObj *cache, int classindex, int shapeindex,
235✔
100
                     int tileindex) {
101
  int i;
102

103
  if (cache->numresults == cache->cachesize) { /* just add it to the end */
235✔
104
    if (cache->cachesize == 0)
39✔
105
      cache->results =
21✔
106
          (resultObj *)malloc(sizeof(resultObj) * MS_RESULTCACHEINCREMENT);
21✔
107
    else
108
      cache->results = (resultObj *)realloc(
18✔
109
          cache->results,
18✔
110
          sizeof(resultObj) * (cache->cachesize + MS_RESULTCACHEINCREMENT));
18✔
111
    if (!cache->results) {
39✔
112
      msSetError(MS_MEMERR, "Realloc() error.", "addResult()");
×
113
      return (MS_FAILURE);
×
114
    }
115
    cache->cachesize += MS_RESULTCACHEINCREMENT;
39✔
116
  }
117

118
  i = cache->numresults;
235✔
119

120
  cache->results[i].classindex = classindex;
235✔
121
  cache->results[i].tileindex = tileindex;
235✔
122
  cache->results[i].shapeindex = shapeindex;
235✔
123
  cache->results[i].resultindex = -1; /* unused */
235✔
124
  cache->results[i].shape = NULL;
235✔
125
  cache->numresults++;
235✔
126

127
  return (MS_SUCCESS);
235✔
128
}
129

130
/************************************************************************/
131
/*                       msRasterLayerInfoFree()                        */
132
/************************************************************************/
133

134
static void msRasterLayerInfoFree(layerObj *layer)
75✔
135

136
{
137
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
75✔
138

139
  if (rlinfo == NULL)
75✔
140
    return;
141

142
  if (rlinfo->qc_x != NULL) {
75✔
143
    free(rlinfo->qc_x);
21✔
144
    free(rlinfo->qc_y);
21✔
145
    free(rlinfo->qc_x_reproj);
21✔
146
    free(rlinfo->qc_y_reproj);
21✔
147
  }
148

149
  if (rlinfo->qc_values)
75✔
150
    free(rlinfo->qc_values);
21✔
151

152
  if (rlinfo->qc_class) {
75✔
153
    free(rlinfo->qc_class);
2✔
154
  }
155

156
  if (rlinfo->qc_red) {
75✔
157
    free(rlinfo->qc_red);
21✔
158
    free(rlinfo->qc_green);
21✔
159
    free(rlinfo->qc_blue);
21✔
160
  }
161

162
  if (rlinfo->qc_count != NULL)
75✔
163
    free(rlinfo->qc_count);
×
164

165
  if (rlinfo->qc_tileindex != NULL)
75✔
166
    free(rlinfo->qc_tileindex);
×
167

168
  free(rlinfo);
75✔
169

170
  layer->layerinfo = NULL;
75✔
171
}
172

173
/************************************************************************/
174
/*                    msRasterLayerInfoInitialize()                     */
175
/************************************************************************/
176

177
static void msRasterLayerInfoInitialize(layerObj *layer)
75✔
178

179
{
180
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
75✔
181

182
  if (rlinfo != NULL)
75✔
183
    return;
184

185
  rlinfo = (rasterLayerInfo *)msSmallCalloc(1, sizeof(rasterLayerInfo));
75✔
186
  layer->layerinfo = rlinfo;
75✔
187

188
  rlinfo->band_count = -1;
75✔
189
  rlinfo->raster_query_mode = RQM_ENTRY_PER_PIXEL;
75✔
190
  rlinfo->range_mode = -1; /* inactive */
75✔
191
  rlinfo->refcount = 0;
75✔
192
  rlinfo->shape_tolerance = 0.0;
75✔
193

194
  /* We need to do this or the layer->layerinfo will be interpreted */
195
  /* as shapefile access info because the default connectiontype is */
196
  /* MS_SHAPEFILE. */
197
  if (layer->connectiontype != MS_WMS &&
75✔
198
      layer->connectiontype != MS_KERNELDENSITY)
199
    layer->connectiontype = MS_RASTER;
45✔
200

201
  rlinfo->query_result_hard_max = 1000000;
75✔
202

203
  if (CSLFetchNameValue(layer->processing, "RASTER_QUERY_MAX_RESULT") != NULL) {
75✔
204
    rlinfo->query_result_hard_max =
×
205
        atoi(CSLFetchNameValue(layer->processing, "RASTER_QUERY_MAX_RESULT"));
×
206
  }
207
}
208

209
/************************************************************************/
210
/*                       msRasterQueryAddPixel()                        */
211
/************************************************************************/
212

213
static void msRasterQueryAddPixel(layerObj *layer, pointObj *location,
235✔
214
                                  pointObj *reprojectedLocation, double *values)
215

216
{
217
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
235✔
218
  int red = 0, green = 0, blue = 0, nodata = FALSE;
219
  int p_class = -1;
220

221
  if (rlinfo->query_results == rlinfo->query_result_hard_max)
235✔
222
    return;
223

224
  /* -------------------------------------------------------------------- */
225
  /*      Is this our first time in?  If so, do an initial allocation     */
226
  /*      for the data arrays suitable to our purposes.                   */
227
  /* -------------------------------------------------------------------- */
228
  if (rlinfo->query_alloc_max == 0) {
235✔
229
    rlinfo->query_alloc_max = 2;
21✔
230

231
    switch (rlinfo->raster_query_mode) {
21✔
232
    case RQM_ENTRY_PER_PIXEL:
21✔
233
      rlinfo->qc_x =
21✔
234
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
21✔
235
      rlinfo->qc_y =
21✔
236
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
21✔
237
      rlinfo->qc_x_reproj =
21✔
238
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
21✔
239
      rlinfo->qc_y_reproj =
21✔
240
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
21✔
241
      rlinfo->qc_values = (double *)msSmallCalloc(
42✔
242
          sizeof(double),
243
          ((size_t)rlinfo->query_alloc_max) * rlinfo->band_count);
21✔
244
      rlinfo->qc_red =
21✔
245
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
21✔
246
      rlinfo->qc_green =
21✔
247
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
21✔
248
      rlinfo->qc_blue =
21✔
249
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
21✔
250
      if (layer->numclasses > 0)
21✔
251
        rlinfo->qc_class =
2✔
252
            (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
2✔
253
      break;
254

255
    case RQM_HIST_ON_CLASS:
256
      break;
257

258
    case RQM_HIST_ON_VALUE:
259
      break;
260

261
    default:
214✔
262
      assert(FALSE);
263
    }
264
  }
265

266
  /* -------------------------------------------------------------------- */
267
  /*      Reallocate the data arrays larger if they are near the max      */
268
  /*      now.                                                            */
269
  /* -------------------------------------------------------------------- */
270
  if (rlinfo->query_results == rlinfo->query_alloc_max) {
235✔
271
    rlinfo->query_alloc_max = rlinfo->query_alloc_max * 2 + 100;
6✔
272

273
    if (rlinfo->qc_x != NULL)
6✔
274
      rlinfo->qc_x = msSmallRealloc(rlinfo->qc_x,
6✔
275
                                    sizeof(double) * rlinfo->query_alloc_max);
6✔
276
    if (rlinfo->qc_y != NULL)
6✔
277
      rlinfo->qc_y = msSmallRealloc(rlinfo->qc_y,
6✔
278
                                    sizeof(double) * rlinfo->query_alloc_max);
6✔
279
    if (rlinfo->qc_x_reproj != NULL)
6✔
280
      rlinfo->qc_x_reproj = msSmallRealloc(
6✔
281
          rlinfo->qc_x_reproj, sizeof(double) * rlinfo->query_alloc_max);
6✔
282
    if (rlinfo->qc_y_reproj != NULL)
6✔
283
      rlinfo->qc_y_reproj = msSmallRealloc(
6✔
284
          rlinfo->qc_y_reproj, sizeof(double) * rlinfo->query_alloc_max);
6✔
285
    if (rlinfo->qc_values != NULL)
6✔
286
      rlinfo->qc_values = msSmallRealloc(
6✔
287
          rlinfo->qc_values,
288
          sizeof(double) * rlinfo->query_alloc_max * rlinfo->band_count);
6✔
289
    if (rlinfo->qc_class != NULL)
6✔
290
      rlinfo->qc_class = msSmallRealloc(rlinfo->qc_class,
×
291
                                        sizeof(int) * rlinfo->query_alloc_max);
×
292
    if (rlinfo->qc_red != NULL)
6✔
293
      rlinfo->qc_red =
6✔
294
          msSmallRealloc(rlinfo->qc_red, sizeof(int) * rlinfo->query_alloc_max);
6✔
295
    if (rlinfo->qc_green != NULL)
6✔
296
      rlinfo->qc_green = msSmallRealloc(rlinfo->qc_green,
6✔
297
                                        sizeof(int) * rlinfo->query_alloc_max);
6✔
298
    if (rlinfo->qc_blue != NULL)
6✔
299
      rlinfo->qc_blue = msSmallRealloc(rlinfo->qc_blue,
6✔
300
                                       sizeof(int) * rlinfo->query_alloc_max);
6✔
301
    if (rlinfo->qc_count != NULL)
6✔
302
      rlinfo->qc_count = msSmallRealloc(rlinfo->qc_count,
×
303
                                        sizeof(int) * rlinfo->query_alloc_max);
×
304
    if (rlinfo->qc_tileindex != NULL)
6✔
305
      rlinfo->qc_tileindex = msSmallRealloc(
×
306
          rlinfo->qc_tileindex, sizeof(int) * rlinfo->query_alloc_max);
×
307
  }
308

309
  /* -------------------------------------------------------------------- */
310
  /*      Handle colormap                                                 */
311
  /* -------------------------------------------------------------------- */
312
  if (rlinfo->hCT != NULL) {
235✔
313
    int pct_index = (int)floor(values[0]);
2✔
314
    GDALColorEntry sEntry;
315

316
    if (GDALGetColorEntryAsRGB(rlinfo->hCT, pct_index, &sEntry)) {
2✔
317
      red = sEntry.c1;
2✔
318
      green = sEntry.c2;
2✔
319
      blue = sEntry.c3;
2✔
320

321
      if (sEntry.c4 == 0)
2✔
322
        nodata = TRUE;
323
    } else
324
      nodata = TRUE;
325
  }
326

327
  /* -------------------------------------------------------------------- */
328
  /*      Color derived from greyscale value.                             */
329
  /* -------------------------------------------------------------------- */
330
  else {
331
    if (rlinfo->band_count >= 3) {
233✔
332
      red = (int)MS_MAX(0, MS_MIN(255, values[0]));
8✔
333
      green = (int)MS_MAX(0, MS_MIN(255, values[1]));
8✔
334
      blue = (int)MS_MAX(0, MS_MIN(255, values[2]));
8✔
335
    } else {
336
      red = green = blue = (int)MS_MAX(0, MS_MIN(255, values[0]));
225✔
337
    }
338
  }
339

340
  /* -------------------------------------------------------------------- */
341
  /*      Handle classification.                                          */
342
  /*                                                                      */
343
  /*      NOTE: The following is really quite inadequate to deal with     */
344
  /*      classifications based on [red], [green] and [blue] as           */
345
  /*      described in:                                                   */
346
  /*       http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=1021         */
347
  /* -------------------------------------------------------------------- */
348
  if (rlinfo->qc_class != NULL) {
235✔
349
    p_class = msGetClass_FloatRGB(layer, values[0], red, green, blue);
2✔
350

351
    if (p_class == -1)
2✔
352
      nodata = TRUE;
353
    else {
354
      nodata = FALSE;
355
      rlinfo->qc_class[rlinfo->query_results] = p_class;
2✔
356
      if (layer->class[p_class] -> numstyles > 0) {
2✔
357
        red = layer->class[p_class]->styles[0]->color.red;
2✔
358
        green = layer->class[p_class]->styles[0]->color.green;
2✔
359
        blue = layer->class[p_class]->styles[0]->color.blue;
2✔
360
      } else {
361
        red = green = blue = 0;
362
      }
363
    }
364
  }
365

366
  /* -------------------------------------------------------------------- */
367
  /*      Record the color.                                               */
368
  /* -------------------------------------------------------------------- */
369
  rlinfo->qc_red[rlinfo->query_results] = red;
235✔
370
  rlinfo->qc_green[rlinfo->query_results] = green;
235✔
371
  rlinfo->qc_blue[rlinfo->query_results] = blue;
235✔
372

373
  /* -------------------------------------------------------------------- */
374
  /*      Record spatial location.                                        */
375
  /* -------------------------------------------------------------------- */
376
  if (rlinfo->qc_x != NULL) {
235✔
377
    rlinfo->qc_x[rlinfo->query_results] = location->x;
235✔
378
    rlinfo->qc_y[rlinfo->query_results] = location->y;
235✔
379
    rlinfo->qc_x_reproj[rlinfo->query_results] = reprojectedLocation->x;
235✔
380
    rlinfo->qc_y_reproj[rlinfo->query_results] = reprojectedLocation->y;
235✔
381
  }
382

383
  /* -------------------------------------------------------------------- */
384
  /*      Record actual pixel value(s).                                   */
385
  /* -------------------------------------------------------------------- */
386
  if (rlinfo->qc_values != NULL)
235✔
387
    memcpy(rlinfo->qc_values + rlinfo->query_results * rlinfo->band_count,
235✔
388
           values, sizeof(double) * rlinfo->band_count);
235✔
389

390
  /* -------------------------------------------------------------------- */
391
  /*      Add to the results cache.                                       */
392
  /* -------------------------------------------------------------------- */
393
  if (!nodata) {
235✔
394
    addResult(layer->resultcache, p_class, rlinfo->query_results, 0);
235✔
395
    rlinfo->query_results++;
235✔
396
  }
397
}
398

399
/************************************************************************/
400
/*                       msRasterQueryByRectLow()                       */
401
/************************************************************************/
402

403
static int msRasterQueryByRectLow(mapObj *map, layerObj *layer,
32✔
404
                                  GDALDatasetH hDS, rectObj queryRect)
405

406
{
407
  double adfGeoTransform[6], adfInvGeoTransform[6];
408
  double dfXMin, dfYMin, dfXMax, dfYMax, dfX, dfY, dfAdjustedRange;
409
  int nWinXOff, nWinYOff, nWinXSize, nWinYSize;
410
  int nRXSize, nRYSize;
411
  double *pafRaster;
412
  int nBandCount, *panBandMap, iPixel, iLine;
413
  CPLErr eErr;
414
  rasterLayerInfo *rlinfo;
415
  rectObj searchrect;
416

417
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
32✔
418

419
  /* -------------------------------------------------------------------- */
420
  /*      Reproject the search rect into the projection of this           */
421
  /*      layer/file if needed.                                           */
422
  /* -------------------------------------------------------------------- */
423
  searchrect = queryRect;
32✔
424
  layer->project =
32✔
425
      msProjectionsDiffer(&(layer->projection), &(map->projection));
32✔
426
  if (layer->project)
32✔
427
    msProjectRect(&(map->projection), &(layer->projection), &searchrect);
12✔
428

429
  /* -------------------------------------------------------------------- */
430
  /*      Transform the rectangle in target ground coordinates to         */
431
  /*      pixel/line extents on the file.  Process all 4 corners, to      */
432
  /*      build extents.                                                  */
433
  /* -------------------------------------------------------------------- */
434
  nRXSize = GDALGetRasterXSize(hDS);
32✔
435
  nRYSize = GDALGetRasterYSize(hDS);
32✔
436

437
  msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform);
32✔
438
  InvGeoTransform(adfGeoTransform, adfInvGeoTransform);
32✔
439

440
  /* top left */
441
  dfXMin = dfXMax =
32✔
442
      GEO_TRANS(adfInvGeoTransform, searchrect.minx, searchrect.maxy);
32✔
443
  dfYMin = dfYMax =
32✔
444
      GEO_TRANS(adfInvGeoTransform + 3, searchrect.minx, searchrect.maxy);
32✔
445

446
  /* top right */
447
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.maxx, searchrect.maxy);
32✔
448
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.maxx, searchrect.maxy);
32✔
449
  dfXMin = MS_MIN(dfXMin, dfX);
32✔
450
  dfXMax = MS_MAX(dfXMax, dfX);
32✔
451
  dfYMin = MS_MIN(dfYMin, dfY);
32✔
452
  dfYMax = MS_MAX(dfYMax, dfY);
32✔
453

454
  /* bottom left */
455
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.minx, searchrect.miny);
32✔
456
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.minx, searchrect.miny);
32✔
457
  dfXMin = MS_MIN(dfXMin, dfX);
32✔
458
  dfXMax = MS_MAX(dfXMax, dfX);
32✔
459
  dfYMin = MS_MIN(dfYMin, dfY);
32✔
460
  dfYMax = MS_MAX(dfYMax, dfY);
32✔
461

462
  /* bottom right */
463
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.maxx, searchrect.miny);
32✔
464
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.maxx, searchrect.miny);
32✔
465
  dfXMin = MS_MIN(dfXMin, dfX);
32✔
466
  dfXMax = MS_MAX(dfXMax, dfX);
32✔
467
  dfYMin = MS_MIN(dfYMin, dfY);
32✔
468
  dfYMax = MS_MAX(dfYMax, dfY);
32✔
469

470
  /* -------------------------------------------------------------------- */
471
  /*      Trim the rectangle to the area of the file itself, but out      */
472
  /*      to the edges of the touched edge pixels.                        */
473
  /* -------------------------------------------------------------------- */
474
  dfXMin = MS_MAX(0.0, MS_MIN(nRXSize, floor(dfXMin)));
32✔
475
  dfYMin = MS_MAX(0.0, MS_MIN(nRYSize, floor(dfYMin)));
32✔
476
  dfXMax = MS_MAX(0.0, MS_MIN(nRXSize, ceil(dfXMax)));
32✔
477
  dfYMax = MS_MAX(0.0, MS_MIN(nRYSize, ceil(dfYMax)));
32✔
478

479
  /* -------------------------------------------------------------------- */
480
  /*      Convert to integer offset/size values.                          */
481
  /* -------------------------------------------------------------------- */
482
  nWinXOff = (int)dfXMin;
32✔
483
  nWinYOff = (int)dfYMin;
32✔
484
  nWinXSize = (int)(dfXMax - dfXMin);
32✔
485
  nWinYSize = (int)(dfYMax - dfYMin);
32✔
486

487
  /* -------------------------------------------------------------------- */
488
  /*      What bands are we operating on?                                 */
489
  /* -------------------------------------------------------------------- */
490
  panBandMap = msGetGDALBandList(layer, hDS, 0, &nBandCount);
32✔
491

492
  if (rlinfo->band_count == -1)
32✔
493
    rlinfo->band_count = nBandCount;
22✔
494

495
  if (nBandCount != rlinfo->band_count) {
32✔
496
    msSetError(MS_IMGERR, "Got %d bands, but expected %d bands.",
×
497
               "msRasterQueryByRectLow()", nBandCount, rlinfo->band_count);
498

499
    return -1;
×
500
  }
501

502
  /* -------------------------------------------------------------------- */
503
  /*      Try to load the raster data.  For now we just load the first    */
504
  /*      band in the file.  Later we will deal with the various band     */
505
  /*      selection criteria.                                             */
506
  /* -------------------------------------------------------------------- */
507

508
  size_t nPixels = (size_t)nWinXSize * nWinYSize * nBandCount;
32✔
509
  pafRaster = (double *)calloc(nPixels, sizeof(double));
32✔
510
  MS_CHECK_ALLOC(pafRaster, sizeof(double) * nPixels, -1);
32✔
511

512
  // read raster block as GDT_Float64
513
  eErr = GDALDatasetRasterIO(
32✔
514
      hDS, GF_Read, nWinXOff, nWinYOff, nWinXSize, nWinYSize, pafRaster,
515
      nWinXSize, nWinYSize, GDT_Float64, nBandCount, panBandMap,
516
      sizeof(double) * nBandCount, sizeof(double) * nBandCount * nWinXSize,
517
      sizeof(double));
518

519
  // store the datatype of the original raster to use for output formatting
520
  rlinfo->eDataType = GDALGetRasterDataType(GDALGetRasterBand(hDS, 1));
32✔
521

522
  if (eErr != CE_None) {
32✔
523
    msSetError(MS_IOERR, "GDALDatasetRasterIO() failed: %s",
×
524
               "msRasterQueryByRectLow()", CPLGetLastErrorMsg());
525

526
    free(pafRaster);
×
527
    return -1;
×
528
  }
529

530
  /* -------------------------------------------------------------------- */
531
  /*      Fetch color table for interpreting colors if needed.             */
532
  /* -------------------------------------------------------------------- */
533
  rlinfo->hCT = GDALGetRasterColorTable(GDALGetRasterBand(hDS, panBandMap[0]));
32✔
534

535
  free(panBandMap);
32✔
536

537
  /* -------------------------------------------------------------------- */
538
  /*      When computing whether pixels are within range we do it         */
539
  /*      based on the center of the pixel to the target point but        */
540
  /*      really it ought to be the nearest point on the pixel.  It       */
541
  /*      would be too much trouble to do this rigerously, so we just     */
542
  /*      add a fudge factor so that a range of zero will find the        */
543
  /*      pixel the target falls in at least.                             */
544
  /* -------------------------------------------------------------------- */
545
  dfAdjustedRange = sqrt(adfGeoTransform[1] * adfGeoTransform[1] +
32✔
546
                         adfGeoTransform[2] * adfGeoTransform[2] +
32✔
547
                         adfGeoTransform[4] * adfGeoTransform[4] +
32✔
548
                         adfGeoTransform[5] * adfGeoTransform[5]) *
32✔
549
                        0.5 * 1.41421356237 +
32✔
550
                    sqrt(rlinfo->range_dist);
32✔
551
  dfAdjustedRange = dfAdjustedRange * dfAdjustedRange;
32✔
552

553
  reprojectionObj *reprojector = NULL;
554
  if (layer->project) {
32✔
555
    reprojector =
556
        msProjectCreateReprojector(&(layer->projection), &(map->projection));
12✔
557
  }
558

559
  /* -------------------------------------------------------------------- */
560
  /*      Loop over all pixels determining which are "in".                */
561
  /* -------------------------------------------------------------------- */
562
  for (iLine = 0; iLine < nWinYSize; iLine++) {
140✔
563
    for (iPixel = 0; iPixel < nWinXSize; iPixel++) {
500✔
564
      pointObj sPixelLocation = {0};
413✔
565

566
      if (rlinfo->query_results == rlinfo->query_result_hard_max)
413✔
567
        break;
568

569
      /* transform pixel/line to georeferenced */
570
      sPixelLocation.x = GEO_TRANS(adfGeoTransform, iPixel + nWinXOff + 0.5,
392✔
571
                                   iLine + nWinYOff + 0.5);
572
      sPixelLocation.y = GEO_TRANS(adfGeoTransform + 3, iPixel + nWinXOff + 0.5,
392✔
573
                                   iLine + nWinYOff + 0.5);
574

575
      /* If projections differ, convert this back into the map  */
576
      /* projection for distance testing, and comprison to the  */
577
      /* search shape.  Save the original pixel location coordinates */
578
      /* in sPixelLocationInLayerSRS, so that we can return those */
579
      /* coordinates if we have a hit */
580
      pointObj sReprojectedPixelLocation = sPixelLocation;
392✔
581
      if (reprojector) {
392✔
582
        msProjectPointEx(reprojector, &sReprojectedPixelLocation);
26✔
583
      }
584

585
      /* If we are doing QueryByShape, check against the shape now */
586
      if (rlinfo->searchshape != NULL) {
392✔
587
        if (rlinfo->shape_tolerance == 0.0 &&
244✔
588
            rlinfo->searchshape->type == MS_SHAPE_POLYGON) {
100✔
589
          if (msIntersectPointPolygon(&sReprojectedPixelLocation,
100✔
590
                                      rlinfo->searchshape) == MS_FALSE)
591
            continue;
157✔
592
        } else {
593
          shapeObj tempShape;
594
          lineObj tempLine;
595

596
          memset(&tempShape, 0, sizeof(shapeObj));
597
          tempShape.type = MS_SHAPE_POINT;
598
          tempShape.numlines = 1;
144✔
599
          tempShape.line = &tempLine;
144✔
600
          tempLine.numpoints = 1;
144✔
601
          tempLine.point = &sReprojectedPixelLocation;
144✔
602

603
          if (msDistanceShapeToShape(rlinfo->searchshape, &tempShape) >
144✔
604
              rlinfo->shape_tolerance)
144✔
605
            continue;
97✔
606
        }
607
      }
608

609
      if (rlinfo->range_mode >= 0) {
250✔
610
        double dist;
611

612
        dist = (rlinfo->target_point.x - sReprojectedPixelLocation.x) *
48✔
613
                   (rlinfo->target_point.x - sReprojectedPixelLocation.x) +
614
               (rlinfo->target_point.y - sReprojectedPixelLocation.y) *
48✔
615
                   (rlinfo->target_point.y - sReprojectedPixelLocation.y);
616

617
        if (dist >= dfAdjustedRange)
48✔
618
          continue;
15✔
619

620
        /* If we can only have one feature, trim range and clear */
621
        /* previous result.  */
622
        if (rlinfo->range_mode == MS_QUERY_SINGLE) {
33✔
623
          rlinfo->range_dist = dist;
14✔
624
          rlinfo->query_results = 0;
14✔
625
        }
626
      }
627

628
      msRasterQueryAddPixel(layer,
235✔
629
                            &sPixelLocation, // return coords in layer SRS
630
                            &sReprojectedPixelLocation,
631
                            pafRaster +
235✔
632
                                (iLine * nWinXSize + iPixel) * nBandCount);
235✔
633
    }
634
  }
635

636
  /* -------------------------------------------------------------------- */
637
  /*      Cleanup.                                                        */
638
  /* -------------------------------------------------------------------- */
639
  free(pafRaster);
32✔
640
  msProjectDestroyReprojector(reprojector);
32✔
641

642
  return MS_SUCCESS;
32✔
643
}
644

645
/************************************************************************/
646
/*                        msRasterQueryByRect()                         */
647
/************************************************************************/
648

649
int msRasterQueryByRect(mapObj *map, layerObj *layer, rectObj queryRect)
23✔
650

651
{
652
  int status = MS_SUCCESS;
653
  char *filename = NULL;
654

655
  layerObj *tlp = NULL; /* pointer to the tile layer either real or temporary */
23✔
656
  int tileitemindex = -1, tilelayerindex = -1, tilesrsindex = -1;
23✔
657
  shapeObj tshp;
658
  char tilename[MS_PATH_LENGTH], tilesrsname[1024];
659
  int done, destroy_on_failure;
660

661
  char szPath[MS_MAXPATHLEN];
662
  rectObj searchrect;
663
  rasterLayerInfo *rlinfo = NULL;
664

665
  /* -------------------------------------------------------------------- */
666
  /*      Get the layer info.                                             */
667
  /* -------------------------------------------------------------------- */
668
  if (!layer->layerinfo) {
23✔
669
    msRasterLayerInfoInitialize(layer);
1✔
670
    destroy_on_failure = 1;
671
  } else {
672
    destroy_on_failure = 0;
673
  }
674
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
23✔
675

676
  /* -------------------------------------------------------------------- */
677
  /*      Clear old results cache.                                        */
678
  /* -------------------------------------------------------------------- */
679
  if (layer->resultcache) {
23✔
680
    if (layer->resultcache->results)
1✔
681
      free(layer->resultcache->results);
×
682
    free(layer->resultcache);
1✔
683
    layer->resultcache = NULL;
1✔
684
  }
685

686
  /* -------------------------------------------------------------------- */
687
  /*      Initialize the results cache.                                   */
688
  /* -------------------------------------------------------------------- */
689
  layer->resultcache = (resultCacheObj *)msSmallMalloc(sizeof(resultCacheObj));
23✔
690
  layer->resultcache->results = NULL;
23✔
691
  layer->resultcache->numresults = layer->resultcache->cachesize = 0;
23✔
692
  layer->resultcache->bounds.minx = layer->resultcache->bounds.miny =
23✔
693
      layer->resultcache->bounds.maxx = layer->resultcache->bounds.maxy = -1;
23✔
694

695
  /* -------------------------------------------------------------------- */
696
  /*      Check if we should really be acting on this layer and           */
697
  /*      provide debug info in various cases.                            */
698
  /* -------------------------------------------------------------------- */
699
  if (layer->debug > 0 || map->debug > 1)
23✔
700
    msDebug("msRasterQueryByRect(%s): entering.\n", layer->name);
×
701

702
  if (!layer->data && !layer->tileindex) {
23✔
703
    if (layer->debug > 0 || map->debug > 0)
×
704
      msDebug("msRasterQueryByRect(%s): layer data and tileindex NULL ... "
×
705
              "doing nothing.",
706
              layer->name);
707
    return MS_SUCCESS;
×
708
  }
709

710
  if ((layer->status != MS_ON) && layer->status != MS_DEFAULT) {
23✔
711
    if (layer->debug > 0)
×
712
      msDebug(
×
713
          "msRasterQueryByRect(%s): not status ON or DEFAULT, doing nothing.",
714
          layer->name);
715
    return MS_SUCCESS;
×
716
  }
717

718
  /* ==================================================================== */
719
  /*      Handle setting up tileindex layer.                              */
720
  /* ==================================================================== */
721
  if (layer->tileindex) { /* we have an index file */
23✔
722
    msInitShape(&tshp);
8✔
723
    searchrect = queryRect;
8✔
724

725
    status = msDrawRasterSetupTileLayer(map, layer, &searchrect, MS_TRUE,
8✔
726
                                        &tilelayerindex, &tileitemindex,
727
                                        &tilesrsindex, &tlp);
728
    if (status != MS_SUCCESS) {
8✔
729
      goto cleanup;
×
730
    }
731
  } else {
732
    /* we have to manually apply to scaletoken logic as we're not going through
733
     * msLayerOpen() */
734
    status = msLayerApplyScaletokens(layer, map->scaledenom);
15✔
735
    if (status != MS_SUCCESS) {
15✔
736
      goto cleanup;
×
737
    }
738
  }
739

740
  /* -------------------------------------------------------------------- */
741
  /*      Iterate over all tiles (just one in untiled case).              */
742
  /* -------------------------------------------------------------------- */
743
  done = MS_FALSE;
744
  while (done == MS_FALSE && status == MS_SUCCESS) {
55✔
745

746
    GDALDatasetH hDS;
747
    char *decrypted_path = NULL;
748

749
    /* -------------------------------------------------------------------- */
750
    /*      Get filename.                                                   */
751
    /* -------------------------------------------------------------------- */
752
    if (layer->tileindex) {
40✔
753
      status = msDrawRasterIterateTileIndex(
25✔
754
          layer, tlp, &tshp, tileitemindex, tilesrsindex, tilename,
755
          sizeof(tilename), tilesrsname, sizeof(tilesrsname));
756
      if (status == MS_FAILURE) {
25✔
757
        break;
758
      }
759

760
      if (status == MS_DONE)
25✔
761
        break; /* no more tiles/images */
762
      filename = tilename;
763
    } else {
764
      filename = layer->data;
15✔
765
      done = MS_TRUE; /* only one image so we're done after this */
766
    }
767

768
    if (strlen(filename) == 0)
32✔
769
      continue;
×
770

771
    /* -------------------------------------------------------------------- */
772
    /*      Open the file.                                                  */
773
    /* -------------------------------------------------------------------- */
774
    msGDALInitialize();
32✔
775

776
    msDrawRasterBuildRasterPath(map, layer, filename, szPath);
32✔
777

778
    decrypted_path = msDecryptStringTokens(map, szPath);
32✔
779
    if (!decrypted_path) {
32✔
780
      status = MS_FAILURE;
781
      goto cleanup;
×
782
    }
783

784
    msAcquireLock(TLOCK_GDAL);
32✔
785
    if (!layer->tileindex) {
32✔
786
      char **connectionoptions =
787
          msGetStringListFromHashTable(&(layer->connectionoptions));
15✔
788
      hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
15✔
789
                       (const char *const *)connectionoptions, NULL);
790
      CSLDestroy(connectionoptions);
15✔
791
    } else {
792
      hDS = GDALOpen(decrypted_path, GA_ReadOnly);
17✔
793
    }
794

795
    if (hDS == NULL) {
32✔
796
      int ignore_missing = msMapIgnoreMissingData(map);
×
797
      const char *cpl_error_msg =
798
          msDrawRasterGetCPLErrorMsg(decrypted_path, szPath);
×
799

800
      msFree(decrypted_path);
×
801
      decrypted_path = NULL;
802

803
      msReleaseLock(TLOCK_GDAL);
×
804

805
      if (ignore_missing == MS_MISSING_DATA_FAIL) {
×
806
        if (layer->debug || map->debug)
×
807
          msSetError(MS_IMGERR,
×
808
                     "Unable to open file %s for layer %s ... fatal error.\n%s",
809
                     "msRasterQueryByRect()", szPath, layer->name,
810
                     cpl_error_msg);
811
        status = MS_FAILURE;
812
        goto cleanup;
×
813
      }
814
      if (ignore_missing == MS_MISSING_DATA_LOG) {
×
815
        if (layer->debug || map->debug)
×
816
          msDebug("Unable to open file %s for layer %s ... ignoring this "
×
817
                  "missing data.\n%s",
818
                  filename, layer->name, cpl_error_msg);
819
      }
820
      continue;
×
821
    }
822

823
    msFree(decrypted_path);
32✔
824
    decrypted_path = NULL;
825

826
    if (msDrawRasterLoadProjection(layer, hDS, filename, tilesrsindex,
32✔
827
                                   tilesrsname) != MS_SUCCESS) {
828
      msReleaseLock(TLOCK_GDAL);
×
829
      status = MS_FAILURE;
830
      goto cleanup;
×
831
    }
832

833
    /* -------------------------------------------------------------------- */
834
    /*      Perform actual query on this file.                              */
835
    /* -------------------------------------------------------------------- */
836
    if (status == MS_SUCCESS)
32✔
837
      status = msRasterQueryByRectLow(map, layer, hDS, queryRect);
32✔
838

839
    GDALClose(hDS);
32✔
840
    msReleaseLock(TLOCK_GDAL);
32✔
841

842
  } /* next tile */
843

844
  /* -------------------------------------------------------------------- */
845
  /*      Cleanup tileindex if it is open.                                */
846
  /* -------------------------------------------------------------------- */
847
cleanup:
23✔
848
  if (layer->tileindex) { /* tiling clean-up */
23✔
849
    msDrawRasterCleanupTileLayer(tlp, tilelayerindex);
8✔
850
  } else {
851
    msLayerRestoreFromScaletokens(layer);
15✔
852
  }
853

854
  /* -------------------------------------------------------------------- */
855
  /*      On failure, or empty result set, cleanup the rlinfo since we    */
856
  /*      likely won't ever have it accessed or cleaned up later.         */
857
  /* -------------------------------------------------------------------- */
858
  if (status == MS_FAILURE || rlinfo->query_results == 0) {
23✔
859
    if (destroy_on_failure) {
2✔
860
      msRasterLayerInfoFree(layer);
×
861
    }
862
  } else {
863
    /* populate the items/numitems layer-level values */
864
    msRASTERLayerGetItems(layer);
21✔
865
  }
866

867
  return status;
868
}
869

870
/************************************************************************/
871
/*                        msRasterQueryByShape()                        */
872
/************************************************************************/
873

874
int msRasterQueryByShape(mapObj *map, layerObj *layer, shapeObj *selectshape)
2✔
875

876
{
877
  rasterLayerInfo *rlinfo = NULL;
878
  int status;
879
  double tolerance;
880
  rectObj searchrect;
881

882
  msRasterLayerInfoInitialize(layer);
2✔
883

884
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
2✔
885

886
  /* If the selection shape is point or line we use the default tolerance of
887
     3, but for polygons we require an exact hit. */
888
  if (layer->tolerance == -1) {
2✔
889
    if (selectshape->type == MS_SHAPE_POINT ||
2✔
890
        selectshape->type == MS_SHAPE_LINE)
891
      tolerance = 3;
892
    else
893
      tolerance = 0;
894
  } else
895
    tolerance = layer->tolerance;
896

897
  if (layer->toleranceunits == MS_PIXELS)
2✔
898
    tolerance =
2✔
899
        tolerance * msAdjustExtent(&(map->extent), map->width, map->height);
2✔
900
  else
901
    tolerance = tolerance * (msInchesPerUnit(layer->toleranceunits, 0) /
×
902
                             msInchesPerUnit(map->units, 0));
×
903

904
  rlinfo->searchshape = selectshape;
2✔
905
  rlinfo->shape_tolerance = tolerance;
2✔
906

907
  msComputeBounds(selectshape);
2✔
908
  searchrect = selectshape->bounds;
2✔
909

910
  searchrect.minx -= tolerance; /* expand the search box to account for layer
2✔
911
                                   tolerances (e.g. buffered searches) */
912
  searchrect.maxx += tolerance;
2✔
913
  searchrect.miny -= tolerance;
2✔
914
  searchrect.maxy += tolerance;
2✔
915

916
  status = msRasterQueryByRect(map, layer, searchrect);
2✔
917

918
  rlinfo->searchshape = NULL;
2✔
919

920
  return status;
2✔
921
}
922

923
/************************************************************************/
924
/*                        msRasterQueryByPoint()                        */
925
/************************************************************************/
926

927
int msRasterQueryByPoint(mapObj *map, layerObj *layer, int mode, pointObj p,
19✔
928
                         double buffer, int maxresults) {
929
  int result;
930
  double layer_tolerance;
931
  rectObj bufferRect;
932
  rasterLayerInfo *rlinfo = NULL;
933

934
  msRasterLayerInfoInitialize(layer);
19✔
935

936
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
19✔
937

938
  /* -------------------------------------------------------------------- */
939
  /*      If the buffer is not set, then use layer tolerances             */
940
  /*      instead.   The "buffer" distince is now in georeferenced        */
941
  /*      units.  Note that tolerances in pixels are basically map        */
942
  /*      display pixels, not underlying raster pixels.  It isn't         */
943
  /*      clear that there is any way of requesting a buffer size in      */
944
  /*      underlying pixels.                                              */
945
  /* -------------------------------------------------------------------- */
946
  if (buffer <= 0) { /* use layer tolerance */
19✔
947
    if (layer->tolerance == -1)
12✔
948
      layer_tolerance = 3;
949
    else
950
      layer_tolerance = layer->tolerance;
951

952
    if (layer->toleranceunits == MS_PIXELS)
12✔
953
      buffer = layer_tolerance *
12✔
954
               msAdjustExtent(&(map->extent), map->width, map->height);
12✔
955
    else
956
      buffer = layer_tolerance * (msInchesPerUnit(layer->toleranceunits, 0) /
×
957
                                  msInchesPerUnit(map->units, 0));
×
958
  }
959

960
  /* -------------------------------------------------------------------- */
961
  /*      Setup target point information, at this point they are in       */
962
  /*      map coordinates.                                                */
963
  /* -------------------------------------------------------------------- */
964
  rlinfo->range_dist = buffer * buffer;
19✔
965
  rlinfo->target_point = p;
19✔
966

967
  /* -------------------------------------------------------------------- */
968
  /*      if we are in the MS_QUERY_SINGLE mode, first try a query with   */
969
  /*      zero tolerance.  If this gets a raster pixel then we can be     */
970
  /*      reasonably assured that it is the closest to the query          */
971
  /*      point.  This will potentially be must more efficient than       */
972
  /*      processing all pixels within the tolerance.                     */
973
  /* -------------------------------------------------------------------- */
974
  if (mode == MS_QUERY_SINGLE) {
19✔
975
    rectObj pointRect;
976

977
    pointRect.minx = p.x;
15✔
978
    pointRect.maxx = p.x;
15✔
979
    pointRect.miny = p.y;
15✔
980
    pointRect.maxy = p.y;
15✔
981

982
    rlinfo->range_mode = MS_QUERY_SINGLE;
15✔
983
    result = msRasterQueryByRect(map, layer, pointRect);
15✔
984
    if (rlinfo->query_results > 0)
15✔
985
      return result;
14✔
986
  }
987

988
  /* -------------------------------------------------------------------- */
989
  /*      Setup a rectangle that is everything within the designated      */
990
  /*      range and do a search against that.                             */
991
  /* -------------------------------------------------------------------- */
992
  bufferRect.minx = p.x - buffer;
5✔
993
  bufferRect.maxx = p.x + buffer;
5✔
994
  bufferRect.miny = p.y - buffer;
5✔
995
  bufferRect.maxy = p.y + buffer;
5✔
996

997
  rlinfo->range_mode = mode;
5✔
998

999
  if (maxresults != 0) {
5✔
1000
    const int previous_maxresults = rlinfo->query_result_hard_max;
2✔
1001
    rlinfo->query_result_hard_max = maxresults;
2✔
1002
    result = msRasterQueryByRect(map, layer, bufferRect);
2✔
1003
    rlinfo->query_result_hard_max = previous_maxresults;
2✔
1004
  } else {
1005
    result = msRasterQueryByRect(map, layer, bufferRect);
3✔
1006
  }
1007

1008
  return result;
1009
}
1010

1011
/************************************************************************/
1012
/* ==================================================================== */
1013
/*                          RASTER Query Layer                          */
1014
/*                                                                      */
1015
/*      The results of a raster query are treated as a set of shapes    */
1016
/*      that can be accessed using the normal layer semantics.          */
1017
/* ==================================================================== */
1018
/************************************************************************/
1019

1020
/************************************************************************/
1021
/*                         msRASTERLayerOpen()                          */
1022
/************************************************************************/
1023

1024
int msRASTERLayerOpen(layerObj *layer) {
63✔
1025
  rasterLayerInfo *rlinfo;
1026

1027
  /* If we don't have info, initialize an empty one now */
1028
  if (layer->layerinfo == NULL)
63✔
1029
    msRasterLayerInfoInitialize(layer);
53✔
1030
  if (layer->layerinfo == NULL)
63✔
1031
    return MS_FAILURE;
1032

1033
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
1034

1035
  rlinfo->refcount = rlinfo->refcount + 1;
63✔
1036

1037
  return MS_SUCCESS;
63✔
1038
}
1039

1040
/************************************************************************/
1041
/*                         msRASTERIsLayerOpen()                        */
1042
/************************************************************************/
1043

1044
int msRASTERLayerIsOpen(layerObj *layer) {
3,767✔
1045
  if (layer->layerinfo)
3,767✔
1046
    return MS_TRUE;
2,595✔
1047
  return MS_FALSE;
1048
}
1049

1050
/************************************************************************/
1051
/*                     msRASTERLayerFreeItemInfo()                      */
1052
/************************************************************************/
1053
void msRASTERLayerFreeItemInfo(layerObj *layer) { (void)layer; }
164✔
1054

1055
/************************************************************************/
1056
/*                     msRASTERLayerInitItemInfo()                      */
1057
/*                                                                      */
1058
/*      Perhaps we should be validating the requested items here?       */
1059
/************************************************************************/
1060

1061
int msRASTERLayerInitItemInfo(layerObj *layer) {
47✔
1062
  (void)layer;
1063
  return MS_SUCCESS;
47✔
1064
}
1065

1066
/************************************************************************/
1067
/*                      msRASTERLayerWhichShapes()                      */
1068
/************************************************************************/
1069
int msRASTERLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
×
1070

1071
{
1072
  (void)isQuery;
1073
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
×
1074

1075
  rlinfo->which_rect = rect;
×
1076
  rlinfo->next_shape = 0;
×
1077

1078
  return MS_SUCCESS;
×
1079
}
1080

1081
/************************************************************************/
1082
/*                         msRASTERLayerClose()                         */
1083
/************************************************************************/
1084

1085
int msRASTERLayerClose(layerObj *layer) {
138✔
1086
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
138✔
1087

1088
  if (rlinfo != NULL) {
138✔
1089
    rlinfo->refcount--;
138✔
1090

1091
    if (rlinfo->refcount < 0)
138✔
1092
      msRasterLayerInfoFree(layer);
75✔
1093
  }
1094
  return MS_SUCCESS;
138✔
1095
}
1096

1097
/************************************************************************/
1098
/*                       msRASTERLayerNextShape()                       */
1099
/************************************************************************/
1100

1101
int msRASTERLayerNextShape(layerObj *layer, shapeObj *shape) {
×
1102
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
×
1103

1104
  if (rlinfo->next_shape < 0 || rlinfo->next_shape >= rlinfo->query_results) {
×
1105
    msFreeShape(shape);
×
1106
    shape->type = MS_SHAPE_NULL;
×
1107
    return MS_DONE;
×
1108
  } else {
1109
    resultObj record;
1110

1111
    record.shapeindex = rlinfo->next_shape++;
×
1112
    record.tileindex = 0;
×
1113
    record.classindex = record.resultindex = -1;
×
1114

1115
    return msRASTERLayerGetShape(layer, shape, &record);
×
1116
  }
1117
}
1118

1119
/************************************************************************/
1120
/*                       msRASTERLayerGetShape()                        */
1121
/************************************************************************/
1122

1123
int msRASTERLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) {
33✔
1124
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
33✔
1125
  int i;
1126

1127
  long shapeindex = record->shapeindex;
33✔
1128

1129
  msFreeShape(shape);
33✔
1130
  shape->type = MS_SHAPE_NULL;
33✔
1131

1132
  /* -------------------------------------------------------------------- */
1133
  /*      Validate requested record id.                                   */
1134
  /* -------------------------------------------------------------------- */
1135
  if (shapeindex < 0 || shapeindex >= rlinfo->query_results) {
33✔
1136
    msSetError(MS_MISCERR,
×
1137
               "Out of range shape index requested.  Requested %ld\n"
1138
               "but only %d shapes available.",
1139
               "msRASTERLayerGetShape()", shapeindex, rlinfo->query_results);
1140
    return MS_FAILURE;
×
1141
  }
1142

1143
  /* -------------------------------------------------------------------- */
1144
  /*      Apply the geometry.                                             */
1145
  /* -------------------------------------------------------------------- */
1146
  if (rlinfo->qc_x != NULL) {
33✔
1147
    lineObj line;
1148
    pointObj point;
1149

1150
    shape->type = MS_SHAPE_POINT;
33✔
1151

1152
    line.numpoints = 1;
33✔
1153
    line.point = &point;
33✔
1154

1155
    point.x = rlinfo->qc_x[shapeindex];
33✔
1156
    point.y = rlinfo->qc_y[shapeindex];
33✔
1157
    point.m = 0.0;
33✔
1158

1159
    msAddLine(shape, &line);
33✔
1160
    msComputeBounds(shape);
33✔
1161
  }
1162

1163
  /* -------------------------------------------------------------------- */
1164
  /*      Apply the requested items.                                      */
1165
  /* -------------------------------------------------------------------- */
1166
  if (layer->numitems > 0) {
33✔
1167
    shape->values = (char **)msSmallMalloc(sizeof(char *) * layer->numitems);
33✔
1168
    shape->numvalues = layer->numitems;
33✔
1169

1170
    for (i = 0; i < layer->numitems; i++) {
296✔
1171
      const size_t bufferSize = 1000;
1172
      char szWork[1000];
1173

1174
      szWork[0] = '\0';
263✔
1175
      if (EQUAL(layer->items[i], "x") && rlinfo->qc_x_reproj)
263✔
1176
        snprintf(szWork, bufferSize, "%.8g", rlinfo->qc_x_reproj[shapeindex]);
33✔
1177
      else if (EQUAL(layer->items[i], "y") && rlinfo->qc_y_reproj)
230✔
1178
        snprintf(szWork, bufferSize, "%.8g", rlinfo->qc_y_reproj[shapeindex]);
33✔
1179

1180
      else if (EQUAL(layer->items[i], "value_list") && rlinfo->qc_values) {
197✔
1181
        int iValue;
1182

1183
        for (iValue = 0; iValue < rlinfo->band_count; iValue++) {
96✔
1184
          if (iValue != 0)
63✔
1185
            strlcat(szWork, ",", bufferSize);
1186

1187
          snprintf(szWork + strlen(szWork), bufferSize - strlen(szWork),
63✔
1188
                   "%.10g",
1189
                   rlinfo->qc_values[shapeindex * rlinfo->band_count + iValue]);
63✔
1190
        }
1191
      } else if (EQUALN(layer->items[i], "value_", 6) && rlinfo->qc_values) {
164✔
1192
        int iValue = atoi(layer->items[i] + 6);
63✔
1193

1194
        double pixelValue =
63✔
1195
            rlinfo->qc_values[shapeindex * rlinfo->band_count + iValue];
63✔
1196

1197
        switch (rlinfo->eDataType) {
63✔
1198
        case GDT_Byte:
62✔
1199
        case GDT_Int8:
1200
        case GDT_UInt16:
1201
        case GDT_Int16:
1202
        case GDT_Int32:
1203
          snprintf(szWork, bufferSize, "%d", (int)pixelValue);
62✔
1204
          break;
1205
        case GDT_UInt32:
×
1206
          snprintf(szWork, bufferSize, "%u", (unsigned int)pixelValue);
×
1207
          break;
1208
        case GDT_Float64:
1209
          snprintf(szWork, bufferSize, "%.12g", pixelValue);
1210
          break;
1211
        default:
1212
          // GDT_Float32
1213
          snprintf(szWork, bufferSize, "%.8g", pixelValue);
1214
          break;
1215
        }
1216

1217
      } else if (EQUAL(layer->items[i], "class") && rlinfo->qc_class) {
101✔
1218
        int p_class = rlinfo->qc_class[shapeindex];
2✔
1219
        if (layer->class[p_class] -> name != NULL)
2✔
1220
          snprintf(szWork, bufferSize, "%.999s", layer->class[p_class] -> name);
1221
        else
1222
          snprintf(szWork, bufferSize, "%d", p_class);
1223
      } else if (EQUAL(layer->items[i], "red") && rlinfo->qc_red)
99✔
1224
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_red[shapeindex]);
33✔
1225
      else if (EQUAL(layer->items[i], "green") && rlinfo->qc_green)
66✔
1226
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_green[shapeindex]);
33✔
1227
      else if (EQUAL(layer->items[i], "blue") && rlinfo->qc_blue)
33✔
1228
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_blue[shapeindex]);
33✔
1229
      else if (EQUAL(layer->items[i], "count") && rlinfo->qc_count)
×
1230
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_count[shapeindex]);
×
1231

1232
      shape->values[i] = msStrdup(szWork);
263✔
1233
    }
1234
  }
1235

1236
  /* -------------------------------------------------------------------- */
1237
  /*      Eventually we should likey apply the geometry properly but      */
1238
  /*      we don't really care about the geometry for query purposes.     */
1239
  /* -------------------------------------------------------------------- */
1240

1241
  return MS_SUCCESS;
1242
}
1243

1244
/************************************************************************/
1245
/*                       msRASTERLayerGetItems()                        */
1246
/************************************************************************/
1247

1248
int msRASTERLayerGetItems(layerObj *layer) {
47✔
1249
  int maxnumitems = 0;
1250
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
47✔
1251

1252
  if (rlinfo == NULL)
47✔
1253
    return MS_FAILURE;
1254

1255
  maxnumitems = 8 + (rlinfo->qc_values ? rlinfo->band_count : 0);
47✔
1256
  layer->items = (char **)msSmallCalloc(sizeof(char *), maxnumitems);
47✔
1257

1258
  layer->numitems = 0;
47✔
1259
  if (rlinfo->qc_x_reproj)
47✔
1260
    layer->items[layer->numitems++] = msStrdup("x");
31✔
1261
  if (rlinfo->qc_y_reproj)
47✔
1262
    layer->items[layer->numitems++] = msStrdup("y");
31✔
1263
  if (rlinfo->qc_values) {
47✔
1264
    int i;
1265
    for (i = 0; i < rlinfo->band_count; i++) {
88✔
1266
      char szName[100];
1267
      snprintf(szName, sizeof(szName), "value_%d", i);
1268
      layer->items[layer->numitems++] = msStrdup(szName);
57✔
1269
    }
1270
    layer->items[layer->numitems++] = msStrdup("value_list");
31✔
1271
  }
1272
  if (rlinfo->qc_class)
47✔
1273
    layer->items[layer->numitems++] = msStrdup("class");
4✔
1274
  if (rlinfo->qc_red)
47✔
1275
    layer->items[layer->numitems++] = msStrdup("red");
31✔
1276
  if (rlinfo->qc_green)
47✔
1277
    layer->items[layer->numitems++] = msStrdup("green");
31✔
1278
  if (rlinfo->qc_blue)
47✔
1279
    layer->items[layer->numitems++] = msStrdup("blue");
31✔
1280
  if (rlinfo->qc_count)
47✔
1281
    layer->items[layer->numitems++] = msStrdup("count");
×
1282

1283
  assert(layer->numitems <= maxnumitems);
1284

1285
  return msRASTERLayerInitItemInfo(layer);
47✔
1286
}
1287

1288
/************************************************************************/
1289
/*                       msRASTERLayerGetExtent()                       */
1290
/************************************************************************/
1291

1292
int msRASTERLayerGetExtent(layerObj *layer, rectObj *extent)
37✔
1293

1294
{
1295
  char szPath[MS_MAXPATHLEN];
1296
  mapObj *map = layer->map;
37✔
1297
  shapefileObj *tileshpfile;
1298

1299
  if ((!layer->data || strlen(layer->data) == 0) && layer->tileindex == NULL) {
37✔
1300
    /* should we be issuing a specific error about not supporting
1301
       extents for tileindexed raster layers? */
1302
    return MS_FAILURE;
1303
  }
1304

1305
  if (map == NULL)
7✔
1306
    return MS_FAILURE;
1307

1308
  /* If the layer use a tileindex, return the extent of the tileindex
1309
   * shapefile/referenced layer */
1310
  if (layer->tileindex) {
7✔
1311
    const int tilelayerindex = msGetLayerIndex(map, layer->tileindex);
2✔
1312
    if (tilelayerindex != -1) /* does the tileindex reference another layer */
2✔
1313
      return msLayerGetExtent(GET_LAYER(map, tilelayerindex), extent);
×
1314
    else {
1315
      tileshpfile = (shapefileObj *)malloc(sizeof(shapefileObj));
2✔
1316
      MS_CHECK_ALLOC(tileshpfile, sizeof(shapefileObj), MS_FAILURE);
2✔
1317

1318
      if (msShapefileOpen(tileshpfile, "rb",
2✔
1319
                          msBuildPath3(szPath, map->mappath, map->shapepath,
2✔
1320
                                       layer->tileindex),
2✔
1321
                          MS_TRUE) == -1)
1322
        if (msShapefileOpen(tileshpfile, "rb",
×
1323
                            msBuildPath(szPath, map->mappath, layer->tileindex),
×
1324
                            MS_TRUE) == -1)
1325
          return MS_FAILURE;
1326

1327
      *extent = tileshpfile->bounds;
2✔
1328
      msShapefileClose(tileshpfile);
2✔
1329
      free(tileshpfile);
2✔
1330
      return MS_SUCCESS;
2✔
1331
    }
1332
  }
1333

1334
  msTryBuildPath3(szPath, map->mappath, map->shapepath, layer->data);
5✔
1335
  char *decrypted_path = msDecryptStringTokens(map, szPath);
5✔
1336
  if (!decrypted_path)
5✔
1337
    return MS_FAILURE;
1338

1339
  char **connectionoptions =
1340
      msGetStringListFromHashTable(&(layer->connectionoptions));
5✔
1341
  GDALDatasetH hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
5✔
1342
                                (const char *const *)connectionoptions, NULL);
1343
  CSLDestroy(connectionoptions);
5✔
1344
  msFree(decrypted_path);
5✔
1345
  if (hDS == NULL) {
5✔
1346
    return MS_FAILURE;
1347
  }
1348

1349
  const int nXSize = GDALGetRasterXSize(hDS);
5✔
1350
  const int nYSize = GDALGetRasterYSize(hDS);
5✔
1351
  double adfGeoTransform[6] = {0};
5✔
1352
  const CPLErr eErr = GDALGetGeoTransform(hDS, adfGeoTransform);
5✔
1353
  if (eErr != CE_None) {
5✔
1354
    GDALClose(hDS);
×
1355
    return MS_FAILURE;
×
1356
  }
1357

1358
  /* If this appears to be an ungeoreferenced raster than flip it for
1359
     mapservers purposes. */
1360
  if (adfGeoTransform[5] == 1.0 && adfGeoTransform[3] == 0.0) {
5✔
1361
    adfGeoTransform[5] = -1.0;
×
1362
    adfGeoTransform[3] = nYSize;
×
1363
  }
1364

1365
  extent->minx = adfGeoTransform[0];
5✔
1366
  extent->maxy = adfGeoTransform[3];
5✔
1367

1368
  extent->maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1];
5✔
1369
  extent->miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5];
5✔
1370

1371
  return MS_SUCCESS;
5✔
1372
}
1373

1374
/************************************************************************/
1375
/*                     msRASTERLayerSetTimeFilter()                     */
1376
/*                                                                      */
1377
/*      This function is actually just used in the context of           */
1378
/*      setting a filter on the tileindex for time based queries.       */
1379
/*      For instance via WMS requests.  So it isn't really related      */
1380
/*      to the "raster query" support at all.                           */
1381
/*                                                                      */
1382
/*      If a local shapefile tileindex is in use, we will set a         */
1383
/*      backtics filter (shapefile compatible).  If another layer is    */
1384
/*      being used as the tileindex then we will forward the            */
1385
/*      SetTimeFilter call to it.  If there is no tileindex in          */
1386
/*      place, we do nothing.                                           */
1387
/************************************************************************/
1388

1389
int msRASTERLayerSetTimeFilter(layerObj *layer, const char *timestring,
×
1390
                               const char *timefield) {
1391
  int tilelayerindex;
1392

1393
  /* -------------------------------------------------------------------- */
1394
  /*      If we don't have a tileindex the time filter has no effect.     */
1395
  /* -------------------------------------------------------------------- */
1396
  if (layer->tileindex == NULL)
×
1397
    return MS_SUCCESS;
1398

1399
  /* -------------------------------------------------------------------- */
1400
  /*      Find the tileindex layer.                                       */
1401
  /* -------------------------------------------------------------------- */
1402
  tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
×
1403

1404
  /* -------------------------------------------------------------------- */
1405
  /*      If we are using a local shapefile as our tileindex (that is     */
1406
  /*      to say, the tileindex name is not of another layer), then we    */
1407
  /*      just install a backtics style filter on the raster layer.       */
1408
  /*      This is propagated to the "working layer" created for the       */
1409
  /*      tileindex by code in mapraster.c.                               */
1410
  /* -------------------------------------------------------------------- */
1411
  if (tilelayerindex == -1)
×
1412
    return msLayerMakeBackticsTimeFilter(layer, timestring, timefield);
×
1413

1414
  /* -------------------------------------------------------------------- */
1415
  /*      Otherwise we invoke the tileindex layers SetTimeFilter          */
1416
  /*      method.                                                         */
1417
  /* -------------------------------------------------------------------- */
1418
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
×
1419
    return MS_FAILURE;
1420
  return msLayerSetTimeFilter(layer->GET_LAYER(map, tilelayerindex), timestring,
×
1421
                              timefield);
1422
}
1423

1424
/************************************************************************/
1425
/*                msRASTERLayerInitializeVirtualTable()                 */
1426
/************************************************************************/
1427

1428
int msRASTERLayerInitializeVirtualTable(layerObj *layer) {
1,194✔
1429
  assert(layer != NULL);
1430
  assert(layer->vtable != NULL);
1431

1432
  layer->vtable->LayerInitItemInfo = msRASTERLayerInitItemInfo;
1,194✔
1433
  layer->vtable->LayerFreeItemInfo = msRASTERLayerFreeItemInfo;
1,194✔
1434
  layer->vtable->LayerOpen = msRASTERLayerOpen;
1,194✔
1435
  layer->vtable->LayerIsOpen = msRASTERLayerIsOpen;
1,194✔
1436
  layer->vtable->LayerWhichShapes = msRASTERLayerWhichShapes;
1,194✔
1437
  layer->vtable->LayerNextShape = msRASTERLayerNextShape;
1,194✔
1438
  layer->vtable->LayerGetShape = msRASTERLayerGetShape;
1,194✔
1439
  /* layer->vtable->LayerGetShapeCount, use default */
1440
  layer->vtable->LayerClose = msRASTERLayerClose;
1,194✔
1441
  layer->vtable->LayerGetItems = msRASTERLayerGetItems;
1,194✔
1442
  layer->vtable->LayerGetExtent = msRASTERLayerGetExtent;
1,194✔
1443
  /* layer->vtable->LayerGetAutoStyle, use default */
1444
  /* layer->vtable->LayerApplyFilterToLayer, use default */
1445
  /* layer->vtable->LayerCloseConnection = msRASTERLayerClose; */
1446
  /* we use backtics for proper tileindex shapefile functioning */
1447
  layer->vtable->LayerSetTimeFilter = msRASTERLayerSetTimeFilter;
1,194✔
1448
  /* layer->vtable->LayerCreateItems, use default */
1449
  /* layer->vtable->LayerGetNumFeatures, use default */
1450

1451
  return MS_SUCCESS;
1,194✔
1452
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc