Coverage Report

Created: 2025-06-13 06:18

/src/MapServer/src/maprasterquery.c
Line
Count
Source (jump to first uncovered line)
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
  float *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
80
  double shape_tolerance;
81
82
} rasterLayerInfo;
83
84
#define RQM_UNKNOWN 0
85
0
#define RQM_ENTRY_PER_PIXEL 1
86
0
#define RQM_HIST_ON_CLASS 2
87
0
#define RQM_HIST_ON_VALUE 3
88
89
extern int InvGeoTransform(double *gt_in, double *gt_out);
90
0
#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,
100
0
                     int tileindex) {
101
0
  int i;
102
103
0
  if (cache->numresults == cache->cachesize) { /* just add it to the end */
104
0
    if (cache->cachesize == 0)
105
0
      cache->results =
106
0
          (resultObj *)malloc(sizeof(resultObj) * MS_RESULTCACHEINCREMENT);
107
0
    else
108
0
      cache->results = (resultObj *)realloc(
109
0
          cache->results,
110
0
          sizeof(resultObj) * (cache->cachesize + MS_RESULTCACHEINCREMENT));
111
0
    if (!cache->results) {
112
0
      msSetError(MS_MEMERR, "Realloc() error.", "addResult()");
113
0
      return (MS_FAILURE);
114
0
    }
115
0
    cache->cachesize += MS_RESULTCACHEINCREMENT;
116
0
  }
117
118
0
  i = cache->numresults;
119
120
0
  cache->results[i].classindex = classindex;
121
0
  cache->results[i].tileindex = tileindex;
122
0
  cache->results[i].shapeindex = shapeindex;
123
0
  cache->results[i].resultindex = -1; /* unused */
124
0
  cache->results[i].shape = NULL;
125
0
  cache->numresults++;
126
127
0
  return (MS_SUCCESS);
128
0
}
129
130
/************************************************************************/
131
/*                       msRasterLayerInfoFree()                        */
132
/************************************************************************/
133
134
static void msRasterLayerInfoFree(layerObj *layer)
135
136
0
{
137
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
138
139
0
  if (rlinfo == NULL)
140
0
    return;
141
142
0
  if (rlinfo->qc_x != NULL) {
143
0
    free(rlinfo->qc_x);
144
0
    free(rlinfo->qc_y);
145
0
    free(rlinfo->qc_x_reproj);
146
0
    free(rlinfo->qc_y_reproj);
147
0
  }
148
149
0
  if (rlinfo->qc_values)
150
0
    free(rlinfo->qc_values);
151
152
0
  if (rlinfo->qc_class) {
153
0
    free(rlinfo->qc_class);
154
0
  }
155
156
0
  if (rlinfo->qc_red) {
157
0
    free(rlinfo->qc_red);
158
0
    free(rlinfo->qc_green);
159
0
    free(rlinfo->qc_blue);
160
0
  }
161
162
0
  if (rlinfo->qc_count != NULL)
163
0
    free(rlinfo->qc_count);
164
165
0
  if (rlinfo->qc_tileindex != NULL)
166
0
    free(rlinfo->qc_tileindex);
167
168
0
  free(rlinfo);
169
170
0
  layer->layerinfo = NULL;
171
0
}
172
173
/************************************************************************/
174
/*                    msRasterLayerInfoInitialize()                     */
175
/************************************************************************/
176
177
static void msRasterLayerInfoInitialize(layerObj *layer)
178
179
0
{
180
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
181
182
0
  if (rlinfo != NULL)
183
0
    return;
184
185
0
  rlinfo = (rasterLayerInfo *)msSmallCalloc(1, sizeof(rasterLayerInfo));
186
0
  layer->layerinfo = rlinfo;
187
188
0
  rlinfo->band_count = -1;
189
0
  rlinfo->raster_query_mode = RQM_ENTRY_PER_PIXEL;
190
0
  rlinfo->range_mode = -1; /* inactive */
191
0
  rlinfo->refcount = 0;
192
0
  rlinfo->shape_tolerance = 0.0;
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
0
  if (layer->connectiontype != MS_WMS &&
198
0
      layer->connectiontype != MS_KERNELDENSITY)
199
0
    layer->connectiontype = MS_RASTER;
200
201
0
  rlinfo->query_result_hard_max = 1000000;
202
203
0
  if (CSLFetchNameValue(layer->processing, "RASTER_QUERY_MAX_RESULT") != NULL) {
204
0
    rlinfo->query_result_hard_max =
205
0
        atoi(CSLFetchNameValue(layer->processing, "RASTER_QUERY_MAX_RESULT"));
206
0
  }
207
0
}
208
209
/************************************************************************/
210
/*                       msRasterQueryAddPixel()                        */
211
/************************************************************************/
212
213
static void msRasterQueryAddPixel(layerObj *layer, pointObj *location,
214
                                  pointObj *reprojectedLocation, float *values)
215
216
0
{
217
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
218
0
  int red = 0, green = 0, blue = 0, nodata = FALSE;
219
0
  int p_class = -1;
220
221
0
  if (rlinfo->query_results == rlinfo->query_result_hard_max)
222
0
    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
0
  if (rlinfo->query_alloc_max == 0) {
229
0
    rlinfo->query_alloc_max = 2;
230
231
0
    switch (rlinfo->raster_query_mode) {
232
0
    case RQM_ENTRY_PER_PIXEL:
233
0
      rlinfo->qc_x =
234
0
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
235
0
      rlinfo->qc_y =
236
0
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
237
0
      rlinfo->qc_x_reproj =
238
0
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
239
0
      rlinfo->qc_y_reproj =
240
0
          (double *)msSmallCalloc(sizeof(double), rlinfo->query_alloc_max);
241
0
      rlinfo->qc_values = (float *)msSmallCalloc(
242
0
          sizeof(float),
243
0
          ((size_t)rlinfo->query_alloc_max) * rlinfo->band_count);
244
0
      rlinfo->qc_red =
245
0
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
246
0
      rlinfo->qc_green =
247
0
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
248
0
      rlinfo->qc_blue =
249
0
          (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
250
0
      if (layer->numclasses > 0)
251
0
        rlinfo->qc_class =
252
0
            (int *)msSmallCalloc(sizeof(int), rlinfo->query_alloc_max);
253
0
      break;
254
255
0
    case RQM_HIST_ON_CLASS:
256
0
      break;
257
258
0
    case RQM_HIST_ON_VALUE:
259
0
      break;
260
261
0
    default:
262
0
      assert(FALSE);
263
0
    }
264
0
  }
265
266
  /* -------------------------------------------------------------------- */
267
  /*      Reallocate the data arrays larger if they are near the max      */
268
  /*      now.                                                            */
269
  /* -------------------------------------------------------------------- */
270
0
  if (rlinfo->query_results == rlinfo->query_alloc_max) {
271
0
    rlinfo->query_alloc_max = rlinfo->query_alloc_max * 2 + 100;
272
273
0
    if (rlinfo->qc_x != NULL)
274
0
      rlinfo->qc_x = msSmallRealloc(rlinfo->qc_x,
275
0
                                    sizeof(double) * rlinfo->query_alloc_max);
276
0
    if (rlinfo->qc_y != NULL)
277
0
      rlinfo->qc_y = msSmallRealloc(rlinfo->qc_y,
278
0
                                    sizeof(double) * rlinfo->query_alloc_max);
279
0
    if (rlinfo->qc_x_reproj != NULL)
280
0
      rlinfo->qc_x_reproj = msSmallRealloc(
281
0
          rlinfo->qc_x_reproj, sizeof(double) * rlinfo->query_alloc_max);
282
0
    if (rlinfo->qc_y_reproj != NULL)
283
0
      rlinfo->qc_y_reproj = msSmallRealloc(
284
0
          rlinfo->qc_y_reproj, sizeof(double) * rlinfo->query_alloc_max);
285
0
    if (rlinfo->qc_values != NULL)
286
0
      rlinfo->qc_values = msSmallRealloc(
287
0
          rlinfo->qc_values,
288
0
          sizeof(float) * rlinfo->query_alloc_max * rlinfo->band_count);
289
0
    if (rlinfo->qc_class != NULL)
290
0
      rlinfo->qc_class = msSmallRealloc(rlinfo->qc_class,
291
0
                                        sizeof(int) * rlinfo->query_alloc_max);
292
0
    if (rlinfo->qc_red != NULL)
293
0
      rlinfo->qc_red =
294
0
          msSmallRealloc(rlinfo->qc_red, sizeof(int) * rlinfo->query_alloc_max);
295
0
    if (rlinfo->qc_green != NULL)
296
0
      rlinfo->qc_green = msSmallRealloc(rlinfo->qc_green,
297
0
                                        sizeof(int) * rlinfo->query_alloc_max);
298
0
    if (rlinfo->qc_blue != NULL)
299
0
      rlinfo->qc_blue = msSmallRealloc(rlinfo->qc_blue,
300
0
                                       sizeof(int) * rlinfo->query_alloc_max);
301
0
    if (rlinfo->qc_count != NULL)
302
0
      rlinfo->qc_count = msSmallRealloc(rlinfo->qc_count,
303
0
                                        sizeof(int) * rlinfo->query_alloc_max);
304
0
    if (rlinfo->qc_tileindex != NULL)
305
0
      rlinfo->qc_tileindex = msSmallRealloc(
306
0
          rlinfo->qc_tileindex, sizeof(int) * rlinfo->query_alloc_max);
307
0
  }
308
309
  /* -------------------------------------------------------------------- */
310
  /*      Handle colormap                                                 */
311
  /* -------------------------------------------------------------------- */
312
0
  if (rlinfo->hCT != NULL) {
313
0
    int pct_index = (int)floor(values[0]);
314
0
    GDALColorEntry sEntry;
315
316
0
    if (GDALGetColorEntryAsRGB(rlinfo->hCT, pct_index, &sEntry)) {
317
0
      red = sEntry.c1;
318
0
      green = sEntry.c2;
319
0
      blue = sEntry.c3;
320
321
0
      if (sEntry.c4 == 0)
322
0
        nodata = TRUE;
323
0
    } else
324
0
      nodata = TRUE;
325
0
  }
326
327
  /* -------------------------------------------------------------------- */
328
  /*      Color derived from greyscale value.                             */
329
  /* -------------------------------------------------------------------- */
330
0
  else {
331
0
    if (rlinfo->band_count >= 3) {
332
0
      red = (int)MS_MAX(0, MS_MIN(255, values[0]));
333
0
      green = (int)MS_MAX(0, MS_MIN(255, values[1]));
334
0
      blue = (int)MS_MAX(0, MS_MIN(255, values[2]));
335
0
    } else {
336
0
      red = green = blue = (int)MS_MAX(0, MS_MIN(255, values[0]));
337
0
    }
338
0
  }
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
0
  if (rlinfo->qc_class != NULL) {
349
0
    p_class = msGetClass_FloatRGB(layer, values[0], red, green, blue);
350
351
0
    if (p_class == -1)
352
0
      nodata = TRUE;
353
0
    else {
354
0
      nodata = FALSE;
355
0
      rlinfo->qc_class[rlinfo->query_results] = p_class;
356
0
      if (layer->class[p_class] -> numstyles > 0) {
357
0
        red = layer->class[p_class]->styles[0]->color.red;
358
0
        green = layer->class[p_class]->styles[0]->color.green;
359
0
        blue = layer->class[p_class]->styles[0]->color.blue;
360
0
      } else {
361
0
        red = green = blue = 0;
362
0
      }
363
0
    }
364
0
  }
365
366
  /* -------------------------------------------------------------------- */
367
  /*      Record the color.                                               */
368
  /* -------------------------------------------------------------------- */
369
0
  rlinfo->qc_red[rlinfo->query_results] = red;
370
0
  rlinfo->qc_green[rlinfo->query_results] = green;
371
0
  rlinfo->qc_blue[rlinfo->query_results] = blue;
372
373
  /* -------------------------------------------------------------------- */
374
  /*      Record spatial location.                                        */
375
  /* -------------------------------------------------------------------- */
376
0
  if (rlinfo->qc_x != NULL) {
377
0
    rlinfo->qc_x[rlinfo->query_results] = location->x;
378
0
    rlinfo->qc_y[rlinfo->query_results] = location->y;
379
0
    rlinfo->qc_x_reproj[rlinfo->query_results] = reprojectedLocation->x;
380
0
    rlinfo->qc_y_reproj[rlinfo->query_results] = reprojectedLocation->y;
381
0
  }
382
383
  /* -------------------------------------------------------------------- */
384
  /*      Record actual pixel value(s).                                   */
385
  /* -------------------------------------------------------------------- */
386
0
  if (rlinfo->qc_values != NULL)
387
0
    memcpy(rlinfo->qc_values + rlinfo->query_results * rlinfo->band_count,
388
0
           values, sizeof(float) * rlinfo->band_count);
389
390
  /* -------------------------------------------------------------------- */
391
  /*      Add to the results cache.                                       */
392
  /* -------------------------------------------------------------------- */
393
0
  if (!nodata) {
394
0
    addResult(layer->resultcache, p_class, rlinfo->query_results, 0);
395
0
    rlinfo->query_results++;
396
0
  }
397
0
}
398
399
/************************************************************************/
400
/*                       msRasterQueryByRectLow()                       */
401
/************************************************************************/
402
403
static int msRasterQueryByRectLow(mapObj *map, layerObj *layer,
404
                                  GDALDatasetH hDS, rectObj queryRect)
405
406
0
{
407
0
  double adfGeoTransform[6], adfInvGeoTransform[6];
408
0
  double dfXMin, dfYMin, dfXMax, dfYMax, dfX, dfY, dfAdjustedRange;
409
0
  int nWinXOff, nWinYOff, nWinXSize, nWinYSize;
410
0
  int nRXSize, nRYSize;
411
0
  float *pafRaster;
412
0
  int nBandCount, *panBandMap, iPixel, iLine;
413
0
  CPLErr eErr;
414
0
  rasterLayerInfo *rlinfo;
415
0
  rectObj searchrect;
416
417
0
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
418
419
  /* -------------------------------------------------------------------- */
420
  /*      Reproject the search rect into the projection of this           */
421
  /*      layer/file if needed.                                           */
422
  /* -------------------------------------------------------------------- */
423
0
  searchrect = queryRect;
424
0
  layer->project =
425
0
      msProjectionsDiffer(&(layer->projection), &(map->projection));
426
0
  if (layer->project)
427
0
    msProjectRect(&(map->projection), &(layer->projection), &searchrect);
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
0
  nRXSize = GDALGetRasterXSize(hDS);
435
0
  nRYSize = GDALGetRasterYSize(hDS);
436
437
0
  msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform);
438
0
  InvGeoTransform(adfGeoTransform, adfInvGeoTransform);
439
440
  /* top left */
441
0
  dfXMin = dfXMax =
442
0
      GEO_TRANS(adfInvGeoTransform, searchrect.minx, searchrect.maxy);
443
0
  dfYMin = dfYMax =
444
0
      GEO_TRANS(adfInvGeoTransform + 3, searchrect.minx, searchrect.maxy);
445
446
  /* top right */
447
0
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.maxx, searchrect.maxy);
448
0
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.maxx, searchrect.maxy);
449
0
  dfXMin = MS_MIN(dfXMin, dfX);
450
0
  dfXMax = MS_MAX(dfXMax, dfX);
451
0
  dfYMin = MS_MIN(dfYMin, dfY);
452
0
  dfYMax = MS_MAX(dfYMax, dfY);
453
454
  /* bottom left */
455
0
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.minx, searchrect.miny);
456
0
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.minx, searchrect.miny);
457
0
  dfXMin = MS_MIN(dfXMin, dfX);
458
0
  dfXMax = MS_MAX(dfXMax, dfX);
459
0
  dfYMin = MS_MIN(dfYMin, dfY);
460
0
  dfYMax = MS_MAX(dfYMax, dfY);
461
462
  /* bottom right */
463
0
  dfX = GEO_TRANS(adfInvGeoTransform, searchrect.maxx, searchrect.miny);
464
0
  dfY = GEO_TRANS(adfInvGeoTransform + 3, searchrect.maxx, searchrect.miny);
465
0
  dfXMin = MS_MIN(dfXMin, dfX);
466
0
  dfXMax = MS_MAX(dfXMax, dfX);
467
0
  dfYMin = MS_MIN(dfYMin, dfY);
468
0
  dfYMax = MS_MAX(dfYMax, dfY);
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
0
  dfXMin = MS_MAX(0.0, MS_MIN(nRXSize, floor(dfXMin)));
475
0
  dfYMin = MS_MAX(0.0, MS_MIN(nRYSize, floor(dfYMin)));
476
0
  dfXMax = MS_MAX(0.0, MS_MIN(nRXSize, ceil(dfXMax)));
477
0
  dfYMax = MS_MAX(0.0, MS_MIN(nRYSize, ceil(dfYMax)));
478
479
  /* -------------------------------------------------------------------- */
480
  /*      Convert to integer offset/size values.                          */
481
  /* -------------------------------------------------------------------- */
482
0
  nWinXOff = (int)dfXMin;
483
0
  nWinYOff = (int)dfYMin;
484
0
  nWinXSize = (int)(dfXMax - dfXMin);
485
0
  nWinYSize = (int)(dfYMax - dfYMin);
486
487
  /* -------------------------------------------------------------------- */
488
  /*      What bands are we operating on?                                 */
489
  /* -------------------------------------------------------------------- */
490
0
  panBandMap = msGetGDALBandList(layer, hDS, 0, &nBandCount);
491
492
0
  if (rlinfo->band_count == -1)
493
0
    rlinfo->band_count = nBandCount;
494
495
0
  if (nBandCount != rlinfo->band_count) {
496
0
    msSetError(MS_IMGERR, "Got %d bands, but expected %d bands.",
497
0
               "msRasterQueryByRectLow()", nBandCount, rlinfo->band_count);
498
499
0
    return -1;
500
0
  }
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
0
  pafRaster = (float *)calloc(((size_t)nWinXSize) * nWinYSize * nBandCount,
508
0
                              sizeof(float));
509
0
  MS_CHECK_ALLOC(pafRaster, sizeof(float) * nWinXSize * nWinYSize * nBandCount,
510
0
                 -1);
511
512
0
  eErr = GDALDatasetRasterIO(hDS, GF_Read, nWinXOff, nWinYOff, nWinXSize,
513
0
                             nWinYSize, pafRaster, nWinXSize, nWinYSize,
514
0
                             GDT_Float32, nBandCount, panBandMap,
515
0
                             4 * nBandCount, 4 * nBandCount * nWinXSize, 4);
516
517
0
  if (eErr != CE_None) {
518
0
    msSetError(MS_IOERR, "GDALDatasetRasterIO() failed: %s",
519
0
               "msRasterQueryByRectLow()", CPLGetLastErrorMsg());
520
521
0
    free(pafRaster);
522
0
    return -1;
523
0
  }
524
525
  /* -------------------------------------------------------------------- */
526
  /*      Fetch color table for interpreting colors if needed.             */
527
  /* -------------------------------------------------------------------- */
528
0
  rlinfo->hCT = GDALGetRasterColorTable(GDALGetRasterBand(hDS, panBandMap[0]));
529
530
0
  free(panBandMap);
531
532
  /* -------------------------------------------------------------------- */
533
  /*      When computing whether pixels are within range we do it         */
534
  /*      based on the center of the pixel to the target point but        */
535
  /*      really it ought to be the nearest point on the pixel.  It       */
536
  /*      would be too much trouble to do this rigerously, so we just     */
537
  /*      add a fudge factor so that a range of zero will find the        */
538
  /*      pixel the target falls in at least.                             */
539
  /* -------------------------------------------------------------------- */
540
0
  dfAdjustedRange = sqrt(adfGeoTransform[1] * adfGeoTransform[1] +
541
0
                         adfGeoTransform[2] * adfGeoTransform[2] +
542
0
                         adfGeoTransform[4] * adfGeoTransform[4] +
543
0
                         adfGeoTransform[5] * adfGeoTransform[5]) *
544
0
                        0.5 * 1.41421356237 +
545
0
                    sqrt(rlinfo->range_dist);
546
0
  dfAdjustedRange = dfAdjustedRange * dfAdjustedRange;
547
548
0
  reprojectionObj *reprojector = NULL;
549
0
  if (layer->project) {
550
0
    reprojector =
551
0
        msProjectCreateReprojector(&(layer->projection), &(map->projection));
552
0
  }
553
554
  /* -------------------------------------------------------------------- */
555
  /*      Loop over all pixels determining which are "in".                */
556
  /* -------------------------------------------------------------------- */
557
0
  for (iLine = 0; iLine < nWinYSize; iLine++) {
558
0
    for (iPixel = 0; iPixel < nWinXSize; iPixel++) {
559
0
      pointObj sPixelLocation = {0};
560
561
0
      if (rlinfo->query_results == rlinfo->query_result_hard_max)
562
0
        break;
563
564
      /* transform pixel/line to georeferenced */
565
0
      sPixelLocation.x = GEO_TRANS(adfGeoTransform, iPixel + nWinXOff + 0.5,
566
0
                                   iLine + nWinYOff + 0.5);
567
0
      sPixelLocation.y = GEO_TRANS(adfGeoTransform + 3, iPixel + nWinXOff + 0.5,
568
0
                                   iLine + nWinYOff + 0.5);
569
570
      /* If projections differ, convert this back into the map  */
571
      /* projection for distance testing, and comprison to the  */
572
      /* search shape.  Save the original pixel location coordinates */
573
      /* in sPixelLocationInLayerSRS, so that we can return those */
574
      /* coordinates if we have a hit */
575
0
      pointObj sReprojectedPixelLocation = sPixelLocation;
576
0
      if (reprojector) {
577
0
        msProjectPointEx(reprojector, &sReprojectedPixelLocation);
578
0
      }
579
580
      /* If we are doing QueryByShape, check against the shape now */
581
0
      if (rlinfo->searchshape != NULL) {
582
0
        if (rlinfo->shape_tolerance == 0.0 &&
583
0
            rlinfo->searchshape->type == MS_SHAPE_POLYGON) {
584
0
          if (msIntersectPointPolygon(&sReprojectedPixelLocation,
585
0
                                      rlinfo->searchshape) == MS_FALSE)
586
0
            continue;
587
0
        } else {
588
0
          shapeObj tempShape;
589
0
          lineObj tempLine;
590
591
0
          memset(&tempShape, 0, sizeof(shapeObj));
592
0
          tempShape.type = MS_SHAPE_POINT;
593
0
          tempShape.numlines = 1;
594
0
          tempShape.line = &tempLine;
595
0
          tempLine.numpoints = 1;
596
0
          tempLine.point = &sReprojectedPixelLocation;
597
598
0
          if (msDistanceShapeToShape(rlinfo->searchshape, &tempShape) >
599
0
              rlinfo->shape_tolerance)
600
0
            continue;
601
0
        }
602
0
      }
603
604
0
      if (rlinfo->range_mode >= 0) {
605
0
        double dist;
606
607
0
        dist = (rlinfo->target_point.x - sReprojectedPixelLocation.x) *
608
0
                   (rlinfo->target_point.x - sReprojectedPixelLocation.x) +
609
0
               (rlinfo->target_point.y - sReprojectedPixelLocation.y) *
610
0
                   (rlinfo->target_point.y - sReprojectedPixelLocation.y);
611
612
0
        if (dist >= dfAdjustedRange)
613
0
          continue;
614
615
        /* If we can only have one feature, trim range and clear */
616
        /* previous result.  */
617
0
        if (rlinfo->range_mode == MS_QUERY_SINGLE) {
618
0
          rlinfo->range_dist = dist;
619
0
          rlinfo->query_results = 0;
620
0
        }
621
0
      }
622
623
0
      msRasterQueryAddPixel(layer,
624
0
                            &sPixelLocation, // return coords in layer SRS
625
0
                            &sReprojectedPixelLocation,
626
0
                            pafRaster +
627
0
                                (iLine * nWinXSize + iPixel) * nBandCount);
628
0
    }
629
0
  }
630
631
  /* -------------------------------------------------------------------- */
632
  /*      Cleanup.                                                        */
633
  /* -------------------------------------------------------------------- */
634
0
  free(pafRaster);
635
0
  msProjectDestroyReprojector(reprojector);
636
637
0
  return MS_SUCCESS;
638
0
}
639
640
/************************************************************************/
641
/*                        msRasterQueryByRect()                         */
642
/************************************************************************/
643
644
int msRasterQueryByRect(mapObj *map, layerObj *layer, rectObj queryRect)
645
646
0
{
647
0
  int status = MS_SUCCESS;
648
0
  char *filename = NULL;
649
650
0
  layerObj *tlp = NULL; /* pointer to the tile layer either real or temporary */
651
0
  int tileitemindex = -1, tilelayerindex = -1, tilesrsindex = -1;
652
0
  shapeObj tshp;
653
0
  char tilename[MS_PATH_LENGTH], tilesrsname[1024];
654
0
  int done, destroy_on_failure;
655
656
0
  char szPath[MS_MAXPATHLEN];
657
0
  rectObj searchrect;
658
0
  rasterLayerInfo *rlinfo = NULL;
659
660
  /* -------------------------------------------------------------------- */
661
  /*      Get the layer info.                                             */
662
  /* -------------------------------------------------------------------- */
663
0
  if (!layer->layerinfo) {
664
0
    msRasterLayerInfoInitialize(layer);
665
0
    destroy_on_failure = 1;
666
0
  } else {
667
0
    destroy_on_failure = 0;
668
0
  }
669
0
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
670
671
  /* -------------------------------------------------------------------- */
672
  /*      Clear old results cache.                                        */
673
  /* -------------------------------------------------------------------- */
674
0
  if (layer->resultcache) {
675
0
    if (layer->resultcache->results)
676
0
      free(layer->resultcache->results);
677
0
    free(layer->resultcache);
678
0
    layer->resultcache = NULL;
679
0
  }
680
681
  /* -------------------------------------------------------------------- */
682
  /*      Initialize the results cache.                                   */
683
  /* -------------------------------------------------------------------- */
684
0
  layer->resultcache = (resultCacheObj *)msSmallMalloc(sizeof(resultCacheObj));
685
0
  layer->resultcache->results = NULL;
686
0
  layer->resultcache->numresults = layer->resultcache->cachesize = 0;
687
0
  layer->resultcache->bounds.minx = layer->resultcache->bounds.miny =
688
0
      layer->resultcache->bounds.maxx = layer->resultcache->bounds.maxy = -1;
689
690
  /* -------------------------------------------------------------------- */
691
  /*      Check if we should really be acting on this layer and           */
692
  /*      provide debug info in various cases.                            */
693
  /* -------------------------------------------------------------------- */
694
0
  if (layer->debug > 0 || map->debug > 1)
695
0
    msDebug("msRasterQueryByRect(%s): entering.\n", layer->name);
696
697
0
  if (!layer->data && !layer->tileindex) {
698
0
    if (layer->debug > 0 || map->debug > 0)
699
0
      msDebug("msRasterQueryByRect(%s): layer data and tileindex NULL ... "
700
0
              "doing nothing.",
701
0
              layer->name);
702
0
    return MS_SUCCESS;
703
0
  }
704
705
0
  if ((layer->status != MS_ON) && layer->status != MS_DEFAULT) {
706
0
    if (layer->debug > 0)
707
0
      msDebug(
708
0
          "msRasterQueryByRect(%s): not status ON or DEFAULT, doing nothing.",
709
0
          layer->name);
710
0
    return MS_SUCCESS;
711
0
  }
712
713
  /* ==================================================================== */
714
  /*      Handle setting up tileindex layer.                              */
715
  /* ==================================================================== */
716
0
  if (layer->tileindex) { /* we have an index file */
717
0
    msInitShape(&tshp);
718
0
    searchrect = queryRect;
719
720
0
    status = msDrawRasterSetupTileLayer(map, layer, &searchrect, MS_TRUE,
721
0
                                        &tilelayerindex, &tileitemindex,
722
0
                                        &tilesrsindex, &tlp);
723
0
    if (status != MS_SUCCESS) {
724
0
      goto cleanup;
725
0
    }
726
0
  } else {
727
    /* we have to manually apply to scaletoken logic as we're not going through
728
     * msLayerOpen() */
729
0
    status = msLayerApplyScaletokens(layer, map->scaledenom);
730
0
    if (status != MS_SUCCESS) {
731
0
      goto cleanup;
732
0
    }
733
0
  }
734
735
  /* -------------------------------------------------------------------- */
736
  /*      Iterate over all tiles (just one in untiled case).              */
737
  /* -------------------------------------------------------------------- */
738
0
  done = MS_FALSE;
739
0
  while (done == MS_FALSE && status == MS_SUCCESS) {
740
741
0
    GDALDatasetH hDS;
742
0
    char *decrypted_path = NULL;
743
744
    /* -------------------------------------------------------------------- */
745
    /*      Get filename.                                                   */
746
    /* -------------------------------------------------------------------- */
747
0
    if (layer->tileindex) {
748
0
      status = msDrawRasterIterateTileIndex(
749
0
          layer, tlp, &tshp, tileitemindex, tilesrsindex, tilename,
750
0
          sizeof(tilename), tilesrsname, sizeof(tilesrsname));
751
0
      if (status == MS_FAILURE) {
752
0
        break;
753
0
      }
754
755
0
      if (status == MS_DONE)
756
0
        break; /* no more tiles/images */
757
0
      filename = tilename;
758
0
    } else {
759
0
      filename = layer->data;
760
0
      done = MS_TRUE; /* only one image so we're done after this */
761
0
    }
762
763
0
    if (strlen(filename) == 0)
764
0
      continue;
765
766
    /* -------------------------------------------------------------------- */
767
    /*      Open the file.                                                  */
768
    /* -------------------------------------------------------------------- */
769
0
    msGDALInitialize();
770
771
0
    msDrawRasterBuildRasterPath(map, layer, filename, szPath);
772
773
0
    decrypted_path = msDecryptStringTokens(map, szPath);
774
0
    if (!decrypted_path) {
775
0
      status = MS_FAILURE;
776
0
      goto cleanup;
777
0
    }
778
779
0
    msAcquireLock(TLOCK_GDAL);
780
0
    if (!layer->tileindex) {
781
0
      char **connectionoptions =
782
0
          msGetStringListFromHashTable(&(layer->connectionoptions));
783
0
      hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
784
0
                       (const char *const *)connectionoptions, NULL);
785
0
      CSLDestroy(connectionoptions);
786
0
    } else {
787
0
      hDS = GDALOpen(decrypted_path, GA_ReadOnly);
788
0
    }
789
790
0
    if (hDS == NULL) {
791
0
      int ignore_missing = msMapIgnoreMissingData(map);
792
0
      const char *cpl_error_msg =
793
0
          msDrawRasterGetCPLErrorMsg(decrypted_path, szPath);
794
795
0
      msFree(decrypted_path);
796
0
      decrypted_path = NULL;
797
798
0
      msReleaseLock(TLOCK_GDAL);
799
800
0
      if (ignore_missing == MS_MISSING_DATA_FAIL) {
801
0
        if (layer->debug || map->debug)
802
0
          msSetError(MS_IMGERR,
803
0
                     "Unable to open file %s for layer %s ... fatal error.\n%s",
804
0
                     "msRasterQueryByRect()", szPath, layer->name,
805
0
                     cpl_error_msg);
806
0
        status = MS_FAILURE;
807
0
        goto cleanup;
808
0
      }
809
0
      if (ignore_missing == MS_MISSING_DATA_LOG) {
810
0
        if (layer->debug || map->debug)
811
0
          msDebug("Unable to open file %s for layer %s ... ignoring this "
812
0
                  "missing data.\n%s",
813
0
                  filename, layer->name, cpl_error_msg);
814
0
      }
815
0
      continue;
816
0
    }
817
818
0
    msFree(decrypted_path);
819
0
    decrypted_path = NULL;
820
821
0
    if (msDrawRasterLoadProjection(layer, hDS, filename, tilesrsindex,
822
0
                                   tilesrsname) != MS_SUCCESS) {
823
0
      msReleaseLock(TLOCK_GDAL);
824
0
      status = MS_FAILURE;
825
0
      goto cleanup;
826
0
    }
827
828
    /* -------------------------------------------------------------------- */
829
    /*      Perform actual query on this file.                              */
830
    /* -------------------------------------------------------------------- */
831
0
    if (status == MS_SUCCESS)
832
0
      status = msRasterQueryByRectLow(map, layer, hDS, queryRect);
833
834
0
    GDALClose(hDS);
835
0
    msReleaseLock(TLOCK_GDAL);
836
837
0
  } /* next tile */
838
839
  /* -------------------------------------------------------------------- */
840
  /*      Cleanup tileindex if it is open.                                */
841
  /* -------------------------------------------------------------------- */
842
0
cleanup:
843
0
  if (layer->tileindex) { /* tiling clean-up */
844
0
    msDrawRasterCleanupTileLayer(tlp, tilelayerindex);
845
0
  } else {
846
0
    msLayerRestoreFromScaletokens(layer);
847
0
  }
848
849
  /* -------------------------------------------------------------------- */
850
  /*      On failure, or empty result set, cleanup the rlinfo since we    */
851
  /*      likely won't ever have it accessed or cleaned up later.         */
852
  /* -------------------------------------------------------------------- */
853
0
  if (status == MS_FAILURE || rlinfo->query_results == 0) {
854
0
    if (destroy_on_failure) {
855
0
      msRasterLayerInfoFree(layer);
856
0
    }
857
0
  } else {
858
    /* populate the items/numitems layer-level values */
859
0
    msRASTERLayerGetItems(layer);
860
0
  }
861
862
0
  return status;
863
0
}
864
865
/************************************************************************/
866
/*                        msRasterQueryByShape()                        */
867
/************************************************************************/
868
869
int msRasterQueryByShape(mapObj *map, layerObj *layer, shapeObj *selectshape)
870
871
0
{
872
0
  rasterLayerInfo *rlinfo = NULL;
873
0
  int status;
874
0
  double tolerance;
875
0
  rectObj searchrect;
876
877
0
  msRasterLayerInfoInitialize(layer);
878
879
0
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
880
881
  /* If the selection shape is point or line we use the default tolerance of
882
     3, but for polygons we require an exact hit. */
883
0
  if (layer->tolerance == -1) {
884
0
    if (selectshape->type == MS_SHAPE_POINT ||
885
0
        selectshape->type == MS_SHAPE_LINE)
886
0
      tolerance = 3;
887
0
    else
888
0
      tolerance = 0;
889
0
  } else
890
0
    tolerance = layer->tolerance;
891
892
0
  if (layer->toleranceunits == MS_PIXELS)
893
0
    tolerance =
894
0
        tolerance * msAdjustExtent(&(map->extent), map->width, map->height);
895
0
  else
896
0
    tolerance = tolerance * (msInchesPerUnit(layer->toleranceunits, 0) /
897
0
                             msInchesPerUnit(map->units, 0));
898
899
0
  rlinfo->searchshape = selectshape;
900
0
  rlinfo->shape_tolerance = tolerance;
901
902
0
  msComputeBounds(selectshape);
903
0
  searchrect = selectshape->bounds;
904
905
0
  searchrect.minx -= tolerance; /* expand the search box to account for layer
906
                                   tolerances (e.g. buffered searches) */
907
0
  searchrect.maxx += tolerance;
908
0
  searchrect.miny -= tolerance;
909
0
  searchrect.maxy += tolerance;
910
911
0
  status = msRasterQueryByRect(map, layer, searchrect);
912
913
0
  rlinfo->searchshape = NULL;
914
915
0
  return status;
916
0
}
917
918
/************************************************************************/
919
/*                        msRasterQueryByPoint()                        */
920
/************************************************************************/
921
922
int msRasterQueryByPoint(mapObj *map, layerObj *layer, int mode, pointObj p,
923
0
                         double buffer, int maxresults) {
924
0
  int result;
925
0
  double layer_tolerance;
926
0
  rectObj bufferRect;
927
0
  rasterLayerInfo *rlinfo = NULL;
928
929
0
  msRasterLayerInfoInitialize(layer);
930
931
0
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
932
933
  /* -------------------------------------------------------------------- */
934
  /*      If the buffer is not set, then use layer tolerances             */
935
  /*      instead.   The "buffer" distince is now in georeferenced        */
936
  /*      units.  Note that tolerances in pixels are basically map        */
937
  /*      display pixels, not underlying raster pixels.  It isn't         */
938
  /*      clear that there is any way of requesting a buffer size in      */
939
  /*      underlying pixels.                                              */
940
  /* -------------------------------------------------------------------- */
941
0
  if (buffer <= 0) { /* use layer tolerance */
942
0
    if (layer->tolerance == -1)
943
0
      layer_tolerance = 3;
944
0
    else
945
0
      layer_tolerance = layer->tolerance;
946
947
0
    if (layer->toleranceunits == MS_PIXELS)
948
0
      buffer = layer_tolerance *
949
0
               msAdjustExtent(&(map->extent), map->width, map->height);
950
0
    else
951
0
      buffer = layer_tolerance * (msInchesPerUnit(layer->toleranceunits, 0) /
952
0
                                  msInchesPerUnit(map->units, 0));
953
0
  }
954
955
  /* -------------------------------------------------------------------- */
956
  /*      Setup target point information, at this point they are in       */
957
  /*      map coordinates.                                                */
958
  /* -------------------------------------------------------------------- */
959
0
  rlinfo->range_dist = buffer * buffer;
960
0
  rlinfo->target_point = p;
961
962
  /* -------------------------------------------------------------------- */
963
  /*      if we are in the MS_QUERY_SINGLE mode, first try a query with   */
964
  /*      zero tolerance.  If this gets a raster pixel then we can be     */
965
  /*      reasonably assured that it is the closest to the query          */
966
  /*      point.  This will potentially be must more efficient than       */
967
  /*      processing all pixels within the tolerance.                     */
968
  /* -------------------------------------------------------------------- */
969
0
  if (mode == MS_QUERY_SINGLE) {
970
0
    rectObj pointRect;
971
972
0
    pointRect.minx = p.x;
973
0
    pointRect.maxx = p.x;
974
0
    pointRect.miny = p.y;
975
0
    pointRect.maxy = p.y;
976
977
0
    rlinfo->range_mode = MS_QUERY_SINGLE;
978
0
    result = msRasterQueryByRect(map, layer, pointRect);
979
0
    if (rlinfo->query_results > 0)
980
0
      return result;
981
0
  }
982
983
  /* -------------------------------------------------------------------- */
984
  /*      Setup a rectangle that is everything within the designated      */
985
  /*      range and do a search against that.                             */
986
  /* -------------------------------------------------------------------- */
987
0
  bufferRect.minx = p.x - buffer;
988
0
  bufferRect.maxx = p.x + buffer;
989
0
  bufferRect.miny = p.y - buffer;
990
0
  bufferRect.maxy = p.y + buffer;
991
992
0
  rlinfo->range_mode = mode;
993
994
0
  if (maxresults != 0) {
995
0
    const int previous_maxresults = rlinfo->query_result_hard_max;
996
0
    rlinfo->query_result_hard_max = maxresults;
997
0
    result = msRasterQueryByRect(map, layer, bufferRect);
998
0
    rlinfo->query_result_hard_max = previous_maxresults;
999
0
  } else {
1000
0
    result = msRasterQueryByRect(map, layer, bufferRect);
1001
0
  }
1002
1003
0
  return result;
1004
0
}
1005
1006
/************************************************************************/
1007
/* ==================================================================== */
1008
/*                          RASTER Query Layer                          */
1009
/*                                                                      */
1010
/*      The results of a raster query are treated as a set of shapes    */
1011
/*      that can be accessed using the normal layer semantics.          */
1012
/* ==================================================================== */
1013
/************************************************************************/
1014
1015
/************************************************************************/
1016
/*                         msRASTERLayerOpen()                          */
1017
/************************************************************************/
1018
1019
0
int msRASTERLayerOpen(layerObj *layer) {
1020
0
  rasterLayerInfo *rlinfo;
1021
1022
  /* If we don't have info, initialize an empty one now */
1023
0
  if (layer->layerinfo == NULL)
1024
0
    msRasterLayerInfoInitialize(layer);
1025
0
  if (layer->layerinfo == NULL)
1026
0
    return MS_FAILURE;
1027
1028
0
  rlinfo = (rasterLayerInfo *)layer->layerinfo;
1029
1030
0
  rlinfo->refcount = rlinfo->refcount + 1;
1031
1032
0
  return MS_SUCCESS;
1033
0
}
1034
1035
/************************************************************************/
1036
/*                         msRASTERIsLayerOpen()                        */
1037
/************************************************************************/
1038
1039
0
int msRASTERLayerIsOpen(layerObj *layer) {
1040
0
  if (layer->layerinfo)
1041
0
    return MS_TRUE;
1042
0
  return MS_FALSE;
1043
0
}
1044
1045
/************************************************************************/
1046
/*                     msRASTERLayerFreeItemInfo()                      */
1047
/************************************************************************/
1048
0
void msRASTERLayerFreeItemInfo(layerObj *layer) { (void)layer; }
1049
1050
/************************************************************************/
1051
/*                     msRASTERLayerInitItemInfo()                      */
1052
/*                                                                      */
1053
/*      Perhaps we should be validating the requested items here?       */
1054
/************************************************************************/
1055
1056
0
int msRASTERLayerInitItemInfo(layerObj *layer) {
1057
0
  (void)layer;
1058
0
  return MS_SUCCESS;
1059
0
}
1060
1061
/************************************************************************/
1062
/*                      msRASTERLayerWhichShapes()                      */
1063
/************************************************************************/
1064
int msRASTERLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
1065
1066
0
{
1067
0
  (void)isQuery;
1068
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
1069
1070
0
  rlinfo->which_rect = rect;
1071
0
  rlinfo->next_shape = 0;
1072
1073
0
  return MS_SUCCESS;
1074
0
}
1075
1076
/************************************************************************/
1077
/*                         msRASTERLayerClose()                         */
1078
/************************************************************************/
1079
1080
0
int msRASTERLayerClose(layerObj *layer) {
1081
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
1082
1083
0
  if (rlinfo != NULL) {
1084
0
    rlinfo->refcount--;
1085
1086
0
    if (rlinfo->refcount < 0)
1087
0
      msRasterLayerInfoFree(layer);
1088
0
  }
1089
0
  return MS_SUCCESS;
1090
0
}
1091
1092
/************************************************************************/
1093
/*                       msRASTERLayerNextShape()                       */
1094
/************************************************************************/
1095
1096
0
int msRASTERLayerNextShape(layerObj *layer, shapeObj *shape) {
1097
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
1098
1099
0
  if (rlinfo->next_shape < 0 || rlinfo->next_shape >= rlinfo->query_results) {
1100
0
    msFreeShape(shape);
1101
0
    shape->type = MS_SHAPE_NULL;
1102
0
    return MS_DONE;
1103
0
  } else {
1104
0
    resultObj record;
1105
1106
0
    record.shapeindex = rlinfo->next_shape++;
1107
0
    record.tileindex = 0;
1108
0
    record.classindex = record.resultindex = -1;
1109
1110
0
    return msRASTERLayerGetShape(layer, shape, &record);
1111
0
  }
1112
0
}
1113
1114
/************************************************************************/
1115
/*                       msRASTERLayerGetShape()                        */
1116
/************************************************************************/
1117
1118
0
int msRASTERLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) {
1119
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
1120
0
  int i;
1121
1122
0
  long shapeindex = record->shapeindex;
1123
1124
0
  msFreeShape(shape);
1125
0
  shape->type = MS_SHAPE_NULL;
1126
1127
  /* -------------------------------------------------------------------- */
1128
  /*      Validate requested record id.                                   */
1129
  /* -------------------------------------------------------------------- */
1130
0
  if (shapeindex < 0 || shapeindex >= rlinfo->query_results) {
1131
0
    msSetError(MS_MISCERR,
1132
0
               "Out of range shape index requested.  Requested %ld\n"
1133
0
               "but only %d shapes available.",
1134
0
               "msRASTERLayerGetShape()", shapeindex, rlinfo->query_results);
1135
0
    return MS_FAILURE;
1136
0
  }
1137
1138
  /* -------------------------------------------------------------------- */
1139
  /*      Apply the geometry.                                             */
1140
  /* -------------------------------------------------------------------- */
1141
0
  if (rlinfo->qc_x != NULL) {
1142
0
    lineObj line;
1143
0
    pointObj point;
1144
1145
0
    shape->type = MS_SHAPE_POINT;
1146
1147
0
    line.numpoints = 1;
1148
0
    line.point = &point;
1149
1150
0
    point.x = rlinfo->qc_x[shapeindex];
1151
0
    point.y = rlinfo->qc_y[shapeindex];
1152
0
    point.m = 0.0;
1153
1154
0
    msAddLine(shape, &line);
1155
0
    msComputeBounds(shape);
1156
0
  }
1157
1158
  /* -------------------------------------------------------------------- */
1159
  /*      Apply the requested items.                                      */
1160
  /* -------------------------------------------------------------------- */
1161
0
  if (layer->numitems > 0) {
1162
0
    shape->values = (char **)msSmallMalloc(sizeof(char *) * layer->numitems);
1163
0
    shape->numvalues = layer->numitems;
1164
1165
0
    for (i = 0; i < layer->numitems; i++) {
1166
0
      const size_t bufferSize = 1000;
1167
0
      char szWork[1000];
1168
1169
0
      szWork[0] = '\0';
1170
0
      if (EQUAL(layer->items[i], "x") && rlinfo->qc_x_reproj)
1171
0
        snprintf(szWork, bufferSize, "%.8g", rlinfo->qc_x_reproj[shapeindex]);
1172
0
      else if (EQUAL(layer->items[i], "y") && rlinfo->qc_y_reproj)
1173
0
        snprintf(szWork, bufferSize, "%.8g", rlinfo->qc_y_reproj[shapeindex]);
1174
1175
0
      else if (EQUAL(layer->items[i], "value_list") && rlinfo->qc_values) {
1176
0
        int iValue;
1177
1178
0
        for (iValue = 0; iValue < rlinfo->band_count; iValue++) {
1179
0
          if (iValue != 0)
1180
0
            strlcat(szWork, ",", bufferSize);
1181
1182
0
          snprintf(szWork + strlen(szWork), bufferSize - strlen(szWork), "%.8g",
1183
0
                   rlinfo->qc_values[shapeindex * rlinfo->band_count + iValue]);
1184
0
        }
1185
0
      } else if (EQUALN(layer->items[i], "value_", 6) && rlinfo->qc_values) {
1186
0
        int iValue = atoi(layer->items[i] + 6);
1187
0
        snprintf(szWork, bufferSize, "%.8g",
1188
0
                 rlinfo->qc_values[shapeindex * rlinfo->band_count + iValue]);
1189
0
      } else if (EQUAL(layer->items[i], "class") && rlinfo->qc_class) {
1190
0
        int p_class = rlinfo->qc_class[shapeindex];
1191
0
        if (layer->class[p_class] -> name != NULL)
1192
0
          snprintf(szWork, bufferSize, "%.999s", layer->class[p_class] -> name);
1193
0
        else
1194
0
          snprintf(szWork, bufferSize, "%d", p_class);
1195
0
      } else if (EQUAL(layer->items[i], "red") && rlinfo->qc_red)
1196
0
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_red[shapeindex]);
1197
0
      else if (EQUAL(layer->items[i], "green") && rlinfo->qc_green)
1198
0
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_green[shapeindex]);
1199
0
      else if (EQUAL(layer->items[i], "blue") && rlinfo->qc_blue)
1200
0
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_blue[shapeindex]);
1201
0
      else if (EQUAL(layer->items[i], "count") && rlinfo->qc_count)
1202
0
        snprintf(szWork, bufferSize, "%d", rlinfo->qc_count[shapeindex]);
1203
1204
0
      shape->values[i] = msStrdup(szWork);
1205
0
    }
1206
0
  }
1207
1208
  /* -------------------------------------------------------------------- */
1209
  /*      Eventually we should likey apply the geometry properly but      */
1210
  /*      we don't really care about the geometry for query purposes.     */
1211
  /* -------------------------------------------------------------------- */
1212
1213
0
  return MS_SUCCESS;
1214
0
}
1215
1216
/************************************************************************/
1217
/*                       msRASTERLayerGetItems()                        */
1218
/************************************************************************/
1219
1220
0
int msRASTERLayerGetItems(layerObj *layer) {
1221
0
  int maxnumitems = 0;
1222
0
  rasterLayerInfo *rlinfo = (rasterLayerInfo *)layer->layerinfo;
1223
1224
0
  if (rlinfo == NULL)
1225
0
    return MS_FAILURE;
1226
1227
0
  maxnumitems = 8 + (rlinfo->qc_values ? rlinfo->band_count : 0);
1228
0
  layer->items = (char **)msSmallCalloc(sizeof(char *), maxnumitems);
1229
1230
0
  layer->numitems = 0;
1231
0
  if (rlinfo->qc_x_reproj)
1232
0
    layer->items[layer->numitems++] = msStrdup("x");
1233
0
  if (rlinfo->qc_y_reproj)
1234
0
    layer->items[layer->numitems++] = msStrdup("y");
1235
0
  if (rlinfo->qc_values) {
1236
0
    int i;
1237
0
    for (i = 0; i < rlinfo->band_count; i++) {
1238
0
      char szName[100];
1239
0
      snprintf(szName, sizeof(szName), "value_%d", i);
1240
0
      layer->items[layer->numitems++] = msStrdup(szName);
1241
0
    }
1242
0
    layer->items[layer->numitems++] = msStrdup("value_list");
1243
0
  }
1244
0
  if (rlinfo->qc_class)
1245
0
    layer->items[layer->numitems++] = msStrdup("class");
1246
0
  if (rlinfo->qc_red)
1247
0
    layer->items[layer->numitems++] = msStrdup("red");
1248
0
  if (rlinfo->qc_green)
1249
0
    layer->items[layer->numitems++] = msStrdup("green");
1250
0
  if (rlinfo->qc_blue)
1251
0
    layer->items[layer->numitems++] = msStrdup("blue");
1252
0
  if (rlinfo->qc_count)
1253
0
    layer->items[layer->numitems++] = msStrdup("count");
1254
1255
0
  assert(layer->numitems <= maxnumitems);
1256
1257
0
  return msRASTERLayerInitItemInfo(layer);
1258
0
}
1259
1260
/************************************************************************/
1261
/*                       msRASTERLayerGetExtent()                       */
1262
/************************************************************************/
1263
1264
int msRASTERLayerGetExtent(layerObj *layer, rectObj *extent)
1265
1266
0
{
1267
0
  char szPath[MS_MAXPATHLEN];
1268
0
  mapObj *map = layer->map;
1269
0
  shapefileObj *tileshpfile;
1270
1271
0
  if ((!layer->data || strlen(layer->data) == 0) && layer->tileindex == NULL) {
1272
    /* should we be issuing a specific error about not supporting
1273
       extents for tileindexed raster layers? */
1274
0
    return MS_FAILURE;
1275
0
  }
1276
1277
0
  if (map == NULL)
1278
0
    return MS_FAILURE;
1279
1280
  /* If the layer use a tileindex, return the extent of the tileindex
1281
   * shapefile/referenced layer */
1282
0
  if (layer->tileindex) {
1283
0
    const int tilelayerindex = msGetLayerIndex(map, layer->tileindex);
1284
0
    if (tilelayerindex != -1) /* does the tileindex reference another layer */
1285
0
      return msLayerGetExtent(GET_LAYER(map, tilelayerindex), extent);
1286
0
    else {
1287
0
      tileshpfile = (shapefileObj *)malloc(sizeof(shapefileObj));
1288
0
      MS_CHECK_ALLOC(tileshpfile, sizeof(shapefileObj), MS_FAILURE);
1289
1290
0
      if (msShapefileOpen(tileshpfile, "rb",
1291
0
                          msBuildPath3(szPath, map->mappath, map->shapepath,
1292
0
                                       layer->tileindex),
1293
0
                          MS_TRUE) == -1)
1294
0
        if (msShapefileOpen(tileshpfile, "rb",
1295
0
                            msBuildPath(szPath, map->mappath, layer->tileindex),
1296
0
                            MS_TRUE) == -1)
1297
0
          return MS_FAILURE;
1298
1299
0
      *extent = tileshpfile->bounds;
1300
0
      msShapefileClose(tileshpfile);
1301
0
      free(tileshpfile);
1302
0
      return MS_SUCCESS;
1303
0
    }
1304
0
  }
1305
1306
0
  msTryBuildPath3(szPath, map->mappath, map->shapepath, layer->data);
1307
0
  char *decrypted_path = msDecryptStringTokens(map, szPath);
1308
0
  if (!decrypted_path)
1309
0
    return MS_FAILURE;
1310
1311
0
  char **connectionoptions =
1312
0
      msGetStringListFromHashTable(&(layer->connectionoptions));
1313
0
  GDALDatasetH hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
1314
0
                                (const char *const *)connectionoptions, NULL);
1315
0
  CSLDestroy(connectionoptions);
1316
0
  msFree(decrypted_path);
1317
0
  if (hDS == NULL) {
1318
0
    return MS_FAILURE;
1319
0
  }
1320
1321
0
  const int nXSize = GDALGetRasterXSize(hDS);
1322
0
  const int nYSize = GDALGetRasterYSize(hDS);
1323
0
  double adfGeoTransform[6] = {0};
1324
0
  const CPLErr eErr = GDALGetGeoTransform(hDS, adfGeoTransform);
1325
0
  if (eErr != CE_None) {
1326
0
    GDALClose(hDS);
1327
0
    return MS_FAILURE;
1328
0
  }
1329
1330
  /* If this appears to be an ungeoreferenced raster than flip it for
1331
     mapservers purposes. */
1332
0
  if (adfGeoTransform[5] == 1.0 && adfGeoTransform[3] == 0.0) {
1333
0
    adfGeoTransform[5] = -1.0;
1334
0
    adfGeoTransform[3] = nYSize;
1335
0
  }
1336
1337
0
  extent->minx = adfGeoTransform[0];
1338
0
  extent->maxy = adfGeoTransform[3];
1339
1340
0
  extent->maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1];
1341
0
  extent->miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5];
1342
1343
0
  return MS_SUCCESS;
1344
0
}
1345
1346
/************************************************************************/
1347
/*                     msRASTERLayerSetTimeFilter()                     */
1348
/*                                                                      */
1349
/*      This function is actually just used in the context of           */
1350
/*      setting a filter on the tileindex for time based queries.       */
1351
/*      For instance via WMS requests.  So it isn't really related      */
1352
/*      to the "raster query" support at all.                           */
1353
/*                                                                      */
1354
/*      If a local shapefile tileindex is in use, we will set a         */
1355
/*      backtics filter (shapefile compatible).  If another layer is    */
1356
/*      being used as the tileindex then we will forward the            */
1357
/*      SetTimeFilter call to it.  If there is no tileindex in          */
1358
/*      place, we do nothing.                                           */
1359
/************************************************************************/
1360
1361
int msRASTERLayerSetTimeFilter(layerObj *layer, const char *timestring,
1362
0
                               const char *timefield) {
1363
0
  int tilelayerindex;
1364
1365
  /* -------------------------------------------------------------------- */
1366
  /*      If we don't have a tileindex the time filter has no effect.     */
1367
  /* -------------------------------------------------------------------- */
1368
0
  if (layer->tileindex == NULL)
1369
0
    return MS_SUCCESS;
1370
1371
  /* -------------------------------------------------------------------- */
1372
  /*      Find the tileindex layer.                                       */
1373
  /* -------------------------------------------------------------------- */
1374
0
  tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
1375
1376
  /* -------------------------------------------------------------------- */
1377
  /*      If we are using a local shapefile as our tileindex (that is     */
1378
  /*      to say, the tileindex name is not of another layer), then we    */
1379
  /*      just install a backtics style filter on the raster layer.       */
1380
  /*      This is propagated to the "working layer" created for the       */
1381
  /*      tileindex by code in mapraster.c.                               */
1382
  /* -------------------------------------------------------------------- */
1383
0
  if (tilelayerindex == -1)
1384
0
    return msLayerMakeBackticsTimeFilter(layer, timestring, timefield);
1385
1386
  /* -------------------------------------------------------------------- */
1387
  /*      Otherwise we invoke the tileindex layers SetTimeFilter          */
1388
  /*      method.                                                         */
1389
  /* -------------------------------------------------------------------- */
1390
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
1391
0
    return MS_FAILURE;
1392
0
  return msLayerSetTimeFilter(layer->GET_LAYER(map, tilelayerindex), timestring,
1393
0
                              timefield);
1394
0
}
1395
1396
/************************************************************************/
1397
/*                msRASTERLayerInitializeVirtualTable()                 */
1398
/************************************************************************/
1399
1400
0
int msRASTERLayerInitializeVirtualTable(layerObj *layer) {
1401
0
  assert(layer != NULL);
1402
0
  assert(layer->vtable != NULL);
1403
1404
0
  layer->vtable->LayerInitItemInfo = msRASTERLayerInitItemInfo;
1405
0
  layer->vtable->LayerFreeItemInfo = msRASTERLayerFreeItemInfo;
1406
0
  layer->vtable->LayerOpen = msRASTERLayerOpen;
1407
0
  layer->vtable->LayerIsOpen = msRASTERLayerIsOpen;
1408
0
  layer->vtable->LayerWhichShapes = msRASTERLayerWhichShapes;
1409
0
  layer->vtable->LayerNextShape = msRASTERLayerNextShape;
1410
0
  layer->vtable->LayerGetShape = msRASTERLayerGetShape;
1411
  /* layer->vtable->LayerGetShapeCount, use default */
1412
0
  layer->vtable->LayerClose = msRASTERLayerClose;
1413
0
  layer->vtable->LayerGetItems = msRASTERLayerGetItems;
1414
0
  layer->vtable->LayerGetExtent = msRASTERLayerGetExtent;
1415
  /* layer->vtable->LayerGetAutoStyle, use default */
1416
  /* layer->vtable->LayerApplyFilterToLayer, use default */
1417
  /* layer->vtable->LayerCloseConnection = msRASTERLayerClose; */
1418
  /* we use backtics for proper tileindex shapefile functioning */
1419
0
  layer->vtable->LayerSetTimeFilter = msRASTERLayerSetTimeFilter;
1420
  /* layer->vtable->LayerCreateItems, use default */
1421
  /* layer->vtable->LayerGetNumFeatures, use default */
1422
1423
0
  return MS_SUCCESS;
1424
0
}