Coverage Report

Created: 2025-06-13 06:29

/src/MapServer/src/mapraster.c
Line
Count
Source (jump to first uncovered line)
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,
60
                             const char *pixel_value, int firstClassToTry)
61
62
0
{
63
0
  int i;
64
0
  const char *tmpstr1 = NULL;
65
0
  int numitems;
66
0
  char *item_names[4] = {"pixel", "red", "green", "blue"};
67
0
  char *item_values[4];
68
0
  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
0
  if ((layer->numclasses == 1) &&
74
0
      !(layer->class[0] -> expression.string)) /* no need to do lookup */
75
0
    return (0);
76
77
  /* -------------------------------------------------------------------- */
78
  /*      Setup values list for expressions.                              */
79
  /* -------------------------------------------------------------------- */
80
0
  numitems = 4;
81
0
  if (color->red == -1 && color->green == -1 && color->blue == -1) {
82
0
    strcpy(red_value, "-1");
83
0
    strcpy(green_value, "-1");
84
0
    strcpy(blue_value, "-1");
85
0
  } else {
86
0
    sprintf(red_value, "%d", color->red);
87
0
    sprintf(green_value, "%d", color->green);
88
0
    sprintf(blue_value, "%d", color->blue);
89
0
  }
90
91
0
  item_values[0] = (char *)pixel_value;
92
0
  item_values[1] = red_value;
93
0
  item_values[2] = green_value;
94
0
  item_values[3] = blue_value;
95
96
  /* -------------------------------------------------------------------- */
97
  /*      Loop over classes till we find a match.                         */
98
  /* -------------------------------------------------------------------- */
99
0
  if (firstClassToTry >= layer->numclasses)
100
0
    firstClassToTry = -1;
101
0
  for (i = 0; i < layer->numclasses; i++) {
102
103
0
    const int idx = firstClassToTry < 0    ? i
104
0
                    : i == 0               ? firstClassToTry
105
0
                    : i <= firstClassToTry ? i - 1
106
0
                                           : i;
107
108
    /* check for correct classgroup, if set */
109
0
    if (layer->class[idx] -> group && layer -> classgroup &&strcasecmp(
110
0
                                                   layer->class[idx] -> group,
111
0
                                                   layer -> classgroup) != 0)
112
0
      continue;
113
114
    /* Empty expression - always matches */
115
0
    if (layer->class[idx] -> expression.string == NULL)
116
0
      return (i);
117
118
0
    switch (layer->class[idx] -> expression.type) {
119
120
      /* -------------------------------------------------------------------- */
121
      /*      Simple string match                                             */
122
      /* -------------------------------------------------------------------- */
123
0
    case (MS_STRING):
124
      /* trim junk white space */
125
0
      tmpstr1 = pixel_value;
126
0
      while (*tmpstr1 == ' ')
127
0
        tmpstr1++;
128
129
0
      if (strcmp(layer->class[idx] -> expression.string, tmpstr1) == 0)
130
0
        return (idx); /* matched */
131
0
      break;
132
133
      /* -------------------------------------------------------------------- */
134
      /*      Regular expression.  Rarely used for raster.                    */
135
      /* -------------------------------------------------------------------- */
136
0
    case (MS_REGEX):
137
0
      if (!layer->class[idx] -> expression.compiled) {
138
0
        if (ms_regcomp(&(layer->class[idx] -> expression.regex),
139
0
                       layer->class[idx] -> expression.string,
140
0
                       MS_REG_EXTENDED | MS_REG_NOSUB) !=
141
0
            0) { /* compile the expression  */
142
0
          msSetError(MS_REGEXERR, "Invalid regular expression.",
143
0
                     "msGetClass()");
144
0
          return (-1);
145
0
        }
146
0
        layer->class[idx]->expression.compiled = MS_TRUE;
147
0
      }
148
149
0
      if (ms_regexec(&(layer->class[idx] -> expression.regex), pixel_value, 0,
150
0
                     NULL, 0) == 0)
151
0
        return (idx); /* got a match */
152
0
      break;
153
154
      /* -------------------------------------------------------------------- */
155
      /*      Parsed expression.                                              */
156
      /* -------------------------------------------------------------------- */
157
0
    case (MS_EXPRESSION): {
158
0
      int status;
159
0
      parseObj p;
160
0
      shapeObj dummy_shape;
161
0
      expressionObj *expression = &(layer->class[idx] -> expression);
162
163
0
      dummy_shape.numvalues = numitems;
164
0
      dummy_shape.values = item_values;
165
166
0
      if (expression->tokens == NULL)
167
0
        msTokenizeExpression(expression, item_names, &numitems);
168
169
0
      p.shape = &dummy_shape;
170
0
      p.expr = expression;
171
0
      p.expr->curtoken = p.expr->tokens; /* reset */
172
0
      p.type = MS_PARSE_TYPE_BOOLEAN;
173
174
0
      status = yyparse(&p);
175
176
0
      if (status != 0) {
177
0
        msSetError(MS_PARSEERR, "Failed to parse expression: %s",
178
0
                   "msGetClass_FloatRGB", expression->string);
179
0
        return -1;
180
0
      }
181
182
0
      if (p.result.intval)
183
0
        return idx;
184
0
      break;
185
0
    }
186
0
    }
187
0
  }
188
189
0
  return (-1); /* not found */
190
0
}
191
192
/************************************************************************/
193
/*                             msGetClass()                             */
194
/************************************************************************/
195
196
0
int msGetClass(layerObj *layer, colorObj *color, int colormap_index) {
197
0
  char pixel_value[12];
198
199
0
  snprintf(pixel_value, sizeof(pixel_value), "%d", colormap_index);
200
201
0
  return msGetClass_String(layer, color, pixel_value, -1);
202
0
}
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,
212
0
                        int blue) {
213
0
  return msGetClass_FloatRGB_WithFirstClassToTry(layer, fValue, red, green,
214
0
                                                 blue, -1);
215
0
}
216
217
int msGetClass_FloatRGB_WithFirstClassToTry(layerObj *layer, float fValue,
218
                                            int red, int green, int blue,
219
0
                                            int firstClassToTry) {
220
0
  char pixel_value[100];
221
0
  colorObj color;
222
223
0
  color.red = red;
224
0
  color.green = green;
225
0
  color.blue = blue;
226
227
0
  snprintf(pixel_value, sizeof(pixel_value), "%18g", fValue);
228
229
0
  return msGetClass_String(layer, &color, pixel_value, firstClassToTry);
230
0
}
231
232
/************************************************************************/
233
/*                      msRasterSetupTileLayer()                        */
234
/*                                                                      */
235
/*      Setup the tile layer.                                           */
236
/************************************************************************/
237
238
int msDrawRasterSetupTileLayer(mapObj *map, layerObj *layer,
239
                               rectObj *psearchrect, int is_query,
240
                               int *ptilelayerindex, /* output */
241
                               int *ptileitemindex,  /* output */
242
                               int *ptilesrsindex,   /* output */
243
0
                               layerObj **ptlp /* output */) {
244
0
  int i;
245
0
  char *requested_fields;
246
0
  int status;
247
0
  layerObj *tlp = NULL;
248
249
0
  *ptilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
250
0
  if (*ptilelayerindex ==
251
0
      -1) { /* the tileindex references a file, not a layer */
252
253
    /* so we create a temporary layer */
254
0
    tlp = (layerObj *)malloc(sizeof(layerObj));
255
0
    MS_CHECK_ALLOC(tlp, sizeof(layerObj), MS_FAILURE);
256
257
0
    initLayer(tlp, map);
258
0
    *ptlp = tlp;
259
260
    /* set a few parameters for a very basic shapefile-based layer */
261
0
    tlp->name = msStrdup("TILE");
262
0
    tlp->type = MS_LAYER_TILEINDEX;
263
0
    tlp->data = msStrdup(layer->tileindex);
264
265
0
    if (is_query) {
266
0
      tlp->map = map; /*needed when scaletokens are applied, to extract current
267
                         map scale */
268
0
      for (i = 0; i < layer->numscaletokens; i++) {
269
0
        if (msGrowLayerScaletokens(tlp) == NULL) {
270
0
          return MS_FAILURE;
271
0
        }
272
0
        initScaleToken(&tlp->scaletokens[i]);
273
0
        msCopyScaleToken(&layer->scaletokens[i], &tlp->scaletokens[i]);
274
0
        tlp->numscaletokens++;
275
0
      }
276
0
    }
277
278
0
    if (layer->projection.numargs > 0 &&
279
0
        EQUAL(layer->projection.args[0], "auto")) {
280
0
      tlp->projection.numargs = 1;
281
0
      tlp->projection.args[0] = msStrdup("auto");
282
0
    }
283
284
0
    if (layer->filteritem)
285
0
      tlp->filteritem = msStrdup(layer->filteritem);
286
0
    if (layer->filter.string) {
287
0
      if (layer->filter.type == MS_EXPRESSION) {
288
0
        char *pszTmp = (char *)msSmallMalloc(
289
0
            sizeof(char) * (strlen(layer->filter.string) + 3));
290
0
        sprintf(pszTmp, "(%s)", layer->filter.string);
291
0
        msLoadExpressionString(&tlp->filter, pszTmp);
292
0
        free(pszTmp);
293
0
      } else if (layer->filter.type == MS_REGEX ||
294
0
                 layer->filter.type == MS_IREGEX) {
295
0
        char *pszTmp = (char *)msSmallMalloc(
296
0
            sizeof(char) * (strlen(layer->filter.string) + 3));
297
0
        sprintf(pszTmp, "/%s/", layer->filter.string);
298
0
        msLoadExpressionString(&tlp->filter, pszTmp);
299
0
        free(pszTmp);
300
0
      } else
301
0
        msLoadExpressionString(&tlp->filter, layer->filter.string);
302
303
0
      tlp->filter.type = layer->filter.type;
304
0
    }
305
306
0
  } else {
307
0
    if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
308
0
      return MS_FAILURE;
309
0
    tlp = (GET_LAYER(layer->map, *ptilelayerindex));
310
0
    *ptlp = tlp;
311
0
  }
312
0
  status = msLayerOpen(tlp);
313
0
  if (status != MS_SUCCESS) {
314
0
    return status;
315
0
  }
316
317
  /* fetch tileitem and tilesrs fields */
318
0
  requested_fields = (char *)msSmallMalloc(
319
0
      sizeof(char) * (strlen(layer->tileitem) + 1 +
320
0
                      (layer->tilesrs ? strlen(layer->tilesrs) : 0) + 1));
321
0
  if (layer->tilesrs != NULL)
322
0
    sprintf(requested_fields, "%s,%s", layer->tileitem, layer->tilesrs);
323
0
  else
324
0
    strcpy(requested_fields, layer->tileitem);
325
0
  status = msLayerWhichItems(tlp, MS_FALSE, requested_fields);
326
0
  msFree(requested_fields);
327
0
  if (status != MS_SUCCESS) {
328
0
    return status;
329
0
  }
330
331
  /* get the tileitem and tilesrs index */
332
0
  for (i = 0; i < tlp->numitems; i++) {
333
0
    if (strcasecmp(tlp->items[i], layer->tileitem) == 0) {
334
0
      *ptileitemindex = i;
335
0
    }
336
0
    if (layer->tilesrs != NULL &&
337
0
        strcasecmp(tlp->items[i], layer->tilesrs) == 0) {
338
0
      *ptilesrsindex = i;
339
0
    }
340
0
  }
341
0
  if (*ptileitemindex < 0) { /* didn't find it */
342
0
    msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.",
343
0
               "msDrawRasterLayerLow()", layer->tileitem);
344
0
    return MS_FAILURE;
345
0
  }
346
0
  if (layer->tilesrs != NULL && *ptilesrsindex < 0) { /* didn't find it */
347
0
    msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.",
348
0
               "msDrawRasterLayerLow()", layer->tilesrs);
349
0
    return MS_FAILURE;
350
0
  }
351
352
  /* if necessary, project the searchrect to source coords */
353
0
  if ((map->projection.numargs > 0) && (layer->projection.numargs > 0) &&
354
0
      !EQUAL(layer->projection.args[0], "auto")) {
355
0
    if (msProjectRect(&map->projection, &layer->projection, psearchrect) !=
356
0
        MS_SUCCESS) {
357
0
      msDebug("msDrawRasterLayerLow(%s): unable to reproject map request "
358
0
              "rectangle into layer projection, canceling.\n",
359
0
              layer->name);
360
0
      return MS_FAILURE;
361
0
    }
362
0
  } else if ((map->projection.numargs > 0) && (tlp->projection.numargs > 0) &&
363
0
             !EQUAL(tlp->projection.args[0], "auto")) {
364
0
    if (msProjectRect(&map->projection, &tlp->projection, psearchrect) !=
365
0
        MS_SUCCESS) {
366
0
      msDebug("msDrawRasterLayerLow(%s): unable to reproject map request "
367
0
              "rectangle into layer projection, canceling.\n",
368
0
              layer->name);
369
0
      return MS_FAILURE;
370
0
    }
371
0
  }
372
0
  return msLayerWhichShapes(tlp, *psearchrect, MS_FALSE);
373
0
}
374
375
/************************************************************************/
376
/*                msDrawRasterCleanupTileLayer()                        */
377
/*                                                                      */
378
/*      Cleanup the tile layer.                                         */
379
/************************************************************************/
380
381
0
void msDrawRasterCleanupTileLayer(layerObj *tlp, int tilelayerindex) {
382
0
  msLayerClose(tlp);
383
0
  if (tilelayerindex == -1) {
384
0
    freeLayer(tlp);
385
0
    free(tlp);
386
0
  }
387
0
}
388
389
/************************************************************************/
390
/*                   msDrawRasterIterateTileIndex()                     */
391
/*                                                                      */
392
/*      Iterate over the tile layer.                                    */
393
/************************************************************************/
394
395
int msDrawRasterIterateTileIndex(layerObj *layer, layerObj *tlp,
396
                                 shapeObj *ptshp, /* input-output */
397
                                 int tileitemindex, int tilesrsindex,
398
                                 char *tilename, /* output */
399
                                 size_t sizeof_tilename,
400
                                 char *tilesrsname, /* output */
401
0
                                 size_t sizeof_tilesrsname) {
402
0
  int status;
403
404
0
  status = msLayerNextShape(tlp, ptshp);
405
0
  if (status == MS_FAILURE || status == MS_DONE) {
406
0
    return status;
407
0
  }
408
409
0
  if (layer->data == NULL ||
410
0
      strlen(layer->data) ==
411
0
          0) { /* assume whole filename is in attribute field */
412
0
    strlcpy(tilename, ptshp->values[tileitemindex], sizeof_tilename);
413
0
  } else
414
0
    snprintf(tilename, sizeof_tilename, "%s/%s", ptshp->values[tileitemindex],
415
0
             layer->data);
416
417
0
  tilesrsname[0] = '\0';
418
419
0
  if (tilesrsindex >= 0) {
420
0
    if (ptshp->values[tilesrsindex] != NULL)
421
0
      strlcpy(tilesrsname, ptshp->values[tilesrsindex], sizeof_tilesrsname);
422
0
  }
423
424
0
  msFreeShape(ptshp); /* done with the shape */
425
426
0
  return status;
427
0
}
428
429
/************************************************************************/
430
/*                   msDrawRasterBuildRasterPath()                      */
431
/*                                                                      */
432
/*      Build the path of the raster to open.                           */
433
/************************************************************************/
434
435
int msDrawRasterBuildRasterPath(mapObj *map, layerObj *layer,
436
                                const char *filename,
437
0
                                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
0
  if (layer->tileindex && !map->shapepath &&
444
0
      msGetLayerIndex(map, layer->tileindex) < 0) {
445
0
    char tiAbsFilePath[MS_MAXPATHLEN];
446
0
    char *tiAbsDirPath = NULL;
447
448
0
    msTryBuildPath(tiAbsFilePath, map->mappath,
449
0
                   layer->tileindex); /* absolute path to tileindex file */
450
0
    tiAbsDirPath = msGetPath(tiAbsFilePath); /* tileindex file's directory */
451
0
    msBuildPath(szPath, tiAbsDirPath, filename);
452
0
    free(tiAbsDirPath);
453
0
  } else {
454
0
    msTryBuildPath3(szPath, map->mappath, map->shapepath, filename);
455
0
  }
456
457
0
  return MS_SUCCESS;
458
0
}
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
0
                                       const char *szPath) {
468
0
  const char *cpl_error_msg = CPLGetLastErrorMsg();
469
470
  /* we wish to avoid reporting decrypted paths */
471
0
  if (cpl_error_msg != NULL && strstr(cpl_error_msg, decrypted_path) != NULL &&
472
0
      strcmp(decrypted_path, szPath) != 0)
473
0
    cpl_error_msg = NULL;
474
475
  /* we wish to avoid reporting the stock GDALOpen error messages */
476
0
  if (cpl_error_msg != NULL &&
477
0
      (strstr(cpl_error_msg, "not recognised as a supported") != NULL ||
478
0
       strstr(cpl_error_msg, "does not exist") != NULL))
479
0
    cpl_error_msg = NULL;
480
481
0
  if (cpl_error_msg == NULL)
482
0
    cpl_error_msg = "";
483
484
0
  return cpl_error_msg;
485
0
}
486
487
/************************************************************************/
488
/*                   msDrawRasterLoadProjection()                       */
489
/*                                                                      */
490
/*      Handle TILESRS or PROJECTION AUTO for each tile.                */
491
/************************************************************************/
492
493
int msDrawRasterLoadProjection(layerObj *layer, GDALDatasetH hDS,
494
                               const char *filename, int tilesrsindex,
495
0
                               const char *tilesrsname) {
496
  /*
497
  ** Generate the projection information if using TILESRS.
498
  */
499
0
  if (tilesrsindex >= 0) {
500
0
    const char *pszWKT;
501
0
    if (tilesrsname[0] != '\0')
502
0
      pszWKT = tilesrsname;
503
0
    else
504
0
      pszWKT = GDALGetProjectionRef(hDS);
505
0
    if (pszWKT != NULL && strlen(pszWKT) > 0) {
506
0
      if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) !=
507
0
          MS_SUCCESS) {
508
0
        char szLongMsg[MESSAGELENGTH * 2];
509
0
        errorObj *ms_error = msGetErrorObj();
510
511
0
        snprintf(szLongMsg, sizeof(szLongMsg),
512
0
                 "%s\n"
513
0
                 "PROJECTION '%s' cannot be used for this "
514
0
                 "GDAL raster (`%s').",
515
0
                 ms_error->message, pszWKT, filename);
516
0
        szLongMsg[MESSAGELENGTH - 1] = '\0';
517
518
0
        msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg);
519
520
0
        return MS_FAILURE;
521
0
      }
522
0
    }
523
0
  }
524
  /*
525
  ** Generate the projection information if using AUTO.
526
  */
527
0
  else if (layer->projection.numargs > 0 &&
528
0
           EQUAL(layer->projection.args[0], "auto")) {
529
0
    const char *pszWKT;
530
531
0
    pszWKT = GDALGetProjectionRef(hDS);
532
533
0
    if (pszWKT != NULL && strlen(pszWKT) > 0) {
534
0
      if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) !=
535
0
          MS_SUCCESS) {
536
0
        char szLongMsg[MESSAGELENGTH * 2];
537
0
        errorObj *ms_error = msGetErrorObj();
538
539
0
        snprintf(szLongMsg, sizeof(szLongMsg),
540
0
                 "%s\n"
541
0
                 "PROJECTION AUTO cannot be used for this "
542
0
                 "GDAL raster (`%s').",
543
0
                 ms_error->message, filename);
544
0
        szLongMsg[MESSAGELENGTH - 1] = '\0';
545
546
0
        msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg);
547
548
0
        return MS_FAILURE;
549
0
      }
550
0
    }
551
0
  }
552
553
0
  return MS_SUCCESS;
554
0
}
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,
568
                                 const char *decrypted_path,
569
0
                                 const char *szPath) {
570
  /*
571
  ** If GDAL doesn't recognise it, and it wasn't successfully opened
572
  ** Generate an error.
573
  */
574
0
  if (hDS == NULL) {
575
0
    int ignore_missing = msMapIgnoreMissingData(map);
576
0
    const char *cpl_error_msg =
577
0
        msDrawRasterGetCPLErrorMsg(decrypted_path, szPath);
578
579
0
    if (ignore_missing == MS_MISSING_DATA_FAIL) {
580
0
      msSetError(MS_IOERR,
581
0
                 "Corrupt, empty or missing file '%s' for layer '%s'. %s",
582
0
                 "msDrawRasterLayerLow()", szPath, layer->name, cpl_error_msg);
583
0
      return (CDRT_RETURN_MS_FAILURE);
584
0
    } else if (ignore_missing == MS_MISSING_DATA_LOG) {
585
0
      if (layer->debug || layer->map->debug) {
586
0
        msDebug("Corrupt, empty or missing file '%s' for layer '%s' ... "
587
0
                "ignoring this missing data.  %s\n",
588
0
                szPath, layer->name, cpl_error_msg);
589
0
      }
590
0
      return (CDRT_CONTINUE_NEXT_TILE);
591
0
    } else if (ignore_missing == MS_MISSING_DATA_IGNORE) {
592
0
      return (CDRT_CONTINUE_NEXT_TILE);
593
0
    } else {
594
      /* never get here */
595
0
      msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.",
596
0
                 "msDrawRasterLayerLow()");
597
0
      return (CDRT_RETURN_MS_FAILURE);
598
0
    }
599
0
  }
600
601
0
  return CDRT_OK;
602
0
}
603
604
/************************************************************************/
605
/*              msDrawRasterLayerLowOpenDataset()                       */
606
/************************************************************************/
607
608
void *msDrawRasterLayerLowOpenDataset(mapObj *map, layerObj *layer,
609
                                      const char *filename,
610
                                      char szPath[MS_MAXPATHLEN],
611
0
                                      char **p_decrypted_path) {
612
0
  const char *pszPath;
613
614
0
  msGDALInitialize();
615
616
0
  if (layer->debug)
617
0
    msDebug("msDrawRasterLayerLow(%s): Filename is: %s\n", layer->name,
618
0
            filename);
619
620
0
  if (strncmp(filename, "<VRTDataset", strlen("<VRTDataset")) == 0) {
621
0
    pszPath = filename;
622
0
  } else {
623
0
    msDrawRasterBuildRasterPath(map, layer, filename, szPath);
624
0
    pszPath = szPath;
625
0
  }
626
0
  if (layer->debug)
627
0
    msDebug("msDrawRasterLayerLow(%s): Path is: %s\n", layer->name, pszPath);
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
0
  *p_decrypted_path = msDecryptStringTokens(map, pszPath);
637
0
  if (*p_decrypted_path == NULL)
638
0
    return NULL;
639
640
0
  msAcquireLock(TLOCK_GDAL);
641
0
  if (!layer->tileindex) {
642
0
    char **connectionoptions =
643
0
        msGetStringListFromHashTable(&(layer->connectionoptions));
644
0
    char **papszAllowedDrivers = NULL;
645
    // ALLOWED_GDAL_DRIVERS typically set by msDrawWMSLayerLow()
646
0
    const char *pszAllowedDrivers =
647
0
        msLayerGetProcessingKey(layer, "ALLOWED_GDAL_DRIVERS");
648
0
    if (pszAllowedDrivers && !EQUAL(pszAllowedDrivers, "*"))
649
0
      papszAllowedDrivers = CSLTokenizeString2(pszAllowedDrivers, ",", 0);
650
0
    GDALDatasetH hDS =
651
0
        GDALOpenEx(*p_decrypted_path, GDAL_OF_RASTER | GDAL_OF_SHARED,
652
0
                   (const char *const *)papszAllowedDrivers,
653
0
                   (const char *const *)connectionoptions, NULL);
654
0
    CSLDestroy(papszAllowedDrivers);
655
0
    CSLDestroy(connectionoptions);
656
657
    // Give a hint about which GDAL driver should be enabled, but only in
658
    // debug mode.
659
0
    if (layer->debug && hDS == NULL && pszAllowedDrivers &&
660
0
        !EQUAL(pszAllowedDrivers, "*")) {
661
0
      GDALDriverH hDrv = GDALIdentifyDriver(*p_decrypted_path, NULL);
662
0
      if (hDrv) {
663
0
        const char *pszDrvName = GDALGetDescription(hDrv);
664
0
        bool bFound = false;
665
0
        for (int i = 0; papszAllowedDrivers && papszAllowedDrivers[i]; ++i) {
666
0
          if (EQUAL(papszAllowedDrivers[i], pszDrvName)) {
667
0
            bFound = true;
668
0
            break;
669
0
          }
670
0
        }
671
0
        if (!bFound) {
672
0
          msSetError(MS_IMGERR,
673
0
                     "Failed to draw layer named '%s'. The image returned "
674
0
                     "is recognized by GDAL driver %s, but it is not allowed "
675
0
                     "currently.",
676
0
                     "msDrawRasterLayerLowOpenDataset()", layer->name,
677
0
                     pszDrvName);
678
0
        }
679
0
      }
680
0
    }
681
0
    return hDS;
682
0
  } else {
683
0
    return GDALOpenShared(*p_decrypted_path, GA_ReadOnly);
684
0
  }
685
0
}
686
687
/************************************************************************/
688
/*                msDrawRasterLayerLowCloseDataset()                    */
689
/************************************************************************/
690
691
0
void msDrawRasterLayerLowCloseDataset(layerObj *layer, void *hDS) {
692
0
  if (hDS) {
693
0
    const char *close_connection;
694
0
    close_connection = msLayerGetProcessingKey(layer, "CLOSE_CONNECTION");
695
696
0
    if (close_connection == NULL && layer->tileindex == NULL)
697
0
      close_connection = "DEFER";
698
699
0
    {
700
      /* Due to how GDAL processes OVERVIEW_LEVEL, datasets returned are */
701
      /* not shared, despite being asked to, so close them for real */
702
0
      char **connectionoptions =
703
0
          msGetStringListFromHashTable(&(layer->connectionoptions));
704
0
      if (CSLFetchNameValue(connectionoptions, "OVERVIEW_LEVEL"))
705
0
        close_connection = NULL;
706
0
      CSLDestroy(connectionoptions);
707
0
    }
708
709
0
    if (close_connection != NULL &&
710
0
        strcasecmp(close_connection, "DEFER") == 0) {
711
0
      GDALDereferenceDataset((GDALDatasetH)hDS);
712
0
    } else {
713
0
      GDALClose((GDALDatasetH)hDS);
714
0
    }
715
0
    msReleaseLock(TLOCK_GDAL);
716
0
  }
717
0
}
718
719
/************************************************************************/
720
/*                msDrawRasterLayerLowCheckIfMustDraw()                 */
721
/*                                                                      */
722
/*      Return 1 if the layer should be drawn.                          */
723
/************************************************************************/
724
725
0
int msDrawRasterLayerLowCheckIfMustDraw(mapObj *map, layerObj *layer) {
726
0
  if (!layer->data && !layer->tileindex &&
727
0
      !(layer->connectiontype == MS_KERNELDENSITY ||
728
0
        layer->connectiontype == MS_IDW)) {
729
0
    if (layer->debug)
730
0
      msDebug("msDrawRasterLayerLow(%s): layer data and tileindex NULL ... "
731
0
              "doing nothing.",
732
0
              layer->name);
733
0
    return (0);
734
0
  }
735
736
0
  if ((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) {
737
0
    if (layer->debug)
738
0
      msDebug(
739
0
          "msDrawRasterLayerLow(%s): not status ON or DEFAULT, doing nothing.",
740
0
          layer->name);
741
0
    return (0);
742
0
  }
743
744
0
  if (map->scaledenom > 0) {
745
0
    if ((layer->maxscaledenom > 0) &&
746
0
        (map->scaledenom > layer->maxscaledenom)) {
747
0
      if (layer->debug)
748
0
        msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g > "
749
0
                "MAXSCALEDENOM=%g\n",
750
0
                layer->name, map->scaledenom, layer->maxscaledenom);
751
0
      return (0);
752
0
    }
753
0
    if ((layer->minscaledenom > 0) &&
754
0
        (map->scaledenom <= layer->minscaledenom)) {
755
0
      if (layer->debug)
756
0
        msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g < "
757
0
                "MINSCALEDENOM=%g\n",
758
0
                layer->name, map->scaledenom, layer->minscaledenom);
759
0
      return (0);
760
0
    }
761
0
  }
762
763
0
  if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
764
0
    if ((layer->maxgeowidth > 0) &&
765
0
        ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {
766
0
      if (layer->debug)
767
0
        msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g > "
768
0
                "MAXSCALEDENOM=%g\n",
769
0
                layer->name, (map->extent.maxx - map->extent.minx),
770
0
                layer->maxgeowidth);
771
0
      return (0);
772
0
    }
773
0
    if ((layer->mingeowidth > 0) &&
774
0
        ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) {
775
0
      if (layer->debug)
776
0
        msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g < "
777
0
                "MINSCALEDENOM=%g\n",
778
0
                layer->name, (map->extent.maxx - map->extent.minx),
779
0
                layer->mingeowidth);
780
0
      return (0);
781
0
    }
782
0
  }
783
784
0
  return 1;
785
0
}
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,
795
0
                         rasterBufferObj *rb) {
796
0
  return msDrawRasterLayerLowWithDataset(map, layer, image, rb, NULL);
797
0
}
798
799
int msDrawRasterLayerLowWithDataset(mapObj *map, layerObj *layer,
800
                                    imageObj *image, rasterBufferObj *rb,
801
0
                                    void *hDatasetIn) {
802
  /* -------------------------------------------------------------------- */
803
  /*      As of MapServer 6.0 GDAL is required for rendering raster       */
804
  /*      imagery.                                                        */
805
  /* -------------------------------------------------------------------- */
806
0
  int status, done;
807
0
  char *filename = NULL, tilename[MS_MAXPATHLEN], tilesrsname[1024];
808
809
0
  layerObj *tlp = NULL; /* pointer to the tile layer either real or temporary */
810
0
  int tileitemindex = -1, tilelayerindex = -1, tilesrsindex = -1;
811
0
  shapeObj tshp;
812
813
0
  char szPath[MS_MAXPATHLEN] = {0};
814
0
  char *decrypted_path = NULL;
815
0
  int final_status = MS_SUCCESS;
816
817
0
  rectObj searchrect;
818
0
  GDALDatasetH hDS;
819
0
  double adfGeoTransform[6];
820
0
  void *kernel_density_cleanup_ptr = NULL;
821
822
0
  if (layer->debug > 0 || map->debug > 1)
823
0
    msDebug("msDrawRasterLayerLow(%s): entering.\n", layer->name);
824
825
0
  if (hDatasetIn == NULL && !msDrawRasterLayerLowCheckIfMustDraw(map, layer)) {
826
0
    return MS_SUCCESS;
827
0
  }
828
829
0
  msGDALInitialize();
830
831
0
  if (layer->tileindex) { /* we have an index file */
832
0
    msInitShape(&tshp);
833
0
    searchrect = map->extent;
834
835
0
    status = msDrawRasterSetupTileLayer(map, layer, &searchrect, MS_FALSE,
836
0
                                        &tilelayerindex, &tileitemindex,
837
0
                                        &tilesrsindex, &tlp);
838
0
    if (status != MS_SUCCESS) {
839
0
      if (status != MS_DONE)
840
0
        final_status = status;
841
0
      goto cleanup;
842
0
    }
843
0
  }
844
845
0
  done = MS_FALSE;
846
0
  while (done != MS_TRUE) {
847
848
0
    if (layer->tileindex) {
849
0
      status = msDrawRasterIterateTileIndex(
850
0
          layer, tlp, &tshp, tileitemindex, tilesrsindex, tilename,
851
0
          sizeof(tilename), tilesrsname, sizeof(tilesrsname));
852
0
      if (status == MS_FAILURE) {
853
0
        final_status = MS_FAILURE;
854
0
        break;
855
0
      }
856
857
0
      if (status == MS_DONE)
858
0
        break; /* no more tiles/images */
859
0
      filename = tilename;
860
0
    } else {
861
0
      filename = layer->data;
862
0
      done = MS_TRUE; /* only one image so we're done after this */
863
0
    }
864
865
0
    if (layer->connectiontype == MS_KERNELDENSITY ||
866
0
        layer->connectiontype == MS_IDW) {
867
0
      msAcquireLock(TLOCK_GDAL);
868
0
      status = msInterpolationDataset(map, image, layer, &hDS,
869
0
                                      &kernel_density_cleanup_ptr);
870
0
      if (status != MS_SUCCESS) {
871
0
        msReleaseLock(TLOCK_GDAL);
872
0
        final_status = status;
873
0
        goto cleanup;
874
0
      }
875
0
      done = MS_TRUE;
876
0
      if (msProjectionsDiffer(&map->projection, &layer->projection)) {
877
0
        char *mapProjStr = msGetProjectionString(&(map->projection));
878
879
        /* Set the projection to the map file projection */
880
0
        if (msLoadProjectionString(&(layer->projection), mapProjStr) != 0) {
881
0
          GDALClose(hDS);
882
0
          msReleaseLock(TLOCK_GDAL);
883
0
          msSetError(MS_CGIERR,
884
0
                     "Unable to set projection on interpolation layer.",
885
0
                     "msDrawRasterLayerLow()");
886
0
          return (MS_FAILURE);
887
0
        }
888
0
        free(mapProjStr);
889
0
      }
890
0
    } else {
891
0
      if (strlen(filename) == 0)
892
0
        continue;
893
0
      if (hDatasetIn) {
894
0
        hDS = (GDALDatasetH)hDatasetIn;
895
0
      } else {
896
0
        hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
897
0
            map, layer, filename, szPath, &decrypted_path);
898
0
      }
899
0
    }
900
901
0
    if (hDatasetIn == NULL) {
902
0
      CheckDatasetReturnType eRet = msDrawRasterLayerLowCheckDataset(
903
0
          map, layer, hDS, decrypted_path, szPath);
904
905
0
      msFree(decrypted_path);
906
0
      decrypted_path = NULL;
907
908
0
      if (eRet == CDRT_CONTINUE_NEXT_TILE) {
909
0
        msReleaseLock(TLOCK_GDAL);
910
0
        continue;
911
0
      }
912
0
      if (eRet == CDRT_RETURN_MS_FAILURE) {
913
0
        msReleaseLock(TLOCK_GDAL);
914
0
        return MS_FAILURE;
915
0
      }
916
0
    }
917
918
0
    if (msDrawRasterLoadProjection(layer, hDS, filename, tilesrsindex,
919
0
                                   tilesrsname) != MS_SUCCESS) {
920
0
      if (hDatasetIn == NULL) {
921
0
        GDALClose(hDS);
922
0
        msReleaseLock(TLOCK_GDAL);
923
0
      }
924
0
      final_status = MS_FAILURE;
925
0
      break;
926
0
    }
927
928
0
    msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform);
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
0
    if (((adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ||
937
0
          adfGeoTransform[5] > 0.0 || adfGeoTransform[1] < 0.0) &&
938
0
         layer->transform) ||
939
0
        msProjectionsDiffer(&(map->projection), &(layer->projection)) ||
940
0
        CSLFetchNameValue(layer->processing, "RESAMPLE") != NULL) {
941
0
      status = msResampleGDALToMap(map, layer, image, rb, hDS);
942
0
    } else {
943
0
      if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) {
944
0
        if (layer->debug || map->debug)
945
0
          msDebug("Layer %s has rotational coefficients but we\n"
946
0
                  "are unable to use them, projections support\n"
947
0
                  "needs to be built in.",
948
0
                  layer->name);
949
0
      }
950
0
      status = msDrawRasterLayerGDAL(map, layer, image, rb, hDS);
951
0
    }
952
953
0
    if (status == -1) {
954
0
      if (hDatasetIn == NULL) {
955
0
        GDALClose(hDS);
956
0
        msReleaseLock(TLOCK_GDAL);
957
0
      }
958
0
      final_status = MS_FAILURE;
959
0
      break;
960
0
    }
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
0
    if (layer->connectiontype == MS_KERNELDENSITY ||
968
0
        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
0
      GDALClose(hDS);
982
0
      msReleaseLock(TLOCK_GDAL);
983
0
    } else {
984
0
      if (hDatasetIn == NULL)
985
0
        msDrawRasterLayerLowCloseDataset(layer, hDS);
986
0
    }
987
988
0
  } /* next tile */
989
990
0
cleanup:
991
0
  if (layer->tileindex) { /* tiling clean-up */
992
0
    msDrawRasterCleanupTileLayer(tlp, tilelayerindex);
993
0
  }
994
0
  if (kernel_density_cleanup_ptr) {
995
0
    msCleanupInterpolationDataset(map, image, layer,
996
0
                                  kernel_density_cleanup_ptr);
997
0
  }
998
999
0
  return final_status;
1000
0
}
1001
1002
/************************************************************************/
1003
/*                         msDrawReferenceMap()                         */
1004
/************************************************************************/
1005
1006
0
imageObj *msDrawReferenceMap(mapObj *map) {
1007
0
  double cellsize;
1008
0
  int x1, y1, x2, y2;
1009
0
  char szPath[MS_MAXPATHLEN];
1010
0
  int status = MS_SUCCESS;
1011
1012
0
  imageObj *image = NULL;
1013
0
  styleObj style;
1014
1015
  /* check to see if we have enough information to actually proceed */
1016
0
  if (!map->reference.image || map->reference.height == 0 ||
1017
0
      map->reference.width == 0) {
1018
0
    msSetError(MS_MISCERR, "Reference map configuration error.",
1019
0
               "msDrawReferenceMap()");
1020
0
    return NULL;
1021
0
  }
1022
1023
0
  rendererVTableObj *renderer = MS_MAP_RENDERER(map);
1024
0
  rasterBufferObj *refImage =
1025
0
      (rasterBufferObj *)calloc(1, sizeof(rasterBufferObj));
1026
0
  MS_CHECK_ALLOC(refImage, sizeof(rasterBufferObj), NULL);
1027
1028
0
  if (MS_SUCCESS !=
1029
0
      renderer->loadImageFromFile(
1030
0
          msBuildPath(szPath, map->mappath, map->reference.image), refImage)) {
1031
0
    msSetError(MS_MISCERR, "Error loading reference image %s.",
1032
0
               "msDrawReferenceMap()", szPath);
1033
0
    free(refImage);
1034
0
    return NULL;
1035
0
  }
1036
1037
0
  image = msImageCreate(refImage->width, refImage->height, map->outputformat,
1038
0
                        map->web.imagepath, map->web.imageurl, map->resolution,
1039
0
                        map->defresolution, &(map->reference.color));
1040
0
  if (!image) {
1041
0
    free(refImage);
1042
0
    return NULL;
1043
0
  }
1044
1045
0
  status = renderer->mergeRasterBuffer(image, refImage, 1.0, 0, 0, 0, 0,
1046
0
                                       refImage->width, refImage->height);
1047
0
  msFreeRasterBuffer(refImage);
1048
0
  free(refImage);
1049
0
  if (MS_UNLIKELY(status == MS_FAILURE))
1050
0
    return NULL;
1051
1052
  /* make sure the extent given in mapfile fits the image */
1053
0
  cellsize =
1054
0
      msAdjustExtent(&(map->reference.extent), image->width, image->height);
1055
1056
  /* convert map extent to reference image coordinates */
1057
0
  x1 = MS_MAP2IMAGE_X(map->extent.minx, map->reference.extent.minx, cellsize);
1058
0
  x2 = MS_MAP2IMAGE_X(map->extent.maxx, map->reference.extent.minx, cellsize);
1059
0
  y1 = MS_MAP2IMAGE_Y(map->extent.maxy, map->reference.extent.maxy, cellsize);
1060
0
  y2 = MS_MAP2IMAGE_Y(map->extent.miny, map->reference.extent.maxy, cellsize);
1061
1062
0
  initStyle(&style);
1063
0
  style.color = map->reference.color;
1064
0
  style.outlinecolor = map->reference.outlinecolor;
1065
1066
  /* if extent are smaller than minbox size */
1067
  /* draw that extent on the reference image */
1068
0
  if ((abs(x2 - x1) > map->reference.minboxsize) ||
1069
0
      (abs(y2 - y1) > map->reference.minboxsize)) {
1070
0
    shapeObj rect;
1071
0
    lineObj line;
1072
0
    pointObj points[5];
1073
0
    msInitShape(&rect);
1074
1075
0
    line.point = points;
1076
0
    line.numpoints = 5;
1077
0
    rect.line = &line;
1078
0
    rect.numlines = 1;
1079
0
    rect.type = MS_SHAPE_POLYGON;
1080
1081
0
    line.point[0].x = x1;
1082
0
    line.point[0].y = y1;
1083
0
    line.point[1].x = x1;
1084
0
    line.point[1].y = y2;
1085
0
    line.point[2].x = x2;
1086
0
    line.point[2].y = y2;
1087
0
    line.point[3].x = x2;
1088
0
    line.point[3].y = y1;
1089
0
    line.point[4].x = line.point[0].x;
1090
0
    line.point[4].y = line.point[0].y;
1091
1092
0
    line.numpoints = 5;
1093
1094
0
    if (map->reference.maxboxsize == 0 ||
1095
0
        ((abs(x2 - x1) < map->reference.maxboxsize) &&
1096
0
         (abs(y2 - y1) < map->reference.maxboxsize))) {
1097
0
      if (MS_UNLIKELY(MS_FAILURE ==
1098
0
                      msDrawShadeSymbol(map, image, &rect, &style, 1.0))) {
1099
0
        msFreeImage(image);
1100
0
        return NULL;
1101
0
      }
1102
0
    }
1103
1104
0
  } else { /* else draw the marker symbol */
1105
0
    if (map->reference.maxboxsize == 0 ||
1106
0
        ((abs(x2 - x1) < map->reference.maxboxsize) &&
1107
0
         (abs(y2 - y1) < map->reference.maxboxsize))) {
1108
0
      style.size = map->reference.markersize;
1109
1110
      /* if the marker symbol is specify draw this symbol else draw a cross */
1111
0
      if (map->reference.marker || map->reference.markername) {
1112
0
        pointObj point;
1113
0
        point.x = (double)(x1 + x2) / 2;
1114
0
        point.y = (double)(y1 + y2) / 2;
1115
1116
0
        if (map->reference.marker) {
1117
0
          style.symbol = map->reference.marker;
1118
0
        } else {
1119
0
          style.symbol = msGetSymbolIndex(&map->symbolset,
1120
0
                                          map->reference.markername, MS_TRUE);
1121
0
        }
1122
1123
0
        if (MS_UNLIKELY(MS_FAILURE ==
1124
0
                        msDrawMarkerSymbol(map, image, &point, &style, 1.0))) {
1125
0
          msFreeImage(image);
1126
0
          return NULL;
1127
0
        }
1128
0
      } else {
1129
0
        int x21, y21;
1130
0
        shapeObj cross;
1131
0
        lineObj lines[4];
1132
0
        pointObj point[8];
1133
0
        int i;
1134
        /* determine the center point */
1135
0
        x21 = MS_NINT((x1 + x2) / 2);
1136
0
        y21 = MS_NINT((y1 + y2) / 2);
1137
1138
0
        msInitShape(&cross);
1139
0
        cross.numlines = 4;
1140
0
        cross.line = lines;
1141
0
        for (i = 0; i < 4; i++) {
1142
0
          cross.line[i].numpoints = 2;
1143
0
          cross.line[i].point = &(point[2 * i]);
1144
0
        }
1145
        /* draw a cross */
1146
0
        cross.line[0].point[0].x = x21 - 8;
1147
0
        cross.line[0].point[0].y = y21;
1148
0
        cross.line[0].point[1].x = x21 - 3;
1149
0
        cross.line[0].point[1].y = y21;
1150
0
        cross.line[1].point[0].x = x21;
1151
0
        cross.line[1].point[0].y = y21 - 8;
1152
0
        cross.line[1].point[1].x = x21;
1153
0
        cross.line[1].point[1].y = y21 - 3;
1154
0
        cross.line[2].point[0].x = x21;
1155
0
        cross.line[2].point[0].y = y21 + 3;
1156
0
        cross.line[2].point[1].x = x21;
1157
0
        cross.line[2].point[1].y = y21 + 8;
1158
0
        cross.line[3].point[0].x = x21 + 3;
1159
0
        cross.line[3].point[0].y = y21;
1160
0
        cross.line[3].point[1].x = x21 + 8;
1161
0
        cross.line[3].point[1].y = y21;
1162
1163
0
        if (MS_UNLIKELY(MS_FAILURE ==
1164
0
                        msDrawLineSymbol(map, image, &cross, &style, 1.0))) {
1165
0
          msFreeImage(image);
1166
0
          return NULL;
1167
0
        }
1168
0
      }
1169
0
    }
1170
0
  }
1171
1172
0
  return (image);
1173
0
}