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

MapServer / MapServer / 20718230642

05 Jan 2026 02:18PM UTC coverage: 41.754% (+0.03%) from 41.725%
20718230642

push

github

web-flow
WMS: rework building of the layer tree to make it faster, and understandable! (#7410)

* WMS: rework building of the layer tree to make it faster, and understandable!

Avoid quadratic performance in the number of layers.

On a 103 MB .map file with 6130 layers and 3 level of nesting, WMS
GetCapabilities response generation goes from 56 seconds to 5 seconds.

* msRenameLayer(): avoid potential overflow

* mapows.cpp: minimal conversion to C++

* msOWSMakeAllLayersUnique(): avoid quadratic performance in number of layers

* loadMap(): optimize CRS creation when they are all the same

360 of 422 new or added lines in 4 files covered. (85.31%)

76 existing lines in 4 files now uncovered.

62888 of 150616 relevant lines covered (41.75%)

25229.23 hits per line

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

87.6
/src/mapuvraster.cpp
1
/**********************************************************************
2
 * $Id: mapuv.c 12629 2011-10-06 18:06:34Z aboudreault $
3
 *
4
 * Project:  MapServer
5
 * Purpose:  UV Layer
6
 * Author:   Alan Boudreault (aboudreault@mapgears.com)
7
 *
8
 **********************************************************************
9
 * Copyright (c) 2011, Alan Boudreault, MapGears
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 OR
22
 * 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 "mapserver.h"
31

32
#include <assert.h>
33
#include <math.h>
34
#include <stdbool.h>
35
#include "mapows.h"
36
#include "mapresample.h"
37
#include "mapthread.h"
38

39
#include <cmath>
40
#include <limits>
41

42
#define MSUVRASTER_NUMITEMS 6
43
#define MSUVRASTER_ANGLE "uv_angle"
44
#define MSUVRASTER_ANGLEINDEX -100
45
#define MSUVRASTER_MINUS_ANGLE "uv_minus_angle"
46
#define MSUVRASTER_MINUSANGLEINDEX -101
47
#define MSUVRASTER_LENGTH "uv_length"
48
#define MSUVRASTER_LENGTHINDEX -102
49
#define MSUVRASTER_LENGTH_2 "uv_length_2"
50
#define MSUVRASTER_LENGTH2INDEX -103
51
#define MSUVRASTER_U "u"
52
#define MSUVRASTER_UINDEX -104
53
#define MSUVRASTER_V "v"
54
#define MSUVRASTER_VINDEX -105
55
#define MSUVRASTER_LON "lon"
56
#define MSUVRASTER_LONINDEX -106
57
#define MSUVRASTER_LAT "lat"
58
#define MSUVRASTER_LATINDEX -107
59

60
typedef struct {
61

62
  /* query cache results */
63
  int query_results = 0;
64

65
  int refcount = 0;
66

67
  float *u = nullptr; /* u values */
68
  float *v = nullptr; /* v values */
69
  int width = 0;
70
  int height = 0;
71
  rectObj extent{};
72
  int next_shape = 0;
73

74
  /* To improve performance of GetShape() when queried on increasing shapeindex
75
   */
76
  long last_queried_shapeindex = 0; // value in [0, query_results[ range
77
  size_t last_raster_off = 0;       // value in [0, width*height[ range
78

79
  bool needsLonLat = false;
80
  reprojectionObj *reprojectorToLonLat = nullptr;
81

82
  /* set if the map->extent and map->projection are
83
     valid in msUVRASTERLayerWhichShapes() */
84
  mapObj *mapToUseForWhichShapes = nullptr;
85

86
  std::string timestring{};
87
  std::string timefield{};
88
} uvRasterLayerInfo;
32✔
89

90
static uvRasterLayerInfo *getLayerInfo(layerObj *layer) {
91
  return static_cast<uvRasterLayerInfo *>(layer->layerinfo);
13,004✔
92
}
93

94
void msUVRASTERLayerUseMapExtentAndProjectionForNextWhichShapes(layerObj *layer,
30✔
95
                                                                mapObj *map) {
96
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
97
  uvlinfo->mapToUseForWhichShapes = map;
30✔
98
}
30✔
99

100
static int msUVRASTERLayerInitItemInfo(layerObj *layer) {
17✔
101
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
102
  int i;
103
  int *itemindexes;
104
  int failed = 0;
105

106
  if (layer->numitems == 0)
17✔
107
    return MS_SUCCESS;
108

109
  if (uvlinfo == NULL) {
17✔
UNCOV
110
    msSetError(MS_MISCERR, "Assertion failed: GDAL layer not opened!!!",
×
111
               "msUVRASTERLayerInitItemInfo()");
UNCOV
112
    return (MS_FAILURE);
×
113
  }
114

115
  if (layer->iteminfo)
17✔
116
    free(layer->iteminfo);
1✔
117

118
  if ((layer->iteminfo = (int *)malloc(sizeof(int) * layer->numitems)) ==
17✔
119
      NULL) {
UNCOV
120
    msSetError(MS_MEMERR, NULL, "msUVRASTERLayerInitItemInfo()");
×
UNCOV
121
    return (MS_FAILURE);
×
122
  }
123

124
  itemindexes = (int *)layer->iteminfo;
125
  for (i = 0; i < layer->numitems; i++) {
60✔
126
    /* Special attribute names. */
127
    if (EQUAL(layer->items[i], MSUVRASTER_ANGLE))
43✔
128
      itemindexes[i] = MSUVRASTER_ANGLEINDEX;
17✔
129
    else if (EQUAL(layer->items[i], MSUVRASTER_MINUS_ANGLE))
26✔
130
      itemindexes[i] = MSUVRASTER_MINUSANGLEINDEX;
2✔
131
    else if (EQUAL(layer->items[i], MSUVRASTER_LENGTH))
24✔
132
      itemindexes[i] = MSUVRASTER_LENGTHINDEX;
7✔
133
    else if (EQUAL(layer->items[i], MSUVRASTER_LENGTH_2))
17✔
134
      itemindexes[i] = MSUVRASTER_LENGTH2INDEX;
7✔
135
    else if (EQUAL(layer->items[i], MSUVRASTER_U))
10✔
136
      itemindexes[i] = MSUVRASTER_UINDEX;
2✔
137
    else if (EQUAL(layer->items[i], MSUVRASTER_V))
8✔
138
      itemindexes[i] = MSUVRASTER_VINDEX;
2✔
139
    else if (EQUAL(layer->items[i], MSUVRASTER_LON)) {
6✔
140
      uvlinfo->needsLonLat = true;
3✔
141
      itemindexes[i] = MSUVRASTER_LONINDEX;
3✔
142
    } else if (EQUAL(layer->items[i], MSUVRASTER_LAT)) {
3✔
143
      uvlinfo->needsLonLat = true;
3✔
144
      itemindexes[i] = MSUVRASTER_LATINDEX;
3✔
145
    } else {
UNCOV
146
      itemindexes[i] = -1;
×
UNCOV
147
      msSetError(MS_MISCERR, "Invalid Field name: %s",
×
148
                 "msUVRASTERLayerInitItemInfo()", layer->items[i]);
149
      failed = 1;
150
    }
151
  }
152

153
  return failed ? (MS_FAILURE) : (MS_SUCCESS);
17✔
154
}
155

156
void msUVRASTERLayerFreeItemInfo(layerObj *layer) {
34✔
157
  if (layer->iteminfo)
34✔
158
    free(layer->iteminfo);
16✔
159
  layer->iteminfo = NULL;
34✔
160
}
34✔
161

162
static void msUVRasterLayerInfoInitialize(layerObj *layer) {
16✔
163
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
164

165
  if (uvlinfo != NULL)
16✔
166
    return;
167

168
  uvlinfo = new uvRasterLayerInfo;
16✔
169
  layer->layerinfo = uvlinfo;
16✔
170

171
  /* Set attribute type to Real, unless the user has explicitly set */
172
  /* something else. */
173
  {
174
    const char *const items[] = {
16✔
175
        MSUVRASTER_ANGLE,    MSUVRASTER_MINUS_ANGLE, MSUVRASTER_LENGTH,
176
        MSUVRASTER_LENGTH_2, MSUVRASTER_U,           MSUVRASTER_V,
177
    };
178
    size_t i;
179
    for (i = 0; i < sizeof(items) / sizeof(items[0]); ++i) {
112✔
180
      char szTmp[100];
181
      snprintf(szTmp, sizeof(szTmp), "%s_type", items[i]);
96✔
182
      if (msOWSLookupMetadata(&(layer->metadata), "OFG", szTmp) == NULL) {
96✔
183
        snprintf(szTmp, sizeof(szTmp), "gml_%s_type", items[i]);
184
        msInsertHashTable(&(layer->metadata), szTmp, "Real");
96✔
185
      }
186
    }
187
  }
188
}
189

190
static void msUVRasterLayerInfoFree(layerObj *layer)
16✔
191

192
{
193
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
194

195
  if (uvlinfo == NULL)
16✔
196
    return;
197

198
  free(uvlinfo->u);
16✔
199
  free(uvlinfo->v);
16✔
200

201
  if (uvlinfo->reprojectorToLonLat) {
16✔
202
    msProjectDestroyReprojector(uvlinfo->reprojectorToLonLat);
2✔
203
  }
204

205
  delete uvlinfo;
16✔
206

207
  layer->layerinfo = NULL;
16✔
208
}
209

210
int msUVRASTERLayerOpen(layerObj *layer) {
16✔
211
  /* If we don't have info, initialize an empty one now */
212
  if (layer->layerinfo == NULL)
16✔
213
    msUVRasterLayerInfoInitialize(layer);
15✔
214
  if (layer->layerinfo == NULL)
16✔
215
    return MS_FAILURE;
216

217
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
218

219
  uvlinfo->refcount = uvlinfo->refcount + 1;
16✔
220

221
  return MS_SUCCESS;
16✔
222
}
223

224
int msUVRASTERLayerIsOpen(layerObj *layer) {
30✔
225
  if (layer->layerinfo)
30✔
226
    return MS_TRUE;
8✔
227
  return MS_FALSE;
228
}
229

230
int msUVRASTERLayerClose(layerObj *layer) {
17✔
231
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
232

233
  if (uvlinfo != NULL) {
17✔
234
    uvlinfo->refcount--;
16✔
235

236
    if (uvlinfo->refcount < 1)
16✔
237
      msUVRasterLayerInfoFree(layer);
16✔
238
  }
239
  return MS_SUCCESS;
17✔
240
}
241

242
int msUVRASTERLayerGetItems(layerObj *layer) {
1✔
243
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
244

245
  if (uvlinfo == NULL)
1✔
246
    return MS_FAILURE;
247

248
  layer->numitems = 0;
1✔
249
  layer->items = (char **)msSmallCalloc(sizeof(char *), 10);
1✔
250

251
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_ANGLE);
1✔
252
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_MINUS_ANGLE);
1✔
253
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_LENGTH);
1✔
254
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_LENGTH_2);
1✔
255
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_U);
1✔
256
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_V);
1✔
257
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_LON);
1✔
258
  layer->items[layer->numitems++] = msStrdup(MSUVRASTER_LAT);
1✔
259
  layer->items[layer->numitems] = NULL;
1✔
260

261
  return msUVRASTERLayerInitItemInfo(layer);
1✔
262
}
263

264
/**********************************************************************
265
 *                     msUVRASTERGetValues()
266
 *
267
 * Special attribute names are used to return some UV params: uv_angle,
268
 * uv_length, u and v.
269
 **********************************************************************/
270
static char **msUVRASTERGetValues(layerObj *layer, float u, float v,
4,292✔
271
                                  const pointObj *point) {
272
  char **values;
273
  int i = 0;
274
  char tmp[100];
275
  float size_scale;
276
  int *itemindexes = (int *)layer->iteminfo;
4,292✔
277
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
278
  double lon = HUGE_VAL;
279
  double lat = HUGE_VAL;
280

281
  if (layer->numitems == 0)
4,292✔
282
    return (NULL);
283

284
  if (!layer->iteminfo) { /* Should not happen... but just in case! */
4,292✔
UNCOV
285
    if (msUVRASTERLayerInitItemInfo(layer) != MS_SUCCESS)
×
286
      return NULL;
UNCOV
287
    itemindexes = (int *)layer->iteminfo; /* reassign after malloc */
×
288
  }
289

290
  if ((values = (char **)malloc(sizeof(char *) * layer->numitems)) == NULL) {
4,292✔
291
    msSetError(MS_MEMERR, NULL, "msUVRASTERGetValues()");
×
UNCOV
292
    return (NULL);
×
293
  }
294

295
  /* -------------------------------------------------------------------- */
296
  /*    Determine desired size_scale.  Default to 1 if not otherwise set  */
297
  /* -------------------------------------------------------------------- */
298
  size_scale = 1;
299
  if (CSLFetchNameValue(layer->processing, "UV_SIZE_SCALE") != NULL) {
4,292✔
300
    size_scale = atof(CSLFetchNameValue(layer->processing, "UV_SIZE_SCALE"));
2,640✔
301
  }
302

303
  if (uvlinfo->needsLonLat) {
4,292✔
304
    if (uvlinfo->reprojectorToLonLat == NULL)
739✔
305
      uvlinfo->reprojectorToLonLat =
2✔
306
          msProjectCreateReprojector(&layer->projection, NULL);
2✔
307
    if (uvlinfo->reprojectorToLonLat) {
739✔
308
      pointObj pointWrk = *point;
739✔
309
      if (msProjectPointEx(uvlinfo->reprojectorToLonLat, &pointWrk) ==
739✔
310
          MS_SUCCESS) {
311
        lon = pointWrk.x;
739✔
312
        lat = pointWrk.y;
739✔
313
      }
314
    }
315
  }
316

317
  for (i = 0; i < layer->numitems; i++) {
15,077✔
318
    if (itemindexes[i] == MSUVRASTER_ANGLEINDEX) {
10,785✔
319
      snprintf(tmp, 100, "%f", (atan2((double)v, (double)u) * 180 / MS_PI));
4,292✔
320
      values[i] = msStrdup(tmp);
4,292✔
321
    } else if (itemindexes[i] == MSUVRASTER_MINUSANGLEINDEX) {
6,493✔
322
      double minus_angle;
323
      minus_angle = (atan2((double)v, (double)u) * 180 / MS_PI) + 180;
475✔
324
      if (minus_angle >= 360)
475✔
UNCOV
325
        minus_angle -= 360;
×
326
      snprintf(tmp, 100, "%f", minus_angle);
327
      values[i] = msStrdup(tmp);
475✔
328
    } else if ((itemindexes[i] == MSUVRASTER_LENGTHINDEX) ||
6,018✔
329
               (itemindexes[i] == MSUVRASTER_LENGTH2INDEX)) {
330
      float length = sqrt((u * u) + (v * v)) * size_scale;
3,590✔
331

332
      if (itemindexes[i] == MSUVRASTER_LENGTHINDEX)
3,590✔
333
        snprintf(tmp, 100, "%f", length);
1,795✔
334
      else
335
        snprintf(tmp, 100, "%f", length / 2);
1,795✔
336

337
      values[i] = msStrdup(tmp);
3,590✔
338
    } else if (itemindexes[i] == MSUVRASTER_UINDEX) {
2,428✔
339
      snprintf(tmp, 100, "%f", u);
475✔
340
      values[i] = msStrdup(tmp);
475✔
341
    } else if (itemindexes[i] == MSUVRASTER_VINDEX) {
1,953✔
342
      snprintf(tmp, 100, "%f", v);
475✔
343
      values[i] = msStrdup(tmp);
475✔
344
    } else if (itemindexes[i] == MSUVRASTER_LONINDEX) {
1,478✔
345
      snprintf(tmp, sizeof(tmp), "%.18g", lon);
346
      values[i] = msStrdup(tmp);
739✔
347
    } else if (itemindexes[i] == MSUVRASTER_LATINDEX) {
739✔
348
      snprintf(tmp, sizeof(tmp), "%.18g", lat);
349
      values[i] = msStrdup(tmp);
739✔
350
    } else {
UNCOV
351
      values[i] = NULL;
×
352
    }
353
  }
354

355
  return values;
356
}
357

358
rectObj msUVRASTERGetSearchRect(layerObj *layer, mapObj *map) {
19✔
359
  rectObj searchrect = map->extent;
19✔
360
  int bDone = MS_FALSE;
361

362
  /* For UVRaster, it is important that the searchrect is not too large */
363
  /* to avoid insufficient intermediate raster resolution, which could */
364
  /* happen if we use the default code path, given potential reprojection */
365
  /* issues when using a map extent that is not in the validity area of */
366
  /* the layer projection. */
367
  if (!layer->projection.gt.need_geotransform &&
38✔
368
      !(msProjIsGeographicCRS(&(map->projection)) &&
33✔
369
        msProjIsGeographicCRS(&(layer->projection)))) {
14✔
370
    rectObj layer_ori_extent;
371

372
    if (msLayerGetExtent(layer, &layer_ori_extent) == MS_SUCCESS) {
9✔
373
      projectionObj map_proj;
374

375
      double map_extent_minx = map->extent.minx;
9✔
376
      double map_extent_miny = map->extent.miny;
9✔
377
      double map_extent_maxx = map->extent.maxx;
9✔
378
      double map_extent_maxy = map->extent.maxy;
9✔
379
      rectObj layer_extent = layer_ori_extent;
9✔
380

381
      /* Create a variant of map->projection without geotransform for */
382
      /* conveniency */
383
      msInitProjection(&map_proj);
9✔
384
      msCopyProjection(&map_proj, &map->projection);
9✔
385
      map_proj.gt.need_geotransform = MS_FALSE;
9✔
386
      if (map->projection.gt.need_geotransform) {
9✔
387
        map_extent_minx =
3✔
388
            map->projection.gt.geotransform[0] +
3✔
389
            map->projection.gt.geotransform[1] * map->extent.minx +
3✔
390
            map->projection.gt.geotransform[2] * map->extent.miny;
3✔
391
        map_extent_miny =
3✔
392
            map->projection.gt.geotransform[3] +
3✔
393
            map->projection.gt.geotransform[4] * map->extent.minx +
3✔
394
            map->projection.gt.geotransform[5] * map->extent.miny;
3✔
395
        map_extent_maxx =
3✔
396
            map->projection.gt.geotransform[0] +
3✔
397
            map->projection.gt.geotransform[1] * map->extent.maxx +
3✔
398
            map->projection.gt.geotransform[2] * map->extent.maxy;
3✔
399
        map_extent_maxy =
3✔
400
            map->projection.gt.geotransform[3] +
3✔
401
            map->projection.gt.geotransform[4] * map->extent.maxx +
3✔
402
            map->projection.gt.geotransform[5] * map->extent.maxy;
3✔
403
      }
404

405
      /* Reproject layer extent to map projection */
406
      msProjectRect(&layer->projection, &map_proj, &layer_extent);
9✔
407

408
      if (layer_extent.minx <= map_extent_minx &&
9✔
409
          layer_extent.miny <= map_extent_miny &&
7✔
410
          layer_extent.maxx >= map_extent_maxx &&
4✔
411
          layer_extent.maxy >= map_extent_maxy) {
4✔
412
        /* do nothing special if area to map is inside layer extent */
413
      } else {
414
        if (layer_extent.minx >= map_extent_minx &&
5✔
415
            layer_extent.maxx <= map_extent_maxx &&
4✔
416
            layer_extent.miny >= map_extent_miny &&
3✔
417
            layer_extent.maxy <= map_extent_maxy) {
3✔
418
          /* if the area to map is larger than the layer extent, then */
419
          /* use full layer extent and add some margin to reflect the */
420
          /* proportion of the useful area over the requested bbox */
421
          double extra_x = (map_extent_maxx - map_extent_minx) /
3✔
422
                           (layer_extent.maxx - layer_extent.minx) *
3✔
423
                           (layer_ori_extent.maxx - layer_ori_extent.minx);
3✔
424
          double extra_y = (map_extent_maxy - map_extent_miny) /
3✔
425
                           (layer_extent.maxy - layer_extent.miny) *
3✔
426
                           (layer_ori_extent.maxy - layer_ori_extent.miny);
3✔
427
          searchrect.minx = layer_ori_extent.minx - extra_x / 2;
3✔
428
          searchrect.maxx = layer_ori_extent.maxx + extra_x / 2;
3✔
429
          searchrect.miny = layer_ori_extent.miny - extra_y / 2;
3✔
430
          searchrect.maxy = layer_ori_extent.maxy + extra_y / 2;
3✔
431
        } else {
3✔
432
          /* otherwise clip the map extent with the reprojected layer */
433
          /* extent */
434
          searchrect.minx = MS_MAX(map_extent_minx, layer_extent.minx);
2✔
435
          searchrect.maxx = MS_MIN(map_extent_maxx, layer_extent.maxx);
2✔
436
          searchrect.miny = MS_MAX(map_extent_miny, layer_extent.miny);
2✔
437
          searchrect.maxy = MS_MIN(map_extent_maxy, layer_extent.maxy);
2✔
438
          /* and reproject into the layer projection */
439
          msProjectRect(&map_proj, &layer->projection, &searchrect);
2✔
440
        }
441
        bDone = MS_TRUE;
442
      }
443

444
      msFreeProjection(&map_proj);
9✔
445
    }
446
  }
447

448
  if (!bDone)
9✔
449
    msProjectRect(&map->projection, &layer->projection,
14✔
450
                  &searchrect); /* project the searchrect to source coords */
451

452
  return searchrect;
19✔
453
}
454

455
int msUVRASTERLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery) {
16✔
456
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
457
  imageObj *image_tmp;
458
  outputFormatObj *outputformat = NULL;
459
  mapObj *map_tmp;
460
  double map_cellsize;
461
  unsigned int spacing;
462
  int width, height;
463
  char **alteredProcessing = NULL, *saved_layer_mask;
464
  char **savedProcessing = NULL;
465
  int bHasLonWrap = MS_FALSE;
466
  double dfLonWrap = 0.0;
467
  rectObj oldLayerExtent;
468
  char *oldLayerData = NULL;
469
  projectionObj oldLayerProjection;
470
  int ret;
471

472
  memset(&oldLayerExtent, 0, sizeof(oldLayerExtent));
473
  memset(&oldLayerProjection, 0, sizeof(oldLayerProjection));
474

475
  if (layer->debug)
16✔
UNCOV
476
    msDebug("Entering msUVRASTERLayerWhichShapes().\n");
×
477

478
  if (uvlinfo == NULL)
16✔
479
    return MS_FAILURE;
480

481
  if (CSLFetchNameValue(layer->processing, "BANDS") == NULL) {
16✔
UNCOV
482
    msSetError(MS_MISCERR,
×
483
               "BANDS processing option is required for UV layer. You have to "
484
               "specified 2 bands.",
485
               "msUVRASTERLayerWhichShapes()");
UNCOV
486
    return MS_FAILURE;
×
487
  }
488

489
  /*
490
  ** Allocate mapObj structure
491
  */
492
  map_tmp = (mapObj *)msSmallCalloc(sizeof(mapObj), 1);
16✔
493
  if (initMap(map_tmp) == -1) { /* initialize this map */
16✔
494
    msFree(map_tmp);
×
UNCOV
495
    return (MS_FAILURE);
×
496
  }
497

498
  /* -------------------------------------------------------------------- */
499
  /*      Determine desired spacing.  Default to 32 if not otherwise set  */
500
  /* -------------------------------------------------------------------- */
501
  spacing = 32;
502
  if (CSLFetchNameValue(layer->processing, "UV_SPACING") != NULL) {
16✔
503
    spacing = atoi(CSLFetchNameValue(layer->processing, "UV_SPACING"));
16✔
504
    if (spacing == 0)
16✔
505
      spacing = 32;
506
  }
507

508
  width = (int)(layer->map->width / spacing);
16✔
509
  height = (int)(layer->map->height / spacing);
16✔
510

511
  /* Initialize our dummy map */
512
  MS_INIT_COLOR(map_tmp->imagecolor, 255, 255, 255, 255);
16✔
513
  map_tmp->resolution = layer->map->resolution;
16✔
514
  map_tmp->defresolution = layer->map->defresolution;
16✔
515

516
  outputformat = (outputFormatObj *)msSmallCalloc(1, sizeof(outputFormatObj));
16✔
517
  outputformat->bands = 2;
16✔
518
  outputformat->name = NULL;
16✔
519
  outputformat->driver = NULL;
16✔
520
  outputformat->refcount = 0;
16✔
521
  outputformat->vtable = NULL;
16✔
522
  outputformat->device = NULL;
16✔
523
  outputformat->renderer = MS_RENDER_WITH_RAWDATA;
16✔
524
  outputformat->imagemode = MS_IMAGEMODE_FLOAT32;
16✔
525
  msAppendOutputFormat(map_tmp, outputformat);
16✔
526

527
  msCopyHashTable(&map_tmp->configoptions, &layer->map->configoptions);
16✔
528
  map_tmp->mappath = msStrdup(layer->map->mappath);
16✔
529
  map_tmp->shapepath = msStrdup(layer->map->shapepath);
16✔
530
  map_tmp->gt.rotation_angle = 0.0;
16✔
531

532
  /* Custom msCopyProjection() that removes lon_wrap parameter */
533
  {
534
    int i;
535

536
    map_tmp->projection.numargs = 0;
16✔
537
    map_tmp->projection.gt = layer->projection.gt;
16✔
538

539
    for (i = 0; i < layer->projection.numargs; i++) {
84✔
540
      if (strncmp(layer->projection.args[i],
68✔
541
                  "lon_wrap=", strlen("lon_wrap=")) == 0) {
542
        bHasLonWrap = MS_TRUE;
543
        dfLonWrap = atof(layer->projection.args[i] + strlen("lon_wrap="));
4✔
544
      } else {
545
        map_tmp->projection.args[map_tmp->projection.numargs++] =
64✔
546
            msStrdup(layer->projection.args[i]);
64✔
547
      }
548
    }
549
    if (map_tmp->projection.numargs != 0) {
16✔
550
      msProcessProjection(&(map_tmp->projection));
16✔
551
    }
552

553
    map_tmp->projection.wellknownprojection =
16✔
554
        layer->projection.wellknownprojection;
16✔
555
  }
556

557
  /* Very special case to improve quality for rasters referenced from lon=0 to
558
   * 360 */
559
  /* We create a temporary VRT that swiches the 2 hemispheres, and then we */
560
  /* modify the georeferencing to be in the more standard [-180, 180] range */
561
  /* and we adjust the layer->data, extent and projection accordingly */
562
  if (layer->tileindex == NULL && uvlinfo->mapToUseForWhichShapes &&
16✔
563
      bHasLonWrap && dfLonWrap == 180.0) {
13✔
564
    rectObj layerExtent;
565
    msLayerGetExtent(layer, &layerExtent);
4✔
566
    if (layerExtent.minx == 0 && layerExtent.maxx == 360) {
4✔
567
      GDALDatasetH hDS = NULL;
568
      char *decrypted_path;
569

570
      if (strncmp(layer->data, "<VRTDataset", strlen("<VRTDataset")) == 0) {
4✔
UNCOV
571
        decrypted_path = msStrdup(layer->data);
×
572
      } else {
573
        char szPath[MS_MAXPATHLEN];
574
        msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
4✔
575
                        layer->data);
576
        decrypted_path = msDecryptStringTokens(layer->map, szPath);
4✔
577
      }
578

579
      if (decrypted_path) {
4✔
580
        char **connectionoptions;
581
        GDALAllRegister();
4✔
582
        connectionoptions =
583
            msGetStringListFromHashTable(&(layer->connectionoptions));
4✔
584
        hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
4✔
585
                         (const char *const *)connectionoptions, NULL);
586
        CSLDestroy(connectionoptions);
4✔
587
      }
588
      if (hDS != NULL) {
4✔
589
        int iBand;
590
        int nXSize = GDALGetRasterXSize(hDS);
4✔
591
        int nYSize = GDALGetRasterYSize(hDS);
4✔
592
        int nBands = GDALGetRasterCount(hDS);
4✔
593
        int nMaxLen = 100 + nBands * (800 + 2 * strlen(decrypted_path));
4✔
594
        int nOffset = 0;
595
        char *pszInlineVRT = static_cast<char *>(msSmallMalloc(nMaxLen));
4✔
596

597
        snprintf(pszInlineVRT, nMaxLen,
598
                 "<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">", nXSize,
599
                 nYSize);
600
        nOffset = strlen(pszInlineVRT);
4✔
601
        for (iBand = 1; iBand <= nBands; iBand++) {
12✔
602
          const char *pszDataType = "Byte";
603
          switch (GDALGetRasterDataType(GDALGetRasterBand(hDS, iBand))) {
8✔
604
          case GDT_Byte:
605
            pszDataType = "Byte";
606
            break;
UNCOV
607
          case GDT_Int16:
×
608
            pszDataType = "Int16";
609
            break;
×
UNCOV
610
          case GDT_UInt16:
×
611
            pszDataType = "UInt16";
612
            break;
×
UNCOV
613
          case GDT_Int32:
×
614
            pszDataType = "Int32";
615
            break;
×
UNCOV
616
          case GDT_UInt32:
×
617
            pszDataType = "UInt32";
618
            break;
×
UNCOV
619
          case GDT_Float32:
×
620
            pszDataType = "Float32";
621
            break;
×
UNCOV
622
          case GDT_Float64:
×
623
            pszDataType = "Float64";
UNCOV
624
            break;
×
625
          default:
626
            break;
627
          }
628

629
          snprintf(pszInlineVRT + nOffset, nMaxLen - nOffset,
8✔
630
                   "    <VRTRasterBand dataType=\"%s\" band=\"%d\">"
631
                   "        <SimpleSource>"
632
                   "            <SourceFilename "
633
                   "relativeToVrt=\"1\"><![CDATA[%s]]></SourceFilename>"
634
                   "            <SourceBand>%d</SourceBand>"
635
                   "            <SrcRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
636
                   "ySize=\"%d\"/>"
637
                   "            <DstRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
638
                   "ySize=\"%d\"/>"
639
                   "        </SimpleSource>"
640
                   "        <SimpleSource>"
641
                   "            <SourceFilename "
642
                   "relativeToVrt=\"1\"><![CDATA[%s]]></SourceFilename>"
643
                   "            <SourceBand>%d</SourceBand>"
644
                   "            <SrcRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
645
                   "ySize=\"%d\"/>"
646
                   "            <DstRect xOff=\"%d\" yOff=\"%d\" xSize=\"%d\" "
647
                   "ySize=\"%d\"/>"
648
                   "        </SimpleSource>"
649
                   "    </VRTRasterBand>",
650
                   pszDataType, iBand, decrypted_path, iBand, nXSize / 2, 0,
651
                   nXSize - nXSize / 2, nYSize, 0, 0, nXSize - nXSize / 2,
652
                   nYSize, decrypted_path, iBand, 0, 0, nXSize / 2, nYSize,
653
                   nXSize - nXSize / 2, 0, nXSize / 2, nYSize);
654

655
          nOffset += strlen(pszInlineVRT + nOffset);
8✔
656
        }
657
        snprintf(pszInlineVRT + nOffset, nMaxLen - nOffset, "</VRTDataset>");
4✔
658

659
        oldLayerExtent = layer->extent;
4✔
660
        oldLayerData = layer->data;
4✔
661
        oldLayerProjection = layer->projection;
4✔
662
        layer->extent.minx = -180;
4✔
663
        layer->extent.maxx = 180;
4✔
664
        layer->data = pszInlineVRT;
4✔
665
        layer->projection = map_tmp->projection;
4✔
666

667
        /* map_tmp->projection is actually layer->projection without lon_wrap */
668
        rect = uvlinfo->mapToUseForWhichShapes->extent;
4✔
669
        msProjectRect(&uvlinfo->mapToUseForWhichShapes->projection,
4✔
670
                      &map_tmp->projection, &rect);
671
        bHasLonWrap = MS_FALSE;
672

673
        GDALClose(hDS);
4✔
674
      }
675
      msFree(decrypted_path);
4✔
676
    }
677
  }
678

679
  if (isQuery) {
16✔
680
    /* For query mode, use layer->map->extent reprojected rather than */
681
    /* the provided rect. Generic query code will filter returned features. */
682
    rect = msUVRASTERGetSearchRect(layer, layer->map);
1✔
683
  }
684

685
  map_cellsize = MS_MAX(MS_CELLSIZE(rect.minx, rect.maxx, layer->map->width),
16✔
686
                        MS_CELLSIZE(rect.miny, rect.maxy, layer->map->height));
687
  map_tmp->cellsize = map_cellsize * spacing;
16✔
688
  map_tmp->extent.minx =
16✔
689
      rect.minx - (0.5 * map_cellsize) + (0.5 * map_tmp->cellsize);
16✔
690
  map_tmp->extent.miny =
16✔
691
      rect.miny - (0.5 * map_cellsize) + (0.5 * map_tmp->cellsize);
16✔
692
  map_tmp->extent.maxx =
16✔
693
      map_tmp->extent.minx + ((width - 1) * map_tmp->cellsize);
16✔
694
  map_tmp->extent.maxy =
16✔
695
      map_tmp->extent.miny + ((height - 1) * map_tmp->cellsize);
16✔
696

697
  if (bHasLonWrap && dfLonWrap == 180.0) {
16✔
UNCOV
698
    if (map_tmp->extent.minx >= 180) {
×
699
      /* Request on the right half of the shifted raster (= western hemisphere)
700
       */
701
      map_tmp->extent.minx -= 360;
×
702
      map_tmp->extent.maxx -= 360;
×
UNCOV
703
    } else if (map_tmp->extent.maxx >= 180.0) {
×
704
      /* Request spanning on the 2 hemispheres => drawing whole planet */
705
      /* Take only into account vertical resolution, as horizontal one */
706
      /* will be unreliable (assuming square pixels...) */
707
      map_cellsize = MS_CELLSIZE(rect.miny, rect.maxy, layer->map->height);
UNCOV
708
      map_tmp->cellsize = map_cellsize * spacing;
×
709

710
      width = 360.0 / map_tmp->cellsize;
×
711
      map_tmp->extent.minx = -180.0 + (0.5 * map_tmp->cellsize);
×
UNCOV
712
      map_tmp->extent.maxx = 180.0 - (0.5 * map_tmp->cellsize);
×
713
    }
714
  }
715

716
  if (layer->debug)
16✔
UNCOV
717
    msDebug(
×
718
        "msUVRASTERLayerWhichShapes(): width: %d, height: %d, cellsize: %g\n",
719
        width, height, map_tmp->cellsize);
720

721
  if (layer->debug == MS_DEBUGLEVEL_VVV)
16✔
UNCOV
722
    msDebug("msUVRASTERLayerWhichShapes(): extent: %g %g %g %g\n",
×
723
            map_tmp->extent.minx, map_tmp->extent.miny, map_tmp->extent.maxx,
724
            map_tmp->extent.maxy);
725

726
  /* important to use that function, to compute map
727
     geotransform, used by the resampling*/
728
  msMapSetSize(map_tmp, width, height);
16✔
729

730
  if (layer->debug == MS_DEBUGLEVEL_VVV)
16✔
UNCOV
731
    msDebug("msUVRASTERLayerWhichShapes(): geotransform: %g %g %g %g %g %g\n",
×
732
            map_tmp->gt.geotransform[0], map_tmp->gt.geotransform[1],
733
            map_tmp->gt.geotransform[2], map_tmp->gt.geotransform[3],
734
            map_tmp->gt.geotransform[4], map_tmp->gt.geotransform[5]);
735

736
  uvlinfo->extent = map_tmp->extent;
16✔
737

738
  image_tmp = msImageCreate(width, height, map_tmp->outputformatlist[0], NULL,
16✔
739
                            NULL, map_tmp->resolution, map_tmp->defresolution,
740
                            &(map_tmp->imagecolor));
741

742
  /* Default set to AVERAGE resampling */
743
  if (CSLFetchNameValue(layer->processing, "RESAMPLE") == NULL) {
16✔
744
    alteredProcessing = CSLDuplicate(layer->processing);
16✔
745
    alteredProcessing =
746
        CSLSetNameValue(alteredProcessing, "RESAMPLE", "AVERAGE");
16✔
747
    savedProcessing = layer->processing;
16✔
748
    layer->processing = alteredProcessing;
16✔
749
  }
750

751
  /* disable masking at this level: we don't want to apply the mask at the
752
   * raster level, it will be applied with the correct cellsize and image size
753
   * in the vector rendering phase.
754
   */
755
  saved_layer_mask = layer->mask;
16✔
756
  layer->mask = NULL;
16✔
757

758
  if (layer->tileindex) {
16✔
759
    expressionObj old_filter;
760
    if (!uvlinfo->timestring.empty()) {
2✔
761
      msInitExpression(&old_filter);
1✔
762
      msCopyExpression(&old_filter, &layer->filter); /* save existing filter */
1✔
763
      msFreeExpression(&layer->filter);
1✔
764
      msLayerMakeBackticsTimeFilter(layer, uvlinfo->timestring.c_str(),
1✔
765
                                    uvlinfo->timefield.c_str());
766
    }
767

768
    ret = msDrawRasterLayerLow(map_tmp, layer, image_tmp, NULL);
2✔
769

770
    if (!uvlinfo->timestring.empty()) {
2✔
771
      msCopyExpression(&layer->filter, &old_filter); /* restore old filter */
1✔
772
      msFreeExpression(&old_filter);
1✔
773
    }
774
  } else {
775
    ret = msDrawRasterLayerLow(map_tmp, layer, image_tmp, NULL);
14✔
776
  }
777

778
  /* restore layer attributes if we went through the above on-the-fly VRT */
779
  if (oldLayerData) {
16✔
780
    msFree(layer->data);
4✔
781
    layer->data = oldLayerData;
4✔
782
    layer->extent = oldLayerExtent;
4✔
783
    layer->projection = oldLayerProjection;
4✔
784
  }
785

786
  /* restore layer mask */
787
  layer->mask = saved_layer_mask;
16✔
788

789
  /* restore the saved processing */
790
  if (alteredProcessing != NULL) {
16✔
791
    layer->processing = savedProcessing;
16✔
792
    CSLDestroy(alteredProcessing);
16✔
793
  }
794

795
  if (ret == MS_FAILURE) {
16✔
UNCOV
796
    msSetError(MS_MISCERR, "Unable to draw raster data.",
×
797
               "msUVRASTERLayerWhichShapes()");
798

UNCOV
799
    msFreeMap(map_tmp);
×
UNCOV
800
    msFreeImage(image_tmp);
×
801

UNCOV
802
    return MS_FAILURE;
×
803
  }
804

805
  /* free old query arrays */
806
  free(uvlinfo->u);
16✔
807
  free(uvlinfo->v);
16✔
808

809
  /* Update our uv layer structure */
810
  uvlinfo->width = width;
16✔
811
  uvlinfo->height = height;
16✔
812
  uvlinfo->query_results = 0;
16✔
813

814
  uvlinfo->last_queried_shapeindex = 0;
16✔
815
  uvlinfo->last_raster_off = 0;
16✔
816

817
  uvlinfo->u = (float *)msSmallMalloc(sizeof(float *) * width * height);
16✔
818
  uvlinfo->v = (float *)msSmallMalloc(sizeof(float *) * width * height);
16✔
819

820
  reprojectionObj *reprojectorLayerToMap = nullptr;
821
  if (layer->project) {
16✔
822
    reprojectorLayerToMap =
823
        msProjectCreateReprojector(&layer->projection, &layer->map->projection);
13✔
824
  }
825
  // To reproject the wind direction, use a ~ 10 m long line. This is a value
826
  // not too small and not too big to be able to reproject a small vector.
827
  double typicalLength = 10.0;
828
  if (msProjIsGeographicCRS(&(layer->projection))) {
16✔
829
    const double dfDegToMeter =
830
        msProjGetSemiMajorAxis(&(layer->projection)) * (MS_PI / 180);
12✔
831
    const double dfMeterToDeg = 1.0 / dfDegToMeter;
12✔
832
    typicalLength *= dfMeterToDeg;
12✔
833
  }
834

835
  for (size_t off = 0; off < static_cast<size_t>(width) * height; ++off) {
7,436✔
836
    /* Ignore invalid pixels (at nodata), or (u,v)=(0,0) */
837
    if (MS_GET_BIT(image_tmp->img_mask, off)) {
7,420✔
838
      uvlinfo->u[off] = image_tmp->img.raw_float[off];
4,486✔
839
      uvlinfo->v[off] =
4,486✔
840
          image_tmp->img.raw_float[off + static_cast<size_t>(width) * height];
4,486✔
841
      if (!(uvlinfo->u[off] == 0 && uvlinfo->v[off] == 0)) {
4,486✔
842
        uvlinfo->query_results++;
4,291✔
843
        if (reprojectorLayerToMap) {
4,291✔
844
          // Compute coordinates of the pixel in layer projection
845
          const int x = static_cast<int>(off % uvlinfo->width);
3,499✔
846
          const int y = static_cast<int>(off / uvlinfo->width);
3,499✔
847
          const double xLayer =
848
              Pix2Georef(x, 0, uvlinfo->width - 1, uvlinfo->extent.minx,
3,499✔
849
                         uvlinfo->extent.maxx, MS_FALSE);
850
          const double yLayer =
851
              Pix2Georef(y, 0, uvlinfo->height - 1, uvlinfo->extent.miny,
3,499✔
852
                         uvlinfo->extent.maxy, MS_TRUE);
853

854
          // Creates a vector starting at point.x,point.y, of length
855
          // ~ 10 m, using the angle defined by (u,v)
856
          pointObj point1, point2;
857
          point1.x = xLayer;
3,499✔
858
          point1.y = yLayer;
3,499✔
859
          const double lengthInLayerProj =
860
              hypotf(uvlinfo->u[off], uvlinfo->v[off]);
3,499✔
861
          point2.x =
3,499✔
862
              xLayer + typicalLength * (uvlinfo->u[off] / lengthInLayerProj);
3,499✔
863
          point2.y =
3,499✔
864
              yLayer + typicalLength * (uvlinfo->v[off] / lengthInLayerProj);
3,499✔
865

866
          // Reprojects that vector to map projection
867
          if (msProjectPointEx(reprojectorLayerToMap, &point1) == MS_SUCCESS &&
6,988✔
868
              msProjectPointEx(reprojectorLayerToMap, &point2) == MS_SUCCESS) {
3,489✔
869
            // Now computes the (u,v) component in map projection,
870
            // preserving its original length
871
            const double dx = point2.x - point1.x;
3,489✔
872
            const double dy = point2.y - point1.y;
3,489✔
873
            const double lengthInMapProj = hypot(dx, dy);
3,489✔
874
            uvlinfo->u[off] =
3,489✔
875
                (float)(lengthInLayerProj * (dx / lengthInMapProj));
3,489✔
876
            uvlinfo->v[off] =
3,489✔
877
                (float)(lengthInLayerProj * (dy / lengthInMapProj));
3,489✔
878
          }
879
        }
880
      } else {
881
        uvlinfo->u[off] = std::numeric_limits<float>::quiet_NaN();
195✔
882
        uvlinfo->v[off] = std::numeric_limits<float>::quiet_NaN();
195✔
883
      }
884
    } else {
885
      uvlinfo->u[off] = std::numeric_limits<float>::quiet_NaN();
2,934✔
886
      uvlinfo->v[off] = std::numeric_limits<float>::quiet_NaN();
2,934✔
887
    }
888
  }
889

890
  msProjectDestroyReprojector(reprojectorLayerToMap);
16✔
891

892
  msFreeImage(image_tmp); /* we do not need the imageObj anymore */
16✔
893
  msFreeMap(map_tmp);
16✔
894

895
  uvlinfo->next_shape = 0;
16✔
896

897
  return MS_SUCCESS;
16✔
898
}
899

900
int msUVRASTERLayerGetShape(layerObj *layer, shapeObj *shape,
4,292✔
901
                            resultObj *record) {
902
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
903
  lineObj line;
904
  pointObj point;
905
  const long shapeindex = record->shapeindex;
4,292✔
906

907
  msFreeShape(shape);
4,292✔
908
  shape->type = MS_SHAPE_NULL;
4,292✔
909

910
  if (shapeindex < 0 || shapeindex >= uvlinfo->query_results) {
4,292✔
UNCOV
911
    msSetError(MS_MISCERR,
×
912
               "Out of range shape index requested.  Requested %ld\n"
913
               "but only %d shapes available.",
914
               "msUVRASTERLayerGetShape()", shapeindex, uvlinfo->query_results);
UNCOV
915
    return MS_FAILURE;
×
916
  }
917

918
  /* loop to the next valid value */
919
  size_t raster_off = (shapeindex >= uvlinfo->last_queried_shapeindex)
4,292✔
920
                          ? uvlinfo->last_raster_off
4,292✔
921
                          : 0;
922
  for (long curshapeindex = (shapeindex >= uvlinfo->last_queried_shapeindex)
7,678✔
923
                                ? uvlinfo->last_queried_shapeindex
4,292✔
924
                                : 0;
925
       raster_off < static_cast<size_t>(uvlinfo->width) * uvlinfo->height;
11,970✔
926
       ++raster_off) {
927
    if (!std::isnan(uvlinfo->u[raster_off])) {
11,970✔
928
      if (curshapeindex == shapeindex) {
8,575✔
929
        uvlinfo->last_queried_shapeindex = shapeindex;
4,292✔
930
        uvlinfo->last_raster_off = raster_off;
4,292✔
931
        break;
4,292✔
932
      }
933
      ++curshapeindex;
4,283✔
934
    }
935
  }
936
  assert(raster_off < static_cast<size_t>(uvlinfo->width) * uvlinfo->height);
937

938
  const int x = static_cast<int>(raster_off % uvlinfo->width);
4,292✔
939
  const int y = static_cast<int>(raster_off / uvlinfo->width);
4,292✔
940
  point.x = Pix2Georef(x, 0, uvlinfo->width - 1, uvlinfo->extent.minx,
4,292✔
941
                       uvlinfo->extent.maxx, MS_FALSE);
942
  point.y = Pix2Georef(y, 0, uvlinfo->height - 1, uvlinfo->extent.miny,
4,292✔
943
                       uvlinfo->extent.maxy, MS_TRUE);
944
  if (layer->debug == MS_DEBUGLEVEL_VVV)
4,292✔
945
    msDebug("msUVRASTERLayerWhichShapes(): shapeindex: %ld, x: %g, y: %g\n",
×
946
            shapeindex, point.x, point.y);
947

948
  point.m = 0.0;
4,292✔
949

950
  shape->type = MS_SHAPE_POINT;
4,292✔
951
  line.numpoints = 1;
4,292✔
952
  line.point = &point;
4,292✔
953
  msAddLine(shape, &line);
4,292✔
954
  msComputeBounds(shape);
4,292✔
955

956
  shape->numvalues = layer->numitems;
4,292✔
957
  shape->values = msUVRASTERGetValues(layer, uvlinfo->u[raster_off],
8,584✔
958
                                      uvlinfo->v[raster_off], &point);
4,292✔
959
  shape->index = shapeindex;
4,292✔
960
  shape->resultindex = shapeindex;
4,292✔
961

962
  return MS_SUCCESS;
4,292✔
963
}
964

965
int msUVRASTERLayerNextShape(layerObj *layer, shapeObj *shape) {
4,307✔
966
  uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
967

968
  if (uvlinfo->next_shape < 0 ||
4,307✔
969
      uvlinfo->next_shape >= uvlinfo->query_results) {
4,307✔
970
    msFreeShape(shape);
16✔
971
    shape->type = MS_SHAPE_NULL;
16✔
972
    return MS_DONE;
16✔
973
  } else {
974
    resultObj record;
975

976
    record.shapeindex = uvlinfo->next_shape++;
4,291✔
977
    record.tileindex = 0;
4,291✔
978
    record.classindex = record.resultindex = -1;
4,291✔
979

980
    return msUVRASTERLayerGetShape(layer, shape, &record);
4,291✔
981
  }
982
}
983

984
/************************************************************************/
985
/*                       msUVRASTERLayerGetExtent()                     */
986
/* Simple copy of the maprasterquery.c file. might change in the future */
987
/************************************************************************/
988

989
int msUVRASTERLayerGetExtent(layerObj *layer, rectObj *extent)
7✔
990

991
{
992
  char szPath[MS_MAXPATHLEN];
993
  mapObj *map = layer->map;
7✔
994
  shapefileObj *tileshpfile;
995

996
  if ((!layer->data || strlen(layer->data) == 0) && layer->tileindex == NULL) {
7✔
997
    /* should we be issuing a specific error about not supporting
998
       extents for tileindexed raster layers? */
999
    return MS_FAILURE;
1000
  }
1001

1002
  if (map == NULL)
7✔
1003
    return MS_FAILURE;
1004

1005
  /* If the layer use a tileindex, return the extent of the tileindex
1006
   * shapefile/referenced layer */
1007
  if (layer->tileindex) {
7✔
UNCOV
1008
    const int tilelayerindex = msGetLayerIndex(map, layer->tileindex);
×
UNCOV
1009
    if (tilelayerindex != -1) /* does the tileindex reference another layer */
×
UNCOV
1010
      return msLayerGetExtent(GET_LAYER(map, tilelayerindex), extent);
×
1011
    else {
UNCOV
1012
      tileshpfile = (shapefileObj *)malloc(sizeof(shapefileObj));
×
UNCOV
1013
      MS_CHECK_ALLOC(tileshpfile, sizeof(shapefileObj), MS_FAILURE);
×
1014

UNCOV
1015
      if (msShapefileOpen(tileshpfile, "rb",
×
UNCOV
1016
                          msBuildPath3(szPath, map->mappath, map->shapepath,
×
UNCOV
1017
                                       layer->tileindex),
×
1018
                          MS_TRUE) == -1)
UNCOV
1019
        if (msShapefileOpen(tileshpfile, "rb",
×
UNCOV
1020
                            msBuildPath(szPath, map->mappath, layer->tileindex),
×
1021
                            MS_TRUE) == -1)
1022
          return MS_FAILURE;
1023

UNCOV
1024
      *extent = tileshpfile->bounds;
×
UNCOV
1025
      msShapefileClose(tileshpfile);
×
UNCOV
1026
      free(tileshpfile);
×
1027
      return MS_SUCCESS;
×
1028
    }
1029
  }
1030

1031
  msTryBuildPath3(szPath, map->mappath, map->shapepath, layer->data);
7✔
1032
  char *decrypted_path = msDecryptStringTokens(map, szPath);
7✔
1033
  if (!decrypted_path)
7✔
1034
    return MS_FAILURE;
1035

1036
  GDALAllRegister();
7✔
1037

1038
  char **connectionoptions =
1039
      msGetStringListFromHashTable(&(layer->connectionoptions));
7✔
1040
  GDALDatasetH hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
7✔
1041
                                (const char *const *)connectionoptions, NULL);
1042
  CSLDestroy(connectionoptions);
7✔
1043
  msFree(decrypted_path);
7✔
1044
  if (hDS == NULL) {
7✔
1045
    return MS_FAILURE;
1046
  }
1047

1048
  const int nXSize = GDALGetRasterXSize(hDS);
7✔
1049
  const int nYSize = GDALGetRasterYSize(hDS);
7✔
1050
  double adfGeoTransform[6] = {0};
7✔
1051
  const CPLErr eErr = GDALGetGeoTransform(hDS, adfGeoTransform);
7✔
1052
  GDALClose(hDS);
7✔
1053
  if (eErr != CE_None) {
7✔
1054
    return MS_FAILURE;
1055
  }
1056

1057
  /* If this appears to be an ungeoreferenced raster than flip it for
1058
     mapservers purposes. */
1059
  if (adfGeoTransform[5] == 1.0 && adfGeoTransform[3] == 0.0) {
7✔
UNCOV
1060
    adfGeoTransform[5] = -1.0;
×
UNCOV
1061
    adfGeoTransform[3] = nYSize;
×
1062
  }
1063

1064
  extent->minx = adfGeoTransform[0];
7✔
1065
  extent->maxy = adfGeoTransform[3];
7✔
1066

1067
  extent->maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1];
7✔
1068
  extent->miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5];
7✔
1069

1070
  return MS_SUCCESS;
7✔
1071
}
1072

1073
/************************************************************************/
1074
/*                     msUVRASTERLayerSetTimeFilter()                   */
1075
/*                                                                      */
1076
/*      This function is actually just used in the context of           */
1077
/*      setting a filter on the tileindex for time based queries.       */
1078
/*      For instance via WMS requests.  So it isn't really related      */
1079
/*      to the "raster query" support at all.                           */
1080
/*                                                                      */
1081
/*      If a local shapefile tileindex is in use, we will set a         */
1082
/*      backtics filter (shapefile compatible).  If another layer is    */
1083
/*      being used as the tileindex then we will forward the            */
1084
/*      SetTimeFilter call to it.  If there is no tileindex in          */
1085
/*      place, we do nothing.                                           */
1086
/************************************************************************/
1087

1088
int msUVRASTERLayerSetTimeFilter(layerObj *layer, const char *timestring,
2✔
1089
                                 const char *timefield) {
1090
  int tilelayerindex;
1091

1092
  /* -------------------------------------------------------------------- */
1093
  /*      If we don't have a tileindex the time filter has no effect.     */
1094
  /* -------------------------------------------------------------------- */
1095
  if (layer->tileindex == NULL)
2✔
1096
    return MS_SUCCESS;
1097

1098
  /* -------------------------------------------------------------------- */
1099
  /*      Find the tileindex layer.                                       */
1100
  /* -------------------------------------------------------------------- */
1101
  tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
2✔
1102

1103
  /* -------------------------------------------------------------------- */
1104
  /*      If we are using a local shapefile as our tileindex (that is     */
1105
  /*      to say, the tileindex name is not of another layer), then we    */
1106
  /*      will install a backtics style filter later.                     */
1107
  /* -------------------------------------------------------------------- */
1108
  if (tilelayerindex == -1) {
2✔
1109
    if (layer->layerinfo == NULL)
1✔
1110
      msUVRasterLayerInfoInitialize(layer);
1✔
1111
    if (layer->layerinfo == NULL)
1✔
1112
      return MS_FAILURE;
1113
    uvRasterLayerInfo *uvlinfo = getLayerInfo(layer);
1114
    if (timestring)
1✔
1115
      uvlinfo->timestring = timestring;
1✔
1116
    if (timefield)
1✔
1117
      uvlinfo->timefield = timefield;
1✔
1118
    return MS_SUCCESS;
1✔
1119
  }
1120

1121
  /* -------------------------------------------------------------------- */
1122
  /*      Otherwise we invoke the tileindex layers SetTimeFilter          */
1123
  /*      method.                                                         */
1124
  /* -------------------------------------------------------------------- */
1125
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
1✔
1126
    return MS_FAILURE;
1127
  return msLayerSetTimeFilter(layer->GET_LAYER(map, tilelayerindex), timestring,
1✔
1128
                              timefield);
1✔
1129
}
1130

1131
/************************************************************************/
1132
/*                msUVRASTERLayerInitializeVirtualTable()               */
1133
/************************************************************************/
1134

1135
int msUVRASTERLayerInitializeVirtualTable(layerObj *layer) {
23✔
1136
  assert(layer != NULL);
1137
  assert(layer->vtable != NULL);
1138

1139
  layer->vtable->LayerInitItemInfo = msUVRASTERLayerInitItemInfo;
23✔
1140
  layer->vtable->LayerFreeItemInfo = msUVRASTERLayerFreeItemInfo;
23✔
1141
  layer->vtable->LayerOpen = msUVRASTERLayerOpen;
23✔
1142
  layer->vtable->LayerIsOpen = msUVRASTERLayerIsOpen;
23✔
1143
  layer->vtable->LayerWhichShapes = msUVRASTERLayerWhichShapes;
23✔
1144
  layer->vtable->LayerNextShape = msUVRASTERLayerNextShape;
23✔
1145
  layer->vtable->LayerGetShape = msUVRASTERLayerGetShape;
23✔
1146
  /* layer->vtable->LayerGetShapeCount, use default */
1147
  layer->vtable->LayerClose = msUVRASTERLayerClose;
23✔
1148
  layer->vtable->LayerGetItems = msUVRASTERLayerGetItems;
23✔
1149
  layer->vtable->LayerGetExtent = msUVRASTERLayerGetExtent;
23✔
1150
  /* layer->vtable->LayerGetAutoStyle, use default */
1151
  /* layer->vtable->LayerApplyFilterToLayer, use default */
1152
  /* layer->vtable->LayerCloseConnection = msUVRASTERLayerClose; */
1153
  /* we use backtics for proper tileindex shapefile functioning */
1154
  layer->vtable->LayerSetTimeFilter = msUVRASTERLayerSetTimeFilter;
23✔
1155
  /* layer->vtable->LayerCreateItems, use default */
1156
  /* layer->vtable->LayerGetNumFeatures, use default */
1157

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

© 2026 Coveralls, Inc