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

geographika / mapserver / 12180102655

05 Dec 2024 12:50PM UTC coverage: 40.361% (+0.08%) from 40.281%
12180102655

push

github

geographika
Add back all Python versions

58368 of 144616 relevant lines covered (40.36%)

25464.83 hits per line

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

80.69
/src/mapogcsld.cpp
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGC SLD implementation
6
 * Author:   Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
7
 *
8
 **********************************************************************
9
 * Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 ****************************************************************************/
28

29
#include "mapogcsld.h"
30
#include "mapogcfilter.h"
31
#include "mapserver.h"
32
#include "mapows.h"
33
#include "mapcopy.h"
34
#include "cpl_string.h"
35

36
extern "C" {
37
extern int yyparse(parseObj *);
38
}
39

40
static inline void IGUR_sizet(size_t ignored) {
41
  (void)ignored;
42
} /* Ignore GCC Unused Result */
43

44
#define SLD_LINE_SYMBOL_NAME "sld_line_symbol"
45
#define SLD_LINE_SYMBOL_DASH_NAME "sld_line_symbol_dash"
46
#define SLD_MARK_SYMBOL_SQUARE "sld_mark_symbol_square"
47
#define SLD_MARK_SYMBOL_SQUARE_FILLED "sld_mark_symbol_square_filled"
48
#define SLD_MARK_SYMBOL_CIRCLE "sld_mark_symbol_circle"
49
#define SLD_MARK_SYMBOL_CIRCLE_FILLED "sld_mark_symbol_circle_filled"
50
#define SLD_MARK_SYMBOL_TRIANGLE "sld_mark_symbol_triangle"
51
#define SLD_MARK_SYMBOL_TRIANGLE_FILLED "sld_mark_symbol_triangle_filled"
52
#define SLD_MARK_SYMBOL_STAR "sld_mark_symbol_star"
53
#define SLD_MARK_SYMBOL_STAR_FILLED "sld_mark_symbol_star_filled"
54
#define SLD_MARK_SYMBOL_CROSS "sld_mark_symbol_cross"
55
#define SLD_MARK_SYMBOL_CROSS_FILLED "sld_mark_symbol_cross_filled"
56
#define SLD_MARK_SYMBOL_X "sld_mark_symbol_x"
57
#define SLD_MARK_SYMBOL_X_FILLED "sld_mark_symbol_x_filled"
58

59
/************************************************************************/
60
/*                             msSLDApplySLDURL                         */
61
/*                                                                      */
62
/*      Use the SLD document given through a URL and apply the SLD      */
63
/*      on the map. Layer name and Named Layer's name parameter are     */
64
/*      used to do the match.                                           */
65
/************************************************************************/
66
int msSLDApplySLDURL(mapObj *map, const char *szURL, int iLayer,
7✔
67
                     const char *pszStyleLayerName, char **ppszLayerNames) {
68
  /* needed for libcurl function msHTTPGetFile in maphttp.c */
69
#if defined(USE_CURL)
70

71
  char *pszSLDTmpFile = NULL;
72
  int status = 0;
7✔
73
  char *pszSLDbuf = NULL;
74
  FILE *fp = NULL;
75
  int nStatus = MS_FAILURE;
76

77
  if (map && szURL) {
7✔
78
    map->sldurl = (char *)szURL;
7✔
79
    pszSLDTmpFile = msTmpFile(map, map->mappath, NULL, "sld.xml");
7✔
80
    if (pszSLDTmpFile == NULL) {
7✔
81
      pszSLDTmpFile = msTmpFile(map, NULL, NULL, "sld.xml");
×
82
    }
83
    if (pszSLDTmpFile == NULL) {
7✔
84
      msSetError(
×
85
          MS_WMSERR,
86
          "Could not determine temporary file. Please make sure that the "
87
          "temporary path is set. The temporary path can be defined for "
88
          "example by setting TEMPPATH in the map file. Please check the "
89
          "MapServer documentation on temporary path settings.",
90
          "msSLDApplySLDURL()");
91
    } else {
92
      int nMaxRemoteSLDBytes;
93
      const char *pszMaxRemoteSLDBytes = msOWSLookupMetadata(
7✔
94
          &(map->web.metadata), "MO", "remote_sld_max_bytes");
95
      if (!pszMaxRemoteSLDBytes) {
7✔
96
        nMaxRemoteSLDBytes = 1024 * 1024; /* 1 megaByte */
97
      } else {
98
        nMaxRemoteSLDBytes = atoi(pszMaxRemoteSLDBytes);
99
      }
100
      if (msHTTPGetFile(szURL, pszSLDTmpFile, &status, -1, 0, 0,
7✔
101
                        nMaxRemoteSLDBytes) == MS_SUCCESS) {
102
        if ((fp = fopen(pszSLDTmpFile, "rb")) != NULL) {
3✔
103
          int nBufsize = 0;
104
          fseek(fp, 0, SEEK_END);
3✔
105
          nBufsize = ftell(fp);
3✔
106
          if (nBufsize > 0) {
3✔
107
            rewind(fp);
3✔
108
            pszSLDbuf = (char *)malloc((nBufsize + 1) * sizeof(char));
3✔
109
            if (pszSLDbuf == NULL) {
3✔
110
              msSetError(MS_MEMERR, "Failed to open SLD file.",
×
111
                         "msSLDApplySLDURL()");
112
            } else {
113
              IGUR_sizet(fread(pszSLDbuf, 1, nBufsize, fp));
3✔
114
              pszSLDbuf[nBufsize] = '\0';
3✔
115
            }
116
          } else {
117
            msSetError(MS_WMSERR, "Could not open SLD %s as it appears empty",
×
118
                       "msSLDApplySLDURL", szURL);
119
          }
120
          fclose(fp);
3✔
121
          unlink(pszSLDTmpFile);
3✔
122
        }
123
      } else {
124
        unlink(pszSLDTmpFile);
4✔
125
        msSetError(
4✔
126
            MS_WMSERR,
127
            "Could not open SLD %s and save it in a temporary file. Please "
128
            "make sure that the sld url is valid and that the temporary path "
129
            "is set. The temporary path can be defined for example by setting "
130
            "TEMPPATH in the map file. Please check the MapServer "
131
            "documentation on temporary path settings.",
132
            "msSLDApplySLDURL", szURL);
133
      }
134
      msFree(pszSLDTmpFile);
7✔
135
      if (pszSLDbuf)
7✔
136
        nStatus = msSLDApplySLD(map, pszSLDbuf, iLayer, pszStyleLayerName,
3✔
137
                                ppszLayerNames);
138
    }
139
    map->sldurl = NULL;
7✔
140
  }
141

142
  msFree(pszSLDbuf);
7✔
143

144
  return nStatus;
7✔
145

146
#else
147
  msSetError(MS_MISCERR, "WMS/WFS client support is not enabled .",
148
             "msSLDApplySLDURL()");
149
  return (MS_FAILURE);
150
#endif
151
}
152

153
/************************************************************************/
154
/*                             msSLDApplyFromFile                       */
155
/*                                                                      */
156
/*      Apply SLD from a file on disk to the layerObj                   */
157
/************************************************************************/
158
int msSLDApplyFromFile(mapObj *map, layerObj *layer, const char *filename) {
120✔
159

160
  FILE *fp = NULL;
161

162
  int nStatus = MS_FAILURE;
163

164
  /* open SLD file */
165
  char szPath[MS_MAXPATHLEN];
166
  char *pszSLDbuf = NULL;
167
  const char *realpath = msBuildPath(szPath, map->mappath, filename);
120✔
168

169
  if ((fp = fopen(realpath, "rb")) != NULL) {
120✔
170
    int nBufsize = 0;
171
    fseek(fp, 0, SEEK_END);
105✔
172
    nBufsize = ftell(fp);
105✔
173
    if (nBufsize > 0) {
105✔
174
      rewind(fp);
105✔
175
      pszSLDbuf = (char *)malloc((nBufsize + 1) * sizeof(char));
105✔
176
      if (pszSLDbuf == NULL) {
105✔
177
        msSetError(MS_MEMERR, "Failed to read SLD file.",
×
178
                   "msSLDApplyFromFile()");
179
      } else {
180
        IGUR_sizet(fread(pszSLDbuf, 1, nBufsize, fp));
105✔
181
        pszSLDbuf[nBufsize] = '\0';
105✔
182
      }
183
    } else {
184
      msSetError(MS_IOERR, "Could not open SLD %s as it appears empty",
×
185
                 "msSLDApplyFromFile", realpath);
186
    }
187
    fclose(fp);
105✔
188
  }
189
  if (pszSLDbuf) {
105✔
190
    // if not set, then use the first NamedStyle in the SLD file
191
    // even if the names don't match
192
    const char *pszMetadataName = "SLD_USE_FIRST_NAMEDLAYER";
193
    const char *pszValue =
194
        msLookupHashTable(&(layer->metadata), pszMetadataName);
105✔
195
    if (pszValue == NULL) {
105✔
196
      msInsertHashTable(&(layer->metadata), pszMetadataName, "true");
90✔
197
    }
198
    nStatus = msSLDApplySLD(map, pszSLDbuf, layer->index, NULL, NULL);
105✔
199
  } else {
200
    msSetError(MS_IOERR, "Invalid SLD filename: \"%s\".",
15✔
201
               "msSLDApplyFromFile()", realpath);
202
  }
203

204
  msFree(pszSLDbuf);
120✔
205
  return nStatus;
120✔
206
}
207

208
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
209
    defined(USE_SOS_SVR)
210
/* -------------------------------------------------------------------- */
211
/*      If the same layer is given more that once, we need to           */
212
/*      duplicate it.                                                   */
213
/* -------------------------------------------------------------------- */
214
static void msSLDApplySLD_DuplicateLayers(mapObj *map, int nSLDLayers,
251✔
215
                                          layerObj *pasSLDLayers) {
216
  int m;
217
  for (m = 0; m < nSLDLayers; m++) {
565✔
218
    int l;
219
    int nIndex = msGetLayerIndex(map, pasSLDLayers[m].name);
314✔
220
    if (pasSLDLayers[m].name == NULL)
314✔
221
      continue;
×
222
    if (nIndex < 0)
314✔
223
      continue;
60✔
224
    for (l = m + 1; l < nSLDLayers; l++) {
320✔
225
      if (pasSLDLayers[l].name == NULL)
66✔
226
        continue;
×
227
      if (strcasecmp(pasSLDLayers[m].name, pasSLDLayers[l].name) == 0) {
66✔
228
        layerObj *psTmpLayer = (layerObj *)malloc(sizeof(layerObj));
22✔
229
        char tmpId[128];
230

231
        initLayer(psTmpLayer, map);
22✔
232
        msCopyLayer(psTmpLayer, GET_LAYER(map, nIndex));
22✔
233
        /* open the source layer */
234
        if (!psTmpLayer->vtable) {
22✔
235
          if (msInitializeVirtualTable(psTmpLayer) != MS_SUCCESS) {
22✔
236
            MS_REFCNT_DECR(psTmpLayer);
×
237
            continue;
×
238
          }
239
        }
240

241
        /*make the name unique*/
242
        snprintf(tmpId, sizeof(tmpId), "%lx_%x_%d", (long)time(NULL),
22✔
243
                 (int)getpid(), map->numlayers);
22✔
244
        if (psTmpLayer->name)
22✔
245
          msFree(psTmpLayer->name);
22✔
246
        psTmpLayer->name = msStrdup(tmpId);
22✔
247
        msFree(pasSLDLayers[l].name);
22✔
248
        pasSLDLayers[l].name = msStrdup(tmpId);
22✔
249
        msInsertLayer(map, psTmpLayer, -1);
22✔
250
        MS_REFCNT_DECR(psTmpLayer);
22✔
251
      }
252
    }
253
  }
254
}
251✔
255
#endif
256

257
static int msApplySldLayerToMapLayer(layerObj *sldLayer, layerObj *lp) {
253✔
258

259
  if (sldLayer->numclasses > 0) {
253✔
260
    int iClass = 0;
261
    bool bSLDHasNamedClass = false;
262

263
    lp->type = sldLayer->type;
249✔
264
    lp->rendermode = MS_ALL_MATCHING_CLASSES;
249✔
265

266
    for (int k = 0; k < sldLayer->numclasses; k++) {
448✔
267
      if (sldLayer->_class[k]->group) {
279✔
268
        bSLDHasNamedClass = true;
269
        break;
270
      }
271
    }
272

273
    for (int k = 0; k < lp->numclasses; k++) {
438✔
274
      if (lp->_class[k] != NULL) {
189✔
275
        lp->_class[k]->layer = NULL;
189✔
276
        if (freeClass(lp->_class[k]) == MS_SUCCESS) {
189✔
277
          msFree(lp->_class[k]);
189✔
278
          lp->_class[k] = NULL;
189✔
279
        }
280
      }
281
    }
282
    lp->numclasses = 0;
249✔
283

284
    if (bSLDHasNamedClass && sldLayer->classgroup) {
249✔
285
      /* Set the class group to the class that has UserStyle.IsDefaultf
286
       */
287
      msFree(lp->classgroup);
47✔
288
      lp->classgroup = msStrdup(sldLayer->classgroup);
47✔
289
    } else {
290
      /*unset the classgroup on the layer if it was set. This allows the
291
      layer to render with all the classes defined in the SLD*/
292
      msFree(lp->classgroup);
202✔
293
      lp->classgroup = NULL;
202✔
294
    }
295

296
    for (int k = 0; k < sldLayer->numclasses; k++) {
3,070✔
297
      if (msGrowLayerClasses(lp) == NULL)
2,821✔
298
        return MS_FAILURE;
299

300
      initClass(lp->_class[iClass]);
2,821✔
301
      msCopyClass(lp->_class[iClass], sldLayer->_class[k], NULL);
2,821✔
302
      lp->_class[iClass]->layer = lp;
2,821✔
303
      lp->numclasses++;
2,821✔
304

305
      /*aliases may have been used as part of the sld text symbolizer
306
        for label element. Try to process it if that is the case #3114*/
307

308
      const int layerWasOpened = msLayerIsOpen(lp);
2,821✔
309

310
      if (layerWasOpened || (msLayerOpen(lp) == MS_SUCCESS &&
3,120✔
311
                             msLayerGetItems(lp) == MS_SUCCESS)) {
299✔
312

313
        if (lp->_class[iClass]->text.string) {
2,821✔
314
          for (int z = 0; z < lp->numitems; z++) {
844✔
315
            if (!lp->items[z] || strlen(lp->items[z]) == 0)
789✔
316
              continue;
×
317

318
            char *pszTmp1 = msStrdup(lp->_class[iClass]->text.string);
789✔
319
            const char *pszFullName = msOWSLookupMetadata(
789✔
320
                &(lp->metadata), "G",
321
                std::string(lp->items[z]).append("_alias").c_str());
789✔
322

323
            if (pszFullName != NULL && (strstr(pszTmp1, pszFullName) != NULL)) {
789✔
324
              pszTmp1 = msReplaceSubstring(pszTmp1, pszFullName, lp->items[z]);
2✔
325
              std::string osTmp("(");
2✔
326
              osTmp.append(pszTmp1).append(")");
2✔
327
              // Silence false positive Coverity Scan warnings which wrongly
328
              // believes that osTmp.c_str() might be used by
329
              // msLoadExpressionString() after osTmp is destroyed.
330
              // coverity[escape]
331
              msLoadExpressionString(&(lp->_class[iClass]->text),
2✔
332
                                     osTmp.c_str());
333
            }
334
            msFree(pszTmp1);
789✔
335
          }
336
        }
337
        if (!layerWasOpened) { // don't close the layer if already
2,821✔
338
                               // open
339
          msLayerClose(lp);
299✔
340
        }
341
      }
342
      iClass++;
2,821✔
343
    }
344
  } else {
345
    /*this is probably an SLD that uses Named styles*/
346
    if (sldLayer->classgroup) {
4✔
347
      int k;
348
      for (k = 0; k < lp->numclasses; k++) {
4✔
349
        if (lp->_class[k]->group &&
4✔
350
            strcasecmp(lp->_class[k]->group, sldLayer->classgroup) == 0)
4✔
351
          break;
352
      }
353
      if (k < lp->numclasses) {
2✔
354
        msFree(lp->classgroup);
2✔
355
        lp->classgroup = msStrdup(sldLayer->classgroup);
2✔
356
      } else {
357
        /* TODO  we throw an exception ?*/
358
      }
359
    }
360
  }
361
  if (sldLayer->labelitem) {
253✔
362
    if (lp->labelitem)
×
363
      free(lp->labelitem);
×
364

365
    lp->labelitem = msStrdup(sldLayer->labelitem);
×
366
  }
367

368
  if (sldLayer->classitem) {
253✔
369
    if (lp->classitem)
16✔
370
      free(lp->classitem);
×
371

372
    lp->classitem = msStrdup(sldLayer->classitem);
16✔
373
  }
374

375
  /* opacity for sld raster */
376
  if (lp->type == MS_LAYER_RASTER && sldLayer->compositer &&
253✔
377
      sldLayer->compositer->opacity != 100)
1✔
378
    msSetLayerOpacity(lp, sldLayer->compositer->opacity);
×
379

380
  /* mark as auto-generate SLD */
381
  if (lp->connectiontype == MS_WMS)
253✔
382
    msInsertHashTable(&(lp->metadata), "wms_sld_body", "auto");
×
383

384
  /* The SLD might have a FeatureTypeConstraint */
385
  if (sldLayer->filter.type == MS_EXPRESSION) {
253✔
386
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
5✔
387
      char *pszBuffer = msStringConcatenate(NULL, "((");
1✔
388
      pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
1✔
389
      pszBuffer = msStringConcatenate(pszBuffer, ") AND (");
1✔
390
      pszBuffer = msStringConcatenate(pszBuffer, sldLayer->filter.string);
1✔
391
      pszBuffer = msStringConcatenate(pszBuffer, "))");
1✔
392
      msFreeExpression(&lp->filter);
1✔
393
      msInitExpression(&lp->filter);
1✔
394
      lp->filter.string = pszBuffer;
1✔
395
      lp->filter.type = MS_EXPRESSION;
1✔
396
    } else {
397
      msFreeExpression(&lp->filter);
4✔
398
      msInitExpression(&lp->filter);
4✔
399
      lp->filter.string = msStrdup(sldLayer->filter.string);
4✔
400
      lp->filter.type = MS_EXPRESSION;
4✔
401
    }
402
  }
403

404
  /*in some cases it would make sense to concatenate all the class
405
    expressions and use it to set the filter on the layer. This
406
    could increase performance. Will do it for db types layers
407
    #2840*/
408
  if (lp->filter.string == NULL ||
253✔
409
      (lp->filter.string && lp->filter.type == MS_STRING && !lp->filteritem)) {
5✔
410
    if (lp->connectiontype == MS_POSTGIS ||
248✔
411
        lp->connectiontype == MS_ORACLESPATIAL ||
221✔
412
        lp->connectiontype == MS_PLUGIN) {
413
      if (lp->numclasses > 0) {
27✔
414
        /* check first that all classes have an expression type.
415
          That is the only way we can concatenate them and set the
416
          filter expression */
417
        int k;
418
        for (k = 0; k < lp->numclasses; k++) {
46✔
419
          if (lp->_class[k]->expression.type != MS_EXPRESSION)
37✔
420
            break;
421
        }
422
        if (k == lp->numclasses) {
27✔
423
          char szTmp[512];
424
          char *pszBuffer = NULL;
425
          for (k = 0; k < lp->numclasses; k++) {
27✔
426
            if (pszBuffer == NULL)
18✔
427
              snprintf(szTmp, sizeof(szTmp), "%s",
428
                       "(("); /* we a building a string expression,
429
                                 explicitly set type below */
430
            else
431
              snprintf(szTmp, sizeof(szTmp), "%s", " OR ");
432

433
            pszBuffer = msStringConcatenate(pszBuffer, szTmp);
18✔
434
            pszBuffer = msStringConcatenate(pszBuffer,
18✔
435
                                            lp->_class[k]->expression.string);
18✔
436
          }
437

438
          snprintf(szTmp, sizeof(szTmp), "%s", "))");
439
          pszBuffer = msStringConcatenate(pszBuffer, szTmp);
9✔
440

441
          msFreeExpression(&lp->filter);
9✔
442
          msInitExpression(&lp->filter);
9✔
443
          lp->filter.string = msStrdup(pszBuffer);
9✔
444
          lp->filter.type = MS_EXPRESSION;
9✔
445

446
          msFree(pszBuffer);
9✔
447
        }
448
      }
449
    }
450
  }
451

452
  return MS_SUCCESS;
453
}
454

455
/************************************************************************/
456
/*                              msSLDApplySLD                           */
457
/*                                                                      */
458
/*      Parses the SLD into array of layers. Go through the map and     */
459
/*      compare the SLD layers and the map layers using the name. If    */
460
/*      they have the same name, copy the classes associated with       */
461
/*      the SLD layers onto the map layers.                             */
462
/************************************************************************/
463
int msSLDApplySLD(mapObj *map, const char *psSLDXML, int iLayer,
254✔
464
                  const char *pszStyleLayerName, char **ppszLayerNames) {
465
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
466
    defined(USE_SOS_SVR)
467
  int nSLDLayers = 0;
254✔
468
  layerObj *pasSLDLayers = NULL;
469
  int nStatus = MS_SUCCESS;
470
  /*const char *pszSLDNotSupported = NULL;*/
471

472
  pasSLDLayers = msSLDParseSLD(map, psSLDXML, &nSLDLayers);
254✔
473
  if (pasSLDLayers == NULL) {
254✔
474
    errorObj *psError = msGetErrorObj();
3✔
475
    if (psError && psError->code != MS_NOERR)
3✔
476
      return MS_FAILURE;
477
  }
478

479
  if (pasSLDLayers && nSLDLayers > 0) {
251✔
480
    int i;
481

482
    msSLDApplySLD_DuplicateLayers(map, nSLDLayers, pasSLDLayers);
251✔
483

484
    for (i = 0; i < map->numlayers; i++) {
762✔
485
      layerObj *lp = NULL;
486
      const char *pszWMSLayerName = NULL;
487
      int j;
488
      int bUseSpecificLayer = 0;
489

490
      if (iLayer >= 0 && iLayer < map->numlayers) {
618✔
491
        i = iLayer;
492
        bUseSpecificLayer = 1;
493
      }
494

495
      lp = GET_LAYER(map, i);
618✔
496

497
      /* compare layer name to wms_name as well */
498
      pszWMSLayerName = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
618✔
499

500
      bool bSldApplied = false;
501

502
      for (j = 0; j < nSLDLayers; j++) {
1,115✔
503
        layerObj *sldLayer = &pasSLDLayers[j];
704✔
504

505
        /* --------------------------------------------------------------------
506
         */
507
        /*      copy :  - class */
508
        /*              - layer's labelitem */
509
        /* --------------------------------------------------------------------
510
         */
511
        if ((sldLayer->name && pszStyleLayerName == NULL &&
704✔
512
             ((strcasecmp(lp->name, sldLayer->name) == 0 ||
704✔
513
               (pszWMSLayerName &&
×
514
                strcasecmp(pszWMSLayerName, sldLayer->name) == 0)) ||
×
515
              (lp->group && strcasecmp(lp->group, sldLayer->name) == 0))) ||
497✔
516
            (bUseSpecificLayer && pszStyleLayerName && sldLayer->name &&
497✔
517
             strcasecmp(sldLayer->name, pszStyleLayerName) == 0)) {
×
518
#ifdef notdef
519
          /*this is a test code if we decide to flag some layers as not
520
           * supporting SLD*/
521
          pszSLDNotSupported =
522
              msOWSLookupMetadata(&(lp->metadata), "M", "SLD_NOT_SUPPORTED");
523
          if (pszSLDNotSupported) {
524
            msSetError(MS_WMSERR, "Layer %s does not support SLD",
525
                       "msSLDApplySLD", sldLayer->name);
526
            nsStatus = MS_FAILURE;
527
            goto sld_cleanup;
528
          }
529
#endif
530
          if (msApplySldLayerToMapLayer(sldLayer, lp) == MS_FAILURE) {
207✔
531
            nStatus = MS_FAILURE;
532
            goto sld_cleanup;
×
533
          };
534
          bSldApplied = true;
535
          break;
536
        }
537
      }
538
      if (bUseSpecificLayer) {
618✔
539
        if (!bSldApplied) {
107✔
540
          // there was no name match between the map layer and the SLD named
541
          // layer - check if we should apply the first SLD layer anyway
542
          const char *pszSLDUseFirstNamedLayer =
543
              msLookupHashTable(&(lp->metadata), "SLD_USE_FIRST_NAMEDLAYER");
61✔
544
          if (pszSLDUseFirstNamedLayer) {
61✔
545
            if (strcasecmp(pszSLDUseFirstNamedLayer, "true") == 0) {
61✔
546
              layerObj *firstSldLayer = &pasSLDLayers[0];
547
              if (msApplySldLayerToMapLayer(firstSldLayer, lp) == MS_FAILURE) {
46✔
548
                nStatus = MS_FAILURE;
549
                goto sld_cleanup;
×
550
              };
551
            }
552
          }
553
        }
554
        break;
555
      }
556
    }
557

558
    /* -------------------------------------------------------------------- */
559
    /*      if needed return a comma separated list of the layers found     */
560
    /*      in the sld.                                                     */
561
    /* -------------------------------------------------------------------- */
562
    if (ppszLayerNames) {
251✔
563
      char *pszTmp = NULL;
564
      for (i = 0; i < nSLDLayers; i++) {
292✔
565
        if (pasSLDLayers[i].name) {
155✔
566
          if (pszTmp != NULL)
155✔
567
            pszTmp = msStringConcatenate(pszTmp, ",");
18✔
568
          pszTmp = msStringConcatenate(pszTmp, pasSLDLayers[i].name);
155✔
569
        }
570
      }
571
      *ppszLayerNames = pszTmp;
137✔
572
    }
573
  }
574

575
  nStatus = MS_SUCCESS;
576

577
sld_cleanup:
251✔
578

579
  if (pasSLDLayers) {
251✔
580
    for (int i = 0; i < nSLDLayers; i++)
565✔
581
      freeLayer(&pasSLDLayers[i]);
314✔
582
    msFree(pasSLDLayers);
251✔
583
  }
584

585
  if (map->debug == MS_DEBUGLEVEL_VVV) {
251✔
586
    char *tmpfilename = msTmpFile(map, map->mappath, NULL, "_sld.map");
×
587
    if (tmpfilename == NULL) {
×
588
      tmpfilename = msTmpFile(map, NULL, NULL, "_sld.map");
×
589
    }
590
    if (tmpfilename) {
×
591
      msSaveMap(map, tmpfilename);
×
592
      msDebug("msApplySLD(): Map file after SLD was applied %s\n", tmpfilename);
×
593
      msFree(tmpfilename);
×
594
    }
595
  }
596
  return nStatus;
597

598
#else
599
  msSetError(MS_MISCERR, "OWS support is not available.", "msSLDApplySLD()");
600
  return (MS_FAILURE);
601
#endif
602
}
603

604
static CPLXMLNode *FindNextChild(CPLXMLNode *psNode, const char *pszChildName) {
6,076✔
605
  while (psNode) {
9,442✔
606
    if (psNode->eType == CXT_Element &&
5,816✔
607
        strcasecmp(psNode->pszValue, pszChildName) == 0) {
5,178✔
608
      return psNode;
2,450✔
609
    }
610
    psNode = psNode->psNext;
3,366✔
611
  }
612
  return NULL;
613
}
614

615
#define LOOP_ON_CHILD_ELEMENT(psParent_, psChild_, pszChildName_)              \
616
  for (psChild_ = FindNextChild(psParent_->psChild, pszChildName_);            \
617
       psChild_ != NULL;                                                       \
618
       psChild_ = FindNextChild(psChild_->psNext, pszChildName_))
619

620
/************************************************************************/
621
/*                              msSLDParseSLD                           */
622
/*                                                                      */
623
/*      Parse the sld document into layers : for each named layer       */
624
/*      there is one mapserver layer created with appropriate           */
625
/*      classes and styles.                                             */
626
/*      Returns an array of mapserver layers. The pnLayres if           */
627
/*      provided will indicate the size of the returned array.          */
628
/************************************************************************/
629
layerObj *msSLDParseSLD(mapObj *map, const char *psSLDXML, int *pnLayers) {
254✔
630
  CPLXMLNode *psRoot = NULL;
631
  CPLXMLNode *psSLD, *psNamedLayer;
632
  layerObj *pasLayers = NULL;
633
  int iLayer = 0;
634
  int nLayers = 0;
635

636
  if (map == NULL || psSLDXML == NULL || strlen(psSLDXML) == 0 ||
254✔
637
      (strstr(psSLDXML, "StyledLayerDescriptor") == NULL)) {
638
    msSetError(MS_WMSERR, "Invalid SLD document", "");
×
639
    return NULL;
×
640
  }
641

642
  psRoot = CPLParseXMLString(psSLDXML);
254✔
643
  if (psRoot == NULL) {
254✔
644
    msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
×
645
    return NULL;
×
646
  }
647

648
  /* strip namespaces ogc and sld and gml */
649
  CPLStripXMLNamespace(psRoot, "ogc", 1);
254✔
650
  CPLStripXMLNamespace(psRoot, "sld", 1);
254✔
651
  CPLStripXMLNamespace(psRoot, "gml", 1);
254✔
652
  CPLStripXMLNamespace(psRoot, "se", 1);
254✔
653

654
  /* -------------------------------------------------------------------- */
655
  /*      get the root element (StyledLayerDescriptor).                   */
656
  /* -------------------------------------------------------------------- */
657
  psSLD = CPLGetXMLNode(psRoot, "=StyledLayerDescriptor");
254✔
658
  if (!psSLD) {
254✔
659
    msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
×
660
    return NULL;
×
661
  }
662

663
  /* -------------------------------------------------------------------- */
664
  /*      Parse the named layers.                                         */
665
  /* -------------------------------------------------------------------- */
666
  LOOP_ON_CHILD_ELEMENT(psSLD, psNamedLayer, "NamedLayer") { nLayers++; }
571✔
667

668
  if (nLayers > 0)
254✔
669
    pasLayers = (layerObj *)malloc(sizeof(layerObj) * nLayers);
254✔
670
  else
671
    return NULL;
672

673
  LOOP_ON_CHILD_ELEMENT(psSLD, psNamedLayer, "NamedLayer") {
568✔
674
    CPLXMLNode *psName = CPLGetXMLNode(psNamedLayer, "Name");
317✔
675
    initLayer(&pasLayers[iLayer], map);
317✔
676

677
    if (psName && psName->psChild && psName->psChild->pszValue)
317✔
678
      pasLayers[iLayer].name = msStrdup(psName->psChild->pszValue);
317✔
679

680
    if (msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]) != MS_SUCCESS) {
317✔
681
      int i;
682
      for (i = 0; i <= iLayer; i++)
6✔
683
        freeLayer(&pasLayers[i]);
3✔
684
      msFree(pasLayers);
3✔
685
      nLayers = 0;
686
      pasLayers = NULL;
687
      break;
3✔
688
    }
689

690
    iLayer++;
314✔
691
  }
692

693
  if (pnLayers)
254✔
694
    *pnLayers = nLayers;
254✔
695

696
  if (psRoot)
697
    CPLDestroyXMLNode(psRoot);
254✔
698

699
  return pasLayers;
254✔
700
}
701

702
/************************************************************************/
703
/*                           _SLDApplyRuleValues                        */
704
/*                                                                      */
705
/*      Utility function to set the scale, title/name for the          */
706
/*      classes created by a Rule.                                      */
707
/************************************************************************/
708
void _SLDApplyRuleValues(CPLXMLNode *psRule, layerObj *psLayer,
366✔
709
                         int nNewClasses) {
710
  CPLXMLNode *psMinScale = NULL, *psMaxScale = NULL;
711
  CPLXMLNode *psName = NULL, *psTitle = NULL;
712
  double dfMinScale = 0, dfMaxScale = 0;
713
  char *pszName = NULL, *pszTitle = NULL;
714

715
  if (psRule && psLayer && nNewClasses > 0) {
366✔
716
    /* -------------------------------------------------------------------- */
717
    /*      parse minscale and maxscale.                                    */
718
    /* -------------------------------------------------------------------- */
719
    psMinScale = CPLGetXMLNode(psRule, "MinScaleDenominator");
362✔
720
    if (psMinScale && psMinScale->psChild && psMinScale->psChild->pszValue)
362✔
721
      dfMinScale = atof(psMinScale->psChild->pszValue);
722

723
    psMaxScale = CPLGetXMLNode(psRule, "MaxScaleDenominator");
362✔
724
    if (psMaxScale && psMaxScale->psChild && psMaxScale->psChild->pszValue)
362✔
725
      dfMaxScale = atof(psMaxScale->psChild->pszValue);
726

727
    /* -------------------------------------------------------------------- */
728
    /*      parse name and title.                                           */
729
    /* -------------------------------------------------------------------- */
730
    psName = CPLGetXMLNode(psRule, "Name");
362✔
731
    if (psName && psName->psChild && psName->psChild->pszValue)
362✔
732
      pszName = psName->psChild->pszValue;
733

734
    psTitle = CPLGetXMLNode(psRule, "Title");
362✔
735
    if (psTitle && psTitle->psChild && psTitle->psChild->pszValue)
362✔
736
      pszTitle = psTitle->psChild->pszValue;
737

738
    /* -------------------------------------------------------------------- */
739
    /*      set the scale to all the classes created by the rule.           */
740
    /* -------------------------------------------------------------------- */
741
    if (dfMinScale > 0 || dfMaxScale > 0) {
362✔
742
      for (int i = 0; i < nNewClasses; i++) {
×
743
        if (dfMinScale > 0)
×
744
          psLayer->_class[psLayer->numclasses - 1 - i]->minscaledenom =
×
745
              dfMinScale;
746
        if (dfMaxScale)
×
747
          psLayer->_class[psLayer->numclasses - 1 - i]->maxscaledenom =
×
748
              dfMaxScale;
749
      }
750
    }
751
    /* -------------------------------------------------------------------- */
752
    /*      set name and title to the classes created by the rule.          */
753
    /* -------------------------------------------------------------------- */
754
    for (int i = 0; i < nNewClasses; i++) {
3,244✔
755
      if (!psLayer->_class[psLayer->numclasses - 1 - i]->name) {
2,882✔
756
        if (pszName)
346✔
757
          psLayer->_class[psLayer->numclasses - 1 - i]->name =
13✔
758
              msStrdup(pszName);
13✔
759
        else if (pszTitle)
333✔
760
          psLayer->_class[psLayer->numclasses - 1 - i]->name =
10✔
761
              msStrdup(pszTitle);
10✔
762
        else {
763
          // Build a name from layer and class info
764
          char szTmp[256];
765
          snprintf(szTmp, sizeof(szTmp), "%s#%d", psLayer->name,
323✔
766
                   psLayer->numclasses - 1 - i);
767
          psLayer->_class[psLayer->numclasses - 1 - i]->name = msStrdup(szTmp);
323✔
768
        }
769
      }
770
    }
771
    if (pszTitle) {
362✔
772
      for (int i = 0; i < nNewClasses; i++) {
20✔
773
        psLayer->_class[psLayer->numclasses - 1 - i]->title =
10✔
774
            msStrdup(pszTitle);
10✔
775
      }
776
    }
777
  }
778
}
366✔
779

780
/************************************************************************/
781
/*                     msSLDGetCommonExpressionFromFilter               */
782
/*                                                                      */
783
/*      Get a common expression valid from the filter valid for the    */
784
/*      temporary layer.                                                */
785
/************************************************************************/
786
static char *msSLDGetCommonExpressionFromFilter(CPLXMLNode *psFilter,
59✔
787
                                                layerObj *psLayer) {
788
  char *pszExpression = NULL;
789
  CPLXMLNode *psTmpNextNode = NULL;
790
  CPLXMLNode *psTmpNode = NULL;
791
  FilterEncodingNode *psNode = NULL;
792
  char *pszTmpFilter = NULL;
793
  layerObj *psCurrentLayer = NULL;
794
  const char *pszWmsName = NULL;
795
  const char *key = NULL;
796

797
  /* clone the tree and set the next node to null */
798
  /* so we only have the Filter node */
799
  psTmpNode = CPLCloneXMLTree(psFilter);
59✔
800
  psTmpNextNode = psTmpNode->psNext;
59✔
801
  psTmpNode->psNext = NULL;
59✔
802
  pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
59✔
803
  psTmpNode->psNext = psTmpNextNode;
59✔
804
  CPLDestroyXMLNode(psTmpNode);
59✔
805

806
  if (pszTmpFilter) {
59✔
807
    psNode = FLTParseFilterEncoding(pszTmpFilter);
59✔
808

809
    CPLFree(pszTmpFilter);
59✔
810
  }
811

812
  if (psNode) {
59✔
813
    int j;
814

815
    /*preparse the filter for possible gml aliases set on the layer's metadata:
816
    "gml_NA3DESC_alias" "alias_name" and filter could be
817
    <ogc:PropertyName>alias_name</ogc:PropertyName> #3079*/
818
    for (j = 0; j < psLayer->map->numlayers; j++) {
146✔
819
      psCurrentLayer = GET_LAYER(psLayer->map, j);
146✔
820

821
      pszWmsName =
822
          msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
146✔
823

824
      if ((psCurrentLayer->name && psLayer->name &&
146✔
825
           strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
146✔
826
          (psCurrentLayer->group && psLayer->name &&
87✔
827
           strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
×
828
          (psLayer->name && pszWmsName &&
87✔
829
           strcasecmp(pszWmsName, psLayer->name) == 0))
×
830
        break;
831
    }
832
    if (j < psLayer->map->numlayers) {
59✔
833
      /*make sure that the tmp layer has all the metadata that
834
      the original layer has, allowing to do parsing for
835
      such things as gml_attribute_type #3052*/
836
      while (1) {
837
        key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
185✔
838
        if (!key)
185✔
839
          break;
840
        else
841
          msInsertHashTable(&psLayer->metadata, key,
126✔
842
                            msLookupHashTable(&psCurrentLayer->metadata, key));
843
      }
844
      FLTPreParseFilterForAliasAndGroup(psNode, psLayer->map, j, "G");
59✔
845
    }
846

847
    pszExpression = FLTGetCommonExpression(psNode, psLayer);
59✔
848
    FLTFreeFilterEncodingNode(psNode);
59✔
849
  }
850

851
  return pszExpression;
59✔
852
}
853

854
/************************************************************************/
855
/*                          msSLDParseUserStyle                         */
856
/*                                                                      */
857
/*      Parse UserStyle node.                                           */
858
/************************************************************************/
859

860
static void msSLDParseUserStyle(CPLXMLNode *psUserStyle, layerObj *psLayer) {
332✔
861
  CPLXMLNode *psFeatureTypeStyle;
862
  const char *pszUserStyleName = CPLGetXMLValue(psUserStyle, "Name", NULL);
332✔
863
  if (pszUserStyleName) {
332✔
864
    const char *pszIsDefault = CPLGetXMLValue(psUserStyle, "IsDefault", "0");
162✔
865
    if (EQUAL(pszIsDefault, "true") || EQUAL(pszIsDefault, "1")) {
162✔
866
      msFree(psLayer->classgroup);
77✔
867
      psLayer->classgroup = msStrdup(pszUserStyleName);
77✔
868
    }
869
  }
870

871
  LOOP_ON_CHILD_ELEMENT(psUserStyle, psFeatureTypeStyle, "FeatureTypeStyle") {
664✔
872
    CPLXMLNode *psRule;
873

874
    /* -------------------------------------------------------------------- */
875
    /*      Parse rules with no Else filter.                                */
876
    /* -------------------------------------------------------------------- */
877
    LOOP_ON_CHILD_ELEMENT(psFeatureTypeStyle, psRule, "Rule") {
695✔
878
      CPLXMLNode *psFilter = NULL;
879
      CPLXMLNode *psElseFilter = NULL;
880
      int nNewClasses = 0, nClassBeforeFilter = 0, nClassAfterFilter = 0;
881
      int nClassAfterRule = 0, nClassBeforeRule = 0;
882

883
      /* used for scale setting */
884
      nClassBeforeRule = psLayer->numclasses;
363✔
885

886
      psElseFilter = CPLGetXMLNode(psRule, "ElseFilter");
363✔
887
      nClassBeforeFilter = psLayer->numclasses;
363✔
888
      if (psElseFilter == NULL)
363✔
889
        msSLDParseRule(psRule, psLayer, pszUserStyleName);
360✔
890
      nClassAfterFilter = psLayer->numclasses;
363✔
891

892
      /* -------------------------------------------------------------------- */
893
      /*      Parse the filter and apply it to the latest class created by    */
894
      /*      the rule.                                                       */
895
      /*      NOTE : Spatial Filter is not supported.                         */
896
      /* -------------------------------------------------------------------- */
897
      psFilter = CPLGetXMLNode(psRule, "Filter");
363✔
898
      if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
363✔
899
        char *pszExpression =
900
            msSLDGetCommonExpressionFromFilter(psFilter, psLayer);
54✔
901
        if (pszExpression) {
54✔
902
          int i;
903
          nNewClasses = nClassAfterFilter - nClassBeforeFilter;
54✔
904
          for (i = 0; i < nNewClasses; i++) {
108✔
905
            expressionObj *exp =
54✔
906
                &(psLayer->_class[psLayer->numclasses - 1 - i]->expression);
54✔
907
            msFreeExpression(exp);
54✔
908
            msInitExpression(exp);
54✔
909
            exp->string = msStrdup(pszExpression);
54✔
910
            exp->type = MS_EXPRESSION;
54✔
911
          }
912
          msFree(pszExpression);
54✔
913
          pszExpression = NULL;
914
        }
915
      }
916
      nClassAfterRule = psLayer->numclasses;
363✔
917
      nNewClasses = nClassAfterRule - nClassBeforeRule;
363✔
918

919
      /* apply scale and title to newly created classes */
920
      _SLDApplyRuleValues(psRule, psLayer, nNewClasses);
363✔
921

922
      /* TODO : parse legendgraphic */
923
    }
924
    /* -------------------------------------------------------------------- */
925
    /*      First parse rules with the else filter. These rules will        */
926
    /*      create the classes that are placed at the end of class          */
927
    /*      list. (See how classes are applied to layers in function        */
928
    /*      msSLDApplySLD).                                                 */
929
    /* -------------------------------------------------------------------- */
930
    LOOP_ON_CHILD_ELEMENT(psFeatureTypeStyle, psRule, "Rule") {
695✔
931
      CPLXMLNode *psElseFilter = CPLGetXMLNode(psRule, "ElseFilter");
363✔
932
      if (psElseFilter) {
363✔
933
        msSLDParseRule(psRule, psLayer, pszUserStyleName);
3✔
934
        _SLDApplyRuleValues(psRule, psLayer, 1);
3✔
935
        psLayer->_class[psLayer->numclasses - 1]->isfallback = TRUE;
3✔
936
      }
937
    }
938
  }
939
}
332✔
940

941
/************************************************************************/
942
/*                           msSLDParseNamedLayer                       */
943
/*                                                                      */
944
/*      Parse NamedLayer root.                                          */
945
/************************************************************************/
946
int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer) {
317✔
947
  CPLXMLNode *psLayerFeatureConstraints = NULL;
948

949
  if (!psRoot || !psLayer)
317✔
950
    return MS_FAILURE;
951

952
  if (CPLGetXMLNode(psRoot, "UserStyle")) {
317✔
953
    CPLXMLNode *psUserStyle;
954
    LOOP_ON_CHILD_ELEMENT(psRoot, psUserStyle, "UserStyle") {
642✔
955
      msSLDParseUserStyle(psUserStyle, psLayer);
332✔
956
    }
957
  }
958
  /* check for Named styles*/
959
  else {
960
    CPLXMLNode *psNamedStyle = CPLGetXMLNode(psRoot, "NamedStyle");
7✔
961
    if (psNamedStyle) {
7✔
962
      CPLXMLNode *psSLDName = CPLGetXMLNode(psNamedStyle, "Name");
5✔
963
      if (psSLDName && psSLDName->psChild && psSLDName->psChild->pszValue) {
5✔
964
        msFree(psLayer->classgroup);
5✔
965
        psLayer->classgroup = msStrdup(psSLDName->psChild->pszValue);
5✔
966
      }
967
    }
968
  }
969

970
  /* Deal with LayerFeatureConstraints */
971
  psLayerFeatureConstraints = CPLGetXMLNode(psRoot, "LayerFeatureConstraints");
317✔
972
  if (psLayerFeatureConstraints != NULL) {
317✔
973
    CPLXMLNode *psIter = psLayerFeatureConstraints->psChild;
8✔
974
    CPLXMLNode *psFeatureTypeConstraint = NULL;
975
    for (; psIter != NULL; psIter = psIter->psNext) {
16✔
976
      if (psIter->eType == CXT_Element &&
9✔
977
          strcmp(psIter->pszValue, "FeatureTypeConstraint") == 0) {
9✔
978
        if (psFeatureTypeConstraint == NULL) {
9✔
979
          psFeatureTypeConstraint = psIter;
980
        } else {
981
          msSetError(MS_WMSERR,
1✔
982
                     "Only one single FeatureTypeConstraint element "
983
                     "per LayerFeatureConstraints is supported",
984
                     "");
985
          return MS_FAILURE;
1✔
986
        }
987
      }
988
    }
989
    if (psFeatureTypeConstraint != NULL) {
7✔
990
      CPLXMLNode *psFilter;
991
      if (CPLGetXMLNode(psFeatureTypeConstraint, "FeatureTypeName") != NULL) {
7✔
992
        msSetError(MS_WMSERR,
1✔
993
                   "FeatureTypeName element is not "
994
                   "supported in FeatureTypeConstraint",
995
                   "");
996
        return MS_FAILURE;
1✔
997
      }
998
      if (CPLGetXMLNode(psFeatureTypeConstraint, "Extent") != NULL) {
6✔
999
        msSetError(MS_WMSERR,
1✔
1000
                   "Extent element is not "
1001
                   "supported in FeatureTypeConstraint",
1002
                   "");
1003
        return MS_FAILURE;
1✔
1004
      }
1005
      psFilter = CPLGetXMLNode(psFeatureTypeConstraint, "Filter");
5✔
1006
      if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
5✔
1007
        char *pszExpression =
1008
            msSLDGetCommonExpressionFromFilter(psFilter, psLayer);
5✔
1009
        if (pszExpression) {
5✔
1010
          msFreeExpression(&psLayer->filter);
5✔
1011
          msInitExpression(&psLayer->filter);
5✔
1012
          psLayer->filter.string = pszExpression;
5✔
1013
          psLayer->filter.type = MS_EXPRESSION;
5✔
1014
        }
1015
      }
1016
    }
1017
  }
1018

1019
  return MS_SUCCESS;
1020
}
1021

1022
/************************************************************************/
1023
/*                      msSLDParseRule()                                */
1024
/*                                                                      */
1025
/*      Parse a Rule node into classes for a specific layer.            */
1026
/************************************************************************/
1027
int msSLDParseRule(CPLXMLNode *psRoot, layerObj *psLayer,
363✔
1028
                   const char *pszUserStyleName) {
1029
  CPLXMLNode *psLineSymbolizer = NULL;
1030
  CPLXMLNode *psPolygonSymbolizer = NULL;
1031
  CPLXMLNode *psPointSymbolizer = NULL;
1032
  CPLXMLNode *psTextSymbolizer = NULL;
1033
  CPLXMLNode *psRasterSymbolizer = NULL;
1034

1035
  int nSymbolizer = 0;
1036

1037
  if (!psRoot || !psLayer)
363✔
1038
    return MS_FAILURE;
1039

1040
  /* TODO : parse name of the rule */
1041
  /* ==================================================================== */
1042
  /*      For each rule a new class is created. If there are more than    */
1043
  /*      one symbolizer, a style is added in the same class.             */
1044
  /* ==================================================================== */
1045

1046
  /* Raster symbolizer */
1047
  LOOP_ON_CHILD_ELEMENT(psRoot, psRasterSymbolizer, "RasterSymbolizer") {
379✔
1048
    msSLDParseRasterSymbolizer(psRasterSymbolizer, psLayer, pszUserStyleName);
16✔
1049
    /* cppcheck-suppress knownConditionTrueFalse */
1050
    if (nSymbolizer == 0) {
1051
      psLayer->type = MS_LAYER_RASTER;
16✔
1052
    }
1053
  }
1054

1055
  /* Polygon symbolizer */
1056
  LOOP_ON_CHILD_ELEMENT(psRoot, psPolygonSymbolizer, "PolygonSymbolizer") {
403✔
1057
    /* cppcheck-suppress knownConditionTrueFalse */
1058
    const int bNewClass = (nSymbolizer == 0);
40✔
1059
    msSLDParsePolygonSymbolizer(psPolygonSymbolizer, psLayer, bNewClass,
40✔
1060
                                pszUserStyleName);
1061
    psLayer->type = MS_LAYER_POLYGON;
40✔
1062
    nSymbolizer++;
40✔
1063
  }
1064

1065
  /* line symbolizer */
1066
  LOOP_ON_CHILD_ELEMENT(psRoot, psLineSymbolizer, "LineSymbolizer") {
613✔
1067
    const int bNewClass = (nSymbolizer == 0);
250✔
1068
    msSLDParseLineSymbolizer(psLineSymbolizer, psLayer, bNewClass,
250✔
1069
                             pszUserStyleName);
1070
    if (bNewClass) {
250✔
1071
      psLayer->type = MS_LAYER_LINE;
239✔
1072
    }
1073
    if (psLayer->type == MS_LAYER_POLYGON) {
250✔
1074
      const int nClassId = psLayer->numclasses - 1;
1✔
1075
      if (nClassId >= 0) {
1✔
1076
        const int nStyleId = psLayer->_class[nClassId]->numstyles - 1;
1✔
1077
        if (nStyleId >= 0) {
1✔
1078
          styleObj *psStyle = psLayer->_class[nClassId]->styles[nStyleId];
1✔
1079
          psStyle->outlinecolor = psStyle->color;
1✔
1080
          MS_INIT_COLOR(psStyle->color, -1, -1, -1, 255);
1✔
1081
          MS_COPYSTRING(
1✔
1082
              psStyle->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].string,
1083
              psStyle->exprBindings[MS_STYLE_BINDING_COLOR].string);
1084
          psStyle->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].type =
1✔
1085
              psStyle->exprBindings[MS_STYLE_BINDING_COLOR].type;
1✔
1086
          msFreeExpression(&(psStyle->exprBindings[MS_STYLE_BINDING_COLOR]));
1✔
1087
          msInitExpression(&(psStyle->exprBindings[MS_STYLE_BINDING_COLOR]));
1✔
1088
        }
1089
      }
1090
    }
1091
    nSymbolizer++;
250✔
1092
  }
1093

1094
  /* Point Symbolizer */
1095
  LOOP_ON_CHILD_ELEMENT(psRoot, psPointSymbolizer, "PointSymbolizer") {
428✔
1096
    const int bNewClass = (nSymbolizer == 0);
65✔
1097
    msSLDParsePointSymbolizer(psPointSymbolizer, psLayer, bNewClass,
65✔
1098
                              pszUserStyleName);
1099
    if (bNewClass) {
65✔
1100
      psLayer->type = MS_LAYER_POINT;
63✔
1101
    }
1102
    if (psLayer->type == MS_LAYER_POLYGON || psLayer->type == MS_LAYER_LINE ||
65✔
1103
        psLayer->type == MS_LAYER_RASTER) {
1104
      const int nClassId = psLayer->numclasses - 1;
2✔
1105
      if (nClassId >= 0) {
2✔
1106
        const int nStyleId = psLayer->_class[nClassId]->numstyles - 1;
2✔
1107
        if (nStyleId >= 0) {
2✔
1108
          styleObj *psStyle = psLayer->_class[nClassId]->styles[nStyleId];
2✔
1109
          msStyleSetGeomTransform(psStyle, "centroid");
2✔
1110
        }
1111
      }
1112
    }
1113
    nSymbolizer++;
65✔
1114
  }
1115

1116
  /* Text symbolizer */
1117
  /* ==================================================================== */
1118
  /*      For text symbolizer, here is how it is translated into          */
1119
  /*      mapserver classes :                                             */
1120
  /*        - If there are other symbolizers(line, polygon, symbol),      */
1121
  /*      the label object created will be created in the same class      */
1122
  /*      (the last class) as the  symbolizer. This allows to have for    */
1123
  /*      example of point layer with labels.                             */
1124
  /*        - If there are no other symbolizers, a new class will be      */
1125
  /*      created to contain the label object.                            */
1126
  /* ==================================================================== */
1127
  if (psLayer->type == MS_LAYER_LINE || psLayer->type == MS_LAYER_POLYGON)
363✔
1128
    nSymbolizer++;
280✔
1129
  LOOP_ON_CHILD_ELEMENT(psRoot, psTextSymbolizer, "TextSymbolizer") {
418✔
1130
    if (nSymbolizer == 0)
55✔
1131
      psLayer->type = MS_LAYER_POINT;
4✔
1132
    msSLDParseTextSymbolizer(psTextSymbolizer, psLayer, nSymbolizer > 0,
55✔
1133
                             pszUserStyleName);
1134
  }
1135

1136
  return MS_SUCCESS;
1137
}
1138

1139
/************************************************************************/
1140
/*                            getClassId()                              */
1141
/************************************************************************/
1142

1143
static int getClassId(layerObj *psLayer, int bNewClass,
355✔
1144
                      const char *pszUserStyleName) {
1145
  int nClassId;
1146
  if (bNewClass || psLayer->numclasses <= 0) {
355✔
1147
    if (msGrowLayerClasses(psLayer) == NULL)
342✔
1148
      return -1;
1149
    initClass(psLayer->_class[psLayer->numclasses]);
342✔
1150
    nClassId = psLayer->numclasses;
342✔
1151
    if (pszUserStyleName)
342✔
1152
      psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
146✔
1153
    psLayer->numclasses++;
342✔
1154
  } else {
1155
    nClassId = psLayer->numclasses - 1;
13✔
1156
  }
1157
  return nClassId;
1158
}
1159

1160
/************************************************************************/
1161
/*                      msSLDParseUomAttribute()                        */
1162
/************************************************************************/
1163

1164
int msSLDParseUomAttribute(CPLXMLNode *node, enum MS_UNITS *sizeunits) {
355✔
1165
  const struct {
1166
    enum MS_UNITS unit;
1167
    const char *const values[10];
1168
  } known_uoms[] = {
355✔
1169
      {MS_INCHES, {"inch", "inches", NULL}},
1170
      {MS_FEET,
1171
       {"foot", "feet", "http://www.opengeospatial.org/se/units/foot", NULL}},
1172
      {MS_MILES, {"mile", "miles", NULL}},
1173
      {MS_METERS,
1174
       {"meter", "meters", "metre", "metres",
1175
        "http://www.opengeospatial.org/se/units/metre", NULL}},
1176
      {MS_KILOMETERS,
1177
       {"kilometer", "kilometers", "kilometre", "kilometres", NULL}},
1178
      {MS_DD, {"dd", NULL}},
1179
      {MS_PIXELS,
1180
       {"pixel", "pixels", "px", "http://www.opengeospatial.org/se/units/pixel",
1181
        NULL}},
1182
      {MS_PERCENTAGES,
1183
       {"percent", "percents", "percentage", "percentages", NULL}},
1184
      {MS_NAUTICALMILES,
1185
       {"nauticalmile", "nauticalmiles", "nautical_mile", "nautical_miles",
1186
        NULL}},
1187
      {MS_INCHES, {NULL}}};
1188

1189
  const char *uom = CPLGetXMLValue(node, "uom", NULL);
355✔
1190
  if (uom) {
355✔
1191
    for (int i = 0; known_uoms[i].values[0]; i++)
19✔
1192
      for (int j = 0; known_uoms[i].values[j]; j++)
71✔
1193
        if (strcmp(uom, known_uoms[i].values[j]) == 0) {
56✔
1194
          // Match found
1195
          *sizeunits = known_uoms[i].unit;
4✔
1196
          return MS_SUCCESS;
4✔
1197
        }
1198
    // No match was found
1199
    return MS_FAILURE;
1200
  }
1201
  // No uom was found
1202
  *sizeunits = MS_PIXELS;
351✔
1203
  return MS_SUCCESS;
351✔
1204
}
1205

1206
/************************************************************************/
1207
/*                 msSLDParseLineSymbolizer()                           */
1208
/*                                                                      */
1209
/*      Parses the LineSymbolizer rule and creates a class in the       */
1210
/*      layer.                                                          */
1211
/*                                                                      */
1212
/*      <xs:element name="LineSymbolizer">                              */
1213
/*      <xs:complexType>                                                */
1214
/*      <xs:sequence>                                                   */
1215
/*      <xs:element ref="sld:Geometry" minOccurs="0"/>                  */
1216
/*      <xs:element ref="sld:Stroke" minOccurs="0"/>                    */
1217
/*      </xs:sequence>                                                  */
1218
/*      </xs:complexType>                                               */
1219
/*      </xs:element>                                                   */
1220
/*                                                                      */
1221
/*      <xs:element name="Stroke">                                      */
1222
/*      <xs:complexType>                                                */
1223
/*      <xs:sequence>                                                   */
1224
/*      <xs:choice minOccurs="0">                                       */
1225
/*      <xs:element ref="sld:GraphicFill"/>                             */
1226
/*      <xs:element ref="sld:GraphicStroke"/>                           */
1227
/*      </xs:choice>                                                    */
1228
/*      <xs:element ref="sld:CssParameter" minOccurs="0"                */
1229
/*      maxOccurs="unbounded"/>                                         */
1230
/*      </xs:sequence>                                                  */
1231
/*      </xs:complexType>                                               */
1232
/*      </xs:element>                                                   */
1233
/*                                                                      */
1234
/*      Example of a rule :                                             */
1235
/*      ...                                                             */
1236
/*      <Rule>                                                          */
1237
/*      <LineSymbolizer>                                                */
1238
/*      <Geometry>                                                      */
1239
/*      <ogc:PropertyName>center-line</ogc:PropertyName>                */
1240
/*      </Geometry>                                                     */
1241
/*      <Stroke>                                                        */
1242
/*      <CssParameter name="stroke">#0000ff</CssParameter>              */
1243
/*      <CssParameter name="stroke-width">5.0</CssParameter>            */
1244
/*      <CssParameter name="stroke-dasharray">10.0 5 5 10</CssParameter>*/
1245
/*      </Stroke>                                                       */
1246
/*      </LineSymbolizer>                                               */
1247
/*      </Rule>                                                         */
1248
/*       ...                                                            */
1249
/************************************************************************/
1250
int msSLDParseLineSymbolizer(CPLXMLNode *psRoot, layerObj *psLayer,
250✔
1251
                             int bNewClass, const char *pszUserStyleName) {
1252
  CPLXMLNode *psStroke = NULL, *psOffset = NULL;
1253

1254
  if (!psRoot || !psLayer)
250✔
1255
    return MS_FAILURE;
1256

1257
  // Get uom if any, defaults to MS_PIXELS
1258
  enum MS_UNITS sizeunits = MS_PIXELS;
250✔
1259
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
250✔
1260
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
×
1261
               "msSLDParsePolygonSymbolizer()");
1262
    return MS_FAILURE;
×
1263
  }
1264

1265
  psStroke = CPLGetXMLNode(psRoot, "Stroke");
250✔
1266
  if (psStroke) {
250✔
1267
    int nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
250✔
1268
    if (nClassId < 0)
250✔
1269
      return MS_FAILURE;
1270

1271
    const int iStyle = psLayer->_class[nClassId]->numstyles;
250✔
1272
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
250✔
1273
    psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
250✔
1274

1275
    msSLDParseStroke(psStroke, psLayer->_class[nClassId]->styles[iStyle],
250✔
1276
                     psLayer->map, 0);
1277

1278
    /*parse PerpendicularOffset SLD 1.1.10*/
1279
    psOffset = CPLGetXMLNode(psRoot, "PerpendicularOffset");
250✔
1280
    if (psOffset && psOffset->psChild && psOffset->psChild->pszValue) {
250✔
1281
      psLayer->_class[nClassId]->styles[iStyle]->offsetx =
×
1282
          atoi(psOffset->psChild->pszValue);
1283
      psLayer->_class[nClassId]->styles[iStyle]->offsety =
×
1284
          MS_STYLE_SINGLE_SIDED_OFFSET;
1285
    }
1286
  }
1287

1288
  return MS_SUCCESS;
1289
}
1290

1291
/************************************************************************/
1292
/*           void msSLDParseStroke(CPLXMLNode *psStroke, styleObj       */
1293
/*      *psStyle, int iColorParam)                                      */
1294
/*                                                                      */
1295
/*      Parse Stroke content into a style object.                       */
1296
/*      The iColorParm is used to indicate which color object to use    */
1297
/*      :                                                               */
1298
/*        0 : for color                                                 */
1299
/*        1 : outlinecolor                                              */
1300
/************************************************************************/
1301
int msSLDParseStroke(CPLXMLNode *psStroke, styleObj *psStyle, mapObj *map,
287✔
1302
                     int iColorParam) {
1303
  CPLXMLNode *psCssParam = NULL, *psGraphicFill = NULL;
1304
  char *psStrkName = NULL;
1305
  char *pszDashValue = NULL;
1306

1307
  if (!psStroke || !psStyle)
287✔
1308
    return MS_FAILURE;
1309

1310
  /* parse css parameters */
1311
  psCssParam = CPLGetXMLNode(psStroke, "CssParameter");
287✔
1312
  /*sld 1.1 used SvgParameter*/
1313
  if (psCssParam == NULL)
287✔
1314
    psCssParam = CPLGetXMLNode(psStroke, "SvgParameter");
215✔
1315

1316
  while (psCssParam && psCssParam->pszValue &&
1,041✔
1317
         (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
754✔
1318
          strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
613✔
1319
    psStrkName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
754✔
1320

1321
    if (psStrkName) {
754✔
1322
      if (strcasecmp(psStrkName, "stroke") == 0) {
754✔
1323
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
283✔
1324
          switch (iColorParam) {
283✔
1325
          case 0:
246✔
1326
            msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
246✔
1327
                                    MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1328
            break;
246✔
1329
          case 1:
37✔
1330
            msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
37✔
1331
                                    MS_STYLE_BINDING_OUTLINECOLOR,
1332
                                    MS_OBJ_STYLE);
1333
            break;
37✔
1334
          }
1335
        }
1336
      } else if (strcasecmp(psStrkName, "stroke-width") == 0) {
471✔
1337
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
278✔
1338
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
278✔
1339
                                  MS_STYLE_BINDING_WIDTH, MS_OBJ_STYLE);
1340
        }
1341
      } else if (strcasecmp(psStrkName, "stroke-dasharray") == 0) {
193✔
1342
        if (psCssParam->psChild && psCssParam->psChild->psNext &&
2✔
1343
            psCssParam->psChild->psNext->pszValue) {
2✔
1344
          int nDash = 0, i;
2✔
1345
          char **aszValues = NULL;
1346
          int nMaxDash;
1347
          if (pszDashValue)
2✔
1348
            free(pszDashValue); /* free previous if multiple stroke-dasharray
×
1349
                                   attributes were found */
1350
          pszDashValue = msStrdup(psCssParam->psChild->psNext->pszValue);
2✔
1351
          aszValues = msStringSplit(pszDashValue, ' ', &nDash);
2✔
1352
          if (nDash > 0) {
2✔
1353
            nMaxDash = nDash;
1354
            if (nDash > MS_MAXPATTERNLENGTH)
1355
              nMaxDash = MS_MAXPATTERNLENGTH;
1356

1357
            psStyle->patternlength = nMaxDash;
2✔
1358
            for (i = 0; i < nMaxDash; i++)
6✔
1359
              psStyle->pattern[i] = atof(aszValues[i]);
4✔
1360

1361
            psStyle->linecap = MS_CJC_BUTT;
2✔
1362
          }
1363
          msFreeCharArray(aszValues, nDash);
2✔
1364
        }
1365
      } else if (strcasecmp(psStrkName, "stroke-opacity") == 0) {
191✔
1366
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
191✔
1367
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
191✔
1368
                                  MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1369
        }
1370
      }
1371
    }
1372
    psCssParam = psCssParam->psNext;
754✔
1373
  }
1374

1375
  /* parse graphic fill or stroke */
1376
  /* graphic fill and graphic stroke pare parsed the same way :  */
1377
  /* TODO : It seems inconsistent to me since the only difference */
1378
  /* between them seems to be fill (fill) or not fill (stroke). And */
1379
  /* then again the fill parameter can be used inside both elements. */
1380
  psGraphicFill = CPLGetXMLNode(psStroke, "GraphicFill");
287✔
1381
  if (psGraphicFill)
287✔
1382
    msSLDParseGraphicFillOrStroke(psGraphicFill, pszDashValue, psStyle, map);
×
1383
  psGraphicFill = CPLGetXMLNode(psStroke, "GraphicStroke");
287✔
1384
  if (psGraphicFill)
287✔
1385
    msSLDParseGraphicFillOrStroke(psGraphicFill, pszDashValue, psStyle, map);
4✔
1386

1387
  if (pszDashValue)
287✔
1388
    free(pszDashValue);
2✔
1389

1390
  return MS_SUCCESS;
1391
}
1392

1393
/************************************************************************/
1394
/*  int msSLDParseOgcExpression(CPLXMLNode *psRoot, styleObj *psStyle,  */
1395
/*                              enum MS_STYLE_BINDING_ENUM binding)     */
1396
/*                                                                      */
1397
/*              Parse an OGC expression in a <SvgParameter>             */
1398
/************************************************************************/
1399
int msSLDParseOgcExpression(CPLXMLNode *psRoot, void *psObj, int binding,
1,478✔
1400
                            enum objType objtype) {
1401
  int status = MS_FAILURE;
1402
  const char *ops = "Add+Sub-Mul*Div/";
1403
  styleObj *psStyle = static_cast<styleObj *>(psObj);
1404
  labelObj *psLabel = static_cast<labelObj *>(psObj);
1405
  int lbinding;
1406
  expressionObj *exprBindings;
1407
  int *nexprbindings;
1408
  enum { MS_STYLE_BASE = 0, MS_LABEL_BASE = 100 };
1409

1410
  switch (objtype) {
1,478✔
1411
  case MS_OBJ_STYLE:
1,256✔
1412
    lbinding = binding + MS_STYLE_BASE;
1413
    exprBindings = psStyle->exprBindings;
1,256✔
1414
    nexprbindings = &psStyle->nexprbindings;
1,256✔
1415
    break;
1,256✔
1416
  case MS_OBJ_LABEL:
222✔
1417
    lbinding = binding + MS_LABEL_BASE;
222✔
1418
    exprBindings = psLabel->exprBindings;
222✔
1419
    nexprbindings = &psLabel->nexprbindings;
222✔
1420
    break;
222✔
1421
  default:
1422
    return MS_FAILURE;
1423
    break;
1424
  }
1425

1426
  switch (psRoot->eType) {
1,478✔
1427
  case CXT_Text:
1,360✔
1428
    // Parse a raw value
1429
    {
1430
      msStringBuffer *literal = msStringBufferAlloc();
1,360✔
1431
      msStringBufferAppend(literal, "(");
1,360✔
1432
      msStringBufferAppend(literal, psRoot->pszValue);
1,360✔
1433
      msStringBufferAppend(literal, ")");
1,360✔
1434
      msFreeExpression(&(exprBindings[binding]));
1,360✔
1435
      msInitExpression(&(exprBindings[binding]));
1,360✔
1436
      exprBindings[binding].string =
1,360✔
1437
          msStringBufferReleaseStringAndFree(literal);
1,360✔
1438
      exprBindings[binding].type = MS_STRING;
1,360✔
1439
    }
1440
    switch (lbinding) {
1441
    case MS_STYLE_BASE + MS_STYLE_BINDING_OFFSET_X:
36✔
1442
      psStyle->offsetx = atoi(psRoot->pszValue);
36✔
1443
      status = MS_SUCCESS;
1444
      break;
36✔
1445
    case MS_STYLE_BASE + MS_STYLE_BINDING_OFFSET_Y:
36✔
1446
      psStyle->offsety = atoi(psRoot->pszValue);
36✔
1447
      status = MS_SUCCESS;
1448
      break;
36✔
1449
    case MS_STYLE_BASE + MS_STYLE_BINDING_ANGLE:
35✔
1450
      psStyle->angle = atof(psRoot->pszValue);
35✔
1451
      status = MS_SUCCESS;
1452
      break;
35✔
1453
    case MS_STYLE_BASE + MS_STYLE_BINDING_SIZE:
72✔
1454
      psStyle->size = atof(psRoot->pszValue);
72✔
1455
      status = MS_SUCCESS;
1456
      break;
72✔
1457
    case MS_STYLE_BASE + MS_STYLE_BINDING_WIDTH:
316✔
1458
      psStyle->width = atof(psRoot->pszValue);
316✔
1459
      status = MS_SUCCESS;
1460
      break;
316✔
1461
    case MS_STYLE_BASE + MS_STYLE_BINDING_OPACITY:
244✔
1462
      psStyle->opacity = atof(psRoot->pszValue) * 100;
244✔
1463
      status = MS_SUCCESS;
1464
      // Apply opacity as the alpha channel color(s)
1465
      if (psStyle->opacity < 100) {
244✔
1466
        int alpha = MS_NINT(psStyle->opacity * 2.55);
239✔
1467
        psStyle->color.alpha = alpha;
239✔
1468
        psStyle->outlinecolor.alpha = alpha;
239✔
1469
        psStyle->mincolor.alpha = alpha;
239✔
1470
        psStyle->maxcolor.alpha = alpha;
239✔
1471
      }
1472
      break;
1473
    case MS_STYLE_BASE + MS_STYLE_BINDING_COLOR:
347✔
1474
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
347✔
1475
        psStyle->color.red = msHexToInt(psRoot->pszValue + 1);
347✔
1476
        psStyle->color.green = msHexToInt(psRoot->pszValue + 3);
347✔
1477
        psStyle->color.blue = msHexToInt(psRoot->pszValue + 5);
347✔
1478
        status = MS_SUCCESS;
1479
      }
1480
      break;
1481
    case MS_STYLE_BASE + MS_STYLE_BINDING_OUTLINECOLOR:
74✔
1482
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
74✔
1483
        psStyle->outlinecolor.red = msHexToInt(psRoot->pszValue + 1);
74✔
1484
        psStyle->outlinecolor.green = msHexToInt(psRoot->pszValue + 3);
74✔
1485
        psStyle->outlinecolor.blue = msHexToInt(psRoot->pszValue + 5);
74✔
1486
        status = MS_SUCCESS;
1487
      }
1488
      break;
1489

1490
    case MS_LABEL_BASE + MS_LABEL_BINDING_SIZE:
56✔
1491
      psLabel->size = atof(psRoot->pszValue);
56✔
1492
      if (psLabel->size <= 0.0) {
56✔
1493
        psLabel->size = 10.0;
×
1494
      }
1495
      status = MS_SUCCESS;
1496
      break;
1497
    case MS_LABEL_BASE + MS_LABEL_BINDING_ANGLE:
50✔
1498
      psLabel->angle = atof(psRoot->pszValue);
50✔
1499
      status = MS_SUCCESS;
1500
      break;
50✔
1501
    case MS_LABEL_BASE + MS_LABEL_BINDING_COLOR:
54✔
1502
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
54✔
1503
        psLabel->color.red = msHexToInt(psRoot->pszValue + 1);
54✔
1504
        psLabel->color.green = msHexToInt(psRoot->pszValue + 3);
54✔
1505
        psLabel->color.blue = msHexToInt(psRoot->pszValue + 5);
54✔
1506
        status = MS_SUCCESS;
1507
      }
1508
      break;
1509
    case MS_LABEL_BASE + MS_LABEL_BINDING_OUTLINECOLOR:
40✔
1510
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
40✔
1511
        psLabel->outlinecolor.red = msHexToInt(psRoot->pszValue + 1);
40✔
1512
        psLabel->outlinecolor.green = msHexToInt(psRoot->pszValue + 3);
40✔
1513
        psLabel->outlinecolor.blue = msHexToInt(psRoot->pszValue + 5);
40✔
1514
        status = MS_SUCCESS;
1515
      }
1516
      break;
1517
    default:
1518
      break;
1519
    }
1520
    break;
1521
  case CXT_Element:
118✔
1522
    if (strcasecmp(psRoot->pszValue, "Literal") == 0 && psRoot->psChild) {
118✔
1523
      // Parse a <ogc:Literal> element
1524
      status =
52✔
1525
          msSLDParseOgcExpression(psRoot->psChild, psObj, binding, objtype);
52✔
1526
    } else if (strcasecmp(psRoot->pszValue, "PropertyName") == 0 &&
66✔
1527
               psRoot->psChild) {
37✔
1528
      // Parse a <ogc:PropertyName> element
1529
      msStringBuffer *property = msStringBufferAlloc();
37✔
1530
      const char *strDelim = "";
1531

1532
      switch (lbinding) {
1533
      case MS_STYLE_BASE + MS_STYLE_BINDING_COLOR:
7✔
1534
      case MS_STYLE_BASE + MS_STYLE_BINDING_OUTLINECOLOR:
1535
      case MS_LABEL_BASE + MS_LABEL_BINDING_COLOR:
1536
      case MS_LABEL_BASE + MS_LABEL_BINDING_OUTLINECOLOR:
1537
        strDelim = "\"";
1538
        /* FALLTHROUGH */
1539
      default:
37✔
1540
        msStringBufferAppend(property, strDelim);
37✔
1541
        msStringBufferAppend(property, "[");
37✔
1542
        msStringBufferAppend(property, psRoot->psChild->pszValue);
37✔
1543
        msStringBufferAppend(property, "]");
37✔
1544
        msStringBufferAppend(property, strDelim);
37✔
1545
        msInitExpression(&(exprBindings[binding]));
37✔
1546
        exprBindings[binding].string =
37✔
1547
            msStringBufferReleaseStringAndFree(property);
37✔
1548
        exprBindings[binding].type = MS_EXPRESSION;
37✔
1549
        (*nexprbindings)++;
37✔
1550
        break;
1551
      }
1552
      status = MS_SUCCESS;
37✔
1553
    } else if (strcasecmp(psRoot->pszValue, "Function") == 0 &&
30✔
1554
               psRoot->psChild && CPLGetXMLValue(psRoot, "name", NULL) &&
29✔
1555
               psRoot->psChild->psNext) {
1✔
1556
      // Parse a <ogc:Function> element
1557
      msStringBuffer *function = msStringBufferAlloc();
1✔
1558

1559
      // Parse function name
1560
      const char *funcname = CPLGetXMLValue(psRoot, "name", NULL);
1✔
1561
      msStringBufferAppend(function, funcname);
1✔
1562
      msStringBufferAppend(function, "(");
1✔
1563
      msInitExpression(&(exprBindings[binding]));
1✔
1564

1565
      // Parse arguments
1566
      const char *sep = "";
1567
      for (CPLXMLNode *argument = psRoot->psChild->psNext; argument;
2✔
1568
           argument = argument->psNext) {
1✔
1569
        status = msSLDParseOgcExpression(argument, psObj, binding, objtype);
1✔
1570
        if (status != MS_SUCCESS)
1✔
1571
          break;
1572
        msStringBufferAppend(function, sep);
1✔
1573
        msStringBufferAppend(function, exprBindings[binding].string);
1✔
1574
        msReplaceFreeableStr(&(exprBindings[binding].string), nullptr);
1✔
1575
        msInitExpression(&(exprBindings[binding]));
1✔
1576
        sep = ",";
1577
      }
1578
      msStringBufferAppend(function, ")");
1✔
1579
      exprBindings[binding].string =
1✔
1580
          msStringBufferReleaseStringAndFree(function);
1✔
1581
      exprBindings[binding].type = MS_EXPRESSION;
1✔
1582
      (*nexprbindings)++;
1✔
1583
      status = MS_SUCCESS;
1584
    } else if (strstr(ops, psRoot->pszValue) && psRoot->psChild &&
28✔
1585
               psRoot->psChild->psNext) {
28✔
1586
      // Parse an arithmetic element <ogc:Add>, <ogc:Sub>, <ogc:Mul>, <ogc:Div>
1587
      const char op[2] = {*(strstr(ops, psRoot->pszValue) + 3), '\0'};
28✔
1588
      msStringBuffer *expression = msStringBufferAlloc();
28✔
1589

1590
      // Parse first operand
1591
      msStringBufferAppend(expression, "(");
28✔
1592
      msInitExpression(&(exprBindings[binding]));
28✔
1593
      status =
1594
          msSLDParseOgcExpression(psRoot->psChild, psObj, binding, objtype);
28✔
1595

1596
      // Parse second operand
1597
      if (status == MS_SUCCESS) {
28✔
1598
        msStringBufferAppend(expression, exprBindings[binding].string);
28✔
1599
        msStringBufferAppend(expression, op);
28✔
1600
        msReplaceFreeableStr(&(exprBindings[binding].string), nullptr);
28✔
1601
        msInitExpression(&(exprBindings[binding]));
28✔
1602
        status = msSLDParseOgcExpression(psRoot->psChild->psNext, psObj,
28✔
1603
                                         binding, objtype);
1604
        if (status == MS_SUCCESS && exprBindings[binding].string) {
28✔
1605
          msStringBufferAppend(expression, exprBindings[binding].string);
28✔
1606
          msStringBufferAppend(expression, ")");
28✔
1607
          msReplaceFreeableStr(&(exprBindings[binding].string),
28✔
1608
                               msStringBufferReleaseStringAndFree(expression));
1609
          expression = NULL;
1610
          exprBindings[binding].type = MS_EXPRESSION;
28✔
1611
          (*nexprbindings)++;
28✔
1612
        }
1613
      }
1614
      if (expression != NULL) {
×
1615
        msStringBufferFree(expression);
×
1616
        msInitExpression(&(exprBindings[binding]));
×
1617
      }
1618
    }
1619
    break;
1620
  default:
1621
    break;
1622
  }
1623

1624
  return status;
1625
}
1626

1627
/************************************************************************/
1628
/*                      msSLDParsePolygonSymbolizer()                   */
1629
/*                                                                      */
1630
/*      <xs:element name="PolygonSymbolizer">                           */
1631
/*      <xs:complexType>                                                */
1632
/*      <xs:sequence>                                                   */
1633
/*      <xs:element ref="sld:Geometry" minOccurs="0"/>                  */
1634
/*      <xs:element ref="sld:Fill" minOccurs="0"/>                      */
1635
/*      <xs:element ref="sld:Stroke" minOccurs="0"/>                    */
1636
/*      </xs:sequence>                                                  */
1637
/*      </xs:complexType>                                               */
1638
/*      </xs:element>                                                   */
1639
/*                                                                      */
1640
/*                                                                      */
1641
/*      <xs:element name="Fill">                                        */
1642
/*      <xs:complexType>                                                */
1643
/*      <xs:sequence>                                                   */
1644
/*      <xs:element ref="sld:GraphicFill" minOccurs="0"/>               */
1645
/*      <xs:element ref="sld:CssParameter" minOccurs="0"                */
1646
/*      maxOccurs="unbounded"/>                                         */
1647
/*      </xs:sequence>                                                  */
1648
/*      </xs:complexType>                                               */
1649
/*      </xs:element>                                                   */
1650
/*                                                                      */
1651
/*      Here, the CssParameter names are fill instead of stroke and     */
1652
/*      fill-opacity instead of stroke-opacity. None of the other
1653
 * CssParameters*/
1654
/*      in Stroke are available for filling and the default value for the fill
1655
 * color in this context is 50% gray (value #808080).*/
1656
/*                                                                      */
1657
/*                                                                      */
1658
/*      <xs:element name="GraphicFill">                                 */
1659
/*      <xs:complexType>                                                */
1660
/*      <xs:sequence>                                                   */
1661
/*      <xs:element ref="sld:Graphic"/>                                 */
1662
/*      </xs:sequence>                                                  */
1663
/*      </xs:complexType>                                               */
1664
/*      </xs:element>                                                   */
1665
/*                                                                      */
1666
/*                                                                      */
1667
/*      <xs:element name="Graphic">                                     */
1668
/*      <xs:complexType>                                                */
1669
/*      <xs:sequence>                                                   */
1670
/*      <xs:choice minOccurs="0" maxOccurs="unbounded">                 */
1671
/*      <xs:element ref="sld:ExternalGraphic"/>                         */
1672
/*      <xs:element ref="sld:Mark"/>                                    */
1673
/*      </xs:choice>                                                    */
1674
/*      <xs:sequence>                                                   */
1675
/*      <xs:element ref="sld:Opacity" minOccurs="0"/>                   */
1676
/*      <xs:element ref="sld:Size" minOccurs="0"/>                      */
1677
/*      <xs:element ref="sld:Rotation" minOccurs="0"/>                  */
1678
/*      </xs:sequence>                                                  */
1679
/*      </xs:sequence>                                                  */
1680
/*      </xs:complexType>                                               */
1681
/*      </xs:element>                                                   */
1682
/*                                                                      */
1683
/*      The default if neither an ExternalGraphic nor a Mark is specified is to
1684
 * use the default*/
1685
/*      mark of a square with a 50%-gray fill and a black outline, with a size
1686
 * of 6 pixels.*/
1687
/*                                                                      */
1688
/*                                                                      */
1689
/*      <xs:element name="Mark">                                        */
1690
/*      <xs:complexType>                                                */
1691
/*      <xs:sequence>                                                   */
1692
/*      <xs:element ref="sld:WellKnownName" minOccurs="0"/>             */
1693
/*      <xs:element ref="sld:Fill" minOccurs="0"/>                      */
1694
/*      <xs:element ref="sld:Stroke" minOccurs="0"/>                    */
1695
/*      </xs:sequence>                                                  */
1696
/*      </xs:complexType>                                               */
1697
/*      <xs:element name="WellKnownName" type="xs:string"/>             */
1698
/*                                                                      */
1699
/*      The WellKnownName element gives the well-known name of the shape of the
1700
 * mark.*/
1701
/*      Allowed values include at least square, circle, triangle, star, cross,*/
1702
/*      and x, though map servers may draw a different symbol instead if they
1703
 * don't have a*/
1704
/*      shape for all of these. The default WellKnownName is square. Renderings
1705
 * of these*/
1706
/*      marks may be made solid or hollow depending on Fill and Stroke
1707
 * elements.*/
1708
/*                                                                      */
1709
/************************************************************************/
1710
int msSLDParsePolygonSymbolizer(CPLXMLNode *psRoot, layerObj *psLayer,
40✔
1711
                                int bNewClass, const char *pszUserStyleName) {
1712
  CPLXMLNode *psFill, *psStroke;
1713
  CPLXMLNode *psDisplacement = NULL, *psDisplacementX = NULL,
1714
             *psDisplacementY = NULL;
1715
  int nOffsetX = -1, nOffsetY = -1;
1716

1717
  if (!psRoot || !psLayer)
40✔
1718
    return MS_FAILURE;
1719

1720
  // Get uom if any, defaults to MS_PIXELS
1721
  enum MS_UNITS sizeunits = MS_PIXELS;
40✔
1722
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
40✔
1723
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
×
1724
               "msSLDParsePolygonSymbolizer()");
1725
    return MS_FAILURE;
×
1726
  }
1727

1728
  /*parse displacement for SLD 1.1.0*/
1729
  psDisplacement = CPLGetXMLNode(psRoot, "Displacement");
40✔
1730
  if (psDisplacement) {
40✔
1731
    psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
×
1732
    psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
×
1733
    /* psCssParam->psChild->psNext->pszValue) */
1734
    if (psDisplacementX && psDisplacementX->psChild &&
×
1735
        psDisplacementX->psChild->pszValue && psDisplacementY &&
×
1736
        psDisplacementY->psChild && psDisplacementY->psChild->pszValue) {
×
1737
      nOffsetX = atoi(psDisplacementX->psChild->pszValue);
1738
      nOffsetY = atoi(psDisplacementY->psChild->pszValue);
×
1739
    }
1740
  }
1741

1742
  psFill = CPLGetXMLNode(psRoot, "Fill");
40✔
1743
  if (psFill) {
40✔
1744
    const int nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
40✔
1745
    if (nClassId < 0)
40✔
1746
      return MS_FAILURE;
1747

1748
    const int iStyle = psLayer->_class[nClassId]->numstyles;
40✔
1749
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
40✔
1750
    psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
40✔
1751

1752
    msSLDParsePolygonFill(psFill, psLayer->_class[nClassId]->styles[iStyle],
40✔
1753
                          psLayer->map);
1754

1755
    if (nOffsetX > 0 && nOffsetY > 0) {
40✔
1756
      psLayer->_class[nClassId]->styles[iStyle]->offsetx = nOffsetX;
×
1757
      psLayer->_class[nClassId]->styles[iStyle]->offsety = nOffsetY;
×
1758
    }
1759
  }
1760
  /* stroke which corresponds to the outline in mapserver */
1761
  /* is drawn after the fill */
1762
  psStroke = CPLGetXMLNode(psRoot, "Stroke");
40✔
1763
  if (psStroke) {
40✔
1764
    /* -------------------------------------------------------------------- */
1765
    /*      there was a fill so add a style to the last class created       */
1766
    /*      by the fill                                                     */
1767
    /* -------------------------------------------------------------------- */
1768
    int nClassId;
1769
    int iStyle;
1770
    if (psFill && psLayer->numclasses > 0) {
37✔
1771
      nClassId = psLayer->numclasses - 1;
37✔
1772
      iStyle = psLayer->_class[nClassId]->numstyles;
37✔
1773
      msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
37✔
1774
      psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
37✔
1775
    } else {
1776
      nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
×
1777
      if (nClassId < 0)
×
1778
        return MS_FAILURE;
1779

1780
      iStyle = psLayer->_class[nClassId]->numstyles;
×
1781
      msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
×
1782
      psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
×
1783
    }
1784
    msSLDParseStroke(psStroke, psLayer->_class[nClassId]->styles[iStyle],
37✔
1785
                     psLayer->map, 1);
1786

1787
    if (nOffsetX > 0 && nOffsetY > 0) {
37✔
1788
      psLayer->_class[nClassId]->styles[iStyle]->offsetx = nOffsetX;
×
1789
      psLayer->_class[nClassId]->styles[iStyle]->offsety = nOffsetY;
×
1790
    }
1791
  }
1792

1793
  return MS_SUCCESS;
1794
}
1795

1796
/************************************************************************/
1797
/*    void msSLDParsePolygonFill(CPLXMLNode *psFill, styleObj *psStyle, */
1798
/*                                 mapObj *map)                         */
1799
/*                                                                      */
1800
/*      Parse the Fill node for a polygon into a style.                 */
1801
/************************************************************************/
1802
int msSLDParsePolygonFill(CPLXMLNode *psFill, styleObj *psStyle, mapObj *map) {
40✔
1803
  CPLXMLNode *psCssParam, *psGraphicFill;
1804
  char *psFillName = NULL;
1805

1806
  if (!psFill || !psStyle || !map)
40✔
1807
    return MS_FAILURE;
1808

1809
  /* sets the default fill color defined in the spec #808080 */
1810
  psStyle->color.red = 128;
40✔
1811
  psStyle->color.green = 128;
40✔
1812
  psStyle->color.blue = 128;
40✔
1813

1814
  psCssParam = CPLGetXMLNode(psFill, "CssParameter");
40✔
1815
  /*sld 1.1 used SvgParameter*/
1816
  if (psCssParam == NULL)
40✔
1817
    psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
31✔
1818

1819
  while (psCssParam && psCssParam->pszValue &&
97✔
1820
         (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
60✔
1821
          strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
51✔
1822
    psFillName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
57✔
1823
    if (psFillName) {
57✔
1824
      if (strcasecmp(psFillName, "fill") == 0) {
57✔
1825
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
40✔
1826
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
40✔
1827
                                  MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1828
        }
1829
      } else if (strcasecmp(psFillName, "fill-opacity") == 0) {
17✔
1830
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
17✔
1831
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
17✔
1832
                                  MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1833
        }
1834
      }
1835
    }
1836
    psCssParam = psCssParam->psNext;
57✔
1837
  }
1838

1839
  /* graphic fill and graphic stroke pare parsed the same way :  */
1840
  /* TODO : It seems inconsistent to me since the only difference */
1841
  /* between them seems to be fill (fill) or not fill (stroke). And */
1842
  /* then again the fill parameter can be used inside both elements. */
1843
  psGraphicFill = CPLGetXMLNode(psFill, "GraphicFill");
40✔
1844
  if (psGraphicFill)
40✔
1845
    msSLDParseGraphicFillOrStroke(psGraphicFill, NULL, psStyle, map);
3✔
1846
  psGraphicFill = CPLGetXMLNode(psFill, "GraphicStroke");
40✔
1847
  if (psGraphicFill)
40✔
1848
    msSLDParseGraphicFillOrStroke(psGraphicFill, NULL, psStyle, map);
×
1849

1850
  return MS_SUCCESS;
1851
}
1852

1853
/************************************************************************/
1854
/*                      msSLDParseGraphicFillOrStroke                   */
1855
/*                                                                      */
1856
/*      Parse the GraphicFill Or GraphicStroke node : look for a        */
1857
/*      Marker symbol and set the style for that symbol.                */
1858
/************************************************************************/
1859
int msSLDParseGraphicFillOrStroke(CPLXMLNode *psRoot, char *pszDashValue_unused,
72✔
1860
                                  styleObj *psStyle, mapObj *map) {
1861
  (void)pszDashValue_unused;
1862
  CPLXMLNode *psCssParam, *psGraphic, *psExternalGraphic, *psMark, *psSize,
1863
      *psGap, *psInitialGap;
1864
  CPLXMLNode *psWellKnownName, *psStroke, *psFill;
1865
  CPLXMLNode *psDisplacement = NULL, *psDisplacementX = NULL,
1866
             *psDisplacementY = NULL;
1867
  CPLXMLNode *psOpacity = NULL, *psRotation = NULL;
1868
  char *psName = NULL, *psValue = NULL;
1869
  char *pszSymbolName = NULL;
1870
  int bFilled = 0;
1871

1872
  if (!psRoot || !psStyle || !map)
72✔
1873
    return MS_FAILURE;
1874
  /* ==================================================================== */
1875
  /*      This a definition taken from the specification (11.3.2) :       */
1876
  /*      Graphics can either be referenced from an external URL in a common
1877
   * format (such as*/
1878
  /*      GIF or SVG) or may be derived from a Mark. Multiple external URLs and
1879
   * marks may be*/
1880
  /*      referenced with the semantic that they all provide the equivalent
1881
   * graphic in different*/
1882
  /*      formats.                                                        */
1883
  /*                                                                      */
1884
  /*      For this reason, we only need to support one Mark and one       */
1885
  /*      ExtrnalGraphic ????                                             */
1886
  /* ==================================================================== */
1887
  psGraphic = CPLGetXMLNode(psRoot, "Graphic");
72✔
1888
  if (psGraphic) {
72✔
1889
    /* extract symbol size */
1890
    psSize = CPLGetXMLNode(psGraphic, "Size");
72✔
1891
    if (psSize && psSize->psChild) {
72✔
1892
      msSLDParseOgcExpression(psSize->psChild, psStyle, MS_STYLE_BINDING_SIZE,
72✔
1893
                              MS_OBJ_STYLE);
1894
    } else {
1895
      /*do not set a default for external symbols #2305*/
1896
      psExternalGraphic = CPLGetXMLNode(psGraphic, "ExternalGraphic");
×
1897
      if (!psExternalGraphic)
×
1898
        psStyle->size = 6; /* default value */
×
1899
    }
1900

1901
    /*SLD 1.1.0 extract opacity, rotation, displacement*/
1902
    psOpacity = CPLGetXMLNode(psGraphic, "Opacity");
72✔
1903
    if (psOpacity && psOpacity->psChild) {
72✔
1904
      msSLDParseOgcExpression(psOpacity->psChild, psStyle,
36✔
1905
                              MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1906
    }
1907

1908
    psRotation = CPLGetXMLNode(psGraphic, "Rotation");
72✔
1909
    if (psRotation && psRotation->psChild) {
72✔
1910
      msSLDParseOgcExpression(psRotation->psChild, psStyle,
36✔
1911
                              MS_STYLE_BINDING_ANGLE, MS_OBJ_STYLE);
1912
    }
1913
    psDisplacement = CPLGetXMLNode(psGraphic, "Displacement");
72✔
1914
    if (psDisplacement && psDisplacement->psChild) {
72✔
1915
      psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
36✔
1916
      if (psDisplacementX && psDisplacementX->psChild) {
36✔
1917
        msSLDParseOgcExpression(psDisplacementX->psChild, psStyle,
36✔
1918
                                MS_STYLE_BINDING_OFFSET_X, MS_OBJ_STYLE);
1919
      }
1920
      psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
36✔
1921
      if (psDisplacementY && psDisplacementY->psChild) {
36✔
1922
        msSLDParseOgcExpression(psDisplacementY->psChild, psStyle,
36✔
1923
                                MS_STYLE_BINDING_OFFSET_Y, MS_OBJ_STYLE);
1924
      }
1925
    }
1926
    /* extract symbol */
1927
    psMark = CPLGetXMLNode(psGraphic, "Mark");
72✔
1928
    if (psMark) {
72✔
1929
      pszSymbolName = NULL;
1930
      psWellKnownName = CPLGetXMLNode(psMark, "WellKnownName");
64✔
1931
      if (psWellKnownName && psWellKnownName->psChild &&
64✔
1932
          psWellKnownName->psChild->pszValue)
64✔
1933
        pszSymbolName = msStrdup(psWellKnownName->psChild->pszValue);
64✔
1934

1935
      /* default symbol is square */
1936

1937
      if (!pszSymbolName || !*pszSymbolName ||
64✔
1938
          (strcasecmp(pszSymbolName, "square") != 0 &&
64✔
1939
           strcasecmp(pszSymbolName, "circle") != 0 &&
64✔
1940
           strcasecmp(pszSymbolName, "triangle") != 0 &&
57✔
1941
           strcasecmp(pszSymbolName, "star") != 0 &&
57✔
1942
           strcasecmp(pszSymbolName, "cross") != 0 &&
×
1943
           strcasecmp(pszSymbolName, "x") != 0)) {
×
1944
        if (!pszSymbolName || !*pszSymbolName ||
×
1945
            msGetSymbolIndex(&map->symbolset, pszSymbolName, MS_FALSE) < 0) {
×
1946
          msFree(pszSymbolName);
×
1947
          pszSymbolName = msStrdup("square");
×
1948
        }
1949
      }
1950

1951
      /* check if the symbol should be filled or not */
1952
      psFill = CPLGetXMLNode(psMark, "Fill");
64✔
1953
      psStroke = CPLGetXMLNode(psMark, "Stroke");
64✔
1954

1955
      if (psFill || psStroke) {
64✔
1956
        if (psFill)
64✔
1957
          bFilled = 1;
1958
        else
1959
          bFilled = 0;
1960

1961
        if (psFill) {
64✔
1962
          psCssParam = CPLGetXMLNode(psFill, "CssParameter");
62✔
1963
          /*sld 1.1 used SvgParameter*/
1964
          if (psCssParam == NULL)
62✔
1965
            psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
39✔
1966

1967
          while (psCssParam && psCssParam->pszValue &&
160✔
1968
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
98✔
1969
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
75✔
1970
            psName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
98✔
1971
            if (psName && strcasecmp(psName, "fill") == 0) {
98✔
1972
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
62✔
1973
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
62✔
1974
                                        MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1975
              }
1976
            } else if (psName && strcasecmp(psName, "fill-opacity") == 0) {
36✔
1977
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
36✔
1978
                psValue = psCssParam->psChild->psNext->pszValue;
36✔
1979
                if (psValue) {
36✔
1980
                  psStyle->color.alpha = (int)(atof(psValue) * 255);
36✔
1981
                }
1982
              }
1983
            }
1984

1985
            psCssParam = psCssParam->psNext;
98✔
1986
          }
1987
        }
1988
        if (psStroke) {
64✔
1989
          psCssParam = CPLGetXMLNode(psStroke, "CssParameter");
41✔
1990
          /*sld 1.1 used SvgParameter*/
1991
          if (psCssParam == NULL)
41✔
1992
            psCssParam = CPLGetXMLNode(psStroke, "SvgParameter");
41✔
1993

1994
          while (psCssParam && psCssParam->pszValue &&
157✔
1995
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
116✔
1996
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
116✔
1997
            psName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
116✔
1998
            if (psName && strcasecmp(psName, "stroke") == 0) {
116✔
1999
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
41✔
2000
                if (bFilled) {
41✔
2001
                  msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
39✔
2002
                                          MS_STYLE_BINDING_OUTLINECOLOR,
2003
                                          MS_OBJ_STYLE);
2004
                } else {
2005
                  msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
2✔
2006
                                          MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
2007
                }
2008
              }
2009
            } else if (psName && strcasecmp(psName, "stroke-opacity") == 0) {
75✔
2010
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
36✔
2011
                psValue = psCssParam->psChild->psNext->pszValue;
36✔
2012
                if (psValue) {
36✔
2013
                  if (bFilled) {
36✔
2014
                    psStyle->outlinecolor.alpha = (int)(atof(psValue) * 255);
36✔
2015
                  } else {
2016
                    psStyle->color.alpha = (int)(atof(psValue) * 255);
×
2017
                  }
2018
                }
2019
              }
2020
            } else if (psName && strcasecmp(psName, "stroke-width") == 0) {
39✔
2021
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
39✔
2022
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
39✔
2023
                                        MS_STYLE_BINDING_WIDTH, MS_OBJ_STYLE);
2024
              }
2025
            }
2026

2027
            psCssParam = psCssParam->psNext;
116✔
2028
          }
2029
        }
2030
      }
2031
      /* set the default color if color is not not already set */
2032
      if ((psStyle->color.red < 0 || psStyle->color.green == -1 ||
64✔
2033
           psStyle->color.blue == -1) &&
63✔
2034
          (psStyle->outlinecolor.red == -1 ||
1✔
2035
           psStyle->outlinecolor.green == -1 ||
1✔
2036
           psStyle->outlinecolor.blue == -1)) {
1✔
2037
        psStyle->color.red = 128;
×
2038
        psStyle->color.green = 128;
×
2039
        psStyle->color.blue = 128;
×
2040
      }
2041

2042
      /* Get the corresponding symbol id  */
2043
      psStyle->symbol = msSLDGetMarkSymbol(map, pszSymbolName, bFilled);
64✔
2044
      if (psStyle->symbol > 0 && psStyle->symbol < map->symbolset.numsymbols)
64✔
2045
        psStyle->symbolname =
64✔
2046
            msStrdup(map->symbolset.symbol[psStyle->symbol]->name);
64✔
2047

2048
    } else {
2049
      psExternalGraphic = CPLGetXMLNode(psGraphic, "ExternalGraphic");
8✔
2050
      if (psExternalGraphic)
8✔
2051
        msSLDParseExternalGraphic(psExternalGraphic, psStyle, map);
8✔
2052
    }
2053
    msFree(pszSymbolName);
72✔
2054
  }
2055
  psGap = CPLGetXMLNode(psRoot, "Gap");
72✔
2056
  if (psGap && psGap->psChild && psGap->psChild->pszValue) {
72✔
2057
    psStyle->gap = atof(psGap->psChild->pszValue);
4✔
2058
  }
2059
  psInitialGap = CPLGetXMLNode(psRoot, "InitialGap");
72✔
2060
  if (psInitialGap && psInitialGap->psChild &&
72✔
2061
      psInitialGap->psChild->pszValue) {
4✔
2062
    psStyle->initialgap = atof(psInitialGap->psChild->pszValue);
4✔
2063
  }
2064

2065
  return MS_SUCCESS;
2066
}
2067

2068
/************************************************************************/
2069
/*                            msSLDGetMarkSymbol                        */
2070
/*                                                                      */
2071
/*      Get a Mark symbol using the name. Mark symbols can be           */
2072
/*      square, circle, triangle, star, cross, x.                       */
2073
/*      If the symbol does not exist add it to the symbol list.        */
2074
/************************************************************************/
2075
int msSLDGetMarkSymbol(mapObj *map, char *pszSymbolName, int bFilled) {
64✔
2076
  int nSymbolId = 0;
2077
  symbolObj *psSymbol = NULL;
2078

2079
  if (!map || !pszSymbolName)
64✔
2080
    return 0;
2081

2082
  if (strcasecmp(pszSymbolName, "square") == 0) {
64✔
2083
    if (bFilled)
×
2084
      nSymbolId = msGetSymbolIndex(&map->symbolset,
×
2085
                                   SLD_MARK_SYMBOL_SQUARE_FILLED, MS_FALSE);
2086
    else
2087
      nSymbolId =
2088
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_SQUARE, MS_FALSE);
×
2089
  } else if (strcasecmp(pszSymbolName, "circle") == 0) {
64✔
2090

2091
    if (bFilled)
7✔
2092
      nSymbolId = msGetSymbolIndex(&map->symbolset,
5✔
2093
                                   SLD_MARK_SYMBOL_CIRCLE_FILLED, MS_FALSE);
2094
    else
2095
      nSymbolId =
2096
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_CIRCLE, MS_FALSE);
2✔
2097
  } else if (strcasecmp(pszSymbolName, "triangle") == 0) {
57✔
2098

2099
    if (bFilled)
×
2100
      nSymbolId = msGetSymbolIndex(&map->symbolset,
×
2101
                                   SLD_MARK_SYMBOL_TRIANGLE_FILLED, MS_FALSE);
2102
    else
2103
      nSymbolId =
2104
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_TRIANGLE, MS_FALSE);
×
2105
  } else if (strcasecmp(pszSymbolName, "star") == 0) {
57✔
2106

2107
    if (bFilled)
57✔
2108
      nSymbolId = msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_STAR_FILLED,
57✔
2109
                                   MS_FALSE);
2110
    else
2111
      nSymbolId =
2112
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_STAR, MS_FALSE);
×
2113
  } else if (strcasecmp(pszSymbolName, "cross") == 0) {
×
2114

2115
    if (bFilled)
×
2116
      nSymbolId = msGetSymbolIndex(&map->symbolset,
×
2117
                                   SLD_MARK_SYMBOL_CROSS_FILLED, MS_FALSE);
2118
    else
2119
      nSymbolId =
2120
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_CROSS, MS_FALSE);
×
2121
  } else if (strcasecmp(pszSymbolName, "x") == 0) {
×
2122

2123
    if (bFilled)
×
2124
      nSymbolId =
2125
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_X_FILLED, MS_FALSE);
×
2126
    else
2127
      nSymbolId =
2128
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_X, MS_FALSE);
×
2129
  } else {
2130
    nSymbolId = msGetSymbolIndex(&map->symbolset, pszSymbolName, MS_FALSE);
×
2131
  }
2132

2133
  if (nSymbolId <= 0) {
64✔
2134
    if ((psSymbol = msGrowSymbolSet(&(map->symbolset))) == NULL)
60✔
2135
      return 0; /* returns 0 for no symbol */
2136

2137
    nSymbolId = map->symbolset.numsymbols;
60✔
2138
    map->symbolset.numsymbols++;
60✔
2139
    initSymbol(psSymbol);
60✔
2140
    psSymbol->inmapfile = MS_TRUE;
60✔
2141
    psSymbol->sizex = 1;
60✔
2142
    psSymbol->sizey = 1;
60✔
2143

2144
    if (strcasecmp(pszSymbolName, "square") == 0) {
60✔
2145
      if (bFilled)
×
2146
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_SQUARE_FILLED);
×
2147
      else
2148
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_SQUARE);
×
2149

2150
      psSymbol->type = MS_SYMBOL_VECTOR;
×
2151
      if (bFilled)
×
2152
        psSymbol->filled = MS_TRUE;
×
2153
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2154
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2155
      psSymbol->numpoints++;
×
2156
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2157
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2158
      psSymbol->numpoints++;
×
2159
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2160
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2161
      psSymbol->numpoints++;
×
2162
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2163
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2164
      psSymbol->numpoints++;
×
2165
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2166
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2167
      psSymbol->numpoints++;
×
2168
    } else if (strcasecmp(pszSymbolName, "circle") == 0) {
60✔
2169
      if (bFilled)
7✔
2170
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CIRCLE_FILLED);
5✔
2171
      else
2172
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CIRCLE);
2✔
2173

2174
      psSymbol->type = MS_SYMBOL_ELLIPSE;
7✔
2175
      if (bFilled)
7✔
2176
        psSymbol->filled = MS_TRUE;
5✔
2177

2178
      psSymbol->points[psSymbol->numpoints].x = 1;
7✔
2179
      psSymbol->points[psSymbol->numpoints].y = 1;
7✔
2180
      psSymbol->sizex = 1;
7✔
2181
      psSymbol->sizey = 1;
7✔
2182
      psSymbol->numpoints++;
7✔
2183
    } else if (strcasecmp(pszSymbolName, "triangle") == 0) {
53✔
2184
      if (bFilled)
×
2185
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_TRIANGLE_FILLED);
×
2186
      else
2187
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_TRIANGLE);
×
2188

2189
      psSymbol->type = MS_SYMBOL_VECTOR;
×
2190
      if (bFilled)
×
2191
        psSymbol->filled = MS_TRUE;
×
2192

2193
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2194
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2195
      psSymbol->numpoints++;
×
2196
      psSymbol->points[psSymbol->numpoints].x = 0.5;
×
2197
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2198
      psSymbol->numpoints++;
×
2199
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2200
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2201
      psSymbol->numpoints++;
×
2202
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2203
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2204
      psSymbol->numpoints++;
×
2205

2206
    } else if (strcasecmp(pszSymbolName, "star") == 0) {
53✔
2207
      if (bFilled)
53✔
2208
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_STAR_FILLED);
53✔
2209
      else
2210
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_STAR);
×
2211

2212
      psSymbol->type = MS_SYMBOL_VECTOR;
53✔
2213
      if (bFilled)
53✔
2214
        psSymbol->filled = MS_TRUE;
53✔
2215

2216
      psSymbol->points[psSymbol->numpoints].x = 0;
53✔
2217
      psSymbol->points[psSymbol->numpoints].y = 0.375;
53✔
2218
      psSymbol->numpoints++;
53✔
2219
      psSymbol->points[psSymbol->numpoints].x = 0.35;
53✔
2220
      psSymbol->points[psSymbol->numpoints].y = 0.375;
53✔
2221
      psSymbol->numpoints++;
53✔
2222
      psSymbol->points[psSymbol->numpoints].x = 0.5;
53✔
2223
      psSymbol->points[psSymbol->numpoints].y = 0;
53✔
2224
      psSymbol->numpoints++;
53✔
2225
      psSymbol->points[psSymbol->numpoints].x = 0.65;
53✔
2226
      psSymbol->points[psSymbol->numpoints].y = 0.375;
53✔
2227
      psSymbol->numpoints++;
53✔
2228
      psSymbol->points[psSymbol->numpoints].x = 1;
53✔
2229
      psSymbol->points[psSymbol->numpoints].y = 0.375;
53✔
2230
      psSymbol->numpoints++;
53✔
2231
      psSymbol->points[psSymbol->numpoints].x = 0.75;
53✔
2232
      psSymbol->points[psSymbol->numpoints].y = 0.625;
53✔
2233
      psSymbol->numpoints++;
53✔
2234
      psSymbol->points[psSymbol->numpoints].x = 0.875;
53✔
2235
      psSymbol->points[psSymbol->numpoints].y = 1;
53✔
2236
      psSymbol->numpoints++;
53✔
2237
      psSymbol->points[psSymbol->numpoints].x = 0.5;
53✔
2238
      psSymbol->points[psSymbol->numpoints].y = 0.75;
53✔
2239
      psSymbol->numpoints++;
53✔
2240
      psSymbol->points[psSymbol->numpoints].x = 0.125;
53✔
2241
      psSymbol->points[psSymbol->numpoints].y = 1;
53✔
2242
      psSymbol->numpoints++;
53✔
2243
      psSymbol->points[psSymbol->numpoints].x = 0.25;
53✔
2244
      psSymbol->points[psSymbol->numpoints].y = 0.625;
53✔
2245
      psSymbol->numpoints++;
53✔
2246
    }
2247
    /* cross is like plus (+) since there is also X symbol ?? */
2248
    else if (strcasecmp(pszSymbolName, "cross") == 0) {
×
2249
      /* NEVER FILL CROSS */
2250
      /* if (bFilled) */
2251
      /* psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CROSS_FILLED); */
2252
      /* else */
2253
      psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CROSS);
×
2254

2255
      psSymbol->type = MS_SYMBOL_VECTOR;
×
2256
      /* if (bFilled) */
2257
      /* psSymbol->filled = MS_TRUE; */
2258

2259
      psSymbol->points[psSymbol->numpoints].x = 0.5;
×
2260
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2261
      psSymbol->numpoints++;
×
2262
      psSymbol->points[psSymbol->numpoints].x = 0.5;
×
2263
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2264
      psSymbol->numpoints++;
×
2265
      psSymbol->points[psSymbol->numpoints].x = -99;
×
2266
      psSymbol->points[psSymbol->numpoints].y = -99;
×
2267
      psSymbol->numpoints++;
×
2268
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2269
      psSymbol->points[psSymbol->numpoints].y = 0.5;
×
2270
      psSymbol->numpoints++;
×
2271
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2272
      psSymbol->points[psSymbol->numpoints].y = 0.5;
×
2273
      psSymbol->numpoints++;
×
2274
    } else if (strcasecmp(pszSymbolName, "x") == 0) {
×
2275
      /* NEVER FILL X */
2276
      /* if (bFilled) */
2277
      /* psSymbol->name = msStrdup(SLD_MARK_SYMBOL_X_FILLED); */
2278
      /* else */
2279
      psSymbol->name = msStrdup(SLD_MARK_SYMBOL_X);
×
2280

2281
      psSymbol->type = MS_SYMBOL_VECTOR;
×
2282
      /* if (bFilled) */
2283
      /* psSymbol->filled = MS_TRUE; */
2284
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2285
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2286
      psSymbol->numpoints++;
×
2287
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2288
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2289
      psSymbol->numpoints++;
×
2290
      psSymbol->points[psSymbol->numpoints].x = -99;
×
2291
      psSymbol->points[psSymbol->numpoints].y = -99;
×
2292
      psSymbol->numpoints++;
×
2293
      psSymbol->points[psSymbol->numpoints].x = 0;
×
2294
      psSymbol->points[psSymbol->numpoints].y = 1;
×
2295
      psSymbol->numpoints++;
×
2296
      psSymbol->points[psSymbol->numpoints].x = 1;
×
2297
      psSymbol->points[psSymbol->numpoints].y = 0;
×
2298
      psSymbol->numpoints++;
×
2299
    }
2300
  }
2301

2302
  return nSymbolId;
2303
}
2304

2305
/************************************************************************/
2306
/*                          msSLDGetGraphicSymbol                       */
2307
/*                                                                      */
2308
/*      Create a symbol entry for an inmap pixmap symbol. Returns       */
2309
/*      the symbol id.                                                  */
2310
/************************************************************************/
2311
int msSLDGetGraphicSymbol(mapObj *map, char *pszFileName, char *extGraphicName,
×
2312
                          int nGap_ignored) {
2313
  (void)nGap_ignored;
2314
  int nSymbolId = 0;
2315
  symbolObj *psSymbol = NULL;
2316

2317
  if (map && pszFileName) {
×
2318
    if ((psSymbol = msGrowSymbolSet(&(map->symbolset))) == NULL)
×
2319
      return 0; /* returns 0 for no symbol */
2320
    nSymbolId = map->symbolset.numsymbols;
×
2321
    map->symbolset.numsymbols++;
×
2322
    initSymbol(psSymbol);
×
2323
    psSymbol->inmapfile = MS_TRUE;
×
2324
    psSymbol->type = MS_SYMBOL_PIXMAP;
×
2325
    psSymbol->name = msStrdup(extGraphicName);
×
2326
    psSymbol->imagepath = msStrdup(pszFileName);
×
2327
    psSymbol->full_pixmap_path = msStrdup(pszFileName);
×
2328
  }
2329
  return nSymbolId;
2330
}
2331

2332
/************************************************************************/
2333
/*      msSLDParsePointSymbolizer                                       */
2334
/*                                                                      */
2335
/*      Parse point symbolizer.                                         */
2336
/*                                                                      */
2337
/*      <xs:element name="PointSymbolizer">                             */
2338
/*      <xs:complexType>                                                */
2339
/*      <xs:sequence>                                                   */
2340
/*      <xs:element ref="sld:Geometry" minOccurs="0"/>                  */
2341
/*      <xs:element ref="sld:Graphic" minOccurs="0"/>                   */
2342
/*      </xs:sequence>                                                  */
2343
/*      </xs:complexType>                                               */
2344
/*      </xs:element>                                                   */
2345
/************************************************************************/
2346
int msSLDParsePointSymbolizer(CPLXMLNode *psRoot, layerObj *psLayer,
65✔
2347
                              int bNewClass, const char *pszUserStyleName) {
2348
  int nClassId = 0;
2349
  int iStyle = 0;
2350

2351
  if (!psRoot || !psLayer)
65✔
2352
    return MS_FAILURE;
2353

2354
  nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
65✔
2355
  if (nClassId < 0)
65✔
2356
    return MS_FAILURE;
2357

2358
  // Get uom if any, defaults to MS_PIXELS
2359
  enum MS_UNITS sizeunits = MS_PIXELS;
65✔
2360
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
65✔
2361
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
×
2362
               "msSLDParsePolygonSymbolizer()");
2363
    return MS_FAILURE;
×
2364
  }
2365

2366
  iStyle = psLayer->_class[nClassId]->numstyles;
65✔
2367
  msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
65✔
2368
  psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
65✔
2369

2370
  msSLDParseGraphicFillOrStroke(
65✔
2371
      psRoot, NULL, psLayer->_class[nClassId]->styles[iStyle], psLayer->map);
2372

2373
  return MS_SUCCESS;
2374
}
2375

2376
/************************************************************************/
2377
/*                        msSLDParseExternalGraphic                     */
2378
/*                                                                      */
2379
/*      Parse external graphic node : download the symbol referenced    */
2380
/*      by the URL and create a PIXMAP inmap symbol. Only GIF and       */
2381
/*      PNG are supported.                                              */
2382
/************************************************************************/
2383
int msSLDParseExternalGraphic(CPLXMLNode *psExternalGraphic, styleObj *psStyle,
8✔
2384
                              mapObj *map) {
2385
  char *pszFormat = NULL;
2386
  CPLXMLNode *psURL = NULL, *psFormat = NULL, *psTmp = NULL;
2387
  char *pszURL = NULL;
2388

2389
  if (!psExternalGraphic || !psStyle || !map)
8✔
2390
    return MS_FAILURE;
2391

2392
  psFormat = CPLGetXMLNode(psExternalGraphic, "Format");
8✔
2393
  if (psFormat && psFormat->psChild && psFormat->psChild->pszValue)
8✔
2394
    pszFormat = psFormat->psChild->pszValue;
2395

2396
  /* supports GIF and PNG and SVG */
2397
  if (pszFormat && (strcasecmp(pszFormat, "GIF") == 0 ||
8✔
2398
                    strcasecmp(pszFormat, "image/gif") == 0 ||
2✔
2399
                    strcasecmp(pszFormat, "PNG") == 0 ||
2✔
2400
                    strcasecmp(pszFormat, "image/png") == 0 ||
2✔
2401
                    strcasecmp(pszFormat, "image/svg+xml") == 0)) {
2✔
2402

2403
    /* <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
2404
     * xlink:type="simple" xlink:href="http://www.vendor.com/geosym/2267.svg"/>
2405
     */
2406
    psURL = CPLGetXMLNode(psExternalGraphic, "OnlineResource");
8✔
2407
    if (psURL && psURL->psChild) {
8✔
2408
      psTmp = psURL->psChild;
2409
      while (psTmp != NULL && psTmp->pszValue &&
16✔
2410
             strcasecmp(psTmp->pszValue, "xlink:href") != 0) {
16✔
2411
        psTmp = psTmp->psNext;
8✔
2412
      }
2413
      if (psTmp && psTmp->psChild) {
8✔
2414
        pszURL = (char *)psTmp->psChild->pszValue;
8✔
2415

2416
        char *symbolurl = NULL;
2417
        // Handle relative URL for ExternalGraphic
2418
        if (map->sldurl && !strstr(pszURL, "://")) {
8✔
2419
          char *baseurl = NULL;
2420
          char *relpath = NULL;
2421
          symbolurl = static_cast<char *>(malloc(sizeof(char) * MS_MAXPATHLEN));
1✔
2422
          if (pszURL[0] != '/') {
1✔
2423
            // Symbol file is relative to SLD file
2424
            // e.g. SLD   : http://example.com/path/to/sld.xml
2425
            //  and symbol: assets/symbol.svg
2426
            //     lead to: http://example.com/path/to/assets/symbol.svg
2427
            baseurl = msGetPath(map->sldurl);
×
2428
            relpath = pszURL;
2429
          } else {
2430
            // Symbol file is relative to the root of SLD server
2431
            // e.g. SLD   : http://example.com/path/to/sld.xml
2432
            //  and symbol: /path/to/assets/symbol.svg
2433
            //     lead to: http://example.com/path/to/assets/symbol.svg
2434
            baseurl = msStrdup(map->sldurl);
1✔
2435
            relpath = pszURL + 1;
1✔
2436
            char *sep = strstr(baseurl, "://");
2437
            if (sep)
1✔
2438
              sep += 3;
1✔
2439
            else
2440
              sep = baseurl;
2441
            sep = strchr(sep, '/');
2442
            if (!sep)
1✔
2443
              sep = baseurl + strlen(baseurl);
×
2444
            sep[1] = '\0';
1✔
2445
          }
2446
          msBuildPath(symbolurl, baseurl, relpath);
1✔
2447
          msFree(baseurl);
1✔
2448
        } else {
2449
          // Absolute URL
2450
          // e.g. symbol: http://example.com/path/to/assets/symbol.svg
2451
          symbolurl = msStrdup(pszURL);
7✔
2452
        }
2453

2454
        /* validate the ExternalGraphic parameter */
2455
        if (msValidateParameter(symbolurl,
8✔
2456
                                msLookupHashTable(&(map->web.validation),
8✔
2457
                                                  "sld_external_graphic"),
2458
                                NULL, NULL, NULL) != MS_SUCCESS) {
2459
          msSetError(MS_WEBERR,
2✔
2460
                     "SLD ExternalGraphic OnlineResource value fails to "
2461
                     "validate against sld_external_graphic VALIDATION",
2462
                     "mapserv()");
2463
          msFree(symbolurl);
2✔
2464
          return MS_FAILURE;
2✔
2465
        }
2466

2467
        /*external symbols using http will be automatically downloaded. The
2468
          file should be saved in a temporary directory (msAddImageSymbol)
2469
          #2305*/
2470
        psStyle->symbol = msGetSymbolIndex(&map->symbolset, symbolurl, MS_TRUE);
6✔
2471
        msFree(symbolurl);
6✔
2472

2473
        if (psStyle->symbol > 0 && psStyle->symbol < map->symbolset.numsymbols)
6✔
2474
          psStyle->symbolname =
6✔
2475
              msStrdup(map->symbolset.symbol[psStyle->symbol]->name);
6✔
2476

2477
        /* set the color parameter if not set. Does not make sense */
2478
        /* for pixmap but mapserver needs it. */
2479
        if (psStyle->color.red == -1 || psStyle->color.green ||
6✔
2480
            psStyle->color.blue) {
1✔
2481
          psStyle->color.red = 0;
5✔
2482
          psStyle->color.green = 0;
5✔
2483
          psStyle->color.blue = 0;
5✔
2484
        }
2485
      }
2486
    }
2487
  }
2488

2489
  return MS_SUCCESS;
2490
}
2491

2492
/************************************************************************/
2493
/*                         msSLDParseTextSymbolizer                     */
2494
/*                                                                      */
2495
/*      Parse text symbolizer.                                          */
2496
/*                                                                      */
2497
/*      <xs:element name="TextSymbolizer">                              */
2498
/*      <xs:complexType>                                                */
2499
/*      <xs:sequence>                                                   */
2500
/*      <xs:element ref="sld:Geometry" minOccurs="0"/>                  */
2501
/*      <xs:element ref="sld:Label" minOccurs="0"/>                     */
2502
/*      <xs:element ref="sld:Font" minOccurs="0"/>                      */
2503
/*      <xs:element ref="sld:LabelPlacement" minOccurs="0"/>            */
2504
/*      <xs:element ref="sld:Halo" minOccurs="0"/>                      */
2505
/*      <xs:element ref="sld:Fill" minOccurs="0"/>                      */
2506
/*      </xs:sequence>                                                  */
2507
/*      </xs:complexType>                                               */
2508
/*      </xs:element>                                                   */
2509
/*                                                                      */
2510
/*      <xs:element name="Label" type="sld:ParameterValueType"/         */
2511
/*                                                                      */
2512
/*      <xs:element name="Font">                                        */
2513
/*      <xs:complexType>                                                */
2514
/*      <xs:sequence>                                                   */
2515
/*      <xs:element ref="sld:CssParameter" minOccurs="0"                */
2516
/*      maxOccurs="unbounded"/>                                         */
2517
/*      </xs:sequence>                                                  */
2518
/*      </xs:complexType>                                               */
2519
/*      </xs:element>                                                   */
2520
/*                                                                      */
2521
/*      Four types of CssParameter are allowed, font-family, font-style,*/
2522
/*      fontweight,and font-size.                                       */
2523
/*                                                                      */
2524
/*      <xs:element name="LabelPlacement">                              */
2525
/*      <xs:complexType>                                                */
2526
/*      <xs:choice>                                                     */
2527
/*      <xs:element ref="sld:PointPlacement"/>                          */
2528
/*      <xs:element ref="sld:LinePlacement"/>                           */
2529
/*      </xs:choice>                                                    */
2530
/*      </xs:complexType>                                               */
2531
/*      </xs:element>                                                   */
2532
/*                                                                      */
2533
/*      <xs:element name="PointPlacement">                              */
2534
/*      <xs:complexType>                                                */
2535
/*      <xs:sequence>                                                   */
2536
/*      <xs:element ref="sld:AnchorPoint" minOccurs="0"/>               */
2537
/*      <xs:element ref="sld:Displacement" minOccurs="0"/>              */
2538
/*      <xs:element ref="sld:Rotation" minOccurs="0"/>                  */
2539
/*      </xs:sequence>                                                  */
2540
/*      </xs:complexType>                                               */
2541
/*      </xs:element>                                                   */
2542
/*                                                                      */
2543
/*      <xs:element name="AnchorPoint">                                 */
2544
/*      <xs:complexType>                                                */
2545
/*      <xs:sequence>                                                   */
2546
/*      <xs:element ref="sld:AnchorPointX"/>                            */
2547
/*      <xs:element ref="sld:AnchorPointY"/>                            */
2548
/*      </xs:sequence>                                                  */
2549
/*      </xs:complexType>                                               */
2550
/*      </xs:element>                                                   */
2551
/*      <xs:element name="AnchorPointX" type="sld:ParameterValueType"/> */
2552
/*      <xs:element name="AnchorPointY"                                 */
2553
/*      type="sld:ParameterValueType"/>                                 */
2554
/*                                                                      */
2555
/*      The coordinates are given as two floating-point numbers in      */
2556
/*      the AnchorPointX and AnchorPointY elements each with values     */
2557
/*      between 0.0 and 1.0 inclusive. The bounding box of the label    */
2558
/*      to be rendered is considered to be in a coorindate space        */
2559
/*      from 0.0 (lowerleft corner) to 1.0 (upper-right corner), and    */
2560
/*      the anchor position is specified as a point in  this            */
2561
/*      space. The default point is X=0, Y=0.5, which is at the         */
2562
/*      middle height of the lefthand side of the label.                */
2563
/*                                                                      */
2564
/*      <xs:element name="Displacement">                                */
2565
/*      <xs:complexType>                                                */
2566
/*      <xs:sequence>                                                   */
2567
/*      <xs:element ref="sld:DisplacementX"/>                           */
2568
/*      <xs:element ref="sld:DisplacementY"/>                           */
2569
/*      </xs:sequence>                                                  */
2570
/*      </xs:complexType>                                               */
2571
/*      </xs:element>                                                   */
2572
/*      <xs:element name="DisplacementX" type="sld:ParameterValueType"/>*/
2573
/*      <xs:element name="DisplacementY"                                */
2574
/*      type="sld:ParameterValueType"/>                                 */
2575
/*                                                                      */
2576
/*      <xs:element name="LinePlacement">                               */
2577
/*      <xs:complexType>                                                */
2578
/*      <xs:sequence>                                                   */
2579
/*      <xs:element ref="sld:PerpendicularOffset" minOccurs="0"/>       */
2580
/*      </xs:sequence>                                                  */
2581
/*      </xs:complexType>                                               */
2582
/*      </xs:element>                                                   */
2583
/************************************************************************/
2584
int msSLDParseTextSymbolizer(CPLXMLNode *psRoot, layerObj *psLayer,
55✔
2585
                             int bOtherSymboliser,
2586
                             const char *pszUserStyleName) {
2587
  int nStyleId = 0, nClassId = 0;
2588

2589
  if (!psRoot || !psLayer)
55✔
2590
    return MS_FAILURE;
2591

2592
  if (!bOtherSymboliser) {
55✔
2593
    if (msGrowLayerClasses(psLayer) == NULL)
4✔
2594
      return MS_FAILURE;
2595
    initClass(psLayer->_class[psLayer->numclasses]);
4✔
2596
    nClassId = psLayer->numclasses;
4✔
2597
    if (pszUserStyleName)
4✔
2598
      psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
×
2599
    psLayer->numclasses++;
4✔
2600
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
4✔
2601
    nStyleId = 0;
2602
  } else {
2603
    nClassId = psLayer->numclasses - 1;
51✔
2604
    if (nClassId >= 0) /* should always be true */
51✔
2605
      nStyleId = psLayer->_class[nClassId]->numstyles - 1;
51✔
2606
  }
2607

2608
  if (nStyleId >= 0 && nClassId >= 0) /* should always be true */
55✔
2609
    msSLDParseTextParams(psRoot, psLayer, psLayer->_class[nClassId]);
55✔
2610

2611
  return MS_SUCCESS;
2612
}
2613

2614
/************************************************************************/
2615
/*                        msSLDParseRasterSymbolizer                    */
2616
/*                                                                      */
2617
/*      Supports the ColorMap parameter in a Raster Symbolizer. In      */
2618
/*      the ColorMap, only color and quantity are used here.            */
2619
/*                                                                      */
2620
/*      <xs:element name="RasterSymbolizer">                            */
2621
/*      <xs:complexType>                                                */
2622
/*      <xs:sequence>                                                   */
2623
/*      <xs:element ref="sld:Geometry" minOccurs="0"/>                  */
2624
/*      <xs:element ref="sld:Opacity" minOccurs="0"/>                   */
2625
/*      <xs:element ref="sld:ChannelSelection" minOccurs="0"/>          */
2626
/*      <xs:element ref="sld:OverlapBehavior" minOccurs="0"/>           */
2627
/*      <xs:element ref="sld:ColorMap" minOccurs="0"/>                  */
2628
/*      <xs:element ref="sld:ContrastEnhancement" minOccurs="0"/>       */
2629
/*      <xs:element ref="sld:ShadedRelief" minOccurs="0"/>              */
2630
/*      <xs:element ref="sld:ImageOutline" minOccurs="0"/>              */
2631
/*      </xs:sequence>                                                  */
2632
/*      </xs:complexType>                                               */
2633
/*      </xs:element>                                                   */
2634
/*                                                                      */
2635
/*      <xs:element name="ColorMap">                                    */
2636
/*      <xs:complexType>                                                */
2637
/*      <xs:choice minOccurs="0" maxOccurs="unbounded">                 */
2638
/*      <xs:element ref="sld:ColorMapEntry"/>                           */
2639
/*      </xs:choice>                                                    */
2640
/*      </xs:complexType>                                               */
2641
/*      </xs:element>                                                   */
2642
/*      <xs:element name="ColorMapEntry">                               */
2643
/*      <xs:complexType>                                                */
2644
/*      <xs:attribute name="color" type="xs:string" use="required"/>    */
2645
/*      <xs:attribute name="opacity" type="xs:double"/>                 */
2646
/*      <xs:attribute name="quantity" type="xs:double"/>                */
2647
/*      <xs:attribute name="label" type="xs:string"/>                   */
2648
/*      </xs:complexType>                                               */
2649
/*      </xs:element>                                                   */
2650
/*                                                                      */
2651
/*      SLD 1.1                                                         */
2652
/*                                                                      */
2653
/*      <xsd:element name="RasterSymbolizer" type="se:RasterSymbolizerType"
2654
 * substitutionGroup="se:Symbolizer"/>*/
2655
/*      <xsd:complexType name="RasterSymbolizerType">                   */
2656
/*      <xsd:complexContent>                                            */
2657
/*      <xsd:extension base="se:SymbolizerType">                        */
2658
/*      <xsd:sequence>                                                  */
2659
/*      <xsd:element ref="se:Geometry" minOccurs="0"/>                  */
2660
/*      <xsd:element ref="se:Opacity" minOccurs="0"/>                   */
2661
/*      <xsd:element ref="se:ChannelSelection" minOccurs="0"/>          */
2662
/*      <xsd:element ref="se:OverlapBehavior" minOccurs="0"/>           */
2663
/*      <xsd:element ref="se:ColorMap" minOccurs="0"/>                  */
2664
/*      <xsd:element ref="se:ContrastEnhancement" minOccurs="0"/>       */
2665
/*      <xsd:element ref="se:ShadedRelief" minOccurs="0"/>              */
2666
/*      <xsd:element ref="se:ImageOutline" minOccurs="0"/>              */
2667
/*      </xsd:sequence>                                                 */
2668
/*      </xsd:extension>                                                */
2669
/*      </xsd:complexContent>                                           */
2670
/*      </xsd:complexType>                                              */
2671
/*                                                                      */
2672
/*      <xsd:element name="ColorMap" type="se:ColorMapType"/>           */
2673
/*      <xsd:complexType name="ColorMapType">                           */
2674
/*      <xsd:choice>                                                    */
2675
/*      <xsd:element ref="se:Categorize"/>                              */
2676
/*      <xsd:element ref="se:Interpolate"/>                             */
2677
/*      </xsd:choice>                                                   */
2678
/*      </xsd:complexType>                                              */
2679
/*                                                                      */
2680
/*      <xsd:element name="Categorize" type="se:CategorizeType"
2681
 * substitutionGroup="se:Function"/>*/
2682
/*      <xsd:complexType name="CategorizeType">                         */
2683
/*      <xsd:complexContent>                                            */
2684
/*      <xsd:extension base="se:FunctionType">                          */
2685
/*      <xsd:sequence>                                                  */
2686
/*      <xsd:element ref="se:LookupValue"/>                             */
2687
/*      <xsd:element ref="se:Value"/>                                   */
2688
/*      <xsd:sequence minOccurs="0" maxOccurs="unbounded">              */
2689
/*      <xsd:element ref="se:Threshold"/>                               */
2690
/*      <xsd:element ref="se:Value"/>                                   */
2691
/*      </xsd:sequence>                                                 */
2692
/*      </xsd:sequence>                                                 */
2693
/*      <xsd:attribute name="thresholdsBelongTo"
2694
 * type="se:ThresholdsBelongToType" use="optional"/>*/
2695
/*      </xsd:extension>                                                */
2696
/*      </xsd:complexContent>                                           */
2697
/*      </xsd:complexType>                                              */
2698
/*      <xsd:element name="LookupValue" type="se:ParameterValueType"/>  */
2699
/*      <xsd:element name="Value" type=" se:ParameterValueType"/>       */
2700
/*      <xsd:element name="Threshold" type=" se:ParameterValueType"/>   */
2701
/*      <xsd:simpleType name="ThresholdsBelongToType">                 */
2702
/*      <xsd:restriction base="xsd:token">                              */
2703
/*      <xsd:enumeration value="succeeding"/>                           */
2704
/*      <xsd:enumeration value="preceding"/>                            */
2705
/*      </xsd:restriction>                                              */
2706
/*      </xsd:simpleType>                                               */
2707
/*                                                                      */
2708
/************************************************************************/
2709
int msSLDParseRasterSymbolizer(CPLXMLNode *psRoot, layerObj *psLayer,
16✔
2710
                               const char *pszUserStyleName) {
2711
  CPLXMLNode *psColorMap = NULL, *psColorEntry = NULL, *psOpacity = NULL;
2712
  char *pszColor = NULL, *pszQuantity = NULL;
2713
  char *pszPreviousColor = NULL, *pszPreviousQuality = NULL;
2714
  colorObj sColor;
2715
  char szExpression[100];
2716
  double dfOpacity = 1.0;
2717
  char *pszLabel = NULL, *pszPreviousLabel = NULL;
2718
  char *pch = NULL, *pchPrevious = NULL;
2719

2720
  CPLXMLNode *psNode = NULL, *psCategorize = NULL;
2721
  char *pszTmp = NULL;
2722
  int nValues = 0, nThresholds = 0;
2723
  int i, nMaxValues = 100, nMaxThreshold = 100;
2724

2725
  if (!psRoot || !psLayer)
16✔
2726
    return MS_FAILURE;
2727

2728
  psOpacity = CPLGetXMLNode(psRoot, "Opacity");
16✔
2729
  if (psOpacity) {
16✔
2730
    if (psOpacity->psChild && psOpacity->psChild->pszValue)
1✔
2731
      dfOpacity = atof(psOpacity->psChild->pszValue);
2732

2733
    /* values in sld goes from 0.0 (for transparent) to 1.0 (for full opacity);
2734
     */
2735
    if (dfOpacity >= 0.0 && dfOpacity <= 1.0)
1✔
2736
      msSetLayerOpacity(psLayer, (int)(dfOpacity * 100));
1✔
2737
    else {
2738
      msSetError(MS_WMSERR,
×
2739
                 "Invalid opacity value. Values should be between 0.0 and 1.0",
2740
                 "msSLDParseRasterSymbolizer()");
2741
      return MS_FAILURE;
×
2742
    }
2743
  }
2744
  psColorMap = CPLGetXMLNode(psRoot, "ColorMap");
16✔
2745
  if (psColorMap) {
16✔
2746
    psColorEntry = CPLGetXMLNode(psColorMap, "ColorMapEntry");
16✔
2747

2748
    if (psColorEntry) { /*SLD 1.0*/
16✔
2749
      while (psColorEntry && psColorEntry->pszValue &&
2,552✔
2750
             strcasecmp(psColorEntry->pszValue, "ColorMapEntry") == 0) {
2,536✔
2751
        pszColor = (char *)CPLGetXMLValue(psColorEntry, "color", NULL);
2,536✔
2752
        pszQuantity = (char *)CPLGetXMLValue(psColorEntry, "quantity", NULL);
2,536✔
2753
        pszLabel = (char *)CPLGetXMLValue(psColorEntry, "label", NULL);
2,536✔
2754

2755
        if (pszColor && pszQuantity) {
2,536✔
2756
          if (pszPreviousColor && pszPreviousQuality) {
2,536✔
2757
            if (strlen(pszPreviousColor) == 7 && pszPreviousColor[0] == '#' &&
2,520✔
2758
                strlen(pszColor) == 7 && pszColor[0] == '#') {
2,520✔
2759
              sColor.red = msHexToInt(pszPreviousColor + 1);
2,520✔
2760
              sColor.green = msHexToInt(pszPreviousColor + 3);
2,520✔
2761
              sColor.blue = msHexToInt(pszPreviousColor + 5);
2,520✔
2762

2763
              /* pszQuantity and pszPreviousQuality may be integer or float */
2764
              pchPrevious = strchr(pszPreviousQuality, '.');
2765
              pch = strchr(pszQuantity, '.');
2766
              if (pchPrevious == NULL && pch == NULL) {
2,520✔
2767
                snprintf(szExpression, sizeof(szExpression),
2768
                         "([pixel] >= %d AND [pixel] < %d)",
2769
                         atoi(pszPreviousQuality), atoi(pszQuantity));
2770
              } else if (pchPrevious != NULL && pch == NULL) {
×
2771
                snprintf(szExpression, sizeof(szExpression),
2772
                         "([pixel] >= %f AND [pixel] < %d)",
2773
                         atof(pszPreviousQuality), atoi(pszQuantity));
2774
              } else if (pchPrevious == NULL && pch != NULL) {
×
2775
                snprintf(szExpression, sizeof(szExpression),
2776
                         "([pixel] >= %d AND [pixel] < %f)",
2777
                         atoi(pszPreviousQuality), atof(pszQuantity));
2778
              } else {
2779
                snprintf(szExpression, sizeof(szExpression),
2780
                         "([pixel] >= %f AND [pixel] < %f)",
2781
                         atof(pszPreviousQuality), atof(pszQuantity));
2782
              }
2783

2784
              if (msGrowLayerClasses(psLayer) == NULL)
2,520✔
2785
                return MS_FAILURE;
2786
              else {
2787
                initClass(psLayer->_class[psLayer->numclasses]);
2,520✔
2788
                psLayer->numclasses++;
2,520✔
2789
                const int nClassId = psLayer->numclasses - 1;
2790
                if (pszUserStyleName)
2,520✔
2791
                  psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
2,520✔
2792

2793
                /*set the class name using the label. If label not defined
2794
                  set it with the quantity*/
2795
                if (pszPreviousLabel)
2,520✔
2796
                  psLayer->_class[nClassId]->name = msStrdup(pszPreviousLabel);
2,520✔
2797
                else
2798
                  psLayer->_class[nClassId]->name =
×
2799
                      msStrdup(pszPreviousQuality);
×
2800

2801
                msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
2,520✔
2802

2803
                psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
2,520✔
2804
                psLayer->_class[nClassId]->styles[0]->color.green =
2,520✔
2805
                    sColor.green;
2806
                psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
2,520✔
2807

2808
                if (!psLayer->classitem ||
2,520✔
2809
                    strcasecmp(psLayer->classitem, "[pixel]") != 0) {
2,505✔
2810
                  free(psLayer->classitem);
15✔
2811
                  psLayer->classitem = msStrdup("[pixel]");
15✔
2812
                }
2813

2814
                msLoadExpressionString(&psLayer->_class[nClassId]->expression,
2,520✔
2815
                                       szExpression);
2816
              }
2817
            } else {
2818
              msSetError(MS_WMSERR, "Invalid ColorMap Entry.",
×
2819
                         "msSLDParseRasterSymbolizer()");
2820
              return MS_FAILURE;
×
2821
            }
2822
          }
2823

2824
          pszPreviousColor = pszColor;
2825
          pszPreviousQuality = pszQuantity;
2826
          pszPreviousLabel = pszLabel;
2827
        }
2828
        psColorEntry = psColorEntry->psNext;
2,536✔
2829
      }
2830
      /* do the last Color Map Entry */
2831
      if (pszColor && pszQuantity) {
16✔
2832
        if (strlen(pszColor) == 7 && pszColor[0] == '#') {
16✔
2833
          sColor.red = msHexToInt(pszColor + 1);
16✔
2834
          sColor.green = msHexToInt(pszColor + 3);
16✔
2835
          sColor.blue = msHexToInt(pszColor + 5);
16✔
2836

2837
          /* pszQuantity may be integer or float */
2838
          pch = strchr(pszQuantity, '.');
2839
          if (pch == NULL) {
16✔
2840
            snprintf(szExpression, sizeof(szExpression), "([pixel] = %d)",
2841
                     atoi(pszQuantity));
2842
          } else {
2843
            snprintf(szExpression, sizeof(szExpression), "([pixel] = %f)",
2844
                     atof(pszQuantity));
2845
          }
2846

2847
          if (msGrowLayerClasses(psLayer) == NULL)
16✔
2848
            return MS_FAILURE;
2849
          else {
2850
            initClass(psLayer->_class[psLayer->numclasses]);
16✔
2851
            psLayer->numclasses++;
16✔
2852
            const int nClassId = psLayer->numclasses - 1;
2853
            if (pszUserStyleName)
16✔
2854
              psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
16✔
2855

2856
            msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
16✔
2857
            if (pszLabel)
16✔
2858
              psLayer->_class[nClassId]->name = msStrdup(pszLabel);
15✔
2859
            else
2860
              psLayer->_class[nClassId]->name = msStrdup(pszQuantity);
1✔
2861
            psLayer->_class[nClassId]->numstyles = 1;
16✔
2862
            psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
16✔
2863
            psLayer->_class[nClassId]->styles[0]->color.green = sColor.green;
16✔
2864
            psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
16✔
2865

2866
            if (!psLayer->classitem ||
16✔
2867
                strcasecmp(psLayer->classitem, "[pixel]") != 0) {
15✔
2868
              free(psLayer->classitem);
1✔
2869
              psLayer->classitem = msStrdup("[pixel]");
1✔
2870
            }
2871

2872
            msLoadExpressionString(&psLayer->_class[nClassId]->expression,
16✔
2873
                                   szExpression);
2874
          }
2875
        }
2876
      }
2877
    } else if ((psCategorize = CPLGetXMLNode(psColorMap, "Categorize"))) {
×
2878
      char **papszValues = (char **)msSmallMalloc(sizeof(char *) * nMaxValues);
×
2879
      char **papszThresholds =
2880
          (char **)msSmallMalloc(sizeof(char *) * nMaxThreshold);
×
2881
      psNode = CPLGetXMLNode(psCategorize, "Value");
×
2882
      while (psNode && psNode->pszValue && psNode->psChild &&
×
2883
             psNode->psChild->pszValue)
×
2884

2885
      {
2886
        if (strcasecmp(psNode->pszValue, "Value") == 0) {
×
2887
          papszValues[nValues] = psNode->psChild->pszValue;
×
2888
          nValues++;
×
2889
          if (nValues == nMaxValues) {
×
2890
            nMaxValues += 100;
×
2891
            papszValues = (char **)msSmallRealloc(papszValues,
×
2892
                                                  sizeof(char *) * nMaxValues);
×
2893
          }
2894
        } else if (strcasecmp(psNode->pszValue, "Threshold") == 0) {
×
2895
          papszThresholds[nThresholds] = psNode->psChild->pszValue;
×
2896
          nThresholds++;
×
2897
          if (nValues == nMaxThreshold) {
×
2898
            nMaxThreshold += 100;
×
2899
            papszThresholds = (char **)msSmallRealloc(
×
2900
                papszThresholds, sizeof(char *) * nMaxThreshold);
×
2901
          }
2902
        }
2903
        psNode = psNode->psNext;
×
2904
      }
2905

2906
      if (nThresholds > 0 && nValues == nThresholds + 1) {
×
2907
        /*free existing classes*/
2908
        for (i = 0; i < psLayer->numclasses; i++) {
×
2909
          if (psLayer->_class[i] != NULL) {
×
2910
            psLayer->_class[i]->layer = NULL;
×
2911
            if (freeClass(psLayer->_class[i]) == MS_SUCCESS) {
×
2912
              msFree(psLayer->_class[i]);
×
2913
              psLayer->_class[i] = NULL;
×
2914
            }
2915
          }
2916
        }
2917
        psLayer->numclasses = 0;
×
2918
        for (i = 0; i < nValues; i++) {
×
2919
          pszTmp = (papszValues[i]);
×
2920
          if (pszTmp && strlen(pszTmp) == 7 && pszTmp[0] == '#') {
×
2921
            sColor.red = msHexToInt(pszTmp + 1);
×
2922
            sColor.green = msHexToInt(pszTmp + 3);
×
2923
            sColor.blue = msHexToInt(pszTmp + 5);
×
2924
            if (i == 0) {
×
2925
              if (strchr(papszThresholds[i], '.'))
×
2926
                snprintf(szExpression, sizeof(szExpression), "([pixel] < %f)",
×
2927
                         atof(papszThresholds[i]));
2928
              else
2929
                snprintf(szExpression, sizeof(szExpression), "([pixel] < %d)",
×
2930
                         atoi(papszThresholds[i]));
2931

2932
            } else if (i < nValues - 1) {
×
2933
              if (strchr(papszThresholds[i], '.'))
×
2934
                snprintf(szExpression, sizeof(szExpression),
×
2935
                         "([pixel] >= %f AND [pixel] < %f)",
2936
                         atof(papszThresholds[i - 1]),
×
2937
                         atof(papszThresholds[i]));
2938
              else
2939
                snprintf(szExpression, sizeof(szExpression),
×
2940
                         "([pixel] >= %d AND [pixel] < %d)",
2941
                         atoi(papszThresholds[i - 1]),
×
2942
                         atoi(papszThresholds[i]));
2943
            } else {
2944
              if (strchr(papszThresholds[i - 1], '.'))
×
2945
                snprintf(szExpression, sizeof(szExpression), "([pixel] >= %f)",
2946
                         atof(papszThresholds[i - 1]));
2947
              else
2948
                snprintf(szExpression, sizeof(szExpression), "([pixel] >= %d)",
2949
                         atoi(papszThresholds[i - 1]));
2950
            }
2951
            if (msGrowLayerClasses(psLayer)) {
×
2952
              initClass(psLayer->_class[psLayer->numclasses]);
×
2953
              psLayer->numclasses++;
×
2954
              const int nClassId = psLayer->numclasses - 1;
2955
              if (pszUserStyleName)
×
2956
                psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
×
2957
              msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
×
2958
              psLayer->_class[nClassId]->numstyles = 1;
×
2959
              psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
×
2960
              psLayer->_class[nClassId]->styles[0]->color.green = sColor.green;
×
2961
              psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
×
2962
              if (psLayer->classitem &&
×
2963
                  strcasecmp(psLayer->classitem, "[pixel]") != 0)
×
2964
                free(psLayer->classitem);
×
2965
              psLayer->classitem = msStrdup("[pixel]");
×
2966
              msLoadExpressionString(&psLayer->_class[nClassId]->expression,
×
2967
                                     szExpression);
2968
            }
2969
          }
2970
        }
2971
      }
2972
      msFree(papszValues);
×
2973
      msFree(papszThresholds);
×
2974

2975
    } else {
2976
      msSetError(MS_WMSERR, "Invalid SLD document. msSLDParseRaster", "");
×
2977
      return MS_FAILURE;
×
2978
    }
2979
  }
2980

2981
  return MS_SUCCESS;
2982
}
2983
/************************************************************************/
2984
/*                           msSLDParseTextParams                       */
2985
/*                                                                      */
2986
/*      Parse text parameters like font, placement and color.           */
2987
/************************************************************************/
2988
int msSLDParseTextParams(CPLXMLNode *psRoot, layerObj *psLayer,
55✔
2989
                         classObj *psClass) {
2990
  char szFontName[100];
2991

2992
  CPLXMLNode *psLabel = NULL, *psFont = NULL;
2993
  CPLXMLNode *psCssParam = NULL;
2994
  char *pszName = NULL, *pszFontFamily = NULL, *pszFontStyle = NULL;
2995
  char *pszFontWeight = NULL;
2996
  CPLXMLNode *psLabelPlacement = NULL, *psPointPlacement = NULL,
2997
             *psLinePlacement = NULL;
2998
  CPLXMLNode *psFill = NULL, *psHalo = NULL, *psHaloRadius = NULL,
2999
             *psHaloFill = NULL;
3000
  labelObj *psLabelObj = NULL;
3001
  szFontName[0] = '\0';
55✔
3002

3003
  if (!psRoot || !psClass || !psLayer)
55✔
3004
    return MS_FAILURE;
3005

3006
  if (psClass->numlabels == 0) {
55✔
3007
    if (msGrowClassLabels(psClass) == NULL)
55✔
3008
      return (MS_FAILURE);
3009
    initLabel(psClass->labels[0]);
55✔
3010
    psClass->numlabels++;
55✔
3011
  }
3012
  psLabelObj = psClass->labels[0];
55✔
3013

3014
  /*set the angle by default to auto. the angle can be
3015
    modified Label Placement #2806*/
3016
  psLabelObj->anglemode = MS_AUTO;
55✔
3017

3018
  /* label  */
3019
  /* support literal expression  and  propertyname
3020
   - <TextSymbolizer><Label>MY_COLUMN</Label>
3021
   -
3022
  <TextSymbolizer><Label><ogc:PropertyName>MY_COLUMN</ogc:PropertyName></Label>
3023
  Bug 1857 */
3024
  psLabel = CPLGetXMLNode(psRoot, "Label");
55✔
3025
  if (psLabel) {
55✔
3026
    const char *sep = "";
3027
    msStringBuffer *classtext = msStringBufferAlloc();
55✔
3028
    msStringBufferAppend(classtext, "(");
55✔
3029
    for (CPLXMLNode *psTmpNode = psLabel->psChild; psTmpNode;
116✔
3030
         psTmpNode = psTmpNode->psNext) {
61✔
3031
      if (psTmpNode->eType == CXT_Text && psTmpNode->pszValue) {
61✔
3032
        msStringBufferAppend(classtext, sep);
3✔
3033
        msStringBufferAppend(classtext, "\"");
3✔
3034
        msStringBufferAppend(classtext, psTmpNode->pszValue);
3✔
3035
        msStringBufferAppend(classtext, "\"");
3✔
3036
        sep = "+";
3037
      } else if (psTmpNode->eType == CXT_Element &&
58✔
3038
                 strcasecmp(psTmpNode->pszValue, "Literal") == 0 &&
58✔
3039
                 psTmpNode->psChild) {
3✔
3040
        msStringBufferAppend(classtext, sep);
3✔
3041
        msStringBufferAppend(classtext, "\"");
3✔
3042
        msStringBufferAppend(classtext, psTmpNode->psChild->pszValue);
3✔
3043
        msStringBufferAppend(classtext, "\"");
3✔
3044
        sep = "+";
3045
      } else if (psTmpNode->eType == CXT_Element &&
55✔
3046
                 strcasecmp(psTmpNode->pszValue, "PropertyName") == 0 &&
55✔
3047
                 psTmpNode->psChild) {
54✔
3048
        msStringBufferAppend(classtext, sep);
54✔
3049
        msStringBufferAppend(classtext, "\"[");
54✔
3050
        msStringBufferAppend(classtext, psTmpNode->psChild->pszValue);
54✔
3051
        msStringBufferAppend(classtext, "]\"");
54✔
3052
        sep = "+";
3053
      } else if (psTmpNode->eType == CXT_Element &&
1✔
3054
                 strcasecmp(psTmpNode->pszValue, "Function") == 0 &&
1✔
3055
                 psTmpNode->psChild) {
1✔
3056
        msStringBufferAppend(classtext, sep);
1✔
3057
        msStringBufferAppend(classtext, "tostring(");
1✔
3058

3059
        labelObj tempExpressionCollector;
3060
        initLabel(&tempExpressionCollector);
1✔
3061
        msSLDParseOgcExpression(psTmpNode, &tempExpressionCollector,
1✔
3062
                                MS_LABEL_BINDING_SIZE, MS_OBJ_LABEL);
3063
        msStringBufferAppend(
1✔
3064
            classtext,
3065
            tempExpressionCollector.exprBindings[MS_LABEL_BINDING_SIZE].string);
1✔
3066
        freeLabel(&tempExpressionCollector);
1✔
3067

3068
        msStringBufferAppend(classtext, ",\"%g\")");
1✔
3069
        sep = "+";
3070
      }
3071
    }
3072
    msStringBufferAppend(classtext, ")");
55✔
3073
    const char *expressionstring = msStringBufferGetString(classtext);
55✔
3074
    if (strlen(expressionstring) > 2) {
55✔
3075
      msLoadExpressionString(&psClass->text, (char *)expressionstring);
55✔
3076
    }
3077
    msStringBufferFree(classtext);
55✔
3078

3079
    {
3080
      /* font */
3081
      psFont = CPLGetXMLNode(psRoot, "Font");
55✔
3082
      if (psFont) {
55✔
3083
        psCssParam = CPLGetXMLNode(psFont, "CssParameter");
55✔
3084
        /*sld 1.1 used SvgParameter*/
3085
        if (psCssParam == NULL)
55✔
3086
          psCssParam = CPLGetXMLNode(psFont, "SvgParameter");
41✔
3087

3088
        while (psCssParam && psCssParam->pszValue &&
234✔
3089
               (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
179✔
3090
                strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
123✔
3091
          pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
179✔
3092
          if (pszName) {
179✔
3093
            if (strcasecmp(pszName, "font-family") == 0) {
179✔
3094
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
55✔
3095
                  psCssParam->psChild->psNext->pszValue)
55✔
3096
                pszFontFamily = psCssParam->psChild->psNext->pszValue;
3097
            }
3098
            /* normal, italic, oblique */
3099
            else if (strcasecmp(pszName, "font-style") == 0) {
124✔
3100
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
14✔
3101
                  psCssParam->psChild->psNext->pszValue)
14✔
3102
                pszFontStyle = psCssParam->psChild->psNext->pszValue;
3103
            }
3104
            /* normal or bold */
3105
            else if (strcasecmp(pszName, "font-weight") == 0) {
110✔
3106
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
55✔
3107
                  psCssParam->psChild->psNext->pszValue)
55✔
3108
                pszFontWeight = psCssParam->psChild->psNext->pszValue;
3109
            }
3110
            /* default is 10 pix */
3111
            else if (strcasecmp(pszName, "font-size") == 0) {
55✔
3112
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
55✔
3113
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psLabelObj,
55✔
3114
                                        MS_LABEL_BINDING_SIZE, MS_OBJ_LABEL);
3115
              }
3116
            }
3117
          }
3118
          psCssParam = psCssParam->psNext;
179✔
3119
        }
3120
      }
3121

3122
      /* -------------------------------------------------------------------- */
3123
      /*      parse the label placement.                                      */
3124
      /* -------------------------------------------------------------------- */
3125
      psLabelPlacement = CPLGetXMLNode(psRoot, "LabelPlacement");
55✔
3126
      if (psLabelPlacement) {
55✔
3127
        psPointPlacement = CPLGetXMLNode(psLabelPlacement, "PointPlacement");
50✔
3128
        psLinePlacement = CPLGetXMLNode(psLabelPlacement, "LinePlacement");
50✔
3129
        if (psPointPlacement)
50✔
3130
          ParseTextPointPlacement(psPointPlacement, psClass);
50✔
3131
        if (psLinePlacement)
50✔
3132
          ParseTextLinePlacement(psLinePlacement, psClass);
×
3133
      }
3134

3135
      /* -------------------------------------------------------------------- */
3136
      /*      build the font name using the font font-family, font-style      */
3137
      /*      and font-weight. The name building uses a - between these       */
3138
      /*      parameters and the resulting name is compared to the list of    */
3139
      /*      available fonts. If the name exists, it will be used else we    */
3140
      /*      go to the bitmap fonts.                                         */
3141
      /* -------------------------------------------------------------------- */
3142
      if (pszFontFamily) {
55✔
3143
        snprintf(szFontName, sizeof(szFontName), "%s", pszFontFamily);
3144
        if (pszFontWeight && strcasecmp(pszFontWeight, "normal") != 0) {
55✔
3145
          strlcat(szFontName, "-", sizeof(szFontName));
55✔
3146
          strlcat(szFontName, pszFontWeight, sizeof(szFontName));
55✔
3147
        }
3148
        if (pszFontStyle && strcasecmp(pszFontStyle, "normal") != 0) {
55✔
3149
          strlcat(szFontName, "-", sizeof(szFontName));
14✔
3150
          strlcat(szFontName, pszFontStyle, sizeof(szFontName));
14✔
3151
        }
3152

3153
        if ((msLookupHashTable(&(psLayer->map->fontset.fonts), szFontName) !=
55✔
3154
             NULL)) {
3155
          psLabelObj->font = msStrdup(szFontName);
41✔
3156
        }
3157
      }
3158

3159
      /* -------------------------------------------------------------------- */
3160
      /*      parse the halo parameter.                                       */
3161
      /* -------------------------------------------------------------------- */
3162
      psHalo = CPLGetXMLNode(psRoot, "Halo");
55✔
3163
      if (psHalo) {
55✔
3164
        psHaloRadius = CPLGetXMLNode(psHalo, "Radius");
45✔
3165
        if (psHaloRadius && psHaloRadius->psChild &&
45✔
3166
            psHaloRadius->psChild->pszValue)
41✔
3167
          psLabelObj->outlinewidth = atoi(psHaloRadius->psChild->pszValue);
41✔
3168

3169
        psHaloFill = CPLGetXMLNode(psHalo, "Fill");
45✔
3170
        if (psHaloFill) {
45✔
3171
          psCssParam = CPLGetXMLNode(psHaloFill, "CssParameter");
41✔
3172
          /*sld 1.1 used SvgParameter*/
3173
          if (psCssParam == NULL)
41✔
3174
            psCssParam = CPLGetXMLNode(psHaloFill, "SvgParameter");
41✔
3175

3176
          while (psCssParam && psCssParam->pszValue &&
82✔
3177
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
41✔
3178
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
41✔
3179
            pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
41✔
3180
            if (pszName) {
41✔
3181
              if (strcasecmp(pszName, "fill") == 0) {
41✔
3182
                if (psCssParam->psChild && psCssParam->psChild->psNext) {
41✔
3183
                  msSLDParseOgcExpression(
41✔
3184
                      psCssParam->psChild->psNext, psLabelObj,
3185
                      MS_LABEL_BINDING_OUTLINECOLOR, MS_OBJ_LABEL);
3186
                }
3187
              }
3188
            }
3189
            psCssParam = psCssParam->psNext;
41✔
3190
          }
3191
        }
3192
      }
3193
      /* -------------------------------------------------------------------- */
3194
      /*      Parse the color                                                 */
3195
      /* -------------------------------------------------------------------- */
3196
      psFill = CPLGetXMLNode(psRoot, "Fill");
55✔
3197
      if (psFill) {
55✔
3198
        psCssParam = CPLGetXMLNode(psFill, "CssParameter");
55✔
3199
        /*sld 1.1 used SvgParameter*/
3200
        if (psCssParam == NULL)
55✔
3201
          psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
41✔
3202

3203
        while (psCssParam && psCssParam->pszValue &&
110✔
3204
               (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
55✔
3205
                strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
41✔
3206
          pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
55✔
3207
          if (pszName) {
55✔
3208
            if (strcasecmp(pszName, "fill") == 0) {
55✔
3209
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
55✔
3210
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psLabelObj,
55✔
3211
                                        MS_LABEL_BINDING_COLOR, MS_OBJ_LABEL);
3212
              }
3213
            }
3214
          }
3215
          psCssParam = psCssParam->psNext;
55✔
3216
        }
3217
      }
3218
    }
3219
  }
3220

3221
  return MS_SUCCESS;
3222
}
3223

3224
/************************************************************************/
3225
/*                         ParseTextPointPlacement                      */
3226
/*                                                                      */
3227
/*      point placement node for the text symbolizer.                  */
3228
/************************************************************************/
3229
int ParseTextPointPlacement(CPLXMLNode *psRoot, classObj *psClass) {
50✔
3230
  CPLXMLNode *psAnchor, *psAnchorX, *psAnchorY;
3231
  CPLXMLNode *psDisplacement, *psDisplacementX, *psDisplacementY;
3232
  CPLXMLNode *psRotation = NULL;
3233
  labelObj *psLabelObj = NULL;
3234

3235
  if (!psRoot || !psClass)
50✔
3236
    return MS_FAILURE;
3237
  if (psClass->numlabels == 0) {
50✔
3238
    if (msGrowClassLabels(psClass) == NULL)
×
3239
      return (MS_FAILURE);
3240
    initLabel(psClass->labels[0]);
×
3241
    psClass->numlabels++;
×
3242
  }
3243
  psLabelObj = psClass->labels[0];
50✔
3244

3245
  /* init the label with the default position */
3246
  psLabelObj->position = MS_CL;
50✔
3247

3248
  /* -------------------------------------------------------------------- */
3249
  /*      parse anchor point. see function msSLDParseTextSymbolizer       */
3250
  /*      for documentation.                                              */
3251
  /* -------------------------------------------------------------------- */
3252
  psAnchor = CPLGetXMLNode(psRoot, "AnchorPoint");
50✔
3253
  if (psAnchor) {
50✔
3254
    psAnchorX = CPLGetXMLNode(psAnchor, "AnchorPointX");
50✔
3255
    psAnchorY = CPLGetXMLNode(psAnchor, "AnchorPointY");
50✔
3256
    /* psCssParam->psChild->psNext->pszValue) */
3257
    if (psAnchorX && psAnchorX->psChild && psAnchorX->psChild->pszValue &&
50✔
3258
        psAnchorY && psAnchorY->psChild && psAnchorY->psChild->pszValue) {
50✔
3259
      const double dfAnchorX = atof(psAnchorX->psChild->pszValue);
3260
      const double dfAnchorY = atof(psAnchorY->psChild->pszValue);
50✔
3261

3262
      if ((dfAnchorX == 0 || dfAnchorX == 0.5 || dfAnchorX == 1) &&
50✔
3263
          (dfAnchorY == 0 || dfAnchorY == 0.5 || dfAnchorY == 1)) {
36✔
3264
        if (dfAnchorX == 0 && dfAnchorY == 0)
36✔
3265
          psLabelObj->position = MS_LL;
1✔
3266
        if (dfAnchorX == 0 && dfAnchorY == 0.5)
36✔
3267
          psLabelObj->position = MS_CL;
×
3268
        if (dfAnchorX == 0 && dfAnchorY == 1)
36✔
3269
          psLabelObj->position = MS_UL;
×
3270

3271
        if (dfAnchorX == 0.5 && dfAnchorY == 0)
36✔
3272
          psLabelObj->position = MS_LC;
×
3273
        if (dfAnchorX == 0.5 && dfAnchorY == 0.5)
36✔
3274
          psLabelObj->position = MS_CC;
35✔
3275
        if (dfAnchorX == 0.5 && dfAnchorY == 1)
36✔
3276
          psLabelObj->position = MS_UC;
×
3277

3278
        if (dfAnchorX == 1 && dfAnchorY == 0)
36✔
3279
          psLabelObj->position = MS_LR;
×
3280
        if (dfAnchorX == 1 && dfAnchorY == 0.5)
36✔
3281
          psLabelObj->position = MS_CR;
×
3282
        if (dfAnchorX == 1 && dfAnchorY == 1)
36✔
3283
          psLabelObj->position = MS_UR;
×
3284
      }
3285
    }
3286
  }
3287

3288
  /* -------------------------------------------------------------------- */
3289
  /*      Parse displacement                                              */
3290
  /* -------------------------------------------------------------------- */
3291
  psDisplacement = CPLGetXMLNode(psRoot, "Displacement");
50✔
3292
  if (psDisplacement) {
50✔
3293
    psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
50✔
3294
    psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
50✔
3295
    /* psCssParam->psChild->psNext->pszValue) */
3296
    if (psDisplacementX && psDisplacementX->psChild &&
50✔
3297
        psDisplacementX->psChild->pszValue && psDisplacementY &&
50✔
3298
        psDisplacementY->psChild && psDisplacementY->psChild->pszValue) {
50✔
3299
      psLabelObj->offsetx = atoi(psDisplacementX->psChild->pszValue);
50✔
3300
      psLabelObj->offsety = atoi(psDisplacementY->psChild->pszValue);
50✔
3301
    }
3302
  }
3303

3304
  /* -------------------------------------------------------------------- */
3305
  /*      parse rotation.                                                 */
3306
  /* -------------------------------------------------------------------- */
3307
  psRotation = CPLGetXMLNode(psRoot, "Rotation");
50✔
3308
  if (psRotation && psRotation->psChild) {
50✔
3309
    msSLDParseOgcExpression(psRotation->psChild, psLabelObj,
50✔
3310
                            MS_LABEL_BINDING_ANGLE, MS_OBJ_LABEL);
3311
  }
3312

3313
  return MS_SUCCESS;
3314
}
3315

3316
/************************************************************************/
3317
/*                          ParseTextLinePlacement                      */
3318
/*                                                                      */
3319
/*      Lineplacement node fro the text symbolizer.                     */
3320
/************************************************************************/
3321
int ParseTextLinePlacement(CPLXMLNode *psRoot, classObj *psClass) {
×
3322
  CPLXMLNode *psOffset = NULL, *psAligned = NULL;
3323
  labelObj *psLabelObj = NULL;
3324

3325
  if (!psRoot || !psClass)
×
3326
    return MS_FAILURE;
3327

3328
  if (psClass->numlabels == 0) {
×
3329
    if (msGrowClassLabels(psClass) == NULL)
×
3330
      return (MS_FAILURE);
3331
    initLabel(psClass->labels[0]);
×
3332
    psClass->numlabels++;
×
3333
  }
3334
  psLabelObj = psClass->labels[0];
×
3335

3336
  /*if there is a line placement, we will assume that the
3337
    best setting for mapserver would be for the text to follow
3338
    the line #2806*/
3339
  psLabelObj->anglemode = MS_FOLLOW;
×
3340

3341
  /*sld 1.1.0 has a parameter IsAligned. default value is true*/
3342
  psAligned = CPLGetXMLNode(psRoot, "IsAligned");
×
3343
  if (psAligned && psAligned->psChild && psAligned->psChild->pszValue &&
×
3344
      strcasecmp(psAligned->psChild->pszValue, "false") == 0) {
×
3345
    psLabelObj->anglemode = MS_NONE;
×
3346
  }
3347
  psOffset = CPLGetXMLNode(psRoot, "PerpendicularOffset");
×
3348
  if (psOffset && psOffset->psChild && psOffset->psChild->pszValue) {
×
3349
    psLabelObj->offsetx = atoi(psOffset->psChild->pszValue);
×
3350
    psLabelObj->offsety = MS_LABEL_PERPENDICULAR_OFFSET;
×
3351

3352
    /*if there is a PerpendicularOffset, we will assume that the
3353
      best setting for mapserver would be to use angle=0 and the
3354
      the offset #2806*/
3355
    /* since sld 1.1.0 introduces the IsAligned parameter, only
3356
       set the angles if the parameter is not set*/
3357
    if (!psAligned) {
×
3358
      psLabelObj->anglemode = MS_NONE;
×
3359
      psLabelObj->offsety = psLabelObj->offsetx;
×
3360
    }
3361
  }
3362

3363
  return MS_SUCCESS;
3364
}
3365

3366
/************************************************************************/
3367
/*           void msSLDSetColorObject(char *psHexColor, colorObj        */
3368
/*      *psColor)                                                       */
3369
/*                                                                      */
3370
/*      Utility function to extract rgb values from an hexadecimal     */
3371
/*      color string (format is : #aaff08) and set it in the color      */
3372
/*      object.                                                         */
3373
/************************************************************************/
3374
int msSLDSetColorObject(char *psHexColor, colorObj *psColor) {
×
3375
  if (psHexColor && psColor && strlen(psHexColor) == 7 &&
×
3376
      psHexColor[0] == '#') {
×
3377

3378
    psColor->red = msHexToInt(psHexColor + 1);
×
3379
    psColor->green = msHexToInt(psHexColor + 3);
×
3380
    psColor->blue = msHexToInt(psHexColor + 5);
×
3381
  }
3382

3383
  return MS_SUCCESS;
×
3384
}
3385

3386
/* -------------------------------------------------------------------- */
3387
/*      client sld support functions                                    */
3388
/* -------------------------------------------------------------------- */
3389

3390
/************************************************************************/
3391
/*                msSLDGenerateSLD(mapObj *map, int iLayer)             */
3392
/*                                                                      */
3393
/*      Return an SLD document for all layers that are on or            */
3394
/*      default. The second argument should be set to -1 to generate    */
3395
/*      on all layers. Or set to the layer index to generate an SLD     */
3396
/*      for a specific layer.                                           */
3397
/*                                                                      */
3398
/*      The caller should free the returned string.                     */
3399
/************************************************************************/
3400
char *msSLDGenerateSLD(mapObj *map, int iLayer, const char *pszVersion) {
62✔
3401
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3402
    defined(USE_SOS_SVR)
3403

3404
  char szTmp[500];
3405
  int i = 0;
3406
  char *pszTmp = NULL;
3407
  char *pszSLD = NULL;
3408
  char *schemalocation = NULL;
3409
  int sld_version = OWS_VERSION_NOTSET;
3410

3411
  sld_version = msOWSParseVersionString(pszVersion);
62✔
3412

3413
  if (sld_version == OWS_VERSION_NOTSET ||
62✔
3414
      (sld_version != OWS_1_0_0 && sld_version != OWS_1_1_0))
61✔
3415
    sld_version = OWS_1_0_0;
3416

3417
  if (map) {
62✔
3418
    schemalocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
62✔
3419
    if (sld_version == OWS_1_0_0)
62✔
3420
      snprintf(szTmp, sizeof(szTmp),
3421
               "<StyledLayerDescriptor version=\"1.0.0\" "
3422
               "xmlns=\"http://www.opengis.net/sld\" "
3423
               "xmlns:gml=\"http://www.opengis.net/gml\" "
3424
               "xmlns:ogc=\"http://www.opengis.net/ogc\" "
3425
               "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3426
               "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
3427
               "xsi:schemaLocation=\"http://www.opengis.net/sld "
3428
               "%s/sld/1.0.0/StyledLayerDescriptor.xsd\">\n",
3429
               schemalocation);
3430
    else
3431
      snprintf(szTmp, sizeof(szTmp),
3432
               "<StyledLayerDescriptor version=\"1.1.0\" "
3433
               "xsi:schemaLocation=\"http://www.opengis.net/sld "
3434
               "%s/sld/1.1.0/StyledLayerDescriptor.xsd\" "
3435
               "xmlns=\"http://www.opengis.net/sld\" "
3436
               "xmlns:ogc=\"http://www.opengis.net/ogc\" "
3437
               "xmlns:se=\"http://www.opengis.net/se\" "
3438
               "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3439
               "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n",
3440
               schemalocation);
3441

3442
    free(schemalocation);
62✔
3443

3444
    pszSLD = msStringConcatenate(pszSLD, szTmp);
62✔
3445
    if (iLayer < 0 || iLayer > map->numlayers - 1) {
62✔
3446
      for (i = 0; i < map->numlayers; i++) {
1,156✔
3447
        pszTmp = msSLDGenerateSLDLayer(GET_LAYER(map, i), sld_version);
1,094✔
3448
        if (pszTmp) {
1,094✔
3449
          pszSLD = msStringConcatenate(pszSLD, pszTmp);
110✔
3450
          free(pszTmp);
110✔
3451
        }
3452
      }
3453
    } else {
3454
      pszTmp = msSLDGenerateSLDLayer(GET_LAYER(map, iLayer), sld_version);
×
3455
      if (pszTmp) {
×
3456
        pszSLD = msStringConcatenate(pszSLD, pszTmp);
×
3457
        free(pszTmp);
×
3458
      }
3459
    }
3460
    snprintf(szTmp, sizeof(szTmp), "%s", "</StyledLayerDescriptor>\n");
3461
    pszSLD = msStringConcatenate(pszSLD, szTmp);
62✔
3462
  }
3463

3464
  return pszSLD;
62✔
3465

3466
#else
3467
  msSetError(MS_MISCERR, "OWS support is not available.",
3468
             "msSLDGenerateSLDLayer()");
3469
  return NULL;
3470

3471
#endif
3472
}
3473

3474
/************************************************************************/
3475
/*                            msSLDGetGraphicSLD                        */
3476
/*                                                                      */
3477
/*      Get an SLD for a style containing a symbol (Mark or external).  */
3478
/************************************************************************/
3479
char *msSLDGetGraphicSLD(styleObj *psStyle, layerObj *psLayer,
142✔
3480
                         int bNeedMarkSybol, int nVersion) {
3481
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3482
    defined(USE_SOS_SVR)
3483

3484
  msStringBuffer *sldString = msStringBufferAlloc();
142✔
3485
  int nSymbol = -1;
3486
  symbolObj *psSymbol = NULL;
3487
  char szTmp[512];
3488
  char szFormat[4];
3489
  int i = 0, nLength = 0;
3490
  int bColorAvailable = 0;
3491
  int bGenerateDefaultSymbol = 0;
3492
  char *pszSymbolName = NULL;
3493
  char sNameSpace[10];
3494
  char sCssParam[30];
3495

3496
  sCssParam[0] = '\0';
3497
  if (nVersion > OWS_1_0_0)
142✔
3498
    strcpy(sCssParam, "se:SvgParameter");
3499
  else
3500
    strcpy(sCssParam, "CssParameter");
3501

3502
  sNameSpace[0] = '\0';
142✔
3503
  if (nVersion > OWS_1_0_0)
142✔
3504
    strcpy(sNameSpace, "se:");
3505

3506
  if (psStyle && psLayer && psLayer->map) {
142✔
3507
    nSymbol = -1;
3508
    if (psStyle->symbol > 0)
142✔
3509
      nSymbol = psStyle->symbol;
3510
    else if (psStyle->symbolname)
122✔
3511
      nSymbol = msGetSymbolIndex(&psLayer->map->symbolset, psStyle->symbolname,
×
3512
                                 MS_FALSE);
3513

3514
    bGenerateDefaultSymbol = 0;
3515

3516
    if (bNeedMarkSybol &&
142✔
3517
        (nSymbol <= 0 || nSymbol >= psLayer->map->symbolset.numsymbols))
16✔
3518
      bGenerateDefaultSymbol = 1;
3519

3520
    if (nSymbol > 0 && nSymbol < psLayer->map->symbolset.numsymbols) {
142✔
3521
      psSymbol = psLayer->map->symbolset.symbol[nSymbol];
20✔
3522
      if (psSymbol->type == MS_SYMBOL_VECTOR ||
20✔
3523
          psSymbol->type == MS_SYMBOL_ELLIPSE) {
3524
        /* Mark symbol */
3525
        if (psSymbol->name)
17✔
3526

3527
        {
3528
          if (strcasecmp(psSymbol->name, "square") == 0 ||
17✔
3529
              strcasecmp(psSymbol->name, "circle") == 0 ||
14✔
3530
              strcasecmp(psSymbol->name, "triangle") == 0 ||
7✔
3531
              strcasecmp(psSymbol->name, "star") == 0 ||
7✔
3532
              strcasecmp(psSymbol->name, "cross") == 0 ||
2✔
3533
              strcasecmp(psSymbol->name, "x") == 0)
2✔
3534
            pszSymbolName = msStrdup(psSymbol->name);
15✔
3535
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_square", 22) ==
2✔
3536
                   0)
3537
            pszSymbolName = msStrdup("square");
×
3538
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_triangle",
2✔
3539
                               24) == 0)
3540
            pszSymbolName = msStrdup("triangle");
×
3541
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_circle", 22) ==
2✔
3542
                   0)
3543
            pszSymbolName = msStrdup("circle");
×
3544
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_star", 20) == 0)
2✔
3545
            pszSymbolName = msStrdup("star");
2✔
3546
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_cross", 21) ==
×
3547
                   0)
3548
            pszSymbolName = msStrdup("cross");
×
3549
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_x", 17) == 0)
×
3550
            pszSymbolName = msStrdup("X");
×
3551

3552
          if (pszSymbolName) {
17✔
3553
            colorObj sTmpFillColor = {128, 128, 128, 255};
3554
            colorObj sTmpStrokeColor = {0, 0, 0, 255};
3555
            int hasFillColor = 0;
3556
            int hasStrokeColor = 0;
3557

3558
            snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3559
            msStringBufferAppend(sldString, szTmp);
17✔
3560

3561
            snprintf(szTmp, sizeof(szTmp), "<%sMark>\n", sNameSpace);
3562
            msStringBufferAppend(sldString, szTmp);
17✔
3563

3564
            snprintf(szTmp, sizeof(szTmp),
3565
                     "<%sWellKnownName>%s</%sWellKnownName>\n", sNameSpace,
3566
                     pszSymbolName, sNameSpace);
3567
            msStringBufferAppend(sldString, szTmp);
17✔
3568

3569
            if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
17✔
3570
                psStyle->color.blue != -1) {
17✔
3571
              sTmpFillColor.red = psStyle->color.red;
3572
              sTmpFillColor.green = psStyle->color.green;
3573
              sTmpFillColor.blue = psStyle->color.blue;
3574
              sTmpFillColor.alpha = psStyle->color.alpha;
17✔
3575
              hasFillColor = 1;
3576
            }
3577
            if (psStyle->outlinecolor.red != -1 &&
17✔
3578
                psStyle->outlinecolor.green != -1 &&
12✔
3579
                psStyle->outlinecolor.blue != -1) {
12✔
3580
              sTmpStrokeColor.red = psStyle->outlinecolor.red;
3581
              sTmpStrokeColor.green = psStyle->outlinecolor.green;
3582
              sTmpStrokeColor.blue = psStyle->outlinecolor.blue;
3583
              sTmpStrokeColor.alpha = psStyle->outlinecolor.alpha;
12✔
3584
              hasStrokeColor = 1;
3585
              // Make defaults implicit
3586
              if (sTmpStrokeColor.red == 0 && sTmpStrokeColor.green == 0 &&
12✔
3587
                  sTmpStrokeColor.blue == 0 && sTmpStrokeColor.alpha == 255 &&
3✔
3588
                  psStyle->width == 1) {
3✔
3589
                hasStrokeColor = 0;
3590
              }
3591
            }
3592
            if (!hasFillColor && !hasStrokeColor) {
17✔
3593
              sTmpFillColor.red = 128;
3594
              sTmpFillColor.green = 128;
3595
              sTmpFillColor.blue = 128;
3596
              sTmpFillColor.alpha = 255;
3597
              hasFillColor = 1;
3598
            }
3599

3600
            if (hasFillColor) {
17✔
3601
              snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3602
              msStringBufferAppend(sldString, szTmp);
17✔
3603
              snprintf(szTmp, sizeof(szTmp),
3604
                       "<%s name=\"fill\">#%02x%02x%02x</%s>\n", sCssParam,
3605
                       sTmpFillColor.red, sTmpFillColor.green,
3606
                       sTmpFillColor.blue, sCssParam);
3607
              msStringBufferAppend(sldString, szTmp);
17✔
3608
              if (sTmpFillColor.alpha != 255 && sTmpFillColor.alpha != -1) {
17✔
3609
                snprintf(szTmp, sizeof(szTmp),
7✔
3610
                         "<%s name=\"fill-opacity\">%.2f</%s>\n", sCssParam,
3611
                         (float)sTmpFillColor.alpha / 255.0, sCssParam);
7✔
3612
                msStringBufferAppend(sldString, szTmp);
7✔
3613
              }
3614
              snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3615
              msStringBufferAppend(sldString, szTmp);
17✔
3616
            }
3617
            if (hasStrokeColor) {
17✔
3618
              snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3619
              msStringBufferAppend(sldString, szTmp);
9✔
3620
              snprintf(szTmp, sizeof(szTmp),
3621
                       "<%s name=\"stroke\">#%02x%02x%02x</%s>\n", sCssParam,
3622
                       sTmpStrokeColor.red, sTmpStrokeColor.green,
3623
                       sTmpStrokeColor.blue, sCssParam);
3624
              msStringBufferAppend(sldString, szTmp);
9✔
3625
              if (psStyle->width > 0) {
9✔
3626
                snprintf(szTmp, sizeof(szTmp),
3627
                         "<%s name=\"stroke-width\">%g</%s>\n", sCssParam,
3628
                         psStyle->width, sCssParam);
3629
                msStringBufferAppend(sldString, szTmp);
9✔
3630
              }
3631
              if (sTmpStrokeColor.alpha != 255 && sTmpStrokeColor.alpha != -1) {
9✔
3632
                snprintf(szTmp, sizeof(szTmp),
6✔
3633
                         "<%s name=\"stroke-opacity\">%.2f</%s>\n", sCssParam,
3634
                         (float)sTmpStrokeColor.alpha / 255.0, sCssParam);
6✔
3635
                msStringBufferAppend(sldString, szTmp);
6✔
3636
              }
3637
              snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3638
              msStringBufferAppend(sldString, szTmp);
9✔
3639
            }
3640

3641
            snprintf(szTmp, sizeof(szTmp), "</%sMark>\n", sNameSpace);
3642
            msStringBufferAppend(sldString, szTmp);
17✔
3643

3644
            if (psStyle->size > 0) {
17✔
3645
              snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n",
3646
                       sNameSpace, psStyle->size, sNameSpace);
3647
              msStringBufferAppend(sldString, szTmp);
17✔
3648
            }
3649

3650
            if (fmod(psStyle->angle, 360)) {
17✔
3651
              snprintf(szTmp, sizeof(szTmp), "<%sRotation>%g</%sRotation>\n",
7✔
3652
                       sNameSpace, psStyle->angle, sNameSpace);
3653
              msStringBufferAppend(sldString, szTmp);
7✔
3654
            }
3655
            // Style opacity is already reported to alpha channel of color and
3656
            // outlinecolor if (psStyle->opacity < 100)
3657
            // {
3658
            //   snprintf(szTmp, sizeof(szTmp), "<%sOpacity>%g</%sOpacity>\n",
3659
            //       sNameSpace, psStyle->opacity/100.0, sNameSpace);
3660
            //   pszSLD = msStringConcatenate(pszSLD, szTmp);
3661
            // }
3662

3663
            if (psStyle->offsetx != 0 || psStyle->offsety != 0) {
17✔
3664
              snprintf(szTmp, sizeof(szTmp), "<%sDisplacement>\n", sNameSpace);
3665
              msStringBufferAppend(sldString, szTmp);
7✔
3666
              snprintf(szTmp, sizeof(szTmp),
7✔
3667
                       "<%sDisplacementX>%g</%sDisplacementX>\n", sNameSpace,
3668
                       psStyle->offsetx, sNameSpace);
3669
              msStringBufferAppend(sldString, szTmp);
7✔
3670
              snprintf(szTmp, sizeof(szTmp),
7✔
3671
                       "<%sDisplacementY>%g</%sDisplacementY>\n", sNameSpace,
3672
                       psStyle->offsety, sNameSpace);
3673
              msStringBufferAppend(sldString, szTmp);
7✔
3674
              snprintf(szTmp, sizeof(szTmp), "</%sDisplacement>\n", sNameSpace);
3675
              msStringBufferAppend(sldString, szTmp);
7✔
3676
            }
3677

3678
            snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3679
            msStringBufferAppend(sldString, szTmp);
17✔
3680

3681
            if (pszSymbolName)
3682
              free(pszSymbolName);
17✔
3683
          }
3684
        } else
3685
          bGenerateDefaultSymbol = 1;
3686
      } else if (psSymbol->type == MS_SYMBOL_PIXMAP ||
3✔
3687
                 psSymbol->type == MS_SYMBOL_SVG) {
3688
        if (psSymbol->name) {
3✔
3689
          const char *pszURL =
3690
              msLookupHashTable(&(psLayer->metadata), "WMS_SLD_SYMBOL_URL");
3✔
3691
          if (!pszURL)
3✔
3692
            pszURL = msLookupHashTable(&(psLayer->map->web.metadata),
3✔
3693
                                       "WMS_SLD_SYMBOL_URL");
3694

3695
          if (pszURL) {
3✔
3696
            snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3697
            msStringBufferAppend(sldString, szTmp);
×
3698

3699
            snprintf(szTmp, sizeof(szTmp), "<%sExternalGraphic>\n", sNameSpace);
3700
            msStringBufferAppend(sldString, szTmp);
×
3701

3702
            snprintf(szTmp, sizeof(szTmp),
×
3703
                     "<%sOnlineResource "
3704
                     "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3705
                     "xlink:type=\"simple\" xlink:href=\"%s%s\"/>\n",
3706
                     sNameSpace, pszURL, psSymbol->imagepath);
3707
            msStringBufferAppend(sldString, szTmp);
×
3708
            /* TODO : extract format from symbol */
3709

3710
            szFormat[0] = '\0';
×
3711
            nLength = strlen(psSymbol->imagepath);
×
3712
            if (nLength > 3) {
×
3713
              for (i = 0; i <= 2; i++)
×
3714
                szFormat[2 - i] = psSymbol->imagepath[nLength - 1 - i];
×
3715
              szFormat[3] = '\0';
×
3716
            }
3717
            if (strlen(szFormat) > 0 && ((strcasecmp(szFormat, "GIF") == 0) ||
×
3718
                                         (strcasecmp(szFormat, "PNG") == 0))) {
×
3719
              if (strcasecmp(szFormat, "GIF") == 0)
×
3720
                snprintf(szTmp, sizeof(szTmp),
3721
                         "<%sFormat>image/gif</%sFormat>\n", sNameSpace,
3722
                         sNameSpace);
3723
              else
3724
                snprintf(szTmp, sizeof(szTmp),
3725
                         "<%sFormat>image/png</%sFormat>\n", sNameSpace,
3726
                         sNameSpace);
3727
            } else
3728
              snprintf(szTmp, sizeof(szTmp), "<%sFormat>%s</%sFormat>\n",
×
3729
                       sNameSpace,
3730
                       (psSymbol->type == MS_SYMBOL_SVG) ? "image/svg+xml"
×
3731
                                                         : "image/gif",
3732
                       sNameSpace);
3733

3734
            msStringBufferAppend(sldString, szTmp);
×
3735

3736
            snprintf(szTmp, sizeof(szTmp), "</%sExternalGraphic>\n",
3737
                     sNameSpace);
3738
            msStringBufferAppend(sldString, szTmp);
×
3739

3740
            if (psStyle->size > 0)
×
3741
              snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n",
3742
                       sNameSpace, psStyle->size, sNameSpace);
3743
            msStringBufferAppend(sldString, szTmp);
×
3744

3745
            snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3746
            msStringBufferAppend(sldString, szTmp);
×
3747
          }
3748
        }
3749
      }
3750
    }
3751
    if (bGenerateDefaultSymbol) { /* generate a default square symbol */
142✔
3752
      snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3753
      msStringBufferAppend(sldString, szTmp);
31✔
3754

3755
      snprintf(szTmp, sizeof(szTmp), "<%sMark>\n", sNameSpace);
3756
      msStringBufferAppend(sldString, szTmp);
31✔
3757

3758
      snprintf(szTmp, sizeof(szTmp), "<%sWellKnownName>%s</%sWellKnownName>\n",
3759
               sNameSpace, "square", sNameSpace);
3760
      msStringBufferAppend(sldString, szTmp);
31✔
3761

3762
      bColorAvailable = 0;
3763
      if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
31✔
3764
          psStyle->color.blue != -1) {
29✔
3765
        snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3766
        msStringBufferAppend(sldString, szTmp);
29✔
3767
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%02x%02x%02x</%s>\n",
29✔
3768
                 sCssParam, psStyle->color.red, psStyle->color.green,
3769
                 psStyle->color.blue, sCssParam);
3770
        msStringBufferAppend(sldString, szTmp);
29✔
3771
        snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3772
        msStringBufferAppend(sldString, szTmp);
29✔
3773
        bColorAvailable = 1;
3774
      }
3775
      if (psStyle->outlinecolor.red != -1 &&
31✔
3776
          psStyle->outlinecolor.green != -1 &&
×
3777
          psStyle->outlinecolor.blue != -1) {
×
3778
        snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3779
        msStringBufferAppend(sldString, szTmp);
×
3780
        snprintf(szTmp, sizeof(szTmp),
×
3781
                 "<%s name=\"Stroke\">#%02x%02x%02x</%s>\n", sCssParam,
3782
                 psStyle->outlinecolor.red, psStyle->outlinecolor.green,
3783
                 psStyle->outlinecolor.blue, sCssParam);
3784
        msStringBufferAppend(sldString, szTmp);
×
3785
        snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3786
        msStringBufferAppend(sldString, szTmp);
×
3787
        bColorAvailable = 1;
3788
      }
3789
      if (!bColorAvailable) {
31✔
3790
        /* default color */
3791
        snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3792
        msStringBufferAppend(sldString, szTmp);
2✔
3793
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">%s</%s>\n", sCssParam,
3794
                 "#808080", sCssParam);
3795
        msStringBufferAppend(sldString, szTmp);
2✔
3796
        snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3797
        msStringBufferAppend(sldString, szTmp);
2✔
3798
      }
3799

3800
      snprintf(szTmp, sizeof(szTmp), "</%sMark>\n", sNameSpace);
3801
      msStringBufferAppend(sldString, szTmp);
31✔
3802

3803
      if (psStyle->size > 0)
31✔
3804
        snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n", sNameSpace,
3805
                 psStyle->size, sNameSpace);
3806
      else
3807
        snprintf(szTmp, sizeof(szTmp), "<%sSize>%d</%sSize>\n", sNameSpace, 1,
3808
                 sNameSpace);
3809
      msStringBufferAppend(sldString, szTmp);
31✔
3810

3811
      snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3812
      msStringBufferAppend(sldString, szTmp);
31✔
3813
    }
3814
  }
3815

3816
  return msStringBufferReleaseStringAndFree(sldString);
142✔
3817

3818
#else
3819
  return NULL;
3820

3821
#endif
3822
}
3823

3824
/************************************************************************/
3825
/*                           msSLDGenerateLineSLD                       */
3826
/*                                                                      */
3827
/*      Generate SLD for a Line layer.                                  */
3828
/************************************************************************/
3829
char *msSLDGenerateLineSLD(styleObj *psStyle, layerObj *psLayer, int nVersion) {
54✔
3830
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3831
    defined(USE_SOS_SVR)
3832

3833
  char *pszSLD = NULL;
3834
  char szTmp[100];
3835
  char szHexColor[7];
3836
  int nSymbol = -1;
3837
  int i = 0;
3838
  double dfSize = 1.0;
3839
  char *pszDashArray = NULL;
3840
  char *pszGraphicSLD = NULL;
3841
  char sCssParam[30];
3842
  char sNameSpace[10];
3843

3844
  if (msCheckParentPointer(psLayer->map, "map") == MS_FAILURE)
54✔
3845
    return NULL;
3846

3847
  sCssParam[0] = '\0';
3848
  if (nVersion > OWS_1_0_0)
54✔
3849
    strcpy(sCssParam, "se:SvgParameter");
3850
  else
3851
    strcpy(sCssParam, "CssParameter");
3852

3853
  sNameSpace[0] = '\0';
54✔
3854
  if (nVersion > OWS_1_0_0)
54✔
3855
    strcpy(sNameSpace, "se:");
3856

3857
  snprintf(szTmp, sizeof(szTmp), "<%sLineSymbolizer>\n", sNameSpace);
3858

3859
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3860

3861
  snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3862

3863
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3864

3865
  pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
54✔
3866
  if (pszGraphicSLD) {
54✔
3867
    snprintf(szTmp, sizeof(szTmp), "<%sGraphicStroke>\n", sNameSpace);
3868

3869
    pszSLD = msStringConcatenate(pszSLD, szTmp);
2✔
3870

3871
    pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
2✔
3872

3873
    if (nVersion >= OWS_1_1_0) {
2✔
3874
      if (psStyle->gap > 0) {
1✔
3875
        snprintf(szTmp, sizeof(szTmp), "<%sGap>%.2f</%sGap>\n", sNameSpace,
3876
                 psStyle->gap, sNameSpace);
3877
      }
3878
      if (psStyle->initialgap > 0) {
1✔
3879
        snprintf(szTmp, sizeof(szTmp), "<%sInitialGap>%.2f</%sInitialGap>\n",
3880
                 sNameSpace, psStyle->initialgap, sNameSpace);
3881
      }
3882
    }
3883

3884
    snprintf(szTmp, sizeof(szTmp), "</%sGraphicStroke>\n", sNameSpace);
3885

3886
    pszSLD = msStringConcatenate(pszSLD, szTmp);
2✔
3887

3888
    free(pszGraphicSLD);
2✔
3889
    pszGraphicSLD = NULL;
3890
  }
3891

3892
  if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
54✔
3893
      psStyle->color.blue != -1)
54✔
3894
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x", psStyle->color.red,
3895
             psStyle->color.green, psStyle->color.blue);
3896
  else
3897
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x",
×
3898
             psStyle->outlinecolor.red, psStyle->outlinecolor.green,
3899
             psStyle->outlinecolor.blue);
3900

3901
  snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke\">#%s</%s>\n", sCssParam,
3902
           szHexColor, sCssParam);
3903
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3904

3905
  if (psStyle->color.alpha != 255 && psStyle->color.alpha != -1) {
54✔
3906
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-opacity\">%.2f</%s>\n",
14✔
3907
             sCssParam, (float)psStyle->color.alpha / 255.0, sCssParam);
14✔
3908
    pszSLD = msStringConcatenate(pszSLD, szTmp);
14✔
3909
  }
3910

3911
  nSymbol = -1;
3912

3913
  if (psStyle->symbol >= 0)
54✔
3914
    nSymbol = psStyle->symbol;
3915
  else if (psStyle->symbolname)
×
3916
    nSymbol = msGetSymbolIndex(&psLayer->map->symbolset, psStyle->symbolname,
×
3917
                               MS_FALSE);
3918

3919
  if (nSymbol < 0)
54✔
3920
    dfSize = 1.0;
3921
  else {
3922
    if (psStyle->size > 0)
54✔
3923
      dfSize = psStyle->size;
3924
    else if (psStyle->width > 0)
52✔
3925
      dfSize = psStyle->width;
3926
    else
3927
      dfSize = 1;
3928
  }
3929

3930
  snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-width\">%.2f</%s>\n",
3931
           sCssParam, dfSize, sCssParam);
3932
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3933

3934
  /* -------------------------------------------------------------------- */
3935
  /*      dash array                                                      */
3936
  /* -------------------------------------------------------------------- */
3937

3938
  if (psStyle->patternlength > 0) {
54✔
3939
    for (i = 0; i < psStyle->patternlength; i++) {
3✔
3940
      snprintf(szTmp, sizeof(szTmp), "%.2f ", psStyle->pattern[i]);
2✔
3941
      pszDashArray = msStringConcatenate(pszDashArray, szTmp);
2✔
3942
    }
3943
    // remove the final trailing space from the last pattern value
3944
    msStringTrimBlanks(pszDashArray);
1✔
3945

3946
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-dasharray\">%s</%s>\n",
3947
             sCssParam, pszDashArray, sCssParam);
3948
    pszSLD = msStringConcatenate(pszSLD, szTmp);
1✔
3949
    msFree(pszDashArray);
1✔
3950
  }
3951

3952
  snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3953

3954
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3955

3956
  snprintf(szTmp, sizeof(szTmp), "</%sLineSymbolizer>\n", sNameSpace);
3957

3958
  pszSLD = msStringConcatenate(pszSLD, szTmp);
54✔
3959

3960
  return pszSLD;
3961

3962
#else
3963
  return NULL;
3964
#endif
3965
}
3966

3967
/************************************************************************/
3968
/*                         msSLDGeneratePolygonSLD                      */
3969
/*                                                                      */
3970
/*       Generate SLD for a Polygon layer.                              */
3971
/************************************************************************/
3972
char *msSLDGeneratePolygonSLD(styleObj *psStyle, layerObj *psLayer,
43✔
3973
                              int nVersion) {
3974
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3975
    defined(USE_SOS_SVR)
3976

3977
  char szTmp[100];
3978
  char *pszSLD = NULL;
3979
  char szHexColor[7];
3980
  double dfSize;
3981
  char sCssParam[30];
3982
  char sNameSpace[10];
3983

3984
  sCssParam[0] = '\0';
3985
  if (nVersion > OWS_1_0_0)
43✔
3986
    strcpy(sCssParam, "se:SvgParameter");
3987
  else
3988
    strcpy(sCssParam, "CssParameter");
3989

3990
  sNameSpace[0] = '\0';
43✔
3991
  if (nVersion > OWS_1_0_0)
43✔
3992
    strcpy(sNameSpace, "se:");
3993

3994
  snprintf(szTmp, sizeof(szTmp), "<%sPolygonSymbolizer>\n", sNameSpace);
3995

3996
  pszSLD = msStringConcatenate(pszSLD, szTmp);
43✔
3997
  /* fill */
3998
  if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
43✔
3999
      psStyle->color.blue != -1) {
39✔
4000

4001
    snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4002

4003
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4004

4005
    char *pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
39✔
4006
    if (pszGraphicSLD) {
39✔
4007
      snprintf(szTmp, sizeof(szTmp), "<%sGraphicFill>\n", sNameSpace);
4008

4009
      pszSLD = msStringConcatenate(pszSLD, szTmp);
2✔
4010

4011
      pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
2✔
4012

4013
      snprintf(szTmp, sizeof(szTmp), "</%sGraphicFill>\n", sNameSpace);
4014

4015
      pszSLD = msStringConcatenate(pszSLD, szTmp);
2✔
4016

4017
      free(pszGraphicSLD);
2✔
4018
    }
4019

4020
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x", psStyle->color.red,
39✔
4021
             psStyle->color.green, psStyle->color.blue);
4022

4023
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%s</%s>\n", sCssParam,
4024
             szHexColor, sCssParam);
4025
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4026

4027
    if (psStyle->color.alpha != 255 && psStyle->color.alpha != -1) {
39✔
4028
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill-opacity\">%.2f</%s>\n",
7✔
4029
               sCssParam, (float)psStyle->color.alpha / 255, sCssParam);
7✔
4030
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4031
    }
4032

4033
    snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4034

4035
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4036
  }
4037
  /* stroke */
4038
  if (psStyle->outlinecolor.red != -1 && psStyle->outlinecolor.green != -1 &&
43✔
4039
      psStyle->outlinecolor.blue != -1) {
39✔
4040
    snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
4041
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4042

4043
    /* If there is a symbol to be used for stroke, the color in the */
4044
    /* style should be set to -1. Else It won't apply here. */
4045
    if (psStyle->color.red == -1 && psStyle->color.green == -1 &&
39✔
4046
        psStyle->color.blue == -1) {
2✔
4047
      char *pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
2✔
4048
      if (pszGraphicSLD) {
2✔
4049
        snprintf(szTmp, sizeof(szTmp), "<%sGraphicFill>\n", sNameSpace);
4050
        pszSLD = msStringConcatenate(pszSLD, szTmp);
×
4051

4052
        pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
×
4053
        snprintf(szTmp, sizeof(szTmp), "</%sGraphicFill>\n", sNameSpace);
4054
        pszSLD = msStringConcatenate(pszSLD, szTmp);
×
4055

4056
        free(pszGraphicSLD);
×
4057
      }
4058
    }
4059

4060
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x",
39✔
4061
             psStyle->outlinecolor.red, psStyle->outlinecolor.green,
4062
             psStyle->outlinecolor.blue);
4063

4064
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke\">#%s</%s>\n", sCssParam,
4065
             szHexColor, sCssParam);
4066
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4067

4068
    dfSize = 1.0;
4069
    if (psStyle->size > 0)
39✔
4070
      dfSize = psStyle->size;
4071
    else if (psStyle->width > 0)
37✔
4072
      dfSize = psStyle->width;
4073

4074
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-width\">%.2f</%s>\n",
4075
             sCssParam, dfSize, sCssParam);
4076
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4077

4078
    if (psStyle->outlinecolor.alpha != 255 &&
39✔
4079
        psStyle->outlinecolor.alpha != -1) {
4080
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-opacity\">%.2f</%s>\n",
7✔
4081
               sCssParam, psStyle->outlinecolor.alpha / 255.0, sCssParam);
4082
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4083
    }
4084

4085
    snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
4086
    pszSLD = msStringConcatenate(pszSLD, szTmp);
39✔
4087
  }
4088

4089
  snprintf(szTmp, sizeof(szTmp), "</%sPolygonSymbolizer>\n", sNameSpace);
4090
  pszSLD = msStringConcatenate(pszSLD, szTmp);
43✔
4091

4092
  return pszSLD;
43✔
4093

4094
#else
4095
  return NULL;
4096
#endif
4097
}
4098

4099
/************************************************************************/
4100
/*                          msSLDGeneratePointSLD                       */
4101
/*                                                                      */
4102
/*      Generate SLD for a Point layer.                                 */
4103
/************************************************************************/
4104
char *msSLDGeneratePointSLD(styleObj *psStyle, layerObj *psLayer,
47✔
4105
                            int nVersion) {
4106
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4107
    defined(USE_SOS_SVR)
4108
  char *pszSLD = NULL;
4109
  char *pszGraphicSLD = NULL;
4110
  char szTmp[100];
4111
  char sNameSpace[10];
4112

4113
  sNameSpace[0] = '\0';
47✔
4114
  if (nVersion > OWS_1_0_0)
47✔
4115
    strcpy(sNameSpace, "se:");
4116

4117
  snprintf(szTmp, sizeof(szTmp), "<%sPointSymbolizer>\n", sNameSpace);
4118
  pszSLD = msStringConcatenate(pszSLD, szTmp);
47✔
4119

4120
  pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 1, nVersion);
47✔
4121
  if (pszGraphicSLD) {
47✔
4122
    pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
44✔
4123
    free(pszGraphicSLD);
44✔
4124
  }
4125

4126
  snprintf(szTmp, sizeof(szTmp), "</%sPointSymbolizer>\n", sNameSpace);
4127
  pszSLD = msStringConcatenate(pszSLD, szTmp);
47✔
4128

4129
  return pszSLD;
47✔
4130

4131
#else
4132
  return NULL;
4133

4134
#endif
4135
}
4136

4137
/************************************************************************/
4138
/*                           msSLDGenerateTextSLD                       */
4139
/*                                                                      */
4140
/*      Generate a TextSymboliser SLD xml based on the class's label    */
4141
/*      object.                                                         */
4142
/************************************************************************/
4143
char *msSLDGenerateTextSLD(classObj *psClass, layerObj *psLayer, int nVersion) {
151✔
4144
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4145
    defined(USE_SOS_SVR)
4146
  char *pszSLD = NULL;
4147

4148
  char szTmp[1000];
4149
  char **aszFontsParts = NULL;
4150
  int nFontParts = 0;
151✔
4151
  char szHexColor[7];
4152
  double dfAnchorX = 0.5, dfAnchorY = 0.5;
4153
  int i = 0;
4154
  int lid;
4155
  char sCssParam[30];
4156
  char sNameSpace[10];
4157
  labelObj *psLabelObj = NULL;
4158

4159
  sCssParam[0] = '\0';
4160
  if (nVersion > OWS_1_0_0)
151✔
4161
    strcpy(sCssParam, "se:SvgParameter");
4162
  else
4163
    strcpy(sCssParam, "CssParameter");
4164

4165
  sNameSpace[0] = '\0';
151✔
4166
  if (nVersion > OWS_1_0_0)
151✔
4167
    strcpy(sNameSpace, "se:");
4168

4169
  if (!psLayer || !psClass)
151✔
4170
    return pszSLD;
4171

4172
  for (lid = 0; lid < psClass->numlabels; lid++) {
167✔
4173
    char *psLabelText;
4174
    expressionObj psLabelExpr;
4175
    parseObj p;
4176

4177
    msInitExpression(&psLabelExpr);
16✔
4178
    psLabelObj = psClass->labels[lid];
16✔
4179

4180
    if (psLabelObj->text.string) {
16✔
4181
      psLabelExpr.string = msStrdup(psLabelObj->text.string);
13✔
4182
      psLabelExpr.type = psLabelObj->text.type;
13✔
4183
    } else if (psClass->text.string) {
3✔
4184
      psLabelExpr.string = msStrdup(psClass->text.string);
2✔
4185
      psLabelExpr.type = psClass->text.type;
2✔
4186
    } else if (psLayer->labelitem) {
1✔
4187
      psLabelExpr.string = msStrdup(psLayer->labelitem);
×
4188
      psLabelExpr.type = MS_STRING;
×
4189
    } else {
4190
      msFreeExpression(&psLabelExpr);
1✔
4191
      continue; // Can't find text content for this <Label>
1✔
4192
    }
4193

4194
    if (psLabelExpr.type == MS_STRING) {
15✔
4195
      // Rewrite string to an expression so that literal strings and attributes
4196
      // are explicitly concatenated, e.g.:
4197
      //   "area is: [area]" becomes ("area is: "+"[area]"+"")
4198
      //             ^^^^^^                     ^^^^^^^^^^^^
4199
      char *result;
4200
      result = msStrdup("\"");
10✔
4201
      result = msStringConcatenate(result, psLabelExpr.string);
10✔
4202
      result = msStringConcatenate(result, "\"");
10✔
4203
      msTokenizeExpression(&psLabelExpr, NULL, NULL);
10✔
4204
      for (tokenListNodeObjPtr t = psLabelExpr.tokens; t; t = t->next) {
38✔
4205
        if (t->token == MS_TOKEN_BINDING_DOUBLE ||
28✔
4206
            t->token == MS_TOKEN_BINDING_INTEGER ||
28✔
4207
            t->token == MS_TOKEN_BINDING_STRING) {
4208
          const size_t nSizeTarget = strlen(t->tokenval.bindval.item) + 3;
10✔
4209
          char *target = static_cast<char *>(msSmallMalloc(nSizeTarget));
10✔
4210
          const size_t nSizeReplacement = strlen(t->tokenval.bindval.item) + 9;
10✔
4211
          char *replacement =
4212
              static_cast<char *>(msSmallMalloc(nSizeReplacement));
10✔
4213
          snprintf(target, nSizeTarget, "[%s]", t->tokenval.bindval.item);
10✔
4214
          snprintf(replacement, nSizeReplacement, "\"+\"[%s]\"+\"",
10✔
4215
                   t->tokenval.bindval.item);
4216
          result = msReplaceSubstring(result, target, replacement);
10✔
4217
          msFree(target);
10✔
4218
          msFree(replacement);
10✔
4219
        }
4220
      }
4221
      msFreeExpression(&psLabelExpr);
10✔
4222
      psLabelExpr.string = msStrdup(result);
10✔
4223
      psLabelExpr.type = MS_EXPRESSION;
10✔
4224
      msFree(result);
10✔
4225
    }
4226

4227
    // Parse label expression to generate SLD tags from MapFile syntax
4228
    msTokenizeExpression(&psLabelExpr, NULL, NULL);
15✔
4229
    p.expr = &psLabelExpr;
15✔
4230
    p.shape = NULL;
15✔
4231
    p.type = MS_PARSE_TYPE_SLD;
15✔
4232
    p.result.strval = NULL;
15✔
4233
    yyparse(&p);
15✔
4234
    // Not totally sure what we should do if p.result.strval. For now
4235
    // we export the raw MapServer expression.
4236
    psLabelText =
4237
        msStrdup(p.result.strval ? p.result.strval : psLabelExpr.string);
15✔
4238
    msFree(p.result.strval);
15✔
4239
    msFreeExpression(&psLabelExpr);
15✔
4240

4241
    snprintf(szTmp, sizeof(szTmp), "<%sTextSymbolizer>\n", sNameSpace);
4242
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4243

4244
    snprintf(szTmp, sizeof(szTmp), "<%sLabel>\n%s</%sLabel>\n", sNameSpace,
4245
             psLabelText, sNameSpace);
4246
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4247

4248
    /* -------------------------------------------------------------------- */
4249
    /*      only true type fonts are exported. Font name should be          */
4250
    /*      something like arial-bold-italic. There are 3 parts to the      */
4251
    /*      name font-family, font-style (italic, oblique, normal),         */
4252
    /*      font-weight (bold, normal). These 3 elements are separated      */
4253
    /*      with -.                                                         */
4254
    /* -------------------------------------------------------------------- */
4255
    if (psLabelObj->font) {
15✔
4256
      aszFontsParts = msStringSplit(psLabelObj->font, '-', &nFontParts);
15✔
4257
      if (nFontParts > 0) {
15✔
4258
        snprintf(szTmp, sizeof(szTmp), "<%sFont>\n", sNameSpace);
4259
        pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4260

4261
        /* assuming first one is font-family */
4262
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-family\">%s</%s>\n",
15✔
4263
                 sCssParam, aszFontsParts[0], sCssParam);
4264
        pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4265
        for (i = 1; i < nFontParts; i++) {
22✔
4266
          if (strcasecmp(aszFontsParts[i], "italic") == 0 ||
7✔
4267
              strcasecmp(aszFontsParts[i], "oblique") == 0) {
7✔
4268
            snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-style\">%s</%s>\n",
4269
                     sCssParam, aszFontsParts[i], sCssParam);
4270
            pszSLD = msStringConcatenate(pszSLD, szTmp);
×
4271
          } else if (strcasecmp(aszFontsParts[i], "bold") == 0) {
7✔
4272
            snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-weight\">%s</%s>\n",
4273
                     sCssParam, aszFontsParts[i], sCssParam);
4274
            pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4275
          }
4276
        }
4277
        /* size */
4278
        if (psLabelObj->size > 0) {
15✔
4279
          snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-size\">%d</%s>\n",
4280
                   sCssParam, psLabelObj->size, sCssParam);
4281
          pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4282
        }
4283
        snprintf(szTmp, sizeof(szTmp), "</%sFont>\n", sNameSpace);
4284
        pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4285
      }
4286
      msFreeCharArray(aszFontsParts, nFontParts);
15✔
4287
    }
4288

4289
    /* label placement */
4290
    snprintf(szTmp, sizeof(szTmp), "<%sLabelPlacement>\n<%sPointPlacement>\n",
4291
             sNameSpace, sNameSpace);
4292
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4293

4294
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPoint>\n", sNameSpace);
4295
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4296

4297
    if (psLabelObj->position == MS_LL) {
15✔
4298
      dfAnchorX = 0;
4299
      dfAnchorY = 0;
4300
    } else if (psLabelObj->position == MS_CL) {
14✔
4301
      dfAnchorX = 0;
4302
      dfAnchorY = 0.5;
4303
    } else if (psLabelObj->position == MS_UL) {
14✔
4304
      dfAnchorX = 0;
4305
      dfAnchorY = 1;
4306
    }
4307

4308
    else if (psLabelObj->position == MS_LC) {
14✔
4309
      dfAnchorX = 0.5;
4310
      dfAnchorY = 0;
4311
    } else if (psLabelObj->position == MS_CC) {
14✔
4312
      dfAnchorX = 0.5;
4313
      dfAnchorY = 0.5;
4314
    } else if (psLabelObj->position == MS_UC) {
×
4315
      dfAnchorX = 0.5;
4316
      dfAnchorY = 1;
4317
    }
4318

4319
    else if (psLabelObj->position == MS_LR) {
×
4320
      dfAnchorX = 1;
4321
      dfAnchorY = 0;
4322
    } else if (psLabelObj->position == MS_CR) {
×
4323
      dfAnchorX = 1;
4324
      dfAnchorY = 0.5;
4325
    } else if (psLabelObj->position == MS_UR) {
×
4326
      dfAnchorX = 1;
4327
      dfAnchorY = 1;
4328
    }
4329
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPointX>%.1f</%sAnchorPointX>\n",
4330
             sNameSpace, dfAnchorX, sNameSpace);
4331
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4332
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPointY>%.1f</%sAnchorPointY>\n",
4333
             sNameSpace, dfAnchorY, sNameSpace);
4334
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4335

4336
    snprintf(szTmp, sizeof(szTmp), "</%sAnchorPoint>\n", sNameSpace);
4337
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4338

4339
    /* displacement */
4340
    if (psLabelObj->offsetx > 0 || psLabelObj->offsety > 0) {
15✔
4341
      snprintf(szTmp, sizeof(szTmp), "<%sDisplacement>\n", sNameSpace);
4342
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4343

4344
      if (psLabelObj->offsetx > 0) {
7✔
4345
        snprintf(szTmp, sizeof(szTmp),
4346
                 "<%sDisplacementX>%d</%sDisplacementX>\n", sNameSpace,
4347
                 psLabelObj->offsetx, sNameSpace);
4348
        pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4349
      }
4350
      if (psLabelObj->offsety > 0) {
7✔
4351
        snprintf(szTmp, sizeof(szTmp),
4352
                 "<%sDisplacementY>%d</%sDisplacementY>\n", sNameSpace,
4353
                 psLabelObj->offsety, sNameSpace);
4354
        pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4355
      }
4356

4357
      snprintf(szTmp, sizeof(szTmp), "</%sDisplacement>\n", sNameSpace);
4358
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4359
    }
4360

4361
    /* rotation */
4362
    if (psLabelObj->angle > 0) {
15✔
4363
      snprintf(szTmp, sizeof(szTmp), "<%sRotation>%.2f</%sRotation>\n",
4364
               sNameSpace, psLabelObj->angle, sNameSpace);
4365
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4366
    }
4367

4368
    snprintf(szTmp, sizeof(szTmp), "</%sPointPlacement>\n</%sLabelPlacement>\n",
4369
             sNameSpace, sNameSpace);
4370
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4371

4372
    if (psLabelObj->outlinecolor.red != -1 &&
15✔
4373
        psLabelObj->outlinecolor.green != -1 &&
7✔
4374
        psLabelObj->outlinecolor.blue != -1) {
7✔
4375
      snprintf(szTmp, sizeof(szTmp), "<%sHalo>\n", sNameSpace);
4376
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4377
      snprintf(szTmp, sizeof(szTmp), "<%sRadius>%d</%sRadius>\n", sNameSpace,
7✔
4378
               psLabelObj->outlinewidth, sNameSpace);
4379
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4380
      snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4381
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4382
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%02x%02x%02x</%s>\n",
7✔
4383
               sCssParam, psLabelObj->outlinecolor.red,
4384
               psLabelObj->outlinecolor.green, psLabelObj->outlinecolor.blue,
4385
               sCssParam);
4386
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4387
      snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4388
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4389
      snprintf(szTmp, sizeof(szTmp), "</%sHalo>\n", sNameSpace);
4390
      pszSLD = msStringConcatenate(pszSLD, szTmp);
7✔
4391
    }
4392

4393
    /* color */
4394
    if (psLabelObj->color.red != -1 && psLabelObj->color.green != -1 &&
15✔
4395
        psLabelObj->color.blue != -1) {
15✔
4396
      snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4397
      pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4398

4399
      snprintf(szHexColor, sizeof(szHexColor), "%02hhx%02hhx%02hhx",
4400
               (unsigned char)psLabelObj->color.red,
15✔
4401
               (unsigned char)psLabelObj->color.green,
15✔
4402
               (unsigned char)psLabelObj->color.blue);
15✔
4403

4404
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%s</%s>\n", sCssParam,
4405
               szHexColor, sCssParam);
4406
      pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4407

4408
      snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4409
      pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4410
    }
4411

4412
    snprintf(szTmp, sizeof(szTmp), "</%sTextSymbolizer>\n", sNameSpace);
4413
    pszSLD = msStringConcatenate(pszSLD, szTmp);
15✔
4414

4415
    msFree(psLabelText);
15✔
4416
  }
4417
  return pszSLD;
4418

4419
#else
4420
  return NULL;
4421
#endif
4422
}
4423

4424
#if (defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||   \
4425
     defined(USE_SOS_SVR))
4426

4427
static void msSLDAppendName(msStringBuffer *sb, const char *pszName,
279✔
4428
                            int nVersion) {
4429
  char *pszEncoded = msEncodeHTMLEntities(pszName);
279✔
4430
  msStringBufferAppend(sb, (nVersion > OWS_1_0_0) ? "<se:Name>" : "<Name>");
294✔
4431
  msStringBufferAppend(sb, pszEncoded);
279✔
4432
  msStringBufferAppend(sb,
294✔
4433
                       (nVersion > OWS_1_0_0) ? "</se:Name>\n" : "</Name>\n");
4434
  msFree(pszEncoded);
279✔
4435
}
279✔
4436

4437
static void msSLDGenerateUserStyle(msStringBuffer *sb, layerObj *psLayer,
133✔
4438
                                   int nVersion, const char *pszTargetGroup) {
4439
  const char *pszWfsFilter;
4440

4441
  msStringBufferAppend(sb, "<UserStyle>\n");
133✔
4442

4443
  if (pszTargetGroup) {
133✔
4444
    msSLDAppendName(sb, pszTargetGroup, nVersion);
68✔
4445
    if (psLayer->classgroup &&
68✔
4446
        strcmp(psLayer->classgroup, pszTargetGroup) == 0) {
66✔
4447
      msStringBufferAppend(sb, nVersion > OWS_1_0_0
40✔
4448
                                   ? "<se:IsDefault>true</se:IsDefault>\n"
4449
                                   : "<IsDefault>true</IsDefault>\n");
4450
    }
4451
  }
4452

4453
  msStringBufferAppend(sb, nVersion > OWS_1_0_0 ? "<se:FeatureTypeStyle>\n"
140✔
4454
                                                : "<FeatureTypeStyle>\n");
4455

4456
  pszWfsFilter = msLookupHashTable(&(psLayer->metadata), "wfs_filter");
133✔
4457
  if (psLayer->numclasses > 0) {
133✔
4458
    int i;
4459
    for (i = 0; i < psLayer->numclasses; i++) {
343✔
4460
      char *pszFilter;
4461
      double dfMinScale = -1, dfMaxScale = -1;
4462

4463
      /* Only write down classes that match the group of interest */
4464
      if (psLayer->_class[i]->group) {
210✔
4465
        if (pszTargetGroup == NULL ||
132✔
4466
            strcmp(psLayer->_class[i]->group, pszTargetGroup) != 0) {
132✔
4467
          continue;
59✔
4468
        }
4469
      } else if (pszTargetGroup != NULL) {
78✔
4470
        continue;
×
4471
      }
4472

4473
      msStringBufferAppend(sb,
161✔
4474
                           nVersion > OWS_1_0_0 ? "<se:Rule>\n" : "<Rule>\n");
4475

4476
      /* if class has a name, use it as the RULE name */
4477
      if (psLayer->_class[i]->name) {
151✔
4478
        msSLDAppendName(sb, psLayer->_class[i]->name, nVersion);
101✔
4479
      }
4480
      /* -------------------------------------------------------------------- */
4481
      /*      get the Filter if there is a class expression.                  */
4482
      /* -------------------------------------------------------------------- */
4483
      pszFilter = msSLDGetFilter(psLayer->_class[i], pszWfsFilter);
151✔
4484

4485
      if (pszFilter) {
151✔
4486
        msStringBufferAppend(sb, pszFilter);
61✔
4487
        free(pszFilter);
61✔
4488
      }
4489
      /* -------------------------------------------------------------------- */
4490
      /*      generate the min/max scale.                                     */
4491
      /* -------------------------------------------------------------------- */
4492
      dfMinScale = -1.0;
4493
      if (psLayer->_class[i]->minscaledenom > 0)
151✔
4494
        dfMinScale = psLayer->_class[i]->minscaledenom;
4495
      else if (psLayer->minscaledenom > 0)
151✔
4496
        dfMinScale = psLayer->minscaledenom;
4497
      else if (psLayer->map && psLayer->map->web.minscaledenom > 0)
147✔
4498
        dfMinScale = psLayer->map->web.minscaledenom;
4499
      if (dfMinScale > 0) {
151✔
4500
        char szTmp[100];
4501
        if (nVersion > OWS_1_0_0)
4✔
4502
          snprintf(szTmp, sizeof(szTmp),
4503
                   "<se:MinScaleDenominator>%f</se:MinScaleDenominator>\n",
4504
                   dfMinScale);
4505
        else
4506
          snprintf(szTmp, sizeof(szTmp),
4507
                   "<MinScaleDenominator>%f</MinScaleDenominator>\n",
4508
                   dfMinScale);
4509

4510
        msStringBufferAppend(sb, szTmp);
4✔
4511
      }
4512

4513
      dfMaxScale = -1.0;
4514
      if (psLayer->_class[i]->maxscaledenom > 0)
151✔
4515
        dfMaxScale = psLayer->_class[i]->maxscaledenom;
4516
      else if (psLayer->maxscaledenom > 0)
151✔
4517
        dfMaxScale = psLayer->maxscaledenom;
4518
      else if (psLayer->map && psLayer->map->web.maxscaledenom > 0)
151✔
4519
        dfMaxScale = psLayer->map->web.maxscaledenom;
4520
      if (dfMaxScale > 0) {
151✔
4521
        char szTmp[100];
4522
        if (nVersion > OWS_1_0_0)
×
4523
          snprintf(szTmp, sizeof(szTmp),
4524
                   "<se:MaxScaleDenominator>%f</se:MaxScaleDenominator>\n",
4525
                   dfMaxScale);
4526
        else
4527
          snprintf(szTmp, sizeof(szTmp),
4528
                   "<MaxScaleDenominator>%f</MaxScaleDenominator>\n",
4529
                   dfMaxScale);
4530

4531
        msStringBufferAppend(sb, szTmp);
×
4532
      }
4533

4534
      /* -------------------------------------------------------------------- */
4535
      /*      Line symbolizer.                                                */
4536
      /*                                                                      */
4537
      /*      Right now only generates a stroke element containing css        */
4538
      /*      parameters.                                                     */
4539
      /*      Lines using symbols TODO (specially for dash lines)             */
4540
      /* -------------------------------------------------------------------- */
4541
      if (psLayer->type == MS_LAYER_LINE) {
151✔
4542
        int j;
4543
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
108✔
4544
          styleObj *psStyle = psLayer->_class[i]->styles[j];
54✔
4545
          char *pszSLD = msSLDGenerateLineSLD(psStyle, psLayer, nVersion);
54✔
4546
          if (pszSLD) {
54✔
4547
            msStringBufferAppend(sb, pszSLD);
54✔
4548
            free(pszSLD);
54✔
4549
          }
4550
        }
4551

4552
      } else if (psLayer->type == MS_LAYER_POLYGON) {
97✔
4553
        int j;
4554
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
84✔
4555
          styleObj *psStyle = psLayer->_class[i]->styles[j];
43✔
4556
          char *pszSLD = msSLDGeneratePolygonSLD(psStyle, psLayer, nVersion);
43✔
4557
          if (pszSLD) {
43✔
4558
            msStringBufferAppend(sb, pszSLD);
43✔
4559
            free(pszSLD);
43✔
4560
          }
4561
        }
4562

4563
      } else if (psLayer->type == MS_LAYER_POINT) {
56✔
4564
        int j;
4565
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
103✔
4566
          styleObj *psStyle = psLayer->_class[i]->styles[j];
47✔
4567
          char *pszSLD = msSLDGeneratePointSLD(psStyle, psLayer, nVersion);
47✔
4568
          if (pszSLD) {
47✔
4569
            msStringBufferAppend(sb, pszSLD);
47✔
4570
            free(pszSLD);
47✔
4571
          }
4572
        }
4573
      }
4574
      {
4575
        /* label if it exists */
4576
        char *pszSLD =
4577
            msSLDGenerateTextSLD(psLayer->_class[i], psLayer, nVersion);
151✔
4578
        if (pszSLD) {
151✔
4579
          msStringBufferAppend(sb, pszSLD);
15✔
4580
          free(pszSLD);
15✔
4581
        }
4582
      }
4583
      msStringBufferAppend(sb,
161✔
4584
                           nVersion > OWS_1_0_0 ? "</se:Rule>\n" : "</Rule>\n");
4585
    }
4586
  }
4587

4588
  msStringBufferAppend(sb, nVersion > OWS_1_0_0 ? "</se:FeatureTypeStyle>\n"
140✔
4589
                                                : "</FeatureTypeStyle>\n");
4590
  msStringBufferAppend(sb, "</UserStyle>\n");
133✔
4591
}
133✔
4592

4593
#endif
4594

4595
/************************************************************************/
4596
/*                          msSLDGenerateSLDLayer                       */
4597
/*                                                                      */
4598
/*      Generate an SLD XML string based on the layer's classes.        */
4599
/************************************************************************/
4600
char *msSLDGenerateSLDLayer(layerObj *psLayer, int nVersion) {
1,094✔
4601
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4602
    defined(USE_SOS_SVR)
4603

4604
  const char *pszWMSLayerName = NULL;
4605
  msStringBuffer *sb = msStringBufferAlloc();
1,094✔
4606

4607
  if (psLayer && (psLayer->status == MS_ON || psLayer->status == MS_DEFAULT) &&
1,094✔
4608
      (psLayer->type == MS_LAYER_POINT || psLayer->type == MS_LAYER_LINE ||
112✔
4609
       psLayer->type == MS_LAYER_POLYGON)) {
4610

4611
    int i;
4612
    int numClassGroupNames = 0;
4613
    char **papszClassGroupNames =
4614
        (char **)msSmallMalloc(sizeof(char *) * psLayer->numclasses);
110✔
4615
    for (i = 0; i < psLayer->numclasses; i++) {
261✔
4616
      const char *group = psLayer->_class[i]->group;
151✔
4617
      int j;
4618
      for (j = 0; j < numClassGroupNames; j++) {
180✔
4619
        if (group == NULL) {
47✔
4620
          if (papszClassGroupNames[j] == NULL)
13✔
4621
            break;
4622
        } else if (papszClassGroupNames[j] != NULL &&
34✔
4623
                   strcmp(papszClassGroupNames[j], group) == 0) {
34✔
4624
          break;
4625
        }
4626
      }
4627
      if (j >= numClassGroupNames) {
151✔
4628
        papszClassGroupNames[numClassGroupNames] =
133✔
4629
            group ? msStrdup(group) : NULL;
133✔
4630
        numClassGroupNames++;
133✔
4631
      }
4632
    }
4633

4634
    msStringBufferAppend(sb, "<NamedLayer>\n");
110✔
4635

4636
    pszWMSLayerName = msOWSLookupMetadata(&(psLayer->metadata), "MO", "name");
110✔
4637
    msSLDAppendName(sb,
220✔
4638
                    pszWMSLayerName ? pszWMSLayerName
4639
                    : psLayer->name ? psLayer->name
110✔
4640
                                    : "NamedLayer",
4641
                    nVersion);
4642

4643
    for (i = 0; i < numClassGroupNames; i++) {
243✔
4644
      msSLDGenerateUserStyle(sb, psLayer, nVersion, papszClassGroupNames[i]);
133✔
4645
      msFree(papszClassGroupNames[i]);
133✔
4646
    }
4647
    msFree(papszClassGroupNames);
110✔
4648

4649
    msStringBufferAppend(sb, "</NamedLayer>\n");
110✔
4650
  }
4651
  return msStringBufferReleaseStringAndFree(sb);
1,094✔
4652

4653
#else
4654
  msSetError(MS_MISCERR, "OWS support is not available.",
4655
             "msSLDGenerateSLDLayer()");
4656
  return NULL;
4657
#endif
4658
}
4659

4660
static const char *msSLDGetComparisonValue(const char *pszExpression) {
49✔
4661
  if (!pszExpression)
49✔
4662
    return NULL;
4663

4664
  if (strstr(pszExpression, "<=") || strstr(pszExpression, " le "))
49✔
4665
    return "PropertyIsLessThanOrEqualTo";
4666
  else if (strstr(pszExpression, "=~"))
43✔
4667
    return "PropertyIsLike";
4668
  else if (strstr(pszExpression, "~*"))
43✔
4669
    return "PropertyIsLike";
4670
  else if (strstr(pszExpression, ">=") || strstr(pszExpression, " ge "))
43✔
4671
    return "PropertyIsGreaterThanOrEqualTo";
4672
  else if (strstr(pszExpression, "!=") || strstr(pszExpression, " ne ") ||
43✔
4673
           strstr(pszExpression, "<>"))
4674
    return "PropertyIsNotEqualTo";
4675
  else if (strstr(pszExpression, "=") || strstr(pszExpression, " eq "))
37✔
4676
    return "PropertyIsEqualTo";
4677
  else if (strstr(pszExpression, "<") || strstr(pszExpression, " lt "))
7✔
4678
    return "PropertyIsLessThan";
4679
  else if (strstr(pszExpression, ">") || strstr(pszExpression, " gt "))
7✔
4680
    return "PropertyIsGreaterThan";
7✔
4681

4682
  return NULL;
4683
}
4684

4685
static const char *msSLDGetLogicalOperator(const char *pszExpression) {
19✔
4686

4687
  if (!pszExpression)
19✔
4688
    return NULL;
4689

4690
  if (strcasestr(pszExpression, " AND ") || strcasestr(pszExpression, "AND("))
19✔
4691
    return "And";
4692

4693
  if (strcasestr(pszExpression, " OR ") || strcasestr(pszExpression, "OR("))
10✔
4694
    return "Or";
4695

4696
  if (strcasestr(pszExpression, "NOT ") || strcasestr(pszExpression, "NOT("))
4✔
4697
    return "Not";
4✔
4698

4699
  return NULL;
4700
}
4701

4702
static char *msSLDGetRightExpressionOfOperator(const char *pszExpression) {
19✔
4703
  const char *pszAnd = NULL, *pszOr = NULL, *pszNot = NULL;
4704

4705
  pszAnd = strcasestr(pszExpression, " AND ");
19✔
4706

4707
  if (!pszAnd) {
19✔
4708
    pszAnd = strcasestr(pszExpression, "AND(");
11✔
4709
  }
4710

4711
  if (pszAnd)
19✔
4712
    return msStrdup(pszAnd + 4);
9✔
4713
  else {
4714
    pszOr = strcasestr(pszExpression, " OR ");
10✔
4715

4716
    if (!pszOr) {
10✔
4717
      pszOr = strcasestr(pszExpression, "OR(");
5✔
4718
    }
4719

4720
    if (pszOr)
10✔
4721
      return msStrdup(pszOr + 3);
6✔
4722
    else {
4723
      pszNot = strcasestr(pszExpression, "NOT ");
4✔
4724

4725
      if (!pszNot)
4✔
4726
        pszNot = strcasestr(pszExpression, "NOT(");
1✔
4727

4728
      if (pszNot)
4✔
4729
        return msStrdup(pszNot + 4);
4✔
4730
    }
4731
  }
4732
  return NULL;
4733
}
4734

4735
static char *msSLDGetLeftExpressionOfOperator(const char *pszExpression) {
19✔
4736
  char *pszReturn = NULL;
4737
  int nLength = 0, iReturn = 0;
4738

4739
  if (!pszExpression || (nLength = strlen(pszExpression)) <= 0)
19✔
4740
    return NULL;
4741

4742
  pszReturn = (char *)malloc(sizeof(char) * (nLength + 1));
19✔
4743
  pszReturn[0] = '\0';
19✔
4744
  if (strcasestr(pszExpression, " AND ")) {
19✔
4745
    for (int i = 0; i < nLength - 5; i++) {
92✔
4746
      if (pszExpression[i] == ' ' && (toupper(pszExpression[i + 1]) == 'A') &&
92✔
4747
          (toupper(pszExpression[i + 2]) == 'N') &&
8✔
4748
          (toupper(pszExpression[i + 3]) == 'D') &&
8✔
4749
          (pszExpression[i + 4] == ' '))
8✔
4750
        break;
4751
      else {
4752
        pszReturn[iReturn++] = pszExpression[i];
84✔
4753
      }
4754
      pszReturn[iReturn] = '\0';
84✔
4755
    }
4756
  } else if (strcasestr(pszExpression, "AND(")) {
11✔
4757
    for (int i = 0; i < nLength - 4; i++) {
12✔
4758
      if ((toupper(pszExpression[i]) == 'A') &&
12✔
4759
          (toupper(pszExpression[i + 1]) == 'N') &&
1✔
4760
          (toupper(pszExpression[i + 2]) == 'D') &&
1✔
4761
          (pszExpression[i + 3] == '('))
1✔
4762
        break;
4763
      else {
4764
        pszReturn[iReturn++] = pszExpression[i];
11✔
4765
      }
4766
      pszReturn[iReturn] = '\0';
11✔
4767
    }
4768
  } else if (strcasestr(pszExpression, " OR ")) {
10✔
4769
    for (int i = 0; i < nLength - 4; i++) {
84✔
4770
      if (pszExpression[i] == ' ' && (toupper(pszExpression[i + 1]) == 'O') &&
84✔
4771
          (toupper(pszExpression[i + 2]) == 'R') && pszExpression[i + 3] == ' ')
5✔
4772
        break;
4773
      else {
4774
        pszReturn[iReturn++] = pszExpression[i];
79✔
4775
      }
4776
      pszReturn[iReturn] = '\0';
79✔
4777
    }
4778
  } else if (strcasestr(pszExpression, "OR(")) {
5✔
4779
    for (int i = 0; i < nLength - 3; i++) {
12✔
4780
      if ((toupper(pszExpression[i]) == 'O') &&
12✔
4781
          (toupper(pszExpression[i + 1]) == 'R') && pszExpression[i + 2] == '(')
1✔
4782
        break;
4783
      else {
4784
        pszReturn[iReturn++] = pszExpression[i];
11✔
4785
      }
4786
      pszReturn[iReturn] = '\0';
11✔
4787
    }
4788
  } else {
4789
    msFree(pszReturn);
4✔
4790
    return NULL;
4✔
4791
  }
4792

4793
  return pszReturn;
4794
}
4795

4796
static int msSLDNumberOfLogicalOperators(const char *pszExpression) {
34✔
4797
  /* -------------------------------------------------------------------- */
4798
  /*      tests here are minimal to be able to parse simple expression    */
4799
  /*      like A AND B, A OR B, NOT A.                                    */
4800
  /*      TODO - add proper expression parsing                            */
4801
  /* -------------------------------------------------------------------- */
4802
  if (!pszExpression)
34✔
4803
    return 0;
4804

4805
  int nAndCount = 0;
4806
  int nNotCount = 0;
4807
  int nOrCount = 0;
4808

4809
  int nArgs = 0;
34✔
4810
  char **papszArgs;
4811
  papszArgs = msStringTokenize(pszExpression, " ", &nArgs, MS_TRUE);
34✔
4812

4813
  for (int i = 0; i < nArgs; i++) {
214✔
4814
    if (strlen(papszArgs[i]) == 0) {
180✔
4815
      free(papszArgs[i]);
19✔
4816
      continue;
19✔
4817
    }
4818

4819
    if ((strncasecmp(papszArgs[i], "AND", 3) == 0) ||
161✔
4820
        (strcasestr(papszArgs[i], "AND("))) {
153✔
4821
      nAndCount += 1;
9✔
4822
    }
4823

4824
    if ((strncasecmp(papszArgs[i], "OR", 3) == 0) ||
161✔
4825
        (strcasestr(papszArgs[i], "OR("))) {
156✔
4826
      nOrCount += 1;
6✔
4827
    }
4828

4829
    if ((strncasecmp(papszArgs[i], "NOT", 3) == 0) ||
161✔
4830
        (strcasestr(papszArgs[i], "NOT("))) {
157✔
4831
      nNotCount += 1;
4✔
4832
    }
4833

4834
    free(papszArgs[i]);
161✔
4835
  }
4836
  free(papszArgs);
34✔
4837

4838
  if (nAndCount == 0 && nNotCount == 0 && nOrCount == 0) {
34✔
4839
    return 0;
4840
  }
4841

4842
  int sum = nAndCount + nNotCount + nOrCount;
19✔
4843
  if (sum == 1) {
19✔
4844
    return 1;
4845
  } else {
4846
    return 2;
×
4847
  }
4848
}
4849

4850
static char *msSLDGetAttributeNameOrValue(const char *pszExpression,
98✔
4851
                                          const char *pszComparisonValue,
4852
                                          int bReturnName) {
4853
  char **aszValues = NULL;
4854
  char *pszAttributeName = NULL;
4855
  char *pszAttributeValue = NULL;
4856
  char cCompare = '=';
4857
  char szCompare[3] = {0};
98✔
4858
  char szCompare2[3] = {0};
98✔
4859
  char szCompare3[3] = {0};
98✔
4860
  int bOneCharCompare = -1, nTokens = 0, nLength = 0;
98✔
4861
  int iValue = 0, i = 0, iValueIndex = 0;
4862
  int bStartCopy = 0, iAtt = 0;
4863
  char *pszFinalAttributeName = NULL, *pszFinalAttributeValue = NULL;
4864
  int bSingleQuote = 0, bDoubleQuote = 0;
4865

4866
  if (!pszExpression || !pszComparisonValue || strlen(pszExpression) == 0)
98✔
4867
    return NULL;
4868

4869
  szCompare[0] = '\0';
98✔
4870
  szCompare2[0] = '\0';
98✔
4871
  szCompare3[0] = '\0';
98✔
4872

4873
  if (strcasecmp(pszComparisonValue, "PropertyIsEqualTo") == 0) {
98✔
4874
    cCompare = '=';
4875
    szCompare[0] = 'e';
60✔
4876
    szCompare[1] = 'q';
60✔
4877
    szCompare[2] = '\0';
60✔
4878

4879
    bOneCharCompare = 1;
4880
  }
4881
  if (strcasecmp(pszComparisonValue, "PropertyIsNotEqualTo") == 0) {
98✔
4882
    szCompare[0] = 'n';
12✔
4883
    szCompare[1] = 'e';
12✔
4884
    szCompare[2] = '\0';
12✔
4885

4886
    szCompare2[0] = '!';
12✔
4887
    szCompare2[1] = '=';
12✔
4888
    szCompare2[2] = '\0';
12✔
4889

4890
    szCompare3[0] = '<';
12✔
4891
    szCompare3[1] = '>';
12✔
4892
    szCompare3[2] = '\0';
12✔
4893

4894
    bOneCharCompare = 0;
4895
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
86✔
4896
    szCompare[0] = '=';
×
4897
    szCompare[1] = '~';
×
4898
    szCompare[2] = '\0';
×
4899

4900
    szCompare2[0] = '~';
×
4901
    szCompare2[1] = '*';
×
4902
    szCompare2[2] = '\0';
×
4903

4904
    bOneCharCompare = 0;
4905
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLessThan") == 0) {
86✔
4906
    cCompare = '<';
4907
    szCompare[0] = 'l';
×
4908
    szCompare[1] = 't';
×
4909
    szCompare[2] = '\0';
×
4910
    bOneCharCompare = 1;
4911
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLessThanOrEqualTo") ==
86✔
4912
             0) {
4913
    szCompare[0] = 'l';
12✔
4914
    szCompare[1] = 'e';
12✔
4915
    szCompare[2] = '\0';
12✔
4916

4917
    szCompare2[0] = '<';
12✔
4918
    szCompare2[1] = '=';
12✔
4919
    szCompare2[2] = '\0';
12✔
4920

4921
    bOneCharCompare = 0;
4922
  } else if (strcasecmp(pszComparisonValue, "PropertyIsGreaterThan") == 0) {
74✔
4923
    cCompare = '>';
4924
    szCompare[0] = 'g';
14✔
4925
    szCompare[1] = 't';
14✔
4926
    szCompare[2] = '\0';
14✔
4927
    bOneCharCompare = 1;
4928
  } else if (strcasecmp(pszComparisonValue, "PropertyIsGreaterThanOrEqualTo") ==
60✔
4929
             0) {
4930
    szCompare[0] = 'g';
×
4931
    szCompare[1] = 'e';
×
4932
    szCompare[2] = '\0';
×
4933

4934
    szCompare2[0] = '>';
×
4935
    szCompare2[1] = '=';
×
4936
    szCompare2[2] = '\0';
×
4937

4938
    bOneCharCompare = 0;
4939
  }
4940

4941
  if (bOneCharCompare == 1) {
60✔
4942
    aszValues = msStringSplit(pszExpression, cCompare, &nTokens);
74✔
4943
    if (nTokens > 1) {
74✔
4944
      // for operators such as >
4945
      pszAttributeName = msStrdup(aszValues[0]);
74✔
4946
      pszAttributeValue = msStrdup(aszValues[1]);
74✔
4947
    } else {
4948
      // for operators such as gt
4949
      nLength = strlen(pszExpression);
×
4950
      pszAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
×
4951
      iValue = 0;
4952
      for (i = 0; i < nLength - 2; i++) {
×
4953
        if (pszExpression[i] != szCompare[0] &&
×
4954
            pszExpression[i] != toupper(szCompare[0])) {
×
4955
          pszAttributeName[iValue++] = pszExpression[i];
×
4956
        } else {
4957
          if ((pszExpression[i + 1] == szCompare[1] ||
×
4958
               pszExpression[i + 1] == toupper(szCompare[1])) &&
×
4959
              (pszExpression[i + 2] == ' ')) {
×
4960
            iValueIndex = i + 3;
×
4961
            pszAttributeValue = msStrdup(pszExpression + iValueIndex);
×
4962
            break;
4963
          } else
4964
            pszAttributeName[iValue++] = pszExpression[i];
×
4965
        }
4966
      }
4967
      pszAttributeName[iValue] = '\0';
×
4968
    }
4969
    msFreeCharArray(aszValues, nTokens);
74✔
4970
  } else if (bOneCharCompare == 0) {
24✔
4971
    nLength = strlen(pszExpression);
24✔
4972
    pszAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
24✔
4973
    iValue = 0;
4974
    for (int i = 0; i < nLength; i++) {
188✔
4975
      // Check for comparison operator
4976
      if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2)) ||
188✔
4977
          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
188✔
4978
          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2))) {
170✔
4979
        // Extract value after the operator
4980
        int offset = 2; // All operators are 2 characters long
4981
        pszAttributeValue = msStrdup(&pszExpression[i + offset]);
24✔
4982
        break;
4983
      } else {
4984
        // Copy character to attribute name
4985
        pszAttributeName[iValue++] = pszExpression[i];
164✔
4986
      }
4987
    }
4988
    pszAttributeName[iValue] = '\0';
24✔
4989
  }
4990

4991
  /* -------------------------------------------------------------------- */
4992
  /*      Return the name of the attribute : It is supposed to be         */
4993
  /*      inside []                                                       */
4994
  /* -------------------------------------------------------------------- */
4995
  if (bReturnName) {
98✔
4996
    if (!pszAttributeName) {
49✔
4997
      msFree(pszAttributeValue);
×
4998
      return NULL;
×
4999
    }
5000

5001
    nLength = strlen(pszAttributeName);
49✔
5002
    pszFinalAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
49✔
5003
    bStartCopy = 0;
5004
    iAtt = 0;
5005
    for (i = 0; i < nLength; i++) {
352✔
5006
      if (pszAttributeName[i] == ' ' && bStartCopy == 0)
352✔
5007
        continue;
33✔
5008

5009
      if (pszAttributeName[i] == '[') {
319✔
5010
        bStartCopy = 1;
5011
        continue;
49✔
5012
      }
5013
      if (pszAttributeName[i] == ']')
270✔
5014
        break;
5015
      if (bStartCopy) {
221✔
5016
        pszFinalAttributeName[iAtt++] = pszAttributeName[i];
209✔
5017
      }
5018
      pszFinalAttributeName[iAtt] = '\0';
221✔
5019
    }
5020

5021
    msFree(pszAttributeName);
49✔
5022
    msFree(pszAttributeValue);
49✔
5023
    return pszFinalAttributeName;
49✔
5024
  } else {
5025

5026
    if (!pszAttributeValue) {
49✔
5027
      msFree(pszAttributeName);
×
5028
      return NULL;
×
5029
    }
5030
    nLength = strlen(pszAttributeValue);
49✔
5031
    pszFinalAttributeValue = (char *)malloc(sizeof(char) * (nLength + 1));
49✔
5032
    pszFinalAttributeValue[0] = '\0';
49✔
5033
    bStartCopy = 0;
5034
    iAtt = 0;
5035
    for (i = 0; i < nLength; i++) {
279✔
5036
      if (pszAttributeValue[i] == ' ' && bStartCopy == 0)
247✔
5037
        continue;
43✔
5038

5039
      if (pszAttributeValue[i] == '\'' && bStartCopy == 0) {
204✔
5040
        bSingleQuote = 1;
5041
        bStartCopy = 1;
5042
        continue;
×
5043
      } else if (pszAttributeValue[i] == '"' && bStartCopy == 0) {
204✔
5044
        bDoubleQuote = 1;
5045
        bStartCopy = 1;
5046
        continue;
7✔
5047
      } else
5048
        bStartCopy = 1;
5049

5050
      if (bStartCopy) {
5051
        if (pszAttributeValue[i] == '\'' && bSingleQuote)
197✔
5052
          break;
5053
        else if (pszAttributeValue[i] == '"' && bDoubleQuote)
197✔
5054
          break;
5055
        else if (pszAttributeValue[i] == ')') {
190✔
5056
          // remove any spaces prior to the closing bracket
5057
          msStringTrimBlanks(pszFinalAttributeValue);
10✔
5058
          break;
5059
        }
5060
        pszFinalAttributeValue[iAtt++] = pszAttributeValue[i];
180✔
5061
      }
5062
      pszFinalAttributeValue[iAtt] = '\0';
180✔
5063
    }
5064

5065
    /*trim  for regular expressions*/
5066
    if (strlen(pszFinalAttributeValue) > 2 &&
49✔
5067
        strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
9✔
5068
      int len = strlen(pszFinalAttributeValue);
×
5069
      msStringTrimBlanks(pszFinalAttributeValue);
×
5070
      if (pszFinalAttributeValue[0] == '/' &&
×
5071
          (pszFinalAttributeValue[len - 1] == '/' ||
×
5072
           (pszFinalAttributeValue[len - 1] == 'i' &&
×
5073
            pszFinalAttributeValue[len - 2] == '/'))) {
×
5074
        if (pszFinalAttributeValue[len - 1] == '/')
×
5075
          pszFinalAttributeValue[len - 1] = '\0';
×
5076
        else
5077
          pszFinalAttributeValue[len - 2] = '\0';
×
5078

5079
        memmove(pszFinalAttributeValue,
×
5080
                pszFinalAttributeValue +
×
5081
                    ((pszFinalAttributeValue[1] == '^') ? 2 : 1),
×
5082
                len - 1);
×
5083

5084
        /*replace wild card string .* with * */
5085
        pszFinalAttributeValue =
5086
            msReplaceSubstring(pszFinalAttributeValue, ".*", "*");
×
5087
      }
5088
    }
5089
    msFree(pszAttributeName);
49✔
5090
    msFree(pszAttributeValue);
49✔
5091
    return pszFinalAttributeValue;
49✔
5092
  }
5093
}
5094

5095
static char *msSLDGetAttributeName(const char *pszExpression,
5096
                                   const char *pszComparisonValue) {
5097
  return msSLDGetAttributeNameOrValue(pszExpression, pszComparisonValue, 1);
49✔
5098
}
5099

5100
static char *msSLDGetAttributeValue(const char *pszExpression,
5101
                                    const char *pszComparisonValue) {
5102
  return msSLDGetAttributeNameOrValue(pszExpression, pszComparisonValue, 0);
49✔
5103
}
5104

5105
/************************************************************************/
5106
/*                           BuildExpressionTree                        */
5107
/*                                                                      */
5108
/*      Build a filter expression node based on mapserver's class       */
5109
/*      expression. This is limited to simple expressions like :        */
5110
/*        A = B, A < B, A <= B, A > B, A >= B, A != B                   */
5111
/*       It also handles one level of logical expressions :             */
5112
/*        A AND B                                                       */
5113
/*        A OR B                                                        */
5114
/*        NOT A                                                         */
5115
/************************************************************************/
5116
static FilterEncodingNode *BuildExpressionTree(const char *pszExpression,
34✔
5117
                                               FilterEncodingNode *psNode) {
5118
  int nOperators = 0;
5119

5120
  if (!pszExpression || strlen(pszExpression) == 0)
34✔
5121
    return NULL;
5122

5123
  /* -------------------------------------------------------------------- */
5124
  /*      First we check how many logical operators are there :           */
5125
  /*       - if none : It means It is a comparison operator (like =,      */
5126
  /*      >, >= .... We get the comparison value as well as the           */
5127
  /*      attribute and the attribute's value and assign it to the node   */
5128
  /*      passed in argument.                                             */
5129
  /*       - if there is one operator, we assign the operator to the      */
5130
  /*      node and adds the expressions into the left and right nodes.    */
5131
  /* -------------------------------------------------------------------- */
5132
  nOperators = msSLDNumberOfLogicalOperators(pszExpression);
34✔
5133
  if (nOperators == 0) {
34✔
5134
    if (!psNode)
15✔
5135
      psNode = FLTCreateFilterEncodingNode();
15✔
5136

5137
    const char *pszComparisonValue = msSLDGetComparisonValue(pszExpression);
15✔
5138
    char *pszAttributeName =
5139
        msSLDGetAttributeName(pszExpression, pszComparisonValue);
5140
    char *pszAttributeValue =
5141
        msSLDGetAttributeValue(pszExpression, pszComparisonValue);
5142
    if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
15✔
5143
      psNode->eType = FILTER_NODE_TYPE_COMPARISON;
15✔
5144
      psNode->pszValue = msStrdup(pszComparisonValue);
15✔
5145

5146
      psNode->psLeftNode = FLTCreateFilterEncodingNode();
15✔
5147
      psNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
15✔
5148
      psNode->psLeftNode->pszValue = msStrdup(pszAttributeName);
15✔
5149

5150
      psNode->psRightNode = FLTCreateFilterEncodingNode();
15✔
5151
      psNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
15✔
5152
      psNode->psRightNode->pszValue = msStrdup(pszAttributeValue);
15✔
5153

5154
      if (strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
15✔
5155
        psNode->pOther = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
×
5156
        ((FEPropertyIsLike *)psNode->pOther)->bCaseInsensitive = 0;
×
5157
        ((FEPropertyIsLike *)psNode->pOther)->pszWildCard = msStrdup("*");
×
5158
        ((FEPropertyIsLike *)psNode->pOther)->pszSingleChar = msStrdup("#");
×
5159
        ((FEPropertyIsLike *)psNode->pOther)->pszEscapeChar = msStrdup("!");
×
5160
      }
5161
    }
5162
    free(pszAttributeName);
15✔
5163
    free(pszAttributeValue);
15✔
5164
    return psNode;
15✔
5165

5166
  } else if (nOperators == 1) {
19✔
5167
    const char *pszOperator = msSLDGetLogicalOperator(pszExpression);
19✔
5168
    if (pszOperator) {
19✔
5169
      if (!psNode)
19✔
5170
        psNode = FLTCreateFilterEncodingNode();
19✔
5171

5172
      psNode->eType = FILTER_NODE_TYPE_LOGICAL;
19✔
5173
      psNode->pszValue = msStrdup(pszOperator);
19✔
5174

5175
      char *pszLeftExpression = msSLDGetLeftExpressionOfOperator(pszExpression);
19✔
5176
      char *pszRightExpression =
5177
          msSLDGetRightExpressionOfOperator(pszExpression);
19✔
5178

5179
      if (pszLeftExpression || pszRightExpression) {
19✔
5180
        if (pszLeftExpression) {
19✔
5181
          const char *pszComparisonValue =
5182
              msSLDGetComparisonValue(pszLeftExpression);
15✔
5183
          char *pszAttributeName =
5184
              msSLDGetAttributeName(pszLeftExpression, pszComparisonValue);
5185
          char *pszAttributeValue =
5186
              msSLDGetAttributeValue(pszLeftExpression, pszComparisonValue);
5187

5188
          if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
15✔
5189
            psNode->psLeftNode = FLTCreateFilterEncodingNode();
15✔
5190
            psNode->psLeftNode->eType = FILTER_NODE_TYPE_COMPARISON;
15✔
5191
            psNode->psLeftNode->pszValue = msStrdup(pszComparisonValue);
15✔
5192

5193
            psNode->psLeftNode->psLeftNode = FLTCreateFilterEncodingNode();
15✔
5194
            psNode->psLeftNode->psLeftNode->eType =
15✔
5195
                FILTER_NODE_TYPE_PROPERTYNAME;
5196
            psNode->psLeftNode->psLeftNode->pszValue =
15✔
5197
                msStrdup(pszAttributeName);
15✔
5198

5199
            psNode->psLeftNode->psRightNode = FLTCreateFilterEncodingNode();
15✔
5200
            psNode->psLeftNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
15✔
5201
            psNode->psLeftNode->psRightNode->pszValue =
15✔
5202
                msStrdup(pszAttributeValue);
15✔
5203
          }
5204

5205
          free(pszAttributeName);
15✔
5206
          free(pszAttributeValue);
15✔
5207
          free(pszLeftExpression);
15✔
5208
        }
5209
        if (pszRightExpression) {
19✔
5210
          const char *pszComparisonValue =
5211
              msSLDGetComparisonValue(pszRightExpression);
19✔
5212
          char *pszAttributeName =
5213
              msSLDGetAttributeName(pszRightExpression, pszComparisonValue);
5214
          char *pszAttributeValue =
5215
              msSLDGetAttributeValue(pszRightExpression, pszComparisonValue);
5216

5217
          if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
19✔
5218
            psNode->psRightNode = FLTCreateFilterEncodingNode();
19✔
5219
            psNode->psRightNode->eType = FILTER_NODE_TYPE_COMPARISON;
19✔
5220
            psNode->psRightNode->pszValue = msStrdup(pszComparisonValue);
19✔
5221

5222
            psNode->psRightNode->psLeftNode = FLTCreateFilterEncodingNode();
19✔
5223
            psNode->psRightNode->psLeftNode->eType =
19✔
5224
                FILTER_NODE_TYPE_PROPERTYNAME;
5225
            psNode->psRightNode->psLeftNode->pszValue =
19✔
5226
                msStrdup(pszAttributeName);
19✔
5227

5228
            psNode->psRightNode->psRightNode = FLTCreateFilterEncodingNode();
19✔
5229
            psNode->psRightNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
19✔
5230
            psNode->psRightNode->psRightNode->pszValue =
19✔
5231
                msStrdup(pszAttributeValue);
19✔
5232
          }
5233
          free(pszAttributeName);
19✔
5234
          free(pszAttributeValue);
19✔
5235
          free(pszRightExpression);
19✔
5236
        }
5237
      }
5238
    }
5239

5240
    return psNode;
19✔
5241
  } else {
5242
    return NULL;
5243
  }
5244
}
5245

5246
static char *msSLDBuildFilterEncoding(const FilterEncodingNode *psNode) {
68✔
5247
  char *pszTmp = NULL;
5248
  char szTmp[200];
5249
  char *pszExpression = NULL;
5250

5251
  if (!psNode)
68✔
5252
    return NULL;
5253

5254
  if (psNode->eType == FILTER_NODE_TYPE_COMPARISON && psNode->pszValue &&
68✔
5255
      psNode->psLeftNode && psNode->psLeftNode->pszValue &&
49✔
5256
      psNode->psRightNode && psNode->psRightNode->pszValue) {
49✔
5257
    snprintf(szTmp, sizeof(szTmp),
5258
             "<ogc:%s><ogc:PropertyName>%s</ogc:PropertyName><ogc:Literal>%s</"
5259
             "ogc:Literal></ogc:%s>",
5260
             psNode->pszValue, psNode->psLeftNode->pszValue,
5261
             psNode->psRightNode->pszValue, psNode->pszValue);
5262
    pszExpression = msStrdup(szTmp);
49✔
5263
  } else if (psNode->eType == FILTER_NODE_TYPE_LOGICAL && psNode->pszValue &&
19✔
5264
             ((psNode->psLeftNode && psNode->psLeftNode->pszValue) ||
19✔
5265
              (psNode->psRightNode && psNode->psRightNode->pszValue))) {
4✔
5266
    snprintf(szTmp, sizeof(szTmp), "<ogc:%s>", psNode->pszValue);
5267
    pszExpression = msStringConcatenate(pszExpression, szTmp);
19✔
5268
    if (psNode->psLeftNode) {
19✔
5269
      pszTmp = msSLDBuildFilterEncoding(psNode->psLeftNode);
15✔
5270
      if (pszTmp) {
15✔
5271
        pszExpression = msStringConcatenate(pszExpression, pszTmp);
15✔
5272
        free(pszTmp);
15✔
5273
      }
5274
    }
5275
    if (psNode->psRightNode) {
19✔
5276
      pszTmp = msSLDBuildFilterEncoding(psNode->psRightNode);
19✔
5277
      if (pszTmp) {
19✔
5278
        pszExpression = msStringConcatenate(pszExpression, pszTmp);
19✔
5279
        free(pszTmp);
19✔
5280
      }
5281
    }
5282
    snprintf(szTmp, sizeof(szTmp), "</ogc:%s>", psNode->pszValue);
19✔
5283
    pszExpression = msStringConcatenate(pszExpression, szTmp);
19✔
5284
  }
5285
  return pszExpression;
5286
}
5287

5288
static char *msSLDParseLogicalExpression(const char *pszExpression,
34✔
5289
                                         const char *pszWfsFilter) {
5290
  FilterEncodingNode *psNode = NULL;
5291
  char *pszFLTExpression = NULL;
5292
  char *pszTmp = NULL;
5293

5294
  if (!pszExpression || strlen(pszExpression) == 0)
34✔
5295
    return NULL;
5296

5297
  psNode = BuildExpressionTree(pszExpression, NULL);
34✔
5298

5299
  if (psNode) {
34✔
5300
    pszFLTExpression = msSLDBuildFilterEncoding(psNode);
34✔
5301
    if (pszFLTExpression) {
34✔
5302
      pszTmp = msStringConcatenate(pszTmp, "<ogc:Filter>");
34✔
5303
      if (pszWfsFilter) {
34✔
5304
        pszTmp = msStringConcatenate(pszTmp, "<ogc:And>");
×
5305
        pszTmp = msStringConcatenate(pszTmp, (char *)pszWfsFilter);
×
5306
      }
5307
      pszTmp = msStringConcatenate(pszTmp, pszFLTExpression);
34✔
5308

5309
      if (pszWfsFilter)
34✔
5310
        pszTmp = msStringConcatenate(pszTmp, "</ogc:And>");
×
5311

5312
      pszTmp = msStringConcatenate(pszTmp, "</ogc:Filter>\n");
34✔
5313

5314
      free(pszFLTExpression);
34✔
5315
      pszFLTExpression = pszTmp;
5316
    }
5317
    FLTFreeFilterEncodingNode(psNode);
34✔
5318
  }
5319

5320
  return pszFLTExpression;
5321
}
5322

5323
/************************************************************************/
5324
/*                     msSLDConvertRegexExpToOgcIsLike                  */
5325
/*                                                                      */
5326
/*      Convert mapserver regex expression to ogc is like property      */
5327
/*      expression.                                                     */
5328
/*                                                                      */
5329
/*      Review bug 1644 for details. Here are the current rules:        */
5330
/*                                                                      */
5331
/*       The filter encoding property like is more limited compared     */
5332
/*      to regular expression that can be built in mapserver. I         */
5333
/*      think we should define what is possible to convert properly     */
5334
/*      and do those, and also identify potential problems.  Example :  */
5335
/*        - any time there is a .* in the expression it will be         */
5336
/*      converted to *                                                  */
5337
/*        - any other character plus all the metacharacters . ^ $ * +   */
5338
/*      ? { [ ] \ | ( ) would be outputted as is. (In case of           */
5339
/*      mapserver, when we read the the ogc filter expression, we       */
5340
/*      convert the wild card character to .*, and we convert the       */
5341
/*      single character to .  and the escape character to \ all        */
5342
/*      other are outputted as is)                                      */
5343
/*        - the  ogc tag would look like <ogc:PropertyIsLike            */
5344
/*      wildCard="*"  singleChar="." escape="\">                        */
5345
/*                                                                      */
5346
/*        - type of potential problem :                                 */
5347
/*           * if an expression is like /T (star)/ it will be           */
5348
/*      converted to T* which is not correct.                           */
5349
/*                                                                      */
5350
/************************************************************************/
5351

5352
static char *msSLDConvertRegexExpToOgcIsLike(const char *pszRegex) {
×
5353
  char szBuffer[1024];
5354
  int iBuffer = 0, i = 0;
5355
  int nLength = 0;
5356

5357
  if (!pszRegex || strlen(pszRegex) == 0)
×
5358
    return NULL;
5359

5360
  szBuffer[0] = '\0';
×
5361
  nLength = strlen(pszRegex);
×
5362

5363
  while (i < nLength) {
×
5364
    if (pszRegex[i] != '.') {
×
5365
      szBuffer[iBuffer++] = pszRegex[i];
×
5366
      i++;
×
5367
    } else {
5368
      if (i < nLength - 1 && pszRegex[i + 1] == '*') {
×
5369
        szBuffer[iBuffer++] = '*';
×
5370
        i = i + 2;
×
5371
      } else {
5372
        szBuffer[iBuffer++] = pszRegex[i];
×
5373
        i++;
×
5374
      }
5375
    }
5376
  }
5377
  szBuffer[iBuffer] = '\0';
×
5378

5379
  return msStrdup(szBuffer);
×
5380
}
5381

5382
/************************************************************************/
5383
/*                          XMLEscape                                   */
5384
/************************************************************************/
5385
static std::string XMLEscape(const char *pszValue) {
64✔
5386
  char *pszEscaped = CPLEscapeString(pszValue, -1, CPLES_XML);
64✔
5387
  std::string osRet(pszEscaped);
64✔
5388
  CPLFree(pszEscaped);
64✔
5389
  return osRet;
64✔
5390
}
5391

5392
static std::string GetPropertyIsEqualTo(const char *pszPropertyName,
32✔
5393
                                        const char *pszLiteral) {
5394
  std::string osFilter("<ogc:PropertyIsEqualTo><ogc:PropertyName>");
32✔
5395
  osFilter += XMLEscape(pszPropertyName);
64✔
5396
  osFilter += "</ogc:PropertyName><ogc:Literal>";
5397
  osFilter += XMLEscape(pszLiteral);
64✔
5398
  osFilter += "</ogc:Literal></ogc:PropertyIsEqualTo>";
5399
  return osFilter;
32✔
5400
}
5401

5402
static std::string GetPropertyIsLike(const char *pszPropertyName,
×
5403
                                     const char *pszLiteral) {
5404
  std::string osFilter("<ogc:PropertyIsLike wildCard=\"*\" singleChar=\".\" "
5405
                       "escape=\"\\\"><ogc:PropertyName>");
×
5406
  osFilter += XMLEscape(pszPropertyName);
×
5407
  osFilter += "</ogc:PropertyName><ogc:Literal>";
5408
  osFilter += XMLEscape(pszLiteral);
×
5409
  osFilter += "</ogc:Literal></ogc:PropertyIsLike>";
5410
  return osFilter;
×
5411
}
5412

5413
/************************************************************************/
5414
/*                              msSLDGetFilter                          */
5415
/*                                                                      */
5416
/*      Get the corresponding ogc Filter based on the class             */
5417
/*      expression. TODO : move function to mapogcfilter.c when         */
5418
/*      finished.                                                       */
5419
/************************************************************************/
5420
char *msSLDGetFilter(classObj *psClass, const char *pszWfsFilter) {
151✔
5421
  char *pszFilter = NULL;
5422
  char *pszOgcFilter = NULL;
5423

5424
  if (psClass && psClass->expression.string) {
151✔
5425

5426
    char *pszExpression = msStrdup(psClass->expression.string);
62✔
5427

5428
    /* string expression */
5429
    if (psClass->expression.type == MS_STRING) {
62✔
5430

5431
      // ensure any trailing whitespace is removed
5432
      msStringTrimBlanks(pszExpression);
22✔
5433
      if (psClass->layer && psClass->layer->classitem) {
22✔
5434
        std::string osFilter("<ogc:Filter>");
22✔
5435
        if (pszWfsFilter) {
22✔
5436
          osFilter += "<ogc:And>";
5437
          osFilter += pszWfsFilter;
5438
        }
5439
        osFilter +=
5440
            GetPropertyIsEqualTo(psClass->layer->classitem, pszExpression);
22✔
5441
        if (pszWfsFilter) {
22✔
5442
          osFilter += "</ogc:And>";
5443
        }
5444
        osFilter += "</ogc:Filter>\n";
5445
        pszFilter = msStrdup(osFilter.c_str());
22✔
5446
      }
5447
    } else if (psClass->expression.type == MS_EXPRESSION) {
40✔
5448
      msStringTrimBlanks(pszExpression);
34✔
5449
      pszFilter = msSLDParseLogicalExpression(pszExpression, pszWfsFilter);
34✔
5450
    } else if (psClass->expression.type == MS_LIST) {
6✔
5451
      if (psClass->layer && psClass->layer->classitem) {
6✔
5452

5453
        char **listExpressionValues = NULL;
5454
        int numListExpressionValues = 0;
6✔
5455
        int i = 0;
5456
        int tokenCount = 0;
5457
        std::string osOrFilters;
5458

5459
        listExpressionValues =
5460
            msStringSplit(pszExpression, ',', &numListExpressionValues);
6✔
5461

5462
        // loop through all values in the list and create a PropertyIsEqualTo
5463
        // for each value
5464
        for (i = 0; i < numListExpressionValues; i++) {
17✔
5465
          if (listExpressionValues[i] && listExpressionValues[i][0] != '\0') {
11✔
5466
            osOrFilters += GetPropertyIsEqualTo(psClass->layer->classitem,
20✔
5467
                                                listExpressionValues[i]);
5468
            osOrFilters += '\n';
5469
            tokenCount++;
10✔
5470
          }
5471
        }
5472

5473
        std::string osFilter("<ogc:Filter>");
6✔
5474

5475
        // no need for an OR clause if there is only one item in the list
5476
        if (tokenCount == 1) {
6✔
5477
          osFilter += osOrFilters;
5478
        } else if (tokenCount > 1) {
5✔
5479
          osFilter += "<ogc:Or>";
5480
          osFilter += osOrFilters;
5481
          osFilter += "</ogc:Or>";
5482
        }
5483
        osFilter += "</ogc:Filter>";
5484

5485
        // don't filter when the list is empty
5486
        if (tokenCount > 0) {
6✔
5487
          pszFilter = msStrdup(osFilter.c_str());
5✔
5488
        }
5489
        msFreeCharArray(listExpressionValues, numListExpressionValues);
6✔
5490
      }
5491
    } else if (psClass->expression.type == MS_REGEX) {
×
5492
      if (psClass->layer && psClass->layer->classitem) {
×
5493
        pszOgcFilter =
5494
            msSLDConvertRegexExpToOgcIsLike(psClass->expression.string);
×
5495

5496
        std::string osFilter("<ogc:Filter>");
×
5497
        if (pszWfsFilter) {
×
5498
          osFilter += "<ogc:And>";
5499
          osFilter += pszWfsFilter;
5500
        }
5501
        osFilter += GetPropertyIsLike(psClass->layer->classitem, pszOgcFilter);
×
5502

5503
        free(pszOgcFilter);
×
5504

5505
        if (pszWfsFilter) {
×
5506
          osFilter += "</ogc:And>";
5507
        }
5508
        osFilter += "</ogc:Filter>\n";
5509
        pszFilter = msStrdup(osFilter.c_str());
×
5510
      }
5511
    }
5512

5513
    msFree(pszExpression);
62✔
5514
  } else if (pszWfsFilter) {
89✔
5515
    std::string osFilter("<ogc:Filter>");
×
5516
    osFilter += pszWfsFilter;
5517
    osFilter += "</ogc:Filter>\n";
5518
    pszFilter = msStrdup(osFilter.c_str());
×
5519
  }
5520
  return pszFilter;
151✔
5521
}
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

© 2025 Coveralls, Inc