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

geographika / mapserver / 13186752183

06 Feb 2025 07:51PM UTC coverage: 40.424% (-0.003%) from 40.427%
13186752183

push

github

geographika
Update default URLs to use mapserver.org and remove scrollbar

58527 of 144784 relevant lines covered (40.42%)

25514.53 hits per line

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

52.72
/src/mapraster.c
1
/******************************************************************************
2
 * $id: mapraster.c 10772 2010-11-29 18:27:02Z aboudreault $
3
 *
4
 * Project:  MapServer
5
 * Purpose:  msDrawRasterLayer(): generic raster layer drawing.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *           Pete Olson (LMIC)
8
 *           Steve Lime
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 1996-2010 Regents of the University of Minnesota.
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a
14
 * copy of this software and associated documentation files (the "Software"),
15
 * to deal in the Software without restriction, including without limitation
16
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
 * and/or sell copies of the Software, and to permit persons to whom the
18
 * Software is furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in
21
 * all copies of this Software or works derived from this Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
 * DEALINGS IN THE SOFTWARE.
30
 ****************************************************************************/
31

32
#include <assert.h>
33
#include "mapserver.h"
34
#include "mapfile.h"
35
#include "mapresample.h"
36
#include "mapthread.h"
37

38
extern int msyylex_destroy(void);
39
extern int yyparse(parseObj *);
40

41
extern parseResultObj yypresult; /* result of parsing, true/false */
42

43
#include "gdal.h"
44
#include "cpl_string.h"
45
#include "mapraster.h"
46

47
#define MAXCOLORS 256
48
#define BUFLEN 1024
49
#define HDRLEN 8
50

51
#define CVT(x) ((x) >> 8) /* converts to 8-bit color value */
52

53
#define NUMGRAYS 16
54

55
/************************************************************************/
56
/*                         msGetClass_String()                          */
57
/************************************************************************/
58

59
static int msGetClass_String(layerObj *layer, colorObj *color,
134,868✔
60
                             const char *pixel_value, int firstClassToTry)
61

62
{
63
  int i;
64
  const char *tmpstr1 = NULL;
65
  int numitems;
66
  char *item_names[4] = {"pixel", "red", "green", "blue"};
134,868✔
67
  char *item_values[4];
68
  char red_value[8], green_value[8], blue_value[8];
69

70
  /* -------------------------------------------------------------------- */
71
  /*      No need to do a lookup in the case of one default class.        */
72
  /* -------------------------------------------------------------------- */
73
  if ((layer->numclasses == 1) &&
134,868✔
74
      !(layer->class[0] -> expression.string)) /* no need to do lookup */
68,605✔
75
    return (0);
76

77
  /* -------------------------------------------------------------------- */
78
  /*      Setup values list for expressions.                              */
79
  /* -------------------------------------------------------------------- */
80
  numitems = 4;
66,779✔
81
  if (color->red == -1 && color->green == -1 && color->blue == -1) {
66,779✔
82
    strcpy(red_value, "-1");
83
    strcpy(green_value, "-1");
84
    strcpy(blue_value, "-1");
85
  } else {
86
    sprintf(red_value, "%d", color->red);
87
    sprintf(green_value, "%d", color->green);
778✔
88
    sprintf(blue_value, "%d", color->blue);
778✔
89
  }
90

91
  item_values[0] = (char *)pixel_value;
66,779✔
92
  item_values[1] = red_value;
66,779✔
93
  item_values[2] = green_value;
66,779✔
94
  item_values[3] = blue_value;
66,779✔
95

96
  /* -------------------------------------------------------------------- */
97
  /*      Loop over classes till we find a match.                         */
98
  /* -------------------------------------------------------------------- */
99
  if (firstClassToTry >= layer->numclasses)
66,779✔
100
    firstClassToTry = -1;
101
  for (i = 0; i < layer->numclasses; i++) {
96,048✔
102

103
    const int idx = firstClassToTry < 0    ? i
104
                    : i == 0               ? firstClassToTry
95,451✔
105
                    : i <= firstClassToTry ? i - 1
66,010✔
106
                                           : i;
15✔
107

108
    /* check for correct classgroup, if set */
109
    if (layer->class[idx] -> group && layer -> classgroup &&strcasecmp(
95,451✔
110
                                                   layer->class[idx] -> group,
111
                                                   layer -> classgroup) != 0)
112
      continue;
×
113

114
    /* Empty expression - always matches */
115
    if (layer->class[idx] -> expression.string == NULL)
95,451✔
116
      return (i);
×
117

118
    switch (layer->class[idx] -> expression.type) {
95,451✔
119

120
      /* -------------------------------------------------------------------- */
121
      /*      Simple string match                                             */
122
      /* -------------------------------------------------------------------- */
123
    case (MS_STRING):
124
      /* trim junk white space */
125
      tmpstr1 = pixel_value;
126
      while (*tmpstr1 == ' ')
×
127
        tmpstr1++;
×
128

129
      if (strcmp(layer->class[idx] -> expression.string, tmpstr1) == 0)
×
130
        return (idx); /* matched */
×
131
      break;
132

133
      /* -------------------------------------------------------------------- */
134
      /*      Regular expression.  Rarely used for raster.                    */
135
      /* -------------------------------------------------------------------- */
136
    case (MS_REGEX):
×
137
      if (!layer->class[idx] -> expression.compiled) {
×
138
        if (ms_regcomp(&(layer->class[idx] -> expression.regex),
×
139
                       layer->class[idx] -> expression.string,
140
                       MS_REG_EXTENDED | MS_REG_NOSUB) !=
141
            0) { /* compile the expression  */
142
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
143
                     "msGetClass()");
144
          return (-1);
×
145
        }
146
        layer->class[idx]->expression.compiled = MS_TRUE;
×
147
      }
148

149
      if (ms_regexec(&(layer->class[idx] -> expression.regex), pixel_value, 0,
×
150
                     NULL, 0) == 0)
151
        return (idx); /* got a match */
×
152
      break;
153

154
      /* -------------------------------------------------------------------- */
155
      /*      Parsed expression.                                              */
156
      /* -------------------------------------------------------------------- */
157
    case (MS_EXPRESSION): {
95,451✔
158
      int status;
159
      parseObj p;
160
      shapeObj dummy_shape;
161
      expressionObj *expression = &(layer->class[idx] -> expression);
95,451✔
162

163
      dummy_shape.numvalues = numitems;
95,451✔
164
      dummy_shape.values = item_values;
95,451✔
165

166
      if (expression->tokens == NULL)
95,451✔
167
        msTokenizeExpression(expression, item_names, &numitems);
194✔
168

169
      p.shape = &dummy_shape;
95,451✔
170
      p.expr = expression;
95,451✔
171
      p.expr->curtoken = p.expr->tokens; /* reset */
95,451✔
172
      p.type = MS_PARSE_TYPE_BOOLEAN;
95,451✔
173

174
      status = yyparse(&p);
95,451✔
175

176
      if (status != 0) {
95,451✔
177
        msSetError(MS_PARSEERR, "Failed to parse expression: %s",
×
178
                   "msGetClass_FloatRGB", expression->string);
179
        return -1;
66,182✔
180
      }
181

182
      if (p.result.intval)
95,451✔
183
        return idx;
66,182✔
184
      break;
29,269✔
185
    }
186
    }
187
  }
29,269✔
188

189
  return (-1); /* not found */
190
}
191

192
/************************************************************************/
193
/*                             msGetClass()                             */
194
/************************************************************************/
195

196
int msGetClass(layerObj *layer, colorObj *color, int colormap_index) {
3,329✔
197
  char pixel_value[12];
198

199
  snprintf(pixel_value, sizeof(pixel_value), "%d", colormap_index);
200

201
  return msGetClass_String(layer, color, pixel_value, -1);
3,329✔
202
}
203

204
/************************************************************************/
205
/*                          msGetClass_FloatRGB()                       */
206
/*                                                                      */
207
/*      Returns the class based on classification of a floating         */
208
/*      pixel value.                                                    */
209
/************************************************************************/
210

211
int msGetClass_FloatRGB(layerObj *layer, float fValue, int red, int green,
2✔
212
                        int blue) {
213
  return msGetClass_FloatRGB_WithFirstClassToTry(layer, fValue, red, green,
2✔
214
                                                 blue, -1);
215
}
216

217
int msGetClass_FloatRGB_WithFirstClassToTry(layerObj *layer, float fValue,
131,539✔
218
                                            int red, int green, int blue,
219
                                            int firstClassToTry) {
220
  char pixel_value[100];
221
  colorObj color;
222

223
  color.red = red;
131,539✔
224
  color.green = green;
131,539✔
225
  color.blue = blue;
131,539✔
226

227
  snprintf(pixel_value, sizeof(pixel_value), "%18g", fValue);
131,539✔
228

229
  return msGetClass_String(layer, &color, pixel_value, firstClassToTry);
131,539✔
230
}
231

232
/************************************************************************/
233
/*                      msRasterSetupTileLayer()                        */
234
/*                                                                      */
235
/*      Setup the tile layer.                                           */
236
/************************************************************************/
237

238
int msDrawRasterSetupTileLayer(mapObj *map, layerObj *layer,
162✔
239
                               rectObj *psearchrect, int is_query,
240
                               int *ptilelayerindex, /* output */
241
                               int *ptileitemindex,  /* output */
242
                               int *ptilesrsindex,   /* output */
243
                               layerObj **ptlp /* output */) {
244
  int i;
245
  char *requested_fields;
246
  int status;
247
  layerObj *tlp = NULL;
248

249
  *ptilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
162✔
250
  if (*ptilelayerindex ==
162✔
251
      -1) { /* the tileindex references a file, not a layer */
252

253
    /* so we create a temporary layer */
254
    tlp = (layerObj *)malloc(sizeof(layerObj));
155✔
255
    MS_CHECK_ALLOC(tlp, sizeof(layerObj), MS_FAILURE);
155✔
256

257
    initLayer(tlp, map);
155✔
258
    *ptlp = tlp;
155✔
259

260
    /* set a few parameters for a very basic shapefile-based layer */
261
    tlp->name = msStrdup("TILE");
155✔
262
    tlp->type = MS_LAYER_TILEINDEX;
155✔
263
    tlp->data = msStrdup(layer->tileindex);
155✔
264

265
    if (is_query) {
155✔
266
      tlp->map = map; /*needed when scaletokens are applied, to extract current
7✔
267
                         map scale */
268
      for (i = 0; i < layer->numscaletokens; i++) {
7✔
269
        if (msGrowLayerScaletokens(tlp) == NULL) {
×
270
          return MS_FAILURE;
271
        }
272
        initScaleToken(&tlp->scaletokens[i]);
×
273
        msCopyScaleToken(&layer->scaletokens[i], &tlp->scaletokens[i]);
×
274
        tlp->numscaletokens++;
×
275
      }
276
    }
277

278
    if (layer->projection.numargs > 0 &&
155✔
279
        EQUAL(layer->projection.args[0], "auto")) {
155✔
280
      tlp->projection.numargs = 1;
2✔
281
      tlp->projection.args[0] = msStrdup("auto");
2✔
282
    }
283

284
    if (layer->filteritem)
155✔
285
      tlp->filteritem = msStrdup(layer->filteritem);
×
286
    if (layer->filter.string) {
155✔
287
      if (layer->filter.type == MS_EXPRESSION) {
×
288
        char *pszTmp = (char *)msSmallMalloc(
×
289
            sizeof(char) * (strlen(layer->filter.string) + 3));
×
290
        sprintf(pszTmp, "(%s)", layer->filter.string);
×
291
        msLoadExpressionString(&tlp->filter, pszTmp);
×
292
        free(pszTmp);
×
293
      } else if (layer->filter.type == MS_REGEX ||
×
294
                 layer->filter.type == MS_IREGEX) {
295
        char *pszTmp = (char *)msSmallMalloc(
×
296
            sizeof(char) * (strlen(layer->filter.string) + 3));
×
297
        sprintf(pszTmp, "/%s/", layer->filter.string);
×
298
        msLoadExpressionString(&tlp->filter, pszTmp);
×
299
        free(pszTmp);
×
300
      } else
301
        msLoadExpressionString(&tlp->filter, layer->filter.string);
×
302

303
      tlp->filter.type = layer->filter.type;
×
304
    }
305

306
  } else {
307
    if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
7✔
308
      return MS_FAILURE;
309
    tlp = (GET_LAYER(layer->map, *ptilelayerindex));
7✔
310
    *ptlp = tlp;
7✔
311
  }
312
  status = msLayerOpen(tlp);
162✔
313
  if (status != MS_SUCCESS) {
162✔
314
    return status;
315
  }
316

317
  /* fetch tileitem and tilesrs fields */
318
  requested_fields = (char *)msSmallMalloc(
162✔
319
      sizeof(char) * (strlen(layer->tileitem) + 1 +
324✔
320
                      (layer->tilesrs ? strlen(layer->tilesrs) : 0) + 1));
162✔
321
  if (layer->tilesrs != NULL)
162✔
322
    sprintf(requested_fields, "%s,%s", layer->tileitem, layer->tilesrs);
7✔
323
  else
324
    strcpy(requested_fields, layer->tileitem);
155✔
325
  status = msLayerWhichItems(tlp, MS_FALSE, requested_fields);
162✔
326
  msFree(requested_fields);
162✔
327
  if (status != MS_SUCCESS) {
162✔
328
    return status;
329
  }
330

331
  /* get the tileitem and tilesrs index */
332
  for (i = 0; i < tlp->numitems; i++) {
335✔
333
    if (strcasecmp(tlp->items[i], layer->tileitem) == 0) {
173✔
334
      *ptileitemindex = i;
162✔
335
    }
336
    if (layer->tilesrs != NULL &&
173✔
337
        strcasecmp(tlp->items[i], layer->tilesrs) == 0) {
14✔
338
      *ptilesrsindex = i;
7✔
339
    }
340
  }
341
  if (*ptileitemindex < 0) { /* didn't find it */
162✔
342
    msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.",
×
343
               "msDrawRasterLayerLow()", layer->tileitem);
344
    return MS_FAILURE;
×
345
  }
346
  if (layer->tilesrs != NULL && *ptilesrsindex < 0) { /* didn't find it */
162✔
347
    msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.",
×
348
               "msDrawRasterLayerLow()", layer->tilesrs);
349
    return MS_FAILURE;
×
350
  }
351

352
  /* if necessary, project the searchrect to source coords */
353
  if ((map->projection.numargs > 0) && (layer->projection.numargs > 0) &&
162✔
354
      !EQUAL(layer->projection.args[0], "auto")) {
162✔
355
    if (msProjectRect(&map->projection, &layer->projection, psearchrect) !=
158✔
356
        MS_SUCCESS) {
357
      msDebug("msDrawRasterLayerLow(%s): unable to reproject map request "
×
358
              "rectangle into layer projection, canceling.\n",
359
              layer->name);
360
      return MS_FAILURE;
×
361
    }
362
  } else if ((map->projection.numargs > 0) && (tlp->projection.numargs > 0) &&
4✔
363
             !EQUAL(tlp->projection.args[0], "auto")) {
4✔
364
    if (msProjectRect(&map->projection, &tlp->projection, psearchrect) !=
4✔
365
        MS_SUCCESS) {
366
      msDebug("msDrawRasterLayerLow(%s): unable to reproject map request "
×
367
              "rectangle into layer projection, canceling.\n",
368
              layer->name);
369
      return MS_FAILURE;
×
370
    }
371
  }
372
  return msLayerWhichShapes(tlp, *psearchrect, MS_FALSE);
162✔
373
}
374

375
/************************************************************************/
376
/*                msDrawRasterCleanupTileLayer()                        */
377
/*                                                                      */
378
/*      Cleanup the tile layer.                                         */
379
/************************************************************************/
380

381
void msDrawRasterCleanupTileLayer(layerObj *tlp, int tilelayerindex) {
162✔
382
  msLayerClose(tlp);
162✔
383
  if (tilelayerindex == -1) {
162✔
384
    freeLayer(tlp);
155✔
385
    free(tlp);
155✔
386
  }
387
}
162✔
388

389
/************************************************************************/
390
/*                   msDrawRasterIterateTileIndex()                     */
391
/*                                                                      */
392
/*      Iterate over the tile layer.                                    */
393
/************************************************************************/
394

395
int msDrawRasterIterateTileIndex(layerObj *layer, layerObj *tlp,
354✔
396
                                 shapeObj *ptshp, /* input-output */
397
                                 int tileitemindex, int tilesrsindex,
398
                                 char *tilename, /* output */
399
                                 size_t sizeof_tilename,
400
                                 char *tilesrsname, /* output */
401
                                 size_t sizeof_tilesrsname) {
402
  int status;
403

404
  status = msLayerNextShape(tlp, ptshp);
354✔
405
  if (status == MS_FAILURE || status == MS_DONE) {
354✔
406
    return status;
407
  }
408

409
  if (layer->data == NULL ||
196✔
410
      strlen(layer->data) ==
×
411
          0) { /* assume whole filename is in attribute field */
412
    strlcpy(tilename, ptshp->values[tileitemindex], sizeof_tilename);
196✔
413
  } else
414
    snprintf(tilename, sizeof_tilename, "%s/%s", ptshp->values[tileitemindex],
×
415
             layer->data);
416

417
  tilesrsname[0] = '\0';
196✔
418

419
  if (tilesrsindex >= 0) {
196✔
420
    if (ptshp->values[tilesrsindex] != NULL)
25✔
421
      strlcpy(tilesrsname, ptshp->values[tilesrsindex], sizeof_tilesrsname);
25✔
422
  }
423

424
  msFreeShape(ptshp); /* done with the shape */
196✔
425

426
  return status;
196✔
427
}
428

429
/************************************************************************/
430
/*                   msDrawRasterBuildRasterPath()                      */
431
/*                                                                      */
432
/*      Build the path of the raster to open.                           */
433
/************************************************************************/
434

435
int msDrawRasterBuildRasterPath(mapObj *map, layerObj *layer,
645✔
436
                                const char *filename,
437
                                char szPath[MS_MAXPATHLEN] /* output */) {
438
  /*
439
  ** If using a tileindex then build the path relative to that file if SHAPEPATH
440
  * is not set, provided that layer->tileindex does not refer to a layer (to
441
  * save a useless file opening attempt)
442
  */
443
  if (layer->tileindex && !map->shapepath &&
689✔
444
      msGetLayerIndex(map, layer->tileindex) < 0) {
44✔
445
    char tiAbsFilePath[MS_MAXPATHLEN];
446
    char *tiAbsDirPath = NULL;
447

448
    msTryBuildPath(tiAbsFilePath, map->mappath,
36✔
449
                   layer->tileindex); /* absolute path to tileindex file */
36✔
450
    tiAbsDirPath = msGetPath(tiAbsFilePath); /* tileindex file's directory */
36✔
451
    msBuildPath(szPath, tiAbsDirPath, filename);
36✔
452
    free(tiAbsDirPath);
36✔
453
  } else {
454
    msTryBuildPath3(szPath, map->mappath, map->shapepath, filename);
609✔
455
  }
456

457
  return MS_SUCCESS;
645✔
458
}
459

460
/************************************************************************/
461
/*                   msDrawRasterGetCPLErrorMsg()                       */
462
/*                                                                      */
463
/*      Return the CPL error message, and filter out sensitive info.    */
464
/************************************************************************/
465

466
const char *msDrawRasterGetCPLErrorMsg(const char *decrypted_path,
×
467
                                       const char *szPath) {
468
  const char *cpl_error_msg = CPLGetLastErrorMsg();
×
469

470
  /* we wish to avoid reporting decrypted paths */
471
  if (cpl_error_msg != NULL && strstr(cpl_error_msg, decrypted_path) != NULL &&
×
472
      strcmp(decrypted_path, szPath) != 0)
×
473
    cpl_error_msg = NULL;
474

475
  /* we wish to avoid reporting the stock GDALOpen error messages */
476
  if (cpl_error_msg != NULL &&
×
477
      (strstr(cpl_error_msg, "not recognised as a supported") != NULL ||
×
478
       strstr(cpl_error_msg, "does not exist") != NULL))
×
479
    cpl_error_msg = NULL;
480

481
  if (cpl_error_msg == NULL)
×
482
    cpl_error_msg = "";
483

484
  return cpl_error_msg;
×
485
}
486

487
/************************************************************************/
488
/*                   msDrawRasterLoadProjection()                       */
489
/*                                                                      */
490
/*      Handle TILESRS or PROJECTION AUTO for each tile.                */
491
/************************************************************************/
492

493
int msDrawRasterLoadProjection(layerObj *layer, GDALDatasetH hDS,
652✔
494
                               const char *filename, int tilesrsindex,
495
                               const char *tilesrsname) {
496
  /*
497
  ** Generate the projection information if using TILESRS.
498
  */
499
  if (tilesrsindex >= 0) {
652✔
500
    const char *pszWKT;
501
    if (tilesrsname[0] != '\0')
25✔
502
      pszWKT = tilesrsname;
503
    else
504
      pszWKT = GDALGetProjectionRef(hDS);
4✔
505
    if (pszWKT != NULL && strlen(pszWKT) > 0) {
25✔
506
      if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) !=
25✔
507
          MS_SUCCESS) {
508
        char szLongMsg[MESSAGELENGTH * 2];
509
        errorObj *ms_error = msGetErrorObj();
×
510

511
        snprintf(szLongMsg, sizeof(szLongMsg),
512
                 "%s\n"
513
                 "PROJECTION '%s' cannot be used for this "
514
                 "GDAL raster (`%s').",
515
                 ms_error->message, pszWKT, filename);
×
516
        szLongMsg[MESSAGELENGTH - 1] = '\0';
×
517

518
        msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg);
×
519

520
        return MS_FAILURE;
521
      }
522
    }
523
  }
524
  /*
525
  ** Generate the projection information if using AUTO.
526
  */
527
  else if (layer->projection.numargs > 0 &&
627✔
528
           EQUAL(layer->projection.args[0], "auto")) {
478✔
529
    const char *pszWKT;
530

531
    pszWKT = GDALGetProjectionRef(hDS);
1✔
532

533
    if (pszWKT != NULL && strlen(pszWKT) > 0) {
1✔
534
      if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) !=
1✔
535
          MS_SUCCESS) {
536
        char szLongMsg[MESSAGELENGTH * 2];
537
        errorObj *ms_error = msGetErrorObj();
×
538

539
        snprintf(szLongMsg, sizeof(szLongMsg),
540
                 "%s\n"
541
                 "PROJECTION AUTO cannot be used for this "
542
                 "GDAL raster (`%s').",
543
                 ms_error->message, filename);
×
544
        szLongMsg[MESSAGELENGTH - 1] = '\0';
×
545

546
        msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg);
×
547

548
        return MS_FAILURE;
549
      }
550
    }
551
  }
552

553
  return MS_SUCCESS;
554
}
555

556
typedef enum {
557
  CDRT_OK,
558
  CDRT_RETURN_MS_FAILURE,
559
  CDRT_CONTINUE_NEXT_TILE
560
} CheckDatasetReturnType;
561

562
/************************************************************************/
563
/*              msDrawRasterLayerLowCheckDataset()                      */
564
/************************************************************************/
565

566
static CheckDatasetReturnType
567
msDrawRasterLayerLowCheckDataset(mapObj *map, layerObj *layer, GDALDatasetH hDS,
582✔
568
                                 const char *decrypted_path,
569
                                 const char *szPath) {
570
  /*
571
  ** If GDAL doesn't recognise it, and it wasn't successfully opened
572
  ** Generate an error.
573
  */
574
  if (hDS == NULL) {
582✔
575
    int ignore_missing = msMapIgnoreMissingData(map);
×
576
    const char *cpl_error_msg =
577
        msDrawRasterGetCPLErrorMsg(decrypted_path, szPath);
×
578

579
    if (ignore_missing == MS_MISSING_DATA_FAIL) {
×
580
      msSetError(MS_IOERR,
×
581
                 "Corrupt, empty or missing file '%s' for layer '%s'. %s",
582
                 "msDrawRasterLayerLow()", szPath, layer->name, cpl_error_msg);
583
      return (CDRT_RETURN_MS_FAILURE);
×
584
    } else if (ignore_missing == MS_MISSING_DATA_LOG) {
×
585
      if (layer->debug || layer->map->debug) {
×
586
        msDebug("Corrupt, empty or missing file '%s' for layer '%s' ... "
×
587
                "ignoring this missing data.  %s\n",
588
                szPath, layer->name, cpl_error_msg);
589
      }
590
      return (CDRT_CONTINUE_NEXT_TILE);
×
591
    } else if (ignore_missing == MS_MISSING_DATA_IGNORE) {
×
592
      return (CDRT_CONTINUE_NEXT_TILE);
593
    } else {
594
      /* never get here */
595
      msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.",
×
596
                 "msDrawRasterLayerLow()");
597
      return (CDRT_RETURN_MS_FAILURE);
×
598
    }
599
  }
600

601
  return CDRT_OK;
602
}
603

604
/************************************************************************/
605
/*              msDrawRasterLayerLowOpenDataset()                       */
606
/************************************************************************/
607

608
void *msDrawRasterLayerLowOpenDataset(mapObj *map, layerObj *layer,
615✔
609
                                      const char *filename,
610
                                      char szPath[MS_MAXPATHLEN],
611
                                      char **p_decrypted_path) {
612
  const char *pszPath;
613

614
  msGDALInitialize();
615✔
615

616
  if (layer->debug)
615✔
617
    msDebug("msDrawRasterLayerLow(%s): Filename is: %s\n", layer->name,
1✔
618
            filename);
619

620
  if (strncmp(filename, "<VRTDataset", strlen("<VRTDataset")) == 0) {
615✔
621
    pszPath = filename;
622
  } else {
623
    msDrawRasterBuildRasterPath(map, layer, filename, szPath);
611✔
624
    pszPath = szPath;
625
  }
626
  if (layer->debug)
615✔
627
    msDebug("msDrawRasterLayerLow(%s): Path is: %s\n", layer->name, pszPath);
1✔
628

629
  /*
630
  ** Note: because we do decryption after the above path expansion
631
  ** which depends on actually finding a file, it essentially means that
632
  ** fancy path manipulation is essentially disabled when using encrypted
633
  ** components. But that is mostly ok, since stuff like sde,postgres and
634
  ** oracle georaster do not use real paths.
635
  */
636
  *p_decrypted_path = msDecryptStringTokens(map, pszPath);
615✔
637
  if (*p_decrypted_path == NULL)
615✔
638
    return NULL;
639

640
  msAcquireLock(TLOCK_GDAL);
615✔
641
  if (!layer->tileindex) {
615✔
642
    char **connectionoptions =
643
        msGetStringListFromHashTable(&(layer->connectionoptions));
440✔
644
    char **papszAllowedDrivers = NULL;
645
    // ALLOWED_GDAL_DRIVERS typically set by msDrawWMSLayerLow()
646
    const char *pszAllowedDrivers =
647
        msLayerGetProcessingKey(layer, "ALLOWED_GDAL_DRIVERS");
440✔
648
    if (pszAllowedDrivers && !EQUAL(pszAllowedDrivers, "*"))
440✔
649
      papszAllowedDrivers = CSLTokenizeString2(pszAllowedDrivers, ",", 0);
12✔
650
    GDALDatasetH hDS =
651
        GDALOpenEx(*p_decrypted_path, GDAL_OF_RASTER | GDAL_OF_SHARED,
440✔
652
                   (const char *const *)papszAllowedDrivers,
653
                   (const char *const *)connectionoptions, NULL);
654
    CSLDestroy(papszAllowedDrivers);
440✔
655
    CSLDestroy(connectionoptions);
440✔
656

657
    // Give a hint about which GDAL driver should be enabled, but only in
658
    // debug mode.
659
    if (layer->debug && hDS == NULL && pszAllowedDrivers &&
440✔
660
        !EQUAL(pszAllowedDrivers, "*")) {
×
661
      GDALDriverH hDrv = GDALIdentifyDriver(*p_decrypted_path, NULL);
×
662
      if (hDrv) {
×
663
        const char *pszDrvName = GDALGetDescription(hDrv);
×
664
        bool bFound = false;
665
        for (int i = 0; papszAllowedDrivers && papszAllowedDrivers[i]; ++i) {
×
666
          if (EQUAL(papszAllowedDrivers[i], pszDrvName)) {
×
667
            bFound = true;
668
            break;
669
          }
670
        }
671
        if (!bFound) {
×
672
          msSetError(MS_IMGERR,
×
673
                     "Failed to draw layer named '%s'. The image returned "
674
                     "is recognized by GDAL driver %s, but it is not allowed "
675
                     "currently.",
676
                     "msDrawRasterLayerLowOpenDataset()", layer->name,
677
                     pszDrvName);
678
        }
679
      }
680
    }
681
    return hDS;
440✔
682
  } else {
683
    return GDALOpenShared(*p_decrypted_path, GA_ReadOnly);
175✔
684
  }
685
}
686

687
/************************************************************************/
688
/*                msDrawRasterLayerLowCloseDataset()                    */
689
/************************************************************************/
690

691
void msDrawRasterLayerLowCloseDataset(layerObj *layer, void *hDS) {
756✔
692
  if (hDS) {
756✔
693
    const char *close_connection;
694
    close_connection = msLayerGetProcessingKey(layer, "CLOSE_CONNECTION");
615✔
695

696
    if (close_connection == NULL && layer->tileindex == NULL)
615✔
697
      close_connection = "DEFER";
698

699
    {
700
      /* Due to how GDAL processes OVERVIEW_LEVEL, datasets returned are */
701
      /* not shared, despite being asked to, so close them for real */
702
      char **connectionoptions =
703
          msGetStringListFromHashTable(&(layer->connectionoptions));
615✔
704
      if (CSLFetchNameValue(connectionoptions, "OVERVIEW_LEVEL"))
615✔
705
        close_connection = NULL;
706
      CSLDestroy(connectionoptions);
615✔
707
    }
708

709
    if (close_connection != NULL &&
615✔
710
        strcasecmp(close_connection, "DEFER") == 0) {
550✔
711
      GDALDereferenceDataset((GDALDatasetH)hDS);
392✔
712
    } else {
713
      GDALClose((GDALDatasetH)hDS);
223✔
714
    }
715
    msReleaseLock(TLOCK_GDAL);
615✔
716
  }
717
}
756✔
718

719
/************************************************************************/
720
/*                msDrawRasterLayerLowCheckIfMustDraw()                 */
721
/*                                                                      */
722
/*      Return 1 if the layer should be drawn.                          */
723
/************************************************************************/
724

725
int msDrawRasterLayerLowCheckIfMustDraw(mapObj *map, layerObj *layer) {
597✔
726
  if (!layer->data && !layer->tileindex &&
597✔
727
      !(layer->connectiontype == MS_KERNELDENSITY ||
7✔
728
        layer->connectiontype == MS_IDW)) {
729
    if (layer->debug)
×
730
      msDebug("msDrawRasterLayerLow(%s): layer data and tileindex NULL ... "
×
731
              "doing nothing.",
732
              layer->name);
733
    return (0);
×
734
  }
735

736
  if ((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) {
597✔
737
    if (layer->debug)
×
738
      msDebug(
×
739
          "msDrawRasterLayerLow(%s): not status ON or DEFAULT, doing nothing.",
740
          layer->name);
741
    return (0);
×
742
  }
743

744
  if (map->scaledenom > 0) {
597✔
745
    if ((layer->maxscaledenom > 0) &&
402✔
746
        (map->scaledenom > layer->maxscaledenom)) {
747
      if (layer->debug)
×
748
        msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g > "
×
749
                "MAXSCALEDENOM=%g\n",
750
                layer->name, map->scaledenom, layer->maxscaledenom);
751
      return (0);
×
752
    }
753
    if ((layer->minscaledenom > 0) &&
402✔
754
        (map->scaledenom <= layer->minscaledenom)) {
755
      if (layer->debug)
×
756
        msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g < "
×
757
                "MINSCALEDENOM=%g\n",
758
                layer->name, map->scaledenom, layer->minscaledenom);
759
      return (0);
×
760
    }
761
  }
762

763
  if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
597✔
764
    if ((layer->maxgeowidth > 0) &&
597✔
765
        ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {
×
766
      if (layer->debug)
×
767
        msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g > "
×
768
                "MAXSCALEDENOM=%g\n",
769
                layer->name, (map->extent.maxx - map->extent.minx),
770
                layer->maxgeowidth);
771
      return (0);
×
772
    }
773
    if ((layer->mingeowidth > 0) &&
597✔
774
        ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) {
×
775
      if (layer->debug)
×
776
        msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g < "
×
777
                "MINSCALEDENOM=%g\n",
778
                layer->name, (map->extent.maxx - map->extent.minx),
779
                layer->mingeowidth);
780
      return (0);
×
781
    }
782
  }
783

784
  return 1;
785
}
786

787
/************************************************************************/
788
/*                        msDrawRasterLayerLow()                        */
789
/*                                                                      */
790
/*      Check for various file types and act appropriately.  Handle     */
791
/*      tile indexing.                                                  */
792
/************************************************************************/
793

794
int msDrawRasterLayerLow(mapObj *map, layerObj *layer, imageObj *image,
416✔
795
                         rasterBufferObj *rb) {
796
  return msDrawRasterLayerLowWithDataset(map, layer, image, rb, NULL);
416✔
797
}
798

799
int msDrawRasterLayerLowWithDataset(mapObj *map, layerObj *layer,
597✔
800
                                    imageObj *image, rasterBufferObj *rb,
801
                                    void *hDatasetIn) {
802
  /* -------------------------------------------------------------------- */
803
  /*      As of MapServer 6.0 GDAL is required for rendering raster       */
804
  /*      imagery.                                                        */
805
  /* -------------------------------------------------------------------- */
806
  int status, done;
807
  char *filename = NULL, tilename[MS_MAXPATHLEN], tilesrsname[1024];
808

809
  layerObj *tlp = NULL; /* pointer to the tile layer either real or temporary */
597✔
810
  int tileitemindex = -1, tilelayerindex = -1, tilesrsindex = -1;
597✔
811
  shapeObj tshp;
812

813
  char szPath[MS_MAXPATHLEN] = {0};
597✔
814
  char *decrypted_path = NULL;
597✔
815
  int final_status = MS_SUCCESS;
816

817
  rectObj searchrect;
818
  GDALDatasetH hDS;
819
  double adfGeoTransform[6];
820
  void *kernel_density_cleanup_ptr = NULL;
597✔
821

822
  if (layer->debug > 0 || map->debug > 1)
597✔
823
    msDebug("msDrawRasterLayerLow(%s): entering.\n", layer->name);
1✔
824

825
  if (hDatasetIn == NULL && !msDrawRasterLayerLowCheckIfMustDraw(map, layer)) {
597✔
826
    return MS_SUCCESS;
827
  }
828

829
  msGDALInitialize();
597✔
830

831
  if (layer->tileindex) { /* we have an index file */
597✔
832
    msInitShape(&tshp);
150✔
833
    searchrect = map->extent;
150✔
834

835
    status = msDrawRasterSetupTileLayer(map, layer, &searchrect, MS_FALSE,
150✔
836
                                        &tilelayerindex, &tileitemindex,
837
                                        &tilesrsindex, &tlp);
838
    if (status != MS_SUCCESS) {
150✔
839
      if (status != MS_DONE)
×
840
        final_status = status;
841
      goto cleanup;
×
842
    }
843
  }
844

845
  done = MS_FALSE;
846
  while (done != MS_TRUE) {
1,219✔
847

848
    if (layer->tileindex) {
772✔
849
      status = msDrawRasterIterateTileIndex(
325✔
850
          layer, tlp, &tshp, tileitemindex, tilesrsindex, tilename,
851
          sizeof(tilename), tilesrsname, sizeof(tilesrsname));
852
      if (status == MS_FAILURE) {
325✔
853
        final_status = MS_FAILURE;
854
        break;
855
      }
856

857
      if (status == MS_DONE)
325✔
858
        break; /* no more tiles/images */
859
      filename = tilename;
860
    } else {
861
      filename = layer->data;
447✔
862
      done = MS_TRUE; /* only one image so we're done after this */
863
    }
864

865
    if (layer->connectiontype == MS_KERNELDENSITY ||
622✔
866
        layer->connectiontype == MS_IDW) {
867
      msAcquireLock(TLOCK_GDAL);
7✔
868
      status = msInterpolationDataset(map, image, layer, &hDS,
7✔
869
                                      &kernel_density_cleanup_ptr);
870
      if (status != MS_SUCCESS) {
7✔
871
        msReleaseLock(TLOCK_GDAL);
×
872
        final_status = status;
873
        goto cleanup;
×
874
      }
875
      done = MS_TRUE;
876
      if (msProjectionsDiffer(&map->projection, &layer->projection)) {
7✔
877
        char *mapProjStr = msGetProjectionString(&(map->projection));
1✔
878

879
        /* Set the projection to the map file projection */
880
        if (msLoadProjectionString(&(layer->projection), mapProjStr) != 0) {
1✔
881
          GDALClose(hDS);
×
882
          msReleaseLock(TLOCK_GDAL);
×
883
          msSetError(MS_CGIERR,
×
884
                     "Unable to set projection on interpolation layer.",
885
                     "msDrawRasterLayerLow()");
886
          return (MS_FAILURE);
×
887
        }
888
        free(mapProjStr);
1✔
889
      }
890
    } else {
891
      if (strlen(filename) == 0)
615✔
892
        continue;
×
893
      if (hDatasetIn) {
615✔
894
        hDS = (GDALDatasetH)hDatasetIn;
40✔
895
      } else {
896
        hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
575✔
897
            map, layer, filename, szPath, &decrypted_path);
898
      }
899
    }
900

901
    if (hDatasetIn == NULL) {
622✔
902
      CheckDatasetReturnType eRet = msDrawRasterLayerLowCheckDataset(
582✔
903
          map, layer, hDS, decrypted_path, szPath);
904

905
      msFree(decrypted_path);
582✔
906
      decrypted_path = NULL;
582✔
907

908
      if (eRet == CDRT_CONTINUE_NEXT_TILE) {
582✔
909
        msReleaseLock(TLOCK_GDAL);
×
910
        continue;
×
911
      }
912
      if (eRet == CDRT_RETURN_MS_FAILURE) {
582✔
913
        msReleaseLock(TLOCK_GDAL);
×
914
        return MS_FAILURE;
×
915
      }
916
    }
917

918
    if (msDrawRasterLoadProjection(layer, hDS, filename, tilesrsindex,
622✔
919
                                   tilesrsname) != MS_SUCCESS) {
920
      if (hDatasetIn == NULL) {
×
921
        GDALClose(hDS);
×
922
        msReleaseLock(TLOCK_GDAL);
×
923
      }
924
      final_status = MS_FAILURE;
925
      break;
926
    }
927

928
    msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform);
622✔
929

930
    /*
931
    ** We want to resample if the source image is rotated, if
932
    ** the projections differ or if resampling has been explicitly
933
    ** requested, or if the image has north-down instead of north-up.
934
    */
935

936
    if (((adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ||
622✔
937
          adfGeoTransform[5] > 0.0 || adfGeoTransform[1] < 0.0) &&
617✔
938
         layer->transform) ||
622✔
939
        msProjectionsDiffer(&(map->projection), &(layer->projection)) ||
1,099✔
940
        CSLFetchNameValue(layer->processing, "RESAMPLE") != NULL) {
487✔
941
      status = msResampleGDALToMap(map, layer, image, rb, hDS);
293✔
942
    } else {
943
      if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) {
329✔
944
        if (layer->debug || map->debug)
×
945
          msDebug("Layer %s has rotational coefficients but we\n"
×
946
                  "are unable to use them, projections support\n"
947
                  "needs to be built in.",
948
                  layer->name);
949
      }
950
      status = msDrawRasterLayerGDAL(map, layer, image, rb, hDS);
329✔
951
    }
952

953
    if (status == -1) {
622✔
954
      if (hDatasetIn == NULL) {
×
955
        GDALClose(hDS);
×
956
        msReleaseLock(TLOCK_GDAL);
×
957
      }
958
      final_status = MS_FAILURE;
959
      break;
960
    }
961

962
    /*
963
    ** Should we keep this file open for future use?
964
    ** default to keeping open for single data files, and
965
    ** to closing for tile indexes
966
    */
967
    if (layer->connectiontype == MS_KERNELDENSITY ||
622✔
968
        layer->connectiontype == MS_IDW) {
969
      /*
970
      ** Fix issue #5330
971
      ** The in-memory kernel density heatmap gdal dataset handle (hDS) gets
972
      *re-used
973
      ** but the associated rasterband cache doesn't get flushed, which causes
974
      *old data
975
      ** to be rendered instead of the newly generated imagery. To fix, simply
976
      *close the
977
      ** the handle and prevent further re-use.
978
      ** Note that instead of this workaround, we could explicitly set
979
      ** CLOSE_CONNECTION=ALWAYS on the kerneldensity layer.
980
      */
981
      GDALClose(hDS);
7✔
982
      msReleaseLock(TLOCK_GDAL);
7✔
983
    } else {
984
      if (hDatasetIn == NULL)
615✔
985
        msDrawRasterLayerLowCloseDataset(layer, hDS);
575✔
986
    }
987

988
  } /* next tile */
989

990
cleanup:
597✔
991
  if (layer->tileindex) { /* tiling clean-up */
597✔
992
    msDrawRasterCleanupTileLayer(tlp, tilelayerindex);
150✔
993
  }
994
  if (kernel_density_cleanup_ptr) {
597✔
995
    msCleanupInterpolationDataset(map, image, layer,
7✔
996
                                  kernel_density_cleanup_ptr);
997
  }
998

999
  return final_status;
1000
}
1001

1002
/************************************************************************/
1003
/*                         msDrawReferenceMap()                         */
1004
/************************************************************************/
1005

1006
imageObj *msDrawReferenceMap(mapObj *map) {
×
1007
  double cellsize;
1008
  int x1, y1, x2, y2;
1009
  char szPath[MS_MAXPATHLEN];
1010
  int status = MS_SUCCESS;
1011

1012
  imageObj *image = NULL;
1013
  styleObj style;
1014

1015
  /* check to see if we have enough information to actually proceed */
1016
  if (!map->reference.image || map->reference.height == 0 ||
×
1017
      map->reference.width == 0) {
×
1018
    msSetError(MS_MISCERR, "Reference map configuration error.",
×
1019
               "msDrawReferenceMap()");
1020
    return NULL;
×
1021
  }
1022

1023
  rendererVTableObj *renderer = MS_MAP_RENDERER(map);
×
1024
  rasterBufferObj *refImage =
1025
      (rasterBufferObj *)calloc(1, sizeof(rasterBufferObj));
×
1026
  MS_CHECK_ALLOC(refImage, sizeof(rasterBufferObj), NULL);
×
1027

1028
  if (MS_SUCCESS !=
×
1029
      renderer->loadImageFromFile(
×
1030
          msBuildPath(szPath, map->mappath, map->reference.image), refImage)) {
×
1031
    msSetError(MS_MISCERR, "Error loading reference image %s.",
×
1032
               "msDrawReferenceMap()", szPath);
1033
    free(refImage);
×
1034
    return NULL;
×
1035
  }
1036

1037
  image = msImageCreate(refImage->width, refImage->height, map->outputformat,
×
1038
                        map->web.imagepath, map->web.imageurl, map->resolution,
1039
                        map->defresolution, &(map->reference.color));
1040
  if (!image) {
×
1041
    free(refImage);
×
1042
    return NULL;
×
1043
  }
1044

1045
  status = renderer->mergeRasterBuffer(image, refImage, 1.0, 0, 0, 0, 0,
×
1046
                                       refImage->width, refImage->height);
×
1047
  msFreeRasterBuffer(refImage);
×
1048
  free(refImage);
×
1049
  if (MS_UNLIKELY(status == MS_FAILURE))
×
1050
    return NULL;
1051

1052
  /* make sure the extent given in mapfile fits the image */
1053
  cellsize =
1054
      msAdjustExtent(&(map->reference.extent), image->width, image->height);
×
1055

1056
  /* convert map extent to reference image coordinates */
1057
  x1 = MS_MAP2IMAGE_X(map->extent.minx, map->reference.extent.minx, cellsize);
×
1058
  x2 = MS_MAP2IMAGE_X(map->extent.maxx, map->reference.extent.minx, cellsize);
×
1059
  y1 = MS_MAP2IMAGE_Y(map->extent.maxy, map->reference.extent.maxy, cellsize);
×
1060
  y2 = MS_MAP2IMAGE_Y(map->extent.miny, map->reference.extent.maxy, cellsize);
×
1061

1062
  initStyle(&style);
×
1063
  style.color = map->reference.color;
×
1064
  style.outlinecolor = map->reference.outlinecolor;
×
1065

1066
  /* if extent are smaller than minbox size */
1067
  /* draw that extent on the reference image */
1068
  if ((abs(x2 - x1) > map->reference.minboxsize) ||
×
1069
      (abs(y2 - y1) > map->reference.minboxsize)) {
×
1070
    shapeObj rect;
1071
    lineObj line;
1072
    pointObj points[5];
1073
    msInitShape(&rect);
×
1074

1075
    line.point = points;
×
1076
    line.numpoints = 5;
×
1077
    rect.line = &line;
×
1078
    rect.numlines = 1;
×
1079
    rect.type = MS_SHAPE_POLYGON;
×
1080

1081
    line.point[0].x = x1;
×
1082
    line.point[0].y = y1;
×
1083
    line.point[1].x = x1;
×
1084
    line.point[1].y = y2;
×
1085
    line.point[2].x = x2;
×
1086
    line.point[2].y = y2;
×
1087
    line.point[3].x = x2;
×
1088
    line.point[3].y = y1;
×
1089
    line.point[4].x = line.point[0].x;
×
1090
    line.point[4].y = line.point[0].y;
×
1091

1092
    line.numpoints = 5;
1093

1094
    if (map->reference.maxboxsize == 0 ||
×
1095
        ((abs(x2 - x1) < map->reference.maxboxsize) &&
×
1096
         (abs(y2 - y1) < map->reference.maxboxsize))) {
×
1097
      if (MS_UNLIKELY(MS_FAILURE ==
×
1098
                      msDrawShadeSymbol(map, image, &rect, &style, 1.0))) {
1099
        msFreeImage(image);
×
1100
        return NULL;
×
1101
      }
1102
    }
1103

1104
  } else { /* else draw the marker symbol */
1105
    if (map->reference.maxboxsize == 0 ||
×
1106
        ((abs(x2 - x1) < map->reference.maxboxsize) &&
×
1107
         (abs(y2 - y1) < map->reference.maxboxsize))) {
1108
      style.size = map->reference.markersize;
×
1109

1110
      /* if the marker symbol is specify draw this symbol else draw a cross */
1111
      if (map->reference.marker || map->reference.markername) {
×
1112
        pointObj point;
1113
        point.x = (double)(x1 + x2) / 2;
×
1114
        point.y = (double)(y1 + y2) / 2;
×
1115

1116
        if (map->reference.marker) {
×
1117
          style.symbol = map->reference.marker;
×
1118
        } else {
1119
          style.symbol = msGetSymbolIndex(&map->symbolset,
×
1120
                                          map->reference.markername, MS_TRUE);
×
1121
        }
1122

1123
        if (MS_UNLIKELY(MS_FAILURE ==
×
1124
                        msDrawMarkerSymbol(map, image, &point, &style, 1.0))) {
1125
          msFreeImage(image);
×
1126
          return NULL;
×
1127
        }
1128
      } else {
1129
        int x21, y21;
1130
        shapeObj cross;
1131
        lineObj lines[4];
1132
        pointObj point[8];
1133
        int i;
1134
        /* determine the center point */
1135
        x21 = MS_NINT((x1 + x2) / 2);
×
1136
        y21 = MS_NINT((y1 + y2) / 2);
×
1137

1138
        msInitShape(&cross);
×
1139
        cross.numlines = 4;
×
1140
        cross.line = lines;
×
1141
        for (i = 0; i < 4; i++) {
×
1142
          cross.line[i].numpoints = 2;
×
1143
          cross.line[i].point = &(point[2 * i]);
×
1144
        }
1145
        /* draw a cross */
1146
        cross.line[0].point[0].x = x21 - 8;
×
1147
        cross.line[0].point[0].y = y21;
×
1148
        cross.line[0].point[1].x = x21 - 3;
×
1149
        cross.line[0].point[1].y = y21;
×
1150
        cross.line[1].point[0].x = x21;
×
1151
        cross.line[1].point[0].y = y21 - 8;
×
1152
        cross.line[1].point[1].x = x21;
×
1153
        cross.line[1].point[1].y = y21 - 3;
×
1154
        cross.line[2].point[0].x = x21;
×
1155
        cross.line[2].point[0].y = y21 + 3;
×
1156
        cross.line[2].point[1].x = x21;
×
1157
        cross.line[2].point[1].y = y21 + 8;
×
1158
        cross.line[3].point[0].x = x21 + 3;
×
1159
        cross.line[3].point[0].y = y21;
×
1160
        cross.line[3].point[1].x = x21 + 8;
×
1161
        cross.line[3].point[1].y = y21;
×
1162

1163
        if (MS_UNLIKELY(MS_FAILURE ==
×
1164
                        msDrawLineSymbol(map, image, &cross, &style, 1.0))) {
1165
          msFreeImage(image);
×
1166
          return NULL;
×
1167
        }
1168
      }
1169
    }
1170
  }
1171

1172
  return (image);
1173
}
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