Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/maprasterlabel.cpp
Line
Count
Source
1
/**********************************************************************
2
 * Project:  MapServer
3
 * Purpose:  Raster Label Layer
4
 * Author:   Even Rouault, <even.rouault@spatialys.com>
5
 *
6
 **********************************************************************
7
 * Copyright (c) 2024, Even Rouault, <even.rouault@spatialys.com>
8
 * Copyright (c) 2011, Alan Boudreault, MapGears
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included in
18
 * all copies of this Software or works derived from this Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 **********************************************************************/
28
29
#include "mapserver.h"
30
31
#include <assert.h>
32
#include <math.h>
33
#include <stdbool.h>
34
#include "mapows.h"
35
#include "mapresample.h"
36
#include "mapthread.h"
37
38
#include <cmath>
39
#include <limits>
40
41
0
#define MSRASTERLABEL_NUMITEMS 1
42
0
#define MSRASTERLABEL_VALUE "value"
43
0
#define MSRASTERLABEL_VALUEINDEX -100
44
45
typedef enum { RASTER_FLOAT, RASTER_DOUBLE } RasterValueType;
46
47
typedef struct {
48
49
  /* query cache results */
50
  int query_results;
51
52
  int refcount;
53
54
  float *f_raster_values; /* raster values */
55
  double *d_raster_values;
56
57
  RasterValueType value_type; // flag to check if floats or doubles are used
58
59
  int width;
60
  int height;
61
  rectObj extent;
62
  int next_shape;
63
64
  /* To improve performance of GetShape() when queried on increasing shapeindex
65
   */
66
  long last_queried_shapeindex; // value in [0, query_results[ range
67
  size_t last_raster_off;       // value in [0, width*height[ range
68
69
  mapObj
70
      *mapToUseForWhichShapes; /* set if the map->extent and map->projection are
71
                                  valid in msRasterLabelLayerWhichShapes() */
72
73
} RasterLabelLayerInfo;
74
75
void msRasterLabelLayerUseMapExtentAndProjectionForNextWhichShapes(
76
0
    layerObj *layer, mapObj *map) {
77
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
78
0
  rllinfo->mapToUseForWhichShapes = map;
79
0
}
80
81
0
static int msRasterLabelLayerInitItemInfo(layerObj *layer) {
82
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
83
0
  int i;
84
0
  int *itemindexes;
85
0
  int failed = 0;
86
87
0
  if (layer->numitems == 0)
88
0
    return MS_SUCCESS;
89
90
0
  if (rllinfo == NULL) {
91
0
    msSetError(MS_MISCERR, "Assertion failed: GDAL layer not opened!!!",
92
0
               "msRasterLabelLayerInitItemInfo()");
93
0
    return (MS_FAILURE);
94
0
  }
95
96
0
  if (layer->iteminfo)
97
0
    free(layer->iteminfo);
98
99
0
  if ((layer->iteminfo = (int *)malloc(sizeof(int) * layer->numitems)) ==
100
0
      NULL) {
101
0
    msSetError(MS_MEMERR, NULL, "msRasterLabelLayerInitItemInfo()");
102
0
    return (MS_FAILURE);
103
0
  }
104
105
0
  itemindexes = (int *)layer->iteminfo;
106
0
  for (i = 0; i < layer->numitems; i++) {
107
    /* Special attribute names. */
108
0
    if (EQUAL(layer->items[i], MSRASTERLABEL_VALUE)) {
109
0
      itemindexes[i] = MSRASTERLABEL_VALUEINDEX;
110
0
    } else {
111
0
      itemindexes[i] = -1;
112
0
      msSetError(MS_MISCERR, "Invalid Field name: %s",
113
0
                 "msRasterLabelLayerInitItemInfo()", layer->items[i]);
114
0
      failed = 1;
115
0
    }
116
0
  }
117
118
0
  return failed ? (MS_FAILURE) : (MS_SUCCESS);
119
0
}
120
121
0
void msRasterLabelLayerFreeItemInfo(layerObj *layer) {
122
0
  if (layer->iteminfo)
123
0
    free(layer->iteminfo);
124
0
  layer->iteminfo = NULL;
125
0
}
126
127
0
static void msRasterLabelLayerInfoInitialize(layerObj *layer) {
128
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
129
130
0
  if (rllinfo != NULL)
131
0
    return;
132
133
0
  rllinfo =
134
0
      (RasterLabelLayerInfo *)msSmallCalloc(1, sizeof(RasterLabelLayerInfo));
135
0
  layer->layerinfo = rllinfo;
136
137
0
  rllinfo->value_type = RASTER_FLOAT;
138
0
  rllinfo->f_raster_values = NULL;
139
0
  rllinfo->d_raster_values = NULL;
140
0
  rllinfo->width = 0;
141
0
  rllinfo->height = 0;
142
143
  /* Set attribute type to Real, unless the user has explicitly set */
144
  /* something else. */
145
0
  {
146
0
    const char *const items[] = {
147
0
        MSRASTERLABEL_VALUE,
148
0
    };
149
0
    size_t i;
150
0
    for (i = 0; i < sizeof(items) / sizeof(items[0]); ++i) {
151
0
      char szTmp[100];
152
0
      snprintf(szTmp, sizeof(szTmp), "%s_type", items[i]);
153
0
      if (msOWSLookupMetadata(&(layer->metadata), "OFG", szTmp) == NULL) {
154
0
        snprintf(szTmp, sizeof(szTmp), "gml_%s_type", items[i]);
155
0
        msInsertHashTable(&(layer->metadata), szTmp, "Real");
156
0
      }
157
0
    }
158
0
  }
159
0
}
160
161
static void msRasterLabelLayerInfoFree(layerObj *layer)
162
163
0
{
164
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
165
166
0
  if (rllinfo == NULL)
167
0
    return;
168
169
0
  free(rllinfo->f_raster_values);
170
0
  free(rllinfo->d_raster_values);
171
0
  free(rllinfo);
172
173
0
  layer->layerinfo = NULL;
174
0
}
175
176
0
int msRasterLabelLayerOpen(layerObj *layer) {
177
  /* If we don't have info, initialize an empty one now */
178
0
  if (layer->layerinfo == NULL)
179
0
    msRasterLabelLayerInfoInitialize(layer);
180
0
  if (layer->layerinfo == NULL)
181
0
    return MS_FAILURE;
182
183
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
184
185
0
  rllinfo->refcount = rllinfo->refcount + 1;
186
187
0
  return MS_SUCCESS;
188
0
}
189
190
0
int msRasterLabelLayerIsOpen(layerObj *layer) {
191
0
  if (layer->layerinfo)
192
0
    return MS_TRUE;
193
0
  return MS_FALSE;
194
0
}
195
196
0
int msRasterLabelLayerClose(layerObj *layer) {
197
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
198
199
0
  if (rllinfo != NULL) {
200
0
    rllinfo->refcount--;
201
202
0
    if (rllinfo->refcount < 1)
203
0
      msRasterLabelLayerInfoFree(layer);
204
0
  }
205
0
  return MS_SUCCESS;
206
0
}
207
208
0
int msRasterLabelLayerGetItems(layerObj *layer) {
209
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
210
211
0
  if (rllinfo == NULL)
212
0
    return MS_FAILURE;
213
214
0
  layer->numitems = 0;
215
0
  layer->items =
216
0
      (char **)msSmallCalloc(sizeof(char *), (MSRASTERLABEL_NUMITEMS + 1));
217
218
0
  layer->items[layer->numitems++] = msStrdup(MSRASTERLABEL_VALUE);
219
0
  layer->items[layer->numitems] = NULL;
220
221
0
  return msRasterLabelLayerInitItemInfo(layer);
222
0
}
223
224
/**********************************************************************
225
 *                     msRasterLabelGetValues()
226
 **********************************************************************/
227
0
static char **msRasterLabelGetValues(layerObj *layer, double value) {
228
0
  char **values;
229
0
  int i = 0;
230
0
  char tmp[100];
231
0
  int *itemindexes = (int *)layer->iteminfo;
232
233
0
  if (layer->numitems == 0)
234
0
    return (NULL);
235
236
0
  if (!layer->iteminfo) { /* Should not happen... but just in case! */
237
0
    if (msRasterLabelLayerInitItemInfo(layer) != MS_SUCCESS)
238
0
      return NULL;
239
0
    itemindexes = (int *)layer->iteminfo; /* reassign after malloc */
240
0
  }
241
242
0
  if ((values = (char **)malloc(sizeof(char *) * layer->numitems)) == NULL) {
243
0
    msSetError(MS_MEMERR, NULL, "msRasterLabelGetValues()");
244
0
    return (NULL);
245
0
  }
246
247
0
  for (i = 0; i < layer->numitems; i++) {
248
0
    if (itemindexes[i] == MSRASTERLABEL_VALUEINDEX) {
249
0
      snprintf(tmp, sizeof(tmp), "%f", value);
250
0
      values[i] = msStrdup(tmp);
251
0
    } else {
252
0
      values[i] = NULL;
253
0
    }
254
0
  }
255
256
0
  return values;
257
0
}
258
259
// Float overload
260
0
static char **msRasterLabelGetValues(layerObj *layer, float value) {
261
  // cast float to double and call the double version
262
0
  return msRasterLabelGetValues(layer, static_cast<double>(value));
263
0
}
264
265
0
rectObj msRasterLabelGetSearchRect(layerObj *layer, mapObj *map) {
266
0
  rectObj searchrect = map->extent;
267
0
  int bDone = MS_FALSE;
268
269
  /* For RasterLabel, it is important that the searchrect is not too large */
270
  /* to avoid insufficient intermediate raster resolution, which could */
271
  /* happen if we use the default code path, given potential reprojection */
272
  /* issues when using a map extent that is not in the validity area of */
273
  /* the layer projection. */
274
0
  if (!layer->projection.gt.need_geotransform &&
275
0
      !(msProjIsGeographicCRS(&(map->projection)) &&
276
0
        msProjIsGeographicCRS(&(layer->projection)))) {
277
0
    rectObj layer_ori_extent;
278
279
0
    if (msLayerGetExtent(layer, &layer_ori_extent) == MS_SUCCESS) {
280
0
      projectionObj map_proj;
281
282
0
      double map_extent_minx = map->extent.minx;
283
0
      double map_extent_miny = map->extent.miny;
284
0
      double map_extent_maxx = map->extent.maxx;
285
0
      double map_extent_maxy = map->extent.maxy;
286
0
      rectObj layer_extent = layer_ori_extent;
287
288
      /* Create a variant of map->projection without geotransform for */
289
      /* conveniency */
290
0
      msInitProjection(&map_proj);
291
0
      msCopyProjection(&map_proj, &map->projection);
292
0
      map_proj.gt.need_geotransform = MS_FALSE;
293
0
      if (map->projection.gt.need_geotransform) {
294
0
        map_extent_minx =
295
0
            map->projection.gt.geotransform[0] +
296
0
            map->projection.gt.geotransform[1] * map->extent.minx +
297
0
            map->projection.gt.geotransform[2] * map->extent.miny;
298
0
        map_extent_miny =
299
0
            map->projection.gt.geotransform[3] +
300
0
            map->projection.gt.geotransform[4] * map->extent.minx +
301
0
            map->projection.gt.geotransform[5] * map->extent.miny;
302
0
        map_extent_maxx =
303
0
            map->projection.gt.geotransform[0] +
304
0
            map->projection.gt.geotransform[1] * map->extent.maxx +
305
0
            map->projection.gt.geotransform[2] * map->extent.maxy;
306
0
        map_extent_maxy =
307
0
            map->projection.gt.geotransform[3] +
308
0
            map->projection.gt.geotransform[4] * map->extent.maxx +
309
0
            map->projection.gt.geotransform[5] * map->extent.maxy;
310
0
      }
311
312
      /* Reproject layer extent to map projection */
313
0
      msProjectRect(&layer->projection, &map_proj, &layer_extent);
314
315
0
      if (layer_extent.minx <= map_extent_minx &&
316
0
          layer_extent.miny <= map_extent_miny &&
317
0
          layer_extent.maxx >= map_extent_maxx &&
318
0
          layer_extent.maxy >= map_extent_maxy) {
319
        /* do nothing special if area to map is inside layer extent */
320
0
      } else {
321
0
        if (layer_extent.minx >= map_extent_minx &&
322
0
            layer_extent.maxx <= map_extent_maxx &&
323
0
            layer_extent.miny >= map_extent_miny &&
324
0
            layer_extent.maxy <= map_extent_maxy) {
325
          /* if the area to map is larger than the layer extent, then */
326
          /* use full layer extent and add some margin to reflect the */
327
          /* proportion of the useful area over the requested bbox */
328
0
          double extra_x = (map_extent_maxx - map_extent_minx) /
329
0
                           (layer_extent.maxx - layer_extent.minx) *
330
0
                           (layer_ori_extent.maxx - layer_ori_extent.minx);
331
0
          double extra_y = (map_extent_maxy - map_extent_miny) /
332
0
                           (layer_extent.maxy - layer_extent.miny) *
333
0
                           (layer_ori_extent.maxy - layer_ori_extent.miny);
334
0
          searchrect.minx = layer_ori_extent.minx - extra_x / 2;
335
0
          searchrect.maxx = layer_ori_extent.maxx + extra_x / 2;
336
0
          searchrect.miny = layer_ori_extent.miny - extra_y / 2;
337
0
          searchrect.maxy = layer_ori_extent.maxy + extra_y / 2;
338
0
        } else {
339
          /* otherwise clip the map extent with the reprojected layer */
340
          /* extent */
341
0
          searchrect.minx = MS_MAX(map_extent_minx, layer_extent.minx);
342
0
          searchrect.maxx = MS_MIN(map_extent_maxx, layer_extent.maxx);
343
0
          searchrect.miny = MS_MAX(map_extent_miny, layer_extent.miny);
344
0
          searchrect.maxy = MS_MIN(map_extent_maxy, layer_extent.maxy);
345
          /* and reproject into the layer projection */
346
0
          msProjectRect(&map_proj, &layer->projection, &searchrect);
347
0
        }
348
0
        bDone = MS_TRUE;
349
0
      }
350
351
0
      msFreeProjection(&map_proj);
352
0
    }
353
0
  }
354
355
0
  if (!bDone)
356
0
    msProjectRect(&map->projection, &layer->projection,
357
0
                  &searchrect); /* project the searchrect to source coords */
358
359
0
  return searchrect;
360
0
}
361
362
0
static GDALDatasetH OpenRaster(layerObj *layer, std::string &osDecryptedPath) {
363
0
  GDALDatasetH hDS = NULL;
364
0
  char *decrypted_path;
365
366
0
  if (strncmp(layer->data, "<VRTDataset", strlen("<VRTDataset")) == 0) {
367
0
    decrypted_path = msStrdup(layer->data);
368
0
  } else {
369
0
    char szPath[MS_MAXPATHLEN];
370
0
    msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
371
0
                    layer->data);
372
0
    decrypted_path = msDecryptStringTokens(layer->map, szPath);
373
0
  }
374
375
0
  if (decrypted_path) {
376
0
    char **connectionoptions;
377
0
    GDALAllRegister();
378
0
    connectionoptions =
379
0
        msGetStringListFromHashTable(&(layer->connectionoptions));
380
0
    hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
381
0
                     (const char *const *)connectionoptions, NULL);
382
0
    CSLDestroy(connectionoptions);
383
384
0
    osDecryptedPath = decrypted_path;
385
0
    msFree(decrypted_path);
386
0
    if (!hDS) {
387
0
      msDebug(
388
0
          "msRasterLabelLayerWhichShapes(): cannot open DATA %s of layer %s\n",
389
0
          layer->data, layer->name);
390
0
      msSetError(MS_MISCERR, "Cannot open DATA of layer %s",
391
0
                 "msRasterLabelLayerWhichShapes()", layer->name);
392
0
    }
393
0
  }
394
0
  return hDS;
395
0
}
396
397
0
int msRasterLabelLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery) {
398
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
399
0
  imageObj *image_tmp;
400
0
  outputFormatObj *outputformat = NULL;
401
0
  mapObj *map_tmp;
402
0
  double map_cellsize;
403
0
  unsigned int spacing;
404
0
  int width, height;
405
0
  char **alteredProcessing = NULL, *saved_layer_mask;
406
0
  char **savedProcessing = NULL;
407
0
  int bHasLonWrap = MS_FALSE;
408
0
  double dfLonWrap = 0.0;
409
0
  rectObj oldLayerExtent;
410
0
  char *oldLayerData = NULL;
411
0
  projectionObj oldLayerProjection;
412
0
  int ret;
413
414
0
  memset(&oldLayerExtent, 0, sizeof(oldLayerExtent));
415
0
  memset(&oldLayerProjection, 0, sizeof(oldLayerProjection));
416
417
0
  if (layer->debug)
418
0
    msDebug("Entering msRasterLabelLayerWhichShapes().\n");
419
420
0
  if (rllinfo == NULL)
421
0
    return MS_FAILURE;
422
423
0
  std::string osDecryptedPath;
424
0
  GDALDatasetH hDS = OpenRaster(layer, osDecryptedPath);
425
0
  if (!hDS) {
426
0
    return MS_FAILURE;
427
0
  }
428
429
0
  if (CSLFetchNameValue(layer->processing, "BANDS") == NULL) {
430
0
    if (GDALGetRasterCount(hDS) == 1) {
431
0
      msLayerSetProcessingKey(layer, "BANDS", "1");
432
0
    } else {
433
0
      GDALClose(hDS);
434
0
      msSetError(MS_MISCERR,
435
0
                 "BANDS processing option is required for RASTER_LABEL layer. "
436
0
                 "You have to specify 1 band.",
437
0
                 "msRasterLabelLayerWhichShapes()");
438
0
      return MS_FAILURE;
439
0
    }
440
0
  }
441
442
  /*
443
  ** Allocate mapObj structure
444
  */
445
0
  map_tmp = (mapObj *)msSmallCalloc(sizeof(mapObj), 1);
446
0
  if (initMap(map_tmp) == -1) { /* initialize this map */
447
0
    msFree(map_tmp);
448
0
    GDALClose(hDS);
449
0
    return (MS_FAILURE);
450
0
  }
451
452
  /* Initialize our dummy map */
453
0
  MS_INIT_COLOR(map_tmp->imagecolor, 255, 255, 255, 255);
454
0
  map_tmp->resolution = layer->map->resolution;
455
0
  map_tmp->defresolution = layer->map->defresolution;
456
457
0
  outputformat = (outputFormatObj *)msSmallCalloc(1, sizeof(outputFormatObj));
458
0
  outputformat->bands = 1;
459
0
  outputformat->name = NULL;
460
0
  outputformat->driver = NULL;
461
0
  outputformat->refcount = 0;
462
0
  outputformat->vtable = NULL;
463
0
  outputformat->device = NULL;
464
0
  outputformat->renderer = MS_RENDER_WITH_RAWDATA;
465
466
  // check type of the first band only to decide output format
467
0
  switch (GDALGetRasterDataType(GDALGetRasterBand(hDS, 1))) {
468
0
  case GDT_Int32:
469
0
  case GDT_Float64:
470
0
    outputformat->imagemode = MS_IMAGEMODE_FLOAT64;
471
0
    rllinfo->value_type = RASTER_DOUBLE;
472
0
    break;
473
0
  default:
474
0
    outputformat->imagemode = MS_IMAGEMODE_FLOAT32;
475
0
    break;
476
0
  }
477
478
0
  msAppendOutputFormat(map_tmp, outputformat);
479
480
0
  msCopyHashTable(&map_tmp->configoptions, &layer->map->configoptions);
481
0
  map_tmp->mappath = msStrdup(layer->map->mappath);
482
0
  map_tmp->shapepath = msStrdup(layer->map->shapepath);
483
0
  map_tmp->gt.rotation_angle = 0.0;
484
485
  /* Custom msCopyProjection() that removes lon_wrap parameter */
486
0
  {
487
0
    int i;
488
489
0
    map_tmp->projection.numargs = 0;
490
0
    map_tmp->projection.gt = layer->projection.gt;
491
492
0
    for (i = 0; i < layer->projection.numargs; i++) {
493
0
      if (strncmp(layer->projection.args[i],
494
0
                  "lon_wrap=", strlen("lon_wrap=")) == 0) {
495
0
        bHasLonWrap = MS_TRUE;
496
0
        dfLonWrap = atof(layer->projection.args[i] + strlen("lon_wrap="));
497
0
      } else {
498
0
        map_tmp->projection.args[map_tmp->projection.numargs++] =
499
0
            msStrdup(layer->projection.args[i]);
500
0
      }
501
0
    }
502
0
    if (map_tmp->projection.numargs != 0) {
503
0
      msProcessProjection(&(map_tmp->projection));
504
0
    }
505
506
0
    map_tmp->projection.wellknownprojection =
507
0
        layer->projection.wellknownprojection;
508
0
  }
509
510
  /* Very special case to improve quality for rasters referenced from lon=0 to
511
   * 360 */
512
  /* We create a temporary VRT that swiches the 2 hemispheres, and then we */
513
  /* modify the georeferencing to be in the more standard [-180, 180] range */
514
  /* and we adjust the layer->data, extent and projection accordingly */
515
0
  if (layer->tileindex == NULL && rllinfo->mapToUseForWhichShapes &&
516
0
      bHasLonWrap && dfLonWrap == 180.0) {
517
0
    rectObj layerExtent;
518
0
    msLayerGetExtent(layer, &layerExtent);
519
0
    if (layerExtent.minx == 0 && layerExtent.maxx == 360) {
520
0
      int iBand;
521
0
      int nXSize = GDALGetRasterXSize(hDS);
522
0
      int nYSize = GDALGetRasterYSize(hDS);
523
0
      int nBands = GDALGetRasterCount(hDS);
524
0
      int nMaxLen = 100 + nBands * (800 + 2 * osDecryptedPath.size());
525
0
      int nOffset = 0;
526
0
      char *pszInlineVRT = static_cast<char *>(msSmallMalloc(nMaxLen));
527
528
0
      snprintf(pszInlineVRT, nMaxLen,
529
0
               "<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">", nXSize,
530
0
               nYSize);
531
0
      nOffset = strlen(pszInlineVRT);
532
0
      for (iBand = 1; iBand <= nBands; iBand++) {
533
0
        const char *pszDataType = "Byte";
534
0
        switch (GDALGetRasterDataType(GDALGetRasterBand(hDS, iBand))) {
535
0
        case GDT_Byte:
536
0
          pszDataType = "Byte";
537
0
          break;
538
0
        case GDT_Int16:
539
0
          pszDataType = "Int16";
540
0
          break;
541
0
        case GDT_UInt16:
542
0
          pszDataType = "UInt16";
543
0
          break;
544
0
        case GDT_Int32:
545
0
          pszDataType = "Int32";
546
0
          break;
547
0
        case GDT_UInt32:
548
0
          pszDataType = "UInt32";
549
0
          break;
550
0
        case GDT_Float32:
551
0
          pszDataType = "Float32";
552
0
          break;
553
0
        case GDT_Float64:
554
0
          pszDataType = "Float64";
555
0
          break;
556
0
        default:
557
0
          break;
558
0
        }
559
560
0
        snprintf(pszInlineVRT + nOffset, nMaxLen - nOffset,
561
0
                 "    <VRTRasterBand dataType=\"%s\" band=\"%d\">"
562
0
                 "        <SimpleSource>"
563
0
                 "            <SourceFilename "
564
0
                 "relativeToVrt=\"1\"><![CDATA[%s]]></SourceFilename>"
565
0
                 "            <SourceBand>%d</SourceBand>"
566
0
                 "            <SrcRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
567
0
                 "ySize=\"%d\"/>"
568
0
                 "            <DstRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
569
0
                 "ySize=\"%d\"/>"
570
0
                 "        </SimpleSource>"
571
0
                 "        <SimpleSource>"
572
0
                 "            <SourceFilename "
573
0
                 "relativeToVrt=\"1\"><![CDATA[%s]]></SourceFilename>"
574
0
                 "            <SourceBand>%d</SourceBand>"
575
0
                 "            <SrcRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
576
0
                 "ySize=\"%d\"/>"
577
0
                 "            <DstRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
578
0
                 "ySize=\"%d\"/>"
579
0
                 "        </SimpleSource>"
580
0
                 "    </VRTRasterBand>",
581
0
                 pszDataType, iBand, osDecryptedPath.c_str(), iBand, nXSize / 2,
582
0
                 0, nXSize - nXSize / 2, nYSize, 0, 0, nXSize - nXSize / 2,
583
0
                 nYSize, osDecryptedPath.c_str(), iBand, 0, 0, nXSize / 2,
584
0
                 nYSize, nXSize - nXSize / 2, 0, nXSize / 2, nYSize);
585
586
0
        nOffset += strlen(pszInlineVRT + nOffset);
587
0
      }
588
0
      snprintf(pszInlineVRT + nOffset, nMaxLen - nOffset, "</VRTDataset>");
589
590
0
      oldLayerExtent = layer->extent;
591
0
      oldLayerData = layer->data;
592
0
      oldLayerProjection = layer->projection;
593
0
      layer->extent.minx = -180;
594
0
      layer->extent.maxx = 180;
595
0
      layer->data = pszInlineVRT;
596
0
      layer->projection = map_tmp->projection;
597
598
      /* map_tmp->projection is actually layer->projection without lon_wrap */
599
0
      rect = rllinfo->mapToUseForWhichShapes->extent;
600
0
      msProjectRect(&rllinfo->mapToUseForWhichShapes->projection,
601
0
                    &map_tmp->projection, &rect);
602
0
      bHasLonWrap = MS_FALSE;
603
0
    }
604
0
  }
605
606
0
  if (isQuery) {
607
    /* For query mode, use layer->map->extent reprojected rather than */
608
    /* the provided rect. Generic query code will filter returned features. */
609
0
    rect = msRasterLabelGetSearchRect(layer, layer->map);
610
0
  }
611
612
  /* -------------------------------------------------------------------- */
613
  /*      Determine desired spacing.  Default to 32 if not otherwise set  */
614
  /* -------------------------------------------------------------------- */
615
0
  spacing = 32;
616
0
  if (CSLFetchNameValue(layer->processing, "LABEL_SPACING") != NULL) {
617
0
    spacing = atoi(CSLFetchNameValue(layer->processing, "LABEL_SPACING"));
618
0
    if (spacing == 0)
619
0
      spacing = 32;
620
0
  }
621
622
0
  width = (int)(layer->map->width / spacing);
623
0
  height = (int)(layer->map->height / spacing);
624
625
0
  map_cellsize = MS_MAX(MS_CELLSIZE(rect.minx, rect.maxx, layer->map->width),
626
0
                        MS_CELLSIZE(rect.miny, rect.maxy, layer->map->height));
627
0
  map_tmp->cellsize = map_cellsize * spacing;
628
629
  // By default, do not oversample beyond the resolution of the layer
630
0
  if (!CPLTestBoolean(
631
0
          CSLFetchNameValueDef(layer->processing, "ALLOW_OVERSAMPLE", "NO"))) {
632
0
    double adfGT[6];
633
0
    if (GDALGetGeoTransform(hDS, adfGT) == CE_None) {
634
0
      if (!msProjectionsDiffer(&(layer->map->projection),
635
0
                               &(layer->projection)) &&
636
0
          map_tmp->cellsize < adfGT[1]) {
637
0
        map_tmp->cellsize = adfGT[1];
638
0
        width = MS_MAX(2, (int)(rect.maxx - rect.minx) / map_tmp->cellsize);
639
0
        height = MS_MAX(2, (int)(rect.maxy - rect.miny) / map_tmp->cellsize);
640
0
        map_tmp->cellsize = MS_MAX(MS_CELLSIZE(rect.minx, rect.maxx, width),
641
0
                                   MS_CELLSIZE(rect.miny, rect.maxy, height));
642
0
      }
643
0
    }
644
0
  }
645
646
0
  map_tmp->extent.minx =
647
0
      rect.minx - (0.5 * map_cellsize) + (0.5 * map_tmp->cellsize);
648
0
  map_tmp->extent.miny =
649
0
      rect.miny - (0.5 * map_cellsize) + (0.5 * map_tmp->cellsize);
650
0
  map_tmp->extent.maxx =
651
0
      map_tmp->extent.minx + ((width - 1) * map_tmp->cellsize);
652
0
  map_tmp->extent.maxy =
653
0
      map_tmp->extent.miny + ((height - 1) * map_tmp->cellsize);
654
655
0
  GDALClose(hDS);
656
657
0
  if (bHasLonWrap && dfLonWrap == 180.0) {
658
0
    if (map_tmp->extent.minx >= 180) {
659
      /* Request on the right half of the shifted raster (= western hemisphere)
660
       */
661
0
      map_tmp->extent.minx -= 360;
662
0
      map_tmp->extent.maxx -= 360;
663
0
    } else if (map_tmp->extent.maxx >= 180.0) {
664
      /* Request spanning on the 2 hemispheres => drawing whole planet */
665
      /* Take only into account vertical resolution, as horizontal one */
666
      /* will be unreliable (assuming square pixels...) */
667
0
      map_cellsize = MS_CELLSIZE(rect.miny, rect.maxy, layer->map->height);
668
0
      map_tmp->cellsize = map_cellsize * spacing;
669
670
0
      width = 360.0 / map_tmp->cellsize;
671
0
      map_tmp->extent.minx = -180.0 + (0.5 * map_tmp->cellsize);
672
0
      map_tmp->extent.maxx = 180.0 - (0.5 * map_tmp->cellsize);
673
0
    }
674
0
  }
675
676
0
  if (layer->debug)
677
0
    msDebug("msRasterLabelLayerWhichShapes(): width: %d, height: %d, cellsize: "
678
0
            "%g\n",
679
0
            width, height, map_tmp->cellsize);
680
681
0
  if (layer->debug == MS_DEBUGLEVEL_VVV)
682
0
    msDebug("msRasterLabelLayerWhichShapes(): extent: %g %g %g %g\n",
683
0
            map_tmp->extent.minx, map_tmp->extent.miny, map_tmp->extent.maxx,
684
0
            map_tmp->extent.maxy);
685
686
  /* important to use that function, to compute map
687
     geotransform, used by the resampling*/
688
0
  msMapSetSize(map_tmp, width, height);
689
690
0
  if (layer->debug == MS_DEBUGLEVEL_VVV)
691
0
    msDebug(
692
0
        "msRasterLabelLayerWhichShapes(): geotransform: %g %g %g %g %g %g\n",
693
0
        map_tmp->gt.geotransform[0], map_tmp->gt.geotransform[1],
694
0
        map_tmp->gt.geotransform[2], map_tmp->gt.geotransform[3],
695
0
        map_tmp->gt.geotransform[4], map_tmp->gt.geotransform[5]);
696
697
0
  rllinfo->extent = map_tmp->extent;
698
699
0
  image_tmp = msImageCreate(width, height, map_tmp->outputformatlist[0], NULL,
700
0
                            NULL, map_tmp->resolution, map_tmp->defresolution,
701
0
                            &(map_tmp->imagecolor));
702
703
  /* Default set to AVERAGE resampling */
704
0
  if (CSLFetchNameValue(layer->processing, "RESAMPLE") == NULL) {
705
0
    alteredProcessing = CSLDuplicate(layer->processing);
706
0
    alteredProcessing =
707
0
        CSLSetNameValue(alteredProcessing, "RESAMPLE", "AVERAGE");
708
0
    savedProcessing = layer->processing;
709
0
    layer->processing = alteredProcessing;
710
0
  }
711
712
  /* disable masking at this level: we don't want to apply the mask at the
713
   * raster level, it will be applied with the correct cellsize and image size
714
   * in the vector rendering phase.
715
   */
716
0
  saved_layer_mask = layer->mask;
717
0
  layer->mask = NULL;
718
0
  ret = msDrawRasterLayerLow(map_tmp, layer, image_tmp, NULL);
719
720
  /* restore layer attributes if we went through the above on-the-fly VRT */
721
0
  if (oldLayerData) {
722
0
    msFree(layer->data);
723
0
    layer->data = oldLayerData;
724
0
    layer->extent = oldLayerExtent;
725
0
    layer->projection = oldLayerProjection;
726
0
  }
727
728
  /* restore layer mask */
729
0
  layer->mask = saved_layer_mask;
730
731
  /* restore the saved processing */
732
0
  if (alteredProcessing != NULL) {
733
0
    layer->processing = savedProcessing;
734
0
    CSLDestroy(alteredProcessing);
735
0
  }
736
737
0
  if (ret == MS_FAILURE) {
738
0
    msSetError(MS_MISCERR, "Unable to draw raster data.",
739
0
               "msRasterLabelLayerWhichShapes()");
740
741
0
    msFreeMap(map_tmp);
742
0
    msFreeImage(image_tmp);
743
744
0
    return MS_FAILURE;
745
0
  }
746
747
  /* Update our rl layer structure */
748
0
  rllinfo->width = width;
749
0
  rllinfo->height = height;
750
0
  rllinfo->query_results = 0;
751
752
0
  rllinfo->last_queried_shapeindex = 0;
753
0
  rllinfo->last_raster_off = 0;
754
755
0
  free(rllinfo->f_raster_values);
756
0
  free(rllinfo->d_raster_values);
757
758
0
  if (rllinfo->value_type == RASTER_DOUBLE) {
759
0
    rllinfo->d_raster_values =
760
0
        (double *)msSmallMalloc(sizeof(double) * width * height);
761
762
0
    for (size_t off = 0; off < static_cast<size_t>(width) * height; ++off) {
763
0
      if (MS_GET_BIT(image_tmp->img_mask, off)) {
764
0
        rllinfo->d_raster_values[off] = image_tmp->img.raw_double[off];
765
0
        rllinfo->query_results++;
766
0
      } else
767
0
        rllinfo->d_raster_values[off] =
768
0
            std::numeric_limits<double>::quiet_NaN();
769
0
    }
770
0
  } else {
771
0
    rllinfo->f_raster_values =
772
0
        (float *)msSmallMalloc(sizeof(float) * width * height);
773
774
0
    for (size_t off = 0; off < static_cast<size_t>(width) * height; ++off) {
775
0
      if (MS_GET_BIT(image_tmp->img_mask, off)) {
776
0
        rllinfo->f_raster_values[off] = image_tmp->img.raw_float[off];
777
0
        rllinfo->query_results++;
778
0
      } else
779
0
        rllinfo->f_raster_values[off] = std::numeric_limits<float>::quiet_NaN();
780
0
    }
781
0
  }
782
783
0
  msFreeImage(image_tmp); /* we do not need the imageObj anymore */
784
0
  msFreeMap(map_tmp);
785
786
0
  rllinfo->next_shape = 0;
787
788
0
  return MS_SUCCESS;
789
0
}
790
791
int msRasterLabelLayerGetShape(layerObj *layer, shapeObj *shape,
792
0
                               resultObj *record) {
793
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
794
0
  lineObj line;
795
0
  pointObj point;
796
0
  const long shapeindex = record->shapeindex;
797
798
0
  msFreeShape(shape);
799
0
  shape->type = MS_SHAPE_NULL;
800
801
0
  if (shapeindex < 0 || shapeindex >= rllinfo->query_results) {
802
0
    msSetError(MS_MISCERR,
803
0
               "Out of range shape index requested.  Requested %ld\n"
804
0
               "but only %d shapes available.",
805
0
               "msRasterLabelLayerGetShape()", shapeindex,
806
0
               rllinfo->query_results);
807
0
    return MS_FAILURE;
808
0
  }
809
810
  /* loop to the next valid value */
811
0
  size_t raster_off = (shapeindex >= rllinfo->last_queried_shapeindex)
812
0
                          ? rllinfo->last_raster_off
813
0
                          : 0;
814
0
  for (long curshapeindex = (shapeindex >= rllinfo->last_queried_shapeindex)
815
0
                                ? rllinfo->last_queried_shapeindex
816
0
                                : 0;
817
0
       raster_off < static_cast<size_t>(rllinfo->width) * rllinfo->height;
818
0
       ++raster_off) {
819
820
0
    bool is_valid = false;
821
0
    if (rllinfo->value_type == RASTER_DOUBLE) {
822
0
      is_valid = !std::isnan(rllinfo->d_raster_values[raster_off]);
823
0
    } else {
824
0
      is_valid = !std::isnan(rllinfo->f_raster_values[raster_off]);
825
0
    }
826
827
0
    if (is_valid) {
828
0
      if (curshapeindex == shapeindex) {
829
0
        rllinfo->last_queried_shapeindex = shapeindex;
830
0
        rllinfo->last_raster_off = raster_off;
831
0
        break;
832
0
      }
833
0
      ++curshapeindex;
834
0
    }
835
0
  }
836
0
  assert(raster_off < static_cast<size_t>(rllinfo->width) * rllinfo->height);
837
838
0
  const int x = static_cast<int>(raster_off % rllinfo->width);
839
0
  const int y = static_cast<int>(raster_off / rllinfo->width);
840
0
  point.x = Pix2Georef(x, 0, rllinfo->width - 1, rllinfo->extent.minx,
841
0
                       rllinfo->extent.maxx, MS_FALSE);
842
0
  point.y = Pix2Georef(y, 0, rllinfo->height - 1, rllinfo->extent.miny,
843
0
                       rllinfo->extent.maxy, MS_TRUE);
844
0
  if (layer->debug == MS_DEBUGLEVEL_VVV)
845
0
    msDebug("msRasterLabelLayerWhichShapes(): shapeindex: %ld, x: %g, y: %g\n",
846
0
            shapeindex, point.x, point.y);
847
848
0
  point.m = 0.0;
849
850
0
  shape->type = MS_SHAPE_POINT;
851
0
  line.numpoints = 1;
852
0
  line.point = &point;
853
0
  msAddLine(shape, &line);
854
0
  msComputeBounds(shape);
855
856
0
  shape->numvalues = layer->numitems;
857
0
  if (rllinfo->value_type == RASTER_DOUBLE) {
858
0
    shape->values =
859
0
        msRasterLabelGetValues(layer, rllinfo->d_raster_values[raster_off]);
860
0
  } else {
861
0
    shape->values =
862
0
        msRasterLabelGetValues(layer, rllinfo->f_raster_values[raster_off]);
863
0
  }
864
0
  shape->index = shapeindex;
865
0
  shape->resultindex = shapeindex;
866
867
0
  return MS_SUCCESS;
868
0
}
869
870
0
int msRasterLabelLayerNextShape(layerObj *layer, shapeObj *shape) {
871
0
  RasterLabelLayerInfo *rllinfo = (RasterLabelLayerInfo *)layer->layerinfo;
872
873
0
  if (rllinfo->next_shape < 0 ||
874
0
      rllinfo->next_shape >= rllinfo->query_results) {
875
0
    msFreeShape(shape);
876
0
    shape->type = MS_SHAPE_NULL;
877
0
    return MS_DONE;
878
0
  } else {
879
0
    resultObj record;
880
881
0
    record.shapeindex = rllinfo->next_shape++;
882
0
    record.tileindex = 0;
883
0
    record.classindex = record.resultindex = -1;
884
885
0
    return msRasterLabelLayerGetShape(layer, shape, &record);
886
0
  }
887
0
}
888
889
/************************************************************************/
890
/*                       msRasterLabelLayerGetExtent()                     */
891
/* Simple copy of the maprasterquery.c file. might change in the future */
892
/************************************************************************/
893
894
int msRasterLabelLayerGetExtent(layerObj *layer, rectObj *extent)
895
896
0
{
897
0
  char szPath[MS_MAXPATHLEN];
898
0
  mapObj *map = layer->map;
899
0
  shapefileObj *tileshpfile;
900
901
0
  if ((!layer->data || strlen(layer->data) == 0) && layer->tileindex == NULL) {
902
    /* should we be issuing a specific error about not supporting
903
       extents for tileindexed raster layers? */
904
0
    return MS_FAILURE;
905
0
  }
906
907
0
  if (map == NULL)
908
0
    return MS_FAILURE;
909
910
  /* If the layer use a tileindex, return the extent of the tileindex
911
   * shapefile/referenced layer */
912
0
  if (layer->tileindex) {
913
0
    const int tilelayerindex = msGetLayerIndex(map, layer->tileindex);
914
0
    if (tilelayerindex != -1) /* does the tileindex reference another layer */
915
0
      return msLayerGetExtent(GET_LAYER(map, tilelayerindex), extent);
916
0
    else {
917
0
      tileshpfile = (shapefileObj *)malloc(sizeof(shapefileObj));
918
0
      MS_CHECK_ALLOC(tileshpfile, sizeof(shapefileObj), MS_FAILURE);
919
920
0
      if (msShapefileOpen(tileshpfile, "rb",
921
0
                          msBuildPath3(szPath, map->mappath, map->shapepath,
922
0
                                       layer->tileindex),
923
0
                          MS_TRUE) == -1)
924
0
        if (msShapefileOpen(tileshpfile, "rb",
925
0
                            msBuildPath(szPath, map->mappath, layer->tileindex),
926
0
                            MS_TRUE) == -1)
927
0
          return MS_FAILURE;
928
929
0
      *extent = tileshpfile->bounds;
930
0
      msShapefileClose(tileshpfile);
931
0
      free(tileshpfile);
932
0
      return MS_SUCCESS;
933
0
    }
934
0
  }
935
936
0
  msTryBuildPath3(szPath, map->mappath, map->shapepath, layer->data);
937
0
  char *decrypted_path = msDecryptStringTokens(map, szPath);
938
0
  if (!decrypted_path)
939
0
    return MS_FAILURE;
940
941
0
  GDALAllRegister();
942
943
0
  char **connectionoptions =
944
0
      msGetStringListFromHashTable(&(layer->connectionoptions));
945
0
  GDALDatasetH hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
946
0
                                (const char *const *)connectionoptions, NULL);
947
0
  CSLDestroy(connectionoptions);
948
0
  msFree(decrypted_path);
949
0
  if (hDS == NULL) {
950
0
    return MS_FAILURE;
951
0
  }
952
953
0
  const int nXSize = GDALGetRasterXSize(hDS);
954
0
  const int nYSize = GDALGetRasterYSize(hDS);
955
0
  double adfGeoTransform[6] = {0};
956
0
  const CPLErr eErr = GDALGetGeoTransform(hDS, adfGeoTransform);
957
0
  if (eErr != CE_None) {
958
0
    GDALClose(hDS);
959
0
    return MS_FAILURE;
960
0
  }
961
962
  /* If this appears to be an ungeoreferenced raster than flip it for
963
     mapservers purposes. */
964
0
  if (adfGeoTransform[5] == 1.0 && adfGeoTransform[3] == 0.0) {
965
0
    adfGeoTransform[5] = -1.0;
966
0
    adfGeoTransform[3] = nYSize;
967
0
  }
968
969
0
  extent->minx = adfGeoTransform[0];
970
0
  extent->maxy = adfGeoTransform[3];
971
972
0
  extent->maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1];
973
0
  extent->miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5];
974
975
0
  return MS_SUCCESS;
976
0
}
977
978
/************************************************************************/
979
/*                  msRasterLabelLayerSetTimeFilter()                   */
980
/*                                                                      */
981
/*      This function is actually just used in the context of           */
982
/*      setting a filter on the tileindex for time based queries.       */
983
/*      For instance via WMS requests.  So it isn't really related      */
984
/*      to the "raster query" support at all.                           */
985
/*                                                                      */
986
/*      If a local shapefile tileindex is in use, we will set a         */
987
/*      backtics filter (shapefile compatible).  If another layer is    */
988
/*      being used as the tileindex then we will forward the            */
989
/*      SetTimeFilter call to it.  If there is no tileindex in          */
990
/*      place, we do nothing.                                           */
991
/************************************************************************/
992
993
int msRasterLabelLayerSetTimeFilter(layerObj *layer, const char *timestring,
994
0
                                    const char *timefield) {
995
0
  int tilelayerindex;
996
997
  /* -------------------------------------------------------------------- */
998
  /*      If we don't have a tileindex the time filter has no effect.     */
999
  /* -------------------------------------------------------------------- */
1000
0
  if (layer->tileindex == NULL)
1001
0
    return MS_SUCCESS;
1002
1003
  /* -------------------------------------------------------------------- */
1004
  /*      Find the tileindex layer.                                       */
1005
  /* -------------------------------------------------------------------- */
1006
0
  tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
1007
1008
  /* -------------------------------------------------------------------- */
1009
  /*      If we are using a local shapefile as our tileindex (that is     */
1010
  /*      to say, the tileindex name is not of another layer), then we    */
1011
  /*      just install a backtics style filter on the raster layer.       */
1012
  /*      This is propagated to the "working layer" created for the       */
1013
  /*      tileindex by code in mapraster.c.                               */
1014
  /* -------------------------------------------------------------------- */
1015
0
  if (tilelayerindex == -1)
1016
0
    return msLayerMakeBackticsTimeFilter(layer, timestring, timefield);
1017
1018
  /* -------------------------------------------------------------------- */
1019
  /*      Otherwise we invoke the tileindex layers SetTimeFilter          */
1020
  /*      method.                                                         */
1021
  /* -------------------------------------------------------------------- */
1022
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
1023
0
    return MS_FAILURE;
1024
0
  return msLayerSetTimeFilter(layer->GET_LAYER(map, tilelayerindex), timestring,
1025
0
                              timefield);
1026
0
}
1027
1028
/************************************************************************/
1029
/*                msRASTERLayerInitializeVirtualTable()                 */
1030
/************************************************************************/
1031
1032
0
int msRasterLabelLayerInitializeVirtualTable(layerObj *layer) {
1033
0
  assert(layer != NULL);
1034
0
  assert(layer->vtable != NULL);
1035
1036
0
  layer->vtable->LayerInitItemInfo = msRasterLabelLayerInitItemInfo;
1037
0
  layer->vtable->LayerFreeItemInfo = msRasterLabelLayerFreeItemInfo;
1038
0
  layer->vtable->LayerOpen = msRasterLabelLayerOpen;
1039
0
  layer->vtable->LayerIsOpen = msRasterLabelLayerIsOpen;
1040
0
  layer->vtable->LayerWhichShapes = msRasterLabelLayerWhichShapes;
1041
0
  layer->vtable->LayerNextShape = msRasterLabelLayerNextShape;
1042
0
  layer->vtable->LayerGetShape = msRasterLabelLayerGetShape;
1043
  /* layer->vtable->LayerGetShapeCount, use default */
1044
0
  layer->vtable->LayerClose = msRasterLabelLayerClose;
1045
0
  layer->vtable->LayerGetItems = msRasterLabelLayerGetItems;
1046
0
  layer->vtable->LayerGetExtent = msRasterLabelLayerGetExtent;
1047
  /* layer->vtable->LayerGetAutoStyle, use default */
1048
  /* layer->vtable->LayerApplyFilterToLayer, use default */
1049
  /* layer->vtable->LayerCloseConnection = msRasterLabelLayerClose; */
1050
  /* we use backtics for proper tileindex shapefile functioning */
1051
0
  layer->vtable->LayerSetTimeFilter = msRasterLabelLayerSetTimeFilter;
1052
  /* layer->vtable->LayerCreateItems, use default */
1053
  /* layer->vtable->LayerGetNumFeatures, use default */
1054
1055
0
  return MS_SUCCESS;
1056
0
}