Coverage Report

Created: 2025-06-13 06:29

/src/MapServer/src/mapogcsld.cpp
Line
Count
Source (jump to first uncovered line)
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
0
static inline void IGUR_sizet(size_t ignored) {
41
0
  (void)ignored;
42
0
} /* 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
0
#define SLD_MARK_SYMBOL_SQUARE "sld_mark_symbol_square"
47
0
#define SLD_MARK_SYMBOL_SQUARE_FILLED "sld_mark_symbol_square_filled"
48
0
#define SLD_MARK_SYMBOL_CIRCLE "sld_mark_symbol_circle"
49
0
#define SLD_MARK_SYMBOL_CIRCLE_FILLED "sld_mark_symbol_circle_filled"
50
0
#define SLD_MARK_SYMBOL_TRIANGLE "sld_mark_symbol_triangle"
51
0
#define SLD_MARK_SYMBOL_TRIANGLE_FILLED "sld_mark_symbol_triangle_filled"
52
0
#define SLD_MARK_SYMBOL_STAR "sld_mark_symbol_star"
53
0
#define SLD_MARK_SYMBOL_STAR_FILLED "sld_mark_symbol_star_filled"
54
0
#define SLD_MARK_SYMBOL_CROSS "sld_mark_symbol_cross"
55
0
#define SLD_MARK_SYMBOL_CROSS_FILLED "sld_mark_symbol_cross_filled"
56
0
#define SLD_MARK_SYMBOL_X "sld_mark_symbol_x"
57
0
#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,
67
0
                     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;
73
  char *pszSLDbuf = NULL;
74
  FILE *fp = NULL;
75
  int nStatus = MS_FAILURE;
76
77
  if (map && szURL) {
78
    map->sldurl = (char *)szURL;
79
    pszSLDTmpFile = msTmpFile(map, map->mappath, NULL, "sld.xml");
80
    if (pszSLDTmpFile == NULL) {
81
      pszSLDTmpFile = msTmpFile(map, NULL, NULL, "sld.xml");
82
    }
83
    if (pszSLDTmpFile == NULL) {
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(
94
          &(map->web.metadata), "MO", "remote_sld_max_bytes");
95
      if (!pszMaxRemoteSLDBytes) {
96
        nMaxRemoteSLDBytes = 1024 * 1024; /* 1 megaByte */
97
      } else {
98
        nMaxRemoteSLDBytes = atoi(pszMaxRemoteSLDBytes);
99
      }
100
      if (msHTTPGetFile(szURL, pszSLDTmpFile, &status, -1, 0, 0,
101
                        nMaxRemoteSLDBytes) == MS_SUCCESS) {
102
        if ((fp = fopen(pszSLDTmpFile, "rb")) != NULL) {
103
          int nBufsize = 0;
104
          fseek(fp, 0, SEEK_END);
105
          nBufsize = ftell(fp);
106
          if (nBufsize > 0) {
107
            rewind(fp);
108
            pszSLDbuf = (char *)malloc((nBufsize + 1) * sizeof(char));
109
            if (pszSLDbuf == NULL) {
110
              msSetError(MS_MEMERR, "Failed to open SLD file.",
111
                         "msSLDApplySLDURL()");
112
            } else {
113
              IGUR_sizet(fread(pszSLDbuf, 1, nBufsize, fp));
114
              pszSLDbuf[nBufsize] = '\0';
115
            }
116
          } else {
117
            msSetError(MS_WMSERR, "Could not open SLD %s as it appears empty",
118
                       "msSLDApplySLDURL", szURL);
119
          }
120
          fclose(fp);
121
          unlink(pszSLDTmpFile);
122
        }
123
      } else {
124
        unlink(pszSLDTmpFile);
125
        msSetError(
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);
135
      if (pszSLDbuf)
136
        nStatus = msSLDApplySLD(map, pszSLDbuf, iLayer, pszStyleLayerName,
137
                                ppszLayerNames);
138
    }
139
    map->sldurl = NULL;
140
  }
141
142
  msFree(pszSLDbuf);
143
144
  return nStatus;
145
146
#else
147
0
  msSetError(MS_MISCERR, "WMS/WFS client support is not enabled .",
148
0
             "msSLDApplySLDURL()");
149
0
  return (MS_FAILURE);
150
0
#endif
151
0
}
152
153
/************************************************************************/
154
/*                             msSLDApplyFromFile                       */
155
/*                                                                      */
156
/*      Apply SLD from a file on disk to the layerObj                   */
157
/************************************************************************/
158
0
int msSLDApplyFromFile(mapObj *map, layerObj *layer, const char *filename) {
159
160
0
  FILE *fp = NULL;
161
162
0
  int nStatus = MS_FAILURE;
163
164
  /* open SLD file */
165
0
  char szPath[MS_MAXPATHLEN];
166
0
  char *pszSLDbuf = NULL;
167
0
  const char *realpath = msBuildPath(szPath, map->mappath, filename);
168
169
0
  if ((fp = fopen(realpath, "rb")) != NULL) {
170
0
    int nBufsize = 0;
171
0
    fseek(fp, 0, SEEK_END);
172
0
    nBufsize = ftell(fp);
173
0
    if (nBufsize > 0) {
174
0
      rewind(fp);
175
0
      pszSLDbuf = (char *)malloc((nBufsize + 1) * sizeof(char));
176
0
      if (pszSLDbuf == NULL) {
177
0
        msSetError(MS_MEMERR, "Failed to read SLD file.",
178
0
                   "msSLDApplyFromFile()");
179
0
      } else {
180
0
        IGUR_sizet(fread(pszSLDbuf, 1, nBufsize, fp));
181
0
        pszSLDbuf[nBufsize] = '\0';
182
0
      }
183
0
    } else {
184
0
      msSetError(MS_IOERR, "Could not open SLD %s as it appears empty",
185
0
                 "msSLDApplyFromFile", realpath);
186
0
    }
187
0
    fclose(fp);
188
0
  }
189
0
  if (pszSLDbuf) {
190
    // if not set, then use the first NamedStyle in the SLD file
191
    // even if the names don't match
192
0
    const char *pszMetadataName = "SLD_USE_FIRST_NAMEDLAYER";
193
0
    const char *pszValue =
194
0
        msLookupHashTable(&(layer->metadata), pszMetadataName);
195
0
    if (pszValue == NULL) {
196
0
      msInsertHashTable(&(layer->metadata), pszMetadataName, "true");
197
0
    }
198
0
    nStatus = msSLDApplySLD(map, pszSLDbuf, layer->index, NULL, NULL);
199
0
  } else {
200
0
    msSetError(MS_IOERR, "Invalid SLD filename: \"%s\".",
201
0
               "msSLDApplyFromFile()", realpath);
202
0
  }
203
204
0
  msFree(pszSLDbuf);
205
0
  return nStatus;
206
0
}
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,
215
0
                                          layerObj *pasSLDLayers) {
216
0
  int m;
217
0
  for (m = 0; m < nSLDLayers; m++) {
218
0
    int l;
219
0
    int nIndex = msGetLayerIndex(map, pasSLDLayers[m].name);
220
0
    if (pasSLDLayers[m].name == NULL)
221
0
      continue;
222
0
    if (nIndex < 0)
223
0
      continue;
224
0
    for (l = m + 1; l < nSLDLayers; l++) {
225
0
      if (pasSLDLayers[l].name == NULL)
226
0
        continue;
227
0
      if (strcasecmp(pasSLDLayers[m].name, pasSLDLayers[l].name) == 0) {
228
0
        layerObj *psTmpLayer = (layerObj *)malloc(sizeof(layerObj));
229
0
        char tmpId[128];
230
231
0
        initLayer(psTmpLayer, map);
232
0
        msCopyLayer(psTmpLayer, GET_LAYER(map, nIndex));
233
        /* open the source layer */
234
0
        if (!psTmpLayer->vtable) {
235
0
          if (msInitializeVirtualTable(psTmpLayer) != MS_SUCCESS) {
236
0
            MS_REFCNT_DECR(psTmpLayer);
237
0
            continue;
238
0
          }
239
0
        }
240
241
        /*make the name unique*/
242
0
        snprintf(tmpId, sizeof(tmpId), "%lx_%x_%d", (long)time(NULL),
243
0
                 (int)getpid(), map->numlayers);
244
0
        if (psTmpLayer->name)
245
0
          msFree(psTmpLayer->name);
246
0
        psTmpLayer->name = msStrdup(tmpId);
247
0
        msFree(pasSLDLayers[l].name);
248
0
        pasSLDLayers[l].name = msStrdup(tmpId);
249
0
        msInsertLayer(map, psTmpLayer, -1);
250
0
        MS_REFCNT_DECR(psTmpLayer);
251
0
      }
252
0
    }
253
0
  }
254
0
}
255
#endif
256
257
0
static int msApplySldLayerToMapLayer(layerObj *sldLayer, layerObj *lp) {
258
259
0
  if (sldLayer->numclasses > 0) {
260
0
    int iClass = 0;
261
0
    bool bSLDHasNamedClass = false;
262
263
0
    lp->type = sldLayer->type;
264
0
    lp->rendermode = MS_ALL_MATCHING_CLASSES;
265
266
0
    for (int k = 0; k < sldLayer->numclasses; k++) {
267
0
      if (sldLayer->_class[k]->group) {
268
0
        bSLDHasNamedClass = true;
269
0
        break;
270
0
      }
271
0
    }
272
273
0
    for (int k = 0; k < lp->numclasses; k++) {
274
0
      if (lp->_class[k] != NULL) {
275
0
        lp->_class[k]->layer = NULL;
276
0
        if (freeClass(lp->_class[k]) == MS_SUCCESS) {
277
0
          msFree(lp->_class[k]);
278
0
          lp->_class[k] = NULL;
279
0
        }
280
0
      }
281
0
    }
282
0
    lp->numclasses = 0;
283
284
0
    if (bSLDHasNamedClass && sldLayer->classgroup) {
285
      /* Set the class group to the class that has UserStyle.IsDefaultf
286
       */
287
0
      msFree(lp->classgroup);
288
0
      lp->classgroup = msStrdup(sldLayer->classgroup);
289
0
    } 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
0
      msFree(lp->classgroup);
293
0
      lp->classgroup = NULL;
294
0
    }
295
296
0
    for (int k = 0; k < sldLayer->numclasses; k++) {
297
0
      if (msGrowLayerClasses(lp) == NULL)
298
0
        return MS_FAILURE;
299
300
0
      initClass(lp->_class[iClass]);
301
0
      msCopyClass(lp->_class[iClass], sldLayer->_class[k], NULL);
302
0
      lp->_class[iClass]->layer = lp;
303
0
      lp->numclasses++;
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
0
      const int layerWasOpened = msLayerIsOpen(lp);
309
310
0
      if (layerWasOpened || (msLayerOpen(lp) == MS_SUCCESS &&
311
0
                             msLayerGetItems(lp) == MS_SUCCESS)) {
312
313
0
        if (lp->_class[iClass]->text.string) {
314
0
          for (int z = 0; z < lp->numitems; z++) {
315
0
            if (!lp->items[z] || strlen(lp->items[z]) == 0)
316
0
              continue;
317
318
0
            char *pszTmp1 = msStrdup(lp->_class[iClass]->text.string);
319
0
            const char *pszFullName = msOWSLookupMetadata(
320
0
                &(lp->metadata), "G",
321
0
                std::string(lp->items[z]).append("_alias").c_str());
322
323
0
            if (pszFullName != NULL && (strstr(pszTmp1, pszFullName) != NULL)) {
324
0
              pszTmp1 = msReplaceSubstring(pszTmp1, pszFullName, lp->items[z]);
325
0
              std::string osTmp("(");
326
0
              osTmp.append(pszTmp1).append(")");
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
0
              msLoadExpressionString(&(lp->_class[iClass]->text),
332
0
                                     osTmp.c_str());
333
0
            }
334
0
            msFree(pszTmp1);
335
0
          }
336
0
        }
337
0
        if (!layerWasOpened) { // don't close the layer if already
338
                               // open
339
0
          msLayerClose(lp);
340
0
        }
341
0
      }
342
0
      iClass++;
343
0
    }
344
0
  } else {
345
    /*this is probably an SLD that uses Named styles*/
346
0
    if (sldLayer->classgroup) {
347
0
      int k;
348
0
      for (k = 0; k < lp->numclasses; k++) {
349
0
        if (lp->_class[k]->group &&
350
0
            strcasecmp(lp->_class[k]->group, sldLayer->classgroup) == 0)
351
0
          break;
352
0
      }
353
0
      if (k < lp->numclasses) {
354
0
        msFree(lp->classgroup);
355
0
        lp->classgroup = msStrdup(sldLayer->classgroup);
356
0
      } else {
357
        /* TODO  we throw an exception ?*/
358
0
      }
359
0
    }
360
0
  }
361
0
  if (sldLayer->labelitem) {
362
0
    if (lp->labelitem)
363
0
      free(lp->labelitem);
364
365
0
    lp->labelitem = msStrdup(sldLayer->labelitem);
366
0
  }
367
368
0
  if (sldLayer->classitem) {
369
0
    if (lp->classitem)
370
0
      free(lp->classitem);
371
372
0
    lp->classitem = msStrdup(sldLayer->classitem);
373
0
  }
374
375
  /* opacity for sld raster */
376
0
  if (lp->type == MS_LAYER_RASTER && sldLayer->compositer &&
377
0
      sldLayer->compositer->opacity != 100)
378
0
    msSetLayerOpacity(lp, sldLayer->compositer->opacity);
379
380
  /* mark as auto-generate SLD */
381
0
  if (lp->connectiontype == MS_WMS)
382
0
    msInsertHashTable(&(lp->metadata), "wms_sld_body", "auto");
383
384
  /* The SLD might have a FeatureTypeConstraint */
385
0
  if (sldLayer->filter.type == MS_EXPRESSION) {
386
0
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
387
0
      char *pszBuffer = msStringConcatenate(NULL, "((");
388
0
      pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
389
0
      pszBuffer = msStringConcatenate(pszBuffer, ") AND (");
390
0
      pszBuffer = msStringConcatenate(pszBuffer, sldLayer->filter.string);
391
0
      pszBuffer = msStringConcatenate(pszBuffer, "))");
392
0
      msFreeExpression(&lp->filter);
393
0
      msInitExpression(&lp->filter);
394
0
      lp->filter.string = pszBuffer;
395
0
      lp->filter.type = MS_EXPRESSION;
396
0
    } else {
397
0
      msFreeExpression(&lp->filter);
398
0
      msInitExpression(&lp->filter);
399
0
      lp->filter.string = msStrdup(sldLayer->filter.string);
400
0
      lp->filter.type = MS_EXPRESSION;
401
0
    }
402
0
  }
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
0
  if (lp->filter.string == NULL ||
409
0
      (lp->filter.string && lp->filter.type == MS_STRING && !lp->filteritem)) {
410
0
    if (lp->connectiontype == MS_POSTGIS ||
411
0
        lp->connectiontype == MS_ORACLESPATIAL ||
412
0
        lp->connectiontype == MS_PLUGIN) {
413
0
      if (lp->numclasses > 0) {
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
0
        int k;
418
0
        for (k = 0; k < lp->numclasses; k++) {
419
0
          if (lp->_class[k]->expression.type != MS_EXPRESSION)
420
0
            break;
421
0
        }
422
0
        if (k == lp->numclasses) {
423
0
          char szTmp[512];
424
0
          char *pszBuffer = NULL;
425
0
          for (k = 0; k < lp->numclasses; k++) {
426
0
            if (pszBuffer == NULL)
427
0
              snprintf(szTmp, sizeof(szTmp), "%s",
428
0
                       "(("); /* we a building a string expression,
429
                                 explicitly set type below */
430
0
            else
431
0
              snprintf(szTmp, sizeof(szTmp), "%s", " OR ");
432
433
0
            pszBuffer = msStringConcatenate(pszBuffer, szTmp);
434
0
            pszBuffer = msStringConcatenate(pszBuffer,
435
0
                                            lp->_class[k]->expression.string);
436
0
          }
437
438
0
          snprintf(szTmp, sizeof(szTmp), "%s", "))");
439
0
          pszBuffer = msStringConcatenate(pszBuffer, szTmp);
440
441
0
          msFreeExpression(&lp->filter);
442
0
          msInitExpression(&lp->filter);
443
0
          lp->filter.string = msStrdup(pszBuffer);
444
0
          lp->filter.type = MS_EXPRESSION;
445
446
0
          msFree(pszBuffer);
447
0
        }
448
0
      }
449
0
    }
450
0
  }
451
452
0
  return MS_SUCCESS;
453
0
}
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,
464
0
                  const char *pszStyleLayerName, char **ppszLayerNames) {
465
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
466
0
    defined(USE_SOS_SVR)
467
0
  int nSLDLayers = 0;
468
0
  layerObj *pasSLDLayers = NULL;
469
0
  int nStatus = MS_SUCCESS;
470
  /*const char *pszSLDNotSupported = NULL;*/
471
472
0
  pasSLDLayers = msSLDParseSLD(map, psSLDXML, &nSLDLayers);
473
0
  if (pasSLDLayers == NULL) {
474
0
    errorObj *psError = msGetErrorObj();
475
0
    if (psError && psError->code != MS_NOERR)
476
0
      return MS_FAILURE;
477
0
  }
478
479
0
  if (pasSLDLayers && nSLDLayers > 0) {
480
0
    int i;
481
482
0
    msSLDApplySLD_DuplicateLayers(map, nSLDLayers, pasSLDLayers);
483
484
0
    for (i = 0; i < map->numlayers; i++) {
485
0
      layerObj *lp = NULL;
486
0
      const char *pszWMSLayerName = NULL;
487
0
      int j;
488
0
      int bUseSpecificLayer = 0;
489
490
0
      if (iLayer >= 0 && iLayer < map->numlayers) {
491
0
        i = iLayer;
492
0
        bUseSpecificLayer = 1;
493
0
      }
494
495
0
      lp = GET_LAYER(map, i);
496
497
      /* compare layer name to wms_name as well */
498
0
      pszWMSLayerName = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
499
500
0
      bool bSldApplied = false;
501
502
0
      for (j = 0; j < nSLDLayers; j++) {
503
0
        layerObj *sldLayer = &pasSLDLayers[j];
504
505
        /* --------------------------------------------------------------------
506
         */
507
        /*      copy :  - class */
508
        /*              - layer's labelitem */
509
        /* --------------------------------------------------------------------
510
         */
511
0
        if ((sldLayer->name && pszStyleLayerName == NULL &&
512
0
             ((strcasecmp(lp->name, sldLayer->name) == 0 ||
513
0
               (pszWMSLayerName &&
514
0
                strcasecmp(pszWMSLayerName, sldLayer->name) == 0)) ||
515
0
              (lp->group && strcasecmp(lp->group, sldLayer->name) == 0))) ||
516
0
            (bUseSpecificLayer && pszStyleLayerName && sldLayer->name &&
517
0
             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
0
          if (msApplySldLayerToMapLayer(sldLayer, lp) == MS_FAILURE) {
531
0
            nStatus = MS_FAILURE;
532
0
            goto sld_cleanup;
533
0
          };
534
0
          bSldApplied = true;
535
0
          break;
536
0
        }
537
0
      }
538
0
      if (bUseSpecificLayer) {
539
0
        if (!bSldApplied) {
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
0
          const char *pszSLDUseFirstNamedLayer =
543
0
              msLookupHashTable(&(lp->metadata), "SLD_USE_FIRST_NAMEDLAYER");
544
0
          if (pszSLDUseFirstNamedLayer) {
545
0
            if (strcasecmp(pszSLDUseFirstNamedLayer, "true") == 0) {
546
0
              layerObj *firstSldLayer = &pasSLDLayers[0];
547
0
              if (msApplySldLayerToMapLayer(firstSldLayer, lp) == MS_FAILURE) {
548
0
                nStatus = MS_FAILURE;
549
0
                goto sld_cleanup;
550
0
              };
551
0
            }
552
0
          }
553
0
        }
554
0
        break;
555
0
      }
556
0
    }
557
558
    /* -------------------------------------------------------------------- */
559
    /*      if needed return a comma separated list of the layers found     */
560
    /*      in the sld.                                                     */
561
    /* -------------------------------------------------------------------- */
562
0
    if (ppszLayerNames) {
563
0
      char *pszTmp = NULL;
564
0
      for (i = 0; i < nSLDLayers; i++) {
565
0
        if (pasSLDLayers[i].name) {
566
0
          if (pszTmp != NULL)
567
0
            pszTmp = msStringConcatenate(pszTmp, ",");
568
0
          pszTmp = msStringConcatenate(pszTmp, pasSLDLayers[i].name);
569
0
        }
570
0
      }
571
0
      *ppszLayerNames = pszTmp;
572
0
    }
573
0
  }
574
575
0
  nStatus = MS_SUCCESS;
576
577
0
sld_cleanup:
578
579
0
  if (pasSLDLayers) {
580
0
    for (int i = 0; i < nSLDLayers; i++)
581
0
      freeLayer(&pasSLDLayers[i]);
582
0
    msFree(pasSLDLayers);
583
0
  }
584
585
0
  if (map->debug == MS_DEBUGLEVEL_VVV) {
586
0
    char *tmpfilename = msTmpFile(map, map->mappath, NULL, "_sld.map");
587
0
    if (tmpfilename == NULL) {
588
0
      tmpfilename = msTmpFile(map, NULL, NULL, "_sld.map");
589
0
    }
590
0
    if (tmpfilename) {
591
0
      msSaveMap(map, tmpfilename);
592
0
      msDebug("msApplySLD(): Map file after SLD was applied %s\n", tmpfilename);
593
0
      msFree(tmpfilename);
594
0
    }
595
0
  }
596
0
  return nStatus;
597
598
#else
599
  msSetError(MS_MISCERR, "OWS support is not available.", "msSLDApplySLD()");
600
  return (MS_FAILURE);
601
#endif
602
0
}
603
604
0
static CPLXMLNode *FindNextChild(CPLXMLNode *psNode, const char *pszChildName) {
605
0
  while (psNode) {
606
0
    if (psNode->eType == CXT_Element &&
607
0
        strcasecmp(psNode->pszValue, pszChildName) == 0) {
608
0
      return psNode;
609
0
    }
610
0
    psNode = psNode->psNext;
611
0
  }
612
0
  return NULL;
613
0
}
614
615
#define LOOP_ON_CHILD_ELEMENT(psParent_, psChild_, pszChildName_)              \
616
0
  for (psChild_ = FindNextChild(psParent_->psChild, pszChildName_);            \
617
0
       psChild_ != NULL;                                                       \
618
0
       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
0
layerObj *msSLDParseSLD(mapObj *map, const char *psSLDXML, int *pnLayers) {
630
0
  CPLXMLNode *psRoot = NULL;
631
0
  CPLXMLNode *psSLD, *psNamedLayer;
632
0
  layerObj *pasLayers = NULL;
633
0
  int iLayer = 0;
634
0
  int nLayers = 0;
635
636
0
  if (map == NULL || psSLDXML == NULL || strlen(psSLDXML) == 0 ||
637
0
      (strstr(psSLDXML, "StyledLayerDescriptor") == NULL)) {
638
0
    msSetError(MS_WMSERR, "Invalid SLD document", "");
639
0
    return NULL;
640
0
  }
641
642
0
  psRoot = CPLParseXMLString(psSLDXML);
643
0
  if (psRoot == NULL) {
644
0
    msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
645
0
    return NULL;
646
0
  }
647
648
  /* strip namespaces ogc and sld and gml */
649
0
  CPLStripXMLNamespace(psRoot, "ogc", 1);
650
0
  CPLStripXMLNamespace(psRoot, "sld", 1);
651
0
  CPLStripXMLNamespace(psRoot, "gml", 1);
652
0
  CPLStripXMLNamespace(psRoot, "se", 1);
653
654
  /* -------------------------------------------------------------------- */
655
  /*      get the root element (StyledLayerDescriptor).                   */
656
  /* -------------------------------------------------------------------- */
657
0
  psSLD = CPLGetXMLNode(psRoot, "=StyledLayerDescriptor");
658
0
  if (!psSLD) {
659
0
    msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
660
0
    return NULL;
661
0
  }
662
663
  /* -------------------------------------------------------------------- */
664
  /*      Parse the named layers.                                         */
665
  /* -------------------------------------------------------------------- */
666
0
  LOOP_ON_CHILD_ELEMENT(psSLD, psNamedLayer, "NamedLayer") { nLayers++; }
667
668
0
  if (nLayers > 0)
669
0
    pasLayers = (layerObj *)malloc(sizeof(layerObj) * nLayers);
670
0
  else
671
0
    return NULL;
672
673
0
  LOOP_ON_CHILD_ELEMENT(psSLD, psNamedLayer, "NamedLayer") {
674
0
    CPLXMLNode *psName = CPLGetXMLNode(psNamedLayer, "Name");
675
0
    initLayer(&pasLayers[iLayer], map);
676
677
0
    if (psName && psName->psChild && psName->psChild->pszValue)
678
0
      pasLayers[iLayer].name = msStrdup(psName->psChild->pszValue);
679
680
0
    if (msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]) != MS_SUCCESS) {
681
0
      int i;
682
0
      for (i = 0; i <= iLayer; i++)
683
0
        freeLayer(&pasLayers[i]);
684
0
      msFree(pasLayers);
685
0
      nLayers = 0;
686
0
      pasLayers = NULL;
687
0
      break;
688
0
    }
689
690
0
    iLayer++;
691
0
  }
692
693
0
  if (pnLayers)
694
0
    *pnLayers = nLayers;
695
696
0
  if (psRoot)
697
0
    CPLDestroyXMLNode(psRoot);
698
699
0
  return pasLayers;
700
0
}
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,
709
0
                         int nNewClasses) {
710
0
  CPLXMLNode *psMinScale = NULL, *psMaxScale = NULL;
711
0
  CPLXMLNode *psName = NULL, *psTitle = NULL;
712
0
  double dfMinScale = 0, dfMaxScale = 0;
713
0
  char *pszName = NULL, *pszTitle = NULL;
714
715
0
  if (psRule && psLayer && nNewClasses > 0) {
716
    /* -------------------------------------------------------------------- */
717
    /*      parse minscale and maxscale.                                    */
718
    /* -------------------------------------------------------------------- */
719
0
    psMinScale = CPLGetXMLNode(psRule, "MinScaleDenominator");
720
0
    if (psMinScale && psMinScale->psChild && psMinScale->psChild->pszValue)
721
0
      dfMinScale = atof(psMinScale->psChild->pszValue);
722
723
0
    psMaxScale = CPLGetXMLNode(psRule, "MaxScaleDenominator");
724
0
    if (psMaxScale && psMaxScale->psChild && psMaxScale->psChild->pszValue)
725
0
      dfMaxScale = atof(psMaxScale->psChild->pszValue);
726
727
    /* -------------------------------------------------------------------- */
728
    /*      parse name and title.                                           */
729
    /* -------------------------------------------------------------------- */
730
0
    psName = CPLGetXMLNode(psRule, "Name");
731
0
    if (psName && psName->psChild && psName->psChild->pszValue)
732
0
      pszName = psName->psChild->pszValue;
733
734
0
    psTitle = CPLGetXMLNode(psRule, "Title");
735
0
    if (psTitle && psTitle->psChild && psTitle->psChild->pszValue)
736
0
      pszTitle = psTitle->psChild->pszValue;
737
738
    /* -------------------------------------------------------------------- */
739
    /*      set the scale to all the classes created by the rule.           */
740
    /* -------------------------------------------------------------------- */
741
0
    if (dfMinScale > 0 || dfMaxScale > 0) {
742
0
      for (int i = 0; i < nNewClasses; i++) {
743
0
        if (dfMinScale > 0)
744
0
          psLayer->_class[psLayer->numclasses - 1 - i]->minscaledenom =
745
0
              dfMinScale;
746
0
        if (dfMaxScale)
747
0
          psLayer->_class[psLayer->numclasses - 1 - i]->maxscaledenom =
748
0
              dfMaxScale;
749
0
      }
750
0
    }
751
    /* -------------------------------------------------------------------- */
752
    /*      set name and title to the classes created by the rule.          */
753
    /* -------------------------------------------------------------------- */
754
0
    for (int i = 0; i < nNewClasses; i++) {
755
0
      if (!psLayer->_class[psLayer->numclasses - 1 - i]->name) {
756
0
        if (pszName)
757
0
          psLayer->_class[psLayer->numclasses - 1 - i]->name =
758
0
              msStrdup(pszName);
759
0
        else if (pszTitle)
760
0
          psLayer->_class[psLayer->numclasses - 1 - i]->name =
761
0
              msStrdup(pszTitle);
762
0
        else {
763
          // Build a name from layer and class info
764
0
          char szTmp[256];
765
0
          snprintf(szTmp, sizeof(szTmp), "%s#%d", psLayer->name,
766
0
                   psLayer->numclasses - 1 - i);
767
0
          psLayer->_class[psLayer->numclasses - 1 - i]->name = msStrdup(szTmp);
768
0
        }
769
0
      }
770
0
    }
771
0
    if (pszTitle) {
772
0
      for (int i = 0; i < nNewClasses; i++) {
773
0
        psLayer->_class[psLayer->numclasses - 1 - i]->title =
774
0
            msStrdup(pszTitle);
775
0
      }
776
0
    }
777
0
  }
778
0
}
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,
787
0
                                                layerObj *psLayer) {
788
0
  char *pszExpression = NULL;
789
0
  CPLXMLNode *psTmpNextNode = NULL;
790
0
  CPLXMLNode *psTmpNode = NULL;
791
0
  FilterEncodingNode *psNode = NULL;
792
0
  char *pszTmpFilter = NULL;
793
0
  layerObj *psCurrentLayer = NULL;
794
0
  const char *pszWmsName = NULL;
795
0
  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
0
  psTmpNode = CPLCloneXMLTree(psFilter);
800
0
  psTmpNextNode = psTmpNode->psNext;
801
0
  psTmpNode->psNext = NULL;
802
0
  pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
803
0
  psTmpNode->psNext = psTmpNextNode;
804
0
  CPLDestroyXMLNode(psTmpNode);
805
806
0
  if (pszTmpFilter) {
807
0
    psNode = FLTParseFilterEncoding(pszTmpFilter);
808
809
0
    CPLFree(pszTmpFilter);
810
0
  }
811
812
0
  if (psNode) {
813
0
    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
0
    for (j = 0; j < psLayer->map->numlayers; j++) {
819
0
      psCurrentLayer = GET_LAYER(psLayer->map, j);
820
821
0
      pszWmsName =
822
0
          msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
823
824
0
      if ((psCurrentLayer->name && psLayer->name &&
825
0
           strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
826
0
          (psCurrentLayer->group && psLayer->name &&
827
0
           strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
828
0
          (psLayer->name && pszWmsName &&
829
0
           strcasecmp(pszWmsName, psLayer->name) == 0))
830
0
        break;
831
0
    }
832
0
    if (j < psLayer->map->numlayers) {
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
0
      while (1) {
837
0
        key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
838
0
        if (!key)
839
0
          break;
840
0
        else
841
0
          msInsertHashTable(&psLayer->metadata, key,
842
0
                            msLookupHashTable(&psCurrentLayer->metadata, key));
843
0
      }
844
0
      FLTPreParseFilterForAliasAndGroup(psNode, psLayer->map, j, "G");
845
0
    }
846
847
0
    pszExpression = FLTGetCommonExpression(psNode, psLayer);
848
0
    FLTFreeFilterEncodingNode(psNode);
849
0
  }
850
851
0
  return pszExpression;
852
0
}
853
854
/************************************************************************/
855
/*                          msSLDParseUserStyle                         */
856
/*                                                                      */
857
/*      Parse UserStyle node.                                           */
858
/************************************************************************/
859
860
0
static void msSLDParseUserStyle(CPLXMLNode *psUserStyle, layerObj *psLayer) {
861
0
  CPLXMLNode *psFeatureTypeStyle;
862
0
  const char *pszUserStyleName = CPLGetXMLValue(psUserStyle, "Name", NULL);
863
0
  if (pszUserStyleName) {
864
0
    const char *pszIsDefault = CPLGetXMLValue(psUserStyle, "IsDefault", "0");
865
0
    if (EQUAL(pszIsDefault, "true") || EQUAL(pszIsDefault, "1")) {
866
0
      msFree(psLayer->classgroup);
867
0
      psLayer->classgroup = msStrdup(pszUserStyleName);
868
0
    }
869
0
  }
870
871
0
  LOOP_ON_CHILD_ELEMENT(psUserStyle, psFeatureTypeStyle, "FeatureTypeStyle") {
872
0
    CPLXMLNode *psRule;
873
874
    /* -------------------------------------------------------------------- */
875
    /*      Parse rules with no Else filter.                                */
876
    /* -------------------------------------------------------------------- */
877
0
    LOOP_ON_CHILD_ELEMENT(psFeatureTypeStyle, psRule, "Rule") {
878
0
      CPLXMLNode *psFilter = NULL;
879
0
      CPLXMLNode *psElseFilter = NULL;
880
0
      int nNewClasses = 0, nClassBeforeFilter = 0, nClassAfterFilter = 0;
881
0
      int nClassAfterRule = 0, nClassBeforeRule = 0;
882
883
      /* used for scale setting */
884
0
      nClassBeforeRule = psLayer->numclasses;
885
886
0
      psElseFilter = CPLGetXMLNode(psRule, "ElseFilter");
887
0
      nClassBeforeFilter = psLayer->numclasses;
888
0
      if (psElseFilter == NULL)
889
0
        msSLDParseRule(psRule, psLayer, pszUserStyleName);
890
0
      nClassAfterFilter = psLayer->numclasses;
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
0
      psFilter = CPLGetXMLNode(psRule, "Filter");
898
0
      if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
899
0
        char *pszExpression =
900
0
            msSLDGetCommonExpressionFromFilter(psFilter, psLayer);
901
0
        if (pszExpression) {
902
0
          int i;
903
0
          nNewClasses = nClassAfterFilter - nClassBeforeFilter;
904
0
          for (i = 0; i < nNewClasses; i++) {
905
0
            expressionObj *exp =
906
0
                &(psLayer->_class[psLayer->numclasses - 1 - i]->expression);
907
0
            msFreeExpression(exp);
908
0
            msInitExpression(exp);
909
0
            exp->string = msStrdup(pszExpression);
910
0
            exp->type = MS_EXPRESSION;
911
0
          }
912
0
          msFree(pszExpression);
913
0
          pszExpression = NULL;
914
0
        }
915
0
      }
916
0
      nClassAfterRule = psLayer->numclasses;
917
0
      nNewClasses = nClassAfterRule - nClassBeforeRule;
918
919
      /* apply scale and title to newly created classes */
920
0
      _SLDApplyRuleValues(psRule, psLayer, nNewClasses);
921
922
      /* TODO : parse legendgraphic */
923
0
    }
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
0
    LOOP_ON_CHILD_ELEMENT(psFeatureTypeStyle, psRule, "Rule") {
931
0
      CPLXMLNode *psElseFilter = CPLGetXMLNode(psRule, "ElseFilter");
932
0
      if (psElseFilter) {
933
0
        msSLDParseRule(psRule, psLayer, pszUserStyleName);
934
0
        _SLDApplyRuleValues(psRule, psLayer, 1);
935
0
        psLayer->_class[psLayer->numclasses - 1]->isfallback = TRUE;
936
0
      }
937
0
    }
938
0
  }
939
0
}
940
941
/************************************************************************/
942
/*                           msSLDParseNamedLayer                       */
943
/*                                                                      */
944
/*      Parse NamedLayer root.                                          */
945
/************************************************************************/
946
0
int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer) {
947
0
  CPLXMLNode *psLayerFeatureConstraints = NULL;
948
949
0
  if (!psRoot || !psLayer)
950
0
    return MS_FAILURE;
951
952
0
  if (CPLGetXMLNode(psRoot, "UserStyle")) {
953
0
    CPLXMLNode *psUserStyle;
954
0
    LOOP_ON_CHILD_ELEMENT(psRoot, psUserStyle, "UserStyle") {
955
0
      msSLDParseUserStyle(psUserStyle, psLayer);
956
0
    }
957
0
  }
958
  /* check for Named styles*/
959
0
  else {
960
0
    CPLXMLNode *psNamedStyle = CPLGetXMLNode(psRoot, "NamedStyle");
961
0
    if (psNamedStyle) {
962
0
      CPLXMLNode *psSLDName = CPLGetXMLNode(psNamedStyle, "Name");
963
0
      if (psSLDName && psSLDName->psChild && psSLDName->psChild->pszValue) {
964
0
        msFree(psLayer->classgroup);
965
0
        psLayer->classgroup = msStrdup(psSLDName->psChild->pszValue);
966
0
      }
967
0
    }
968
0
  }
969
970
  /* Deal with LayerFeatureConstraints */
971
0
  psLayerFeatureConstraints = CPLGetXMLNode(psRoot, "LayerFeatureConstraints");
972
0
  if (psLayerFeatureConstraints != NULL) {
973
0
    CPLXMLNode *psIter = psLayerFeatureConstraints->psChild;
974
0
    CPLXMLNode *psFeatureTypeConstraint = NULL;
975
0
    for (; psIter != NULL; psIter = psIter->psNext) {
976
0
      if (psIter->eType == CXT_Element &&
977
0
          strcmp(psIter->pszValue, "FeatureTypeConstraint") == 0) {
978
0
        if (psFeatureTypeConstraint == NULL) {
979
0
          psFeatureTypeConstraint = psIter;
980
0
        } else {
981
0
          msSetError(MS_WMSERR,
982
0
                     "Only one single FeatureTypeConstraint element "
983
0
                     "per LayerFeatureConstraints is supported",
984
0
                     "");
985
0
          return MS_FAILURE;
986
0
        }
987
0
      }
988
0
    }
989
0
    if (psFeatureTypeConstraint != NULL) {
990
0
      CPLXMLNode *psFilter;
991
0
      if (CPLGetXMLNode(psFeatureTypeConstraint, "FeatureTypeName") != NULL) {
992
0
        msSetError(MS_WMSERR,
993
0
                   "FeatureTypeName element is not "
994
0
                   "supported in FeatureTypeConstraint",
995
0
                   "");
996
0
        return MS_FAILURE;
997
0
      }
998
0
      if (CPLGetXMLNode(psFeatureTypeConstraint, "Extent") != NULL) {
999
0
        msSetError(MS_WMSERR,
1000
0
                   "Extent element is not "
1001
0
                   "supported in FeatureTypeConstraint",
1002
0
                   "");
1003
0
        return MS_FAILURE;
1004
0
      }
1005
0
      psFilter = CPLGetXMLNode(psFeatureTypeConstraint, "Filter");
1006
0
      if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
1007
0
        char *pszExpression =
1008
0
            msSLDGetCommonExpressionFromFilter(psFilter, psLayer);
1009
0
        if (pszExpression) {
1010
0
          msFreeExpression(&psLayer->filter);
1011
0
          msInitExpression(&psLayer->filter);
1012
0
          psLayer->filter.string = pszExpression;
1013
0
          psLayer->filter.type = MS_EXPRESSION;
1014
0
        }
1015
0
      }
1016
0
    }
1017
0
  }
1018
1019
0
  return MS_SUCCESS;
1020
0
}
1021
1022
/************************************************************************/
1023
/*                      msSLDParseRule()                                */
1024
/*                                                                      */
1025
/*      Parse a Rule node into classes for a specific layer.            */
1026
/************************************************************************/
1027
int msSLDParseRule(CPLXMLNode *psRoot, layerObj *psLayer,
1028
0
                   const char *pszUserStyleName) {
1029
0
  CPLXMLNode *psLineSymbolizer = NULL;
1030
0
  CPLXMLNode *psPolygonSymbolizer = NULL;
1031
0
  CPLXMLNode *psPointSymbolizer = NULL;
1032
0
  CPLXMLNode *psTextSymbolizer = NULL;
1033
0
  CPLXMLNode *psRasterSymbolizer = NULL;
1034
1035
0
  int nSymbolizer = 0;
1036
1037
0
  if (!psRoot || !psLayer)
1038
0
    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
0
  LOOP_ON_CHILD_ELEMENT(psRoot, psRasterSymbolizer, "RasterSymbolizer") {
1048
0
    msSLDParseRasterSymbolizer(psRasterSymbolizer, psLayer, pszUserStyleName);
1049
    /* cppcheck-suppress knownConditionTrueFalse */
1050
0
    if (nSymbolizer == 0) {
1051
0
      psLayer->type = MS_LAYER_RASTER;
1052
0
    }
1053
0
  }
1054
1055
  /* Polygon symbolizer */
1056
0
  LOOP_ON_CHILD_ELEMENT(psRoot, psPolygonSymbolizer, "PolygonSymbolizer") {
1057
    /* cppcheck-suppress knownConditionTrueFalse */
1058
0
    const int bNewClass = (nSymbolizer == 0);
1059
0
    msSLDParsePolygonSymbolizer(psPolygonSymbolizer, psLayer, bNewClass,
1060
0
                                pszUserStyleName);
1061
0
    psLayer->type = MS_LAYER_POLYGON;
1062
0
    nSymbolizer++;
1063
0
  }
1064
1065
  /* line symbolizer */
1066
0
  LOOP_ON_CHILD_ELEMENT(psRoot, psLineSymbolizer, "LineSymbolizer") {
1067
0
    const int bNewClass = (nSymbolizer == 0);
1068
0
    msSLDParseLineSymbolizer(psLineSymbolizer, psLayer, bNewClass,
1069
0
                             pszUserStyleName);
1070
0
    if (bNewClass) {
1071
0
      psLayer->type = MS_LAYER_LINE;
1072
0
    }
1073
0
    if (psLayer->type == MS_LAYER_POLYGON) {
1074
0
      const int nClassId = psLayer->numclasses - 1;
1075
0
      if (nClassId >= 0) {
1076
0
        const int nStyleId = psLayer->_class[nClassId]->numstyles - 1;
1077
0
        if (nStyleId >= 0) {
1078
0
          styleObj *psStyle = psLayer->_class[nClassId]->styles[nStyleId];
1079
0
          psStyle->outlinecolor = psStyle->color;
1080
0
          MS_INIT_COLOR(psStyle->color, -1, -1, -1, 255);
1081
0
          MS_COPYSTRING(
1082
0
              psStyle->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].string,
1083
0
              psStyle->exprBindings[MS_STYLE_BINDING_COLOR].string);
1084
0
          psStyle->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].type =
1085
0
              psStyle->exprBindings[MS_STYLE_BINDING_COLOR].type;
1086
0
          msFreeExpression(&(psStyle->exprBindings[MS_STYLE_BINDING_COLOR]));
1087
0
          msInitExpression(&(psStyle->exprBindings[MS_STYLE_BINDING_COLOR]));
1088
0
        }
1089
0
      }
1090
0
    }
1091
0
    nSymbolizer++;
1092
0
  }
1093
1094
  /* Point Symbolizer */
1095
0
  LOOP_ON_CHILD_ELEMENT(psRoot, psPointSymbolizer, "PointSymbolizer") {
1096
0
    const int bNewClass = (nSymbolizer == 0);
1097
0
    msSLDParsePointSymbolizer(psPointSymbolizer, psLayer, bNewClass,
1098
0
                              pszUserStyleName);
1099
0
    if (bNewClass) {
1100
0
      psLayer->type = MS_LAYER_POINT;
1101
0
    }
1102
0
    if (psLayer->type == MS_LAYER_POLYGON || psLayer->type == MS_LAYER_LINE ||
1103
0
        psLayer->type == MS_LAYER_RASTER) {
1104
0
      const int nClassId = psLayer->numclasses - 1;
1105
0
      if (nClassId >= 0) {
1106
0
        const int nStyleId = psLayer->_class[nClassId]->numstyles - 1;
1107
0
        if (nStyleId >= 0) {
1108
0
          styleObj *psStyle = psLayer->_class[nClassId]->styles[nStyleId];
1109
0
          msStyleSetGeomTransform(psStyle, "centroid");
1110
0
        }
1111
0
      }
1112
0
    }
1113
0
    nSymbolizer++;
1114
0
  }
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
0
  if (psLayer->type == MS_LAYER_LINE || psLayer->type == MS_LAYER_POLYGON)
1128
0
    nSymbolizer++;
1129
0
  LOOP_ON_CHILD_ELEMENT(psRoot, psTextSymbolizer, "TextSymbolizer") {
1130
0
    if (nSymbolizer == 0)
1131
0
      psLayer->type = MS_LAYER_POINT;
1132
0
    msSLDParseTextSymbolizer(psTextSymbolizer, psLayer, nSymbolizer > 0,
1133
0
                             pszUserStyleName);
1134
0
  }
1135
1136
0
  return MS_SUCCESS;
1137
0
}
1138
1139
/************************************************************************/
1140
/*                            getClassId()                              */
1141
/************************************************************************/
1142
1143
static int getClassId(layerObj *psLayer, int bNewClass,
1144
0
                      const char *pszUserStyleName) {
1145
0
  int nClassId;
1146
0
  if (bNewClass || psLayer->numclasses <= 0) {
1147
0
    if (msGrowLayerClasses(psLayer) == NULL)
1148
0
      return -1;
1149
0
    initClass(psLayer->_class[psLayer->numclasses]);
1150
0
    nClassId = psLayer->numclasses;
1151
0
    if (pszUserStyleName)
1152
0
      psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
1153
0
    psLayer->numclasses++;
1154
0
  } else {
1155
0
    nClassId = psLayer->numclasses - 1;
1156
0
  }
1157
0
  return nClassId;
1158
0
}
1159
1160
/************************************************************************/
1161
/*                      msSLDParseUomAttribute()                        */
1162
/************************************************************************/
1163
1164
0
int msSLDParseUomAttribute(CPLXMLNode *node, enum MS_UNITS *sizeunits) {
1165
0
  const struct {
1166
0
    enum MS_UNITS unit;
1167
0
    const char *const values[10];
1168
0
  } known_uoms[] = {
1169
0
      {MS_INCHES, {"inch", "inches", NULL}},
1170
0
      {MS_FEET,
1171
0
       {"foot", "feet", "http://www.opengeospatial.org/se/units/foot", NULL}},
1172
0
      {MS_MILES, {"mile", "miles", NULL}},
1173
0
      {MS_METERS,
1174
0
       {"meter", "meters", "metre", "metres",
1175
0
        "http://www.opengeospatial.org/se/units/metre", NULL}},
1176
0
      {MS_KILOMETERS,
1177
0
       {"kilometer", "kilometers", "kilometre", "kilometres", NULL}},
1178
0
      {MS_DD, {"dd", NULL}},
1179
0
      {MS_PIXELS,
1180
0
       {"pixel", "pixels", "px", "http://www.opengeospatial.org/se/units/pixel",
1181
0
        NULL}},
1182
0
      {MS_PERCENTAGES,
1183
0
       {"percent", "percents", "percentage", "percentages", NULL}},
1184
0
      {MS_NAUTICALMILES,
1185
0
       {"nauticalmile", "nauticalmiles", "nautical_mile", "nautical_miles",
1186
0
        NULL}},
1187
0
      {MS_INCHES, {NULL}}};
1188
1189
0
  const char *uom = CPLGetXMLValue(node, "uom", NULL);
1190
0
  if (uom) {
1191
0
    for (int i = 0; known_uoms[i].values[0]; i++)
1192
0
      for (int j = 0; known_uoms[i].values[j]; j++)
1193
0
        if (strcmp(uom, known_uoms[i].values[j]) == 0) {
1194
          // Match found
1195
0
          *sizeunits = known_uoms[i].unit;
1196
0
          return MS_SUCCESS;
1197
0
        }
1198
    // No match was found
1199
0
    return MS_FAILURE;
1200
0
  }
1201
  // No uom was found
1202
0
  *sizeunits = MS_PIXELS;
1203
0
  return MS_SUCCESS;
1204
0
}
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,
1251
0
                             int bNewClass, const char *pszUserStyleName) {
1252
0
  CPLXMLNode *psStroke = NULL, *psOffset = NULL;
1253
1254
0
  if (!psRoot || !psLayer)
1255
0
    return MS_FAILURE;
1256
1257
  // Get uom if any, defaults to MS_PIXELS
1258
0
  enum MS_UNITS sizeunits = MS_PIXELS;
1259
0
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
1260
0
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
1261
0
               "msSLDParsePolygonSymbolizer()");
1262
0
    return MS_FAILURE;
1263
0
  }
1264
1265
0
  psStroke = CPLGetXMLNode(psRoot, "Stroke");
1266
0
  if (psStroke) {
1267
0
    int nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
1268
0
    if (nClassId < 0)
1269
0
      return MS_FAILURE;
1270
1271
0
    const int iStyle = psLayer->_class[nClassId]->numstyles;
1272
0
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
1273
0
    psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
1274
1275
0
    msSLDParseStroke(psStroke, psLayer->_class[nClassId]->styles[iStyle],
1276
0
                     psLayer->map, 0);
1277
1278
    /*parse PerpendicularOffset SLD 1.1.10*/
1279
0
    psOffset = CPLGetXMLNode(psRoot, "PerpendicularOffset");
1280
0
    if (psOffset && psOffset->psChild && psOffset->psChild->pszValue) {
1281
0
      psLayer->_class[nClassId]->styles[iStyle]->offsetx =
1282
0
          atoi(psOffset->psChild->pszValue);
1283
0
      psLayer->_class[nClassId]->styles[iStyle]->offsety =
1284
0
          MS_STYLE_SINGLE_SIDED_OFFSET;
1285
0
    }
1286
0
  }
1287
1288
0
  return MS_SUCCESS;
1289
0
}
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,
1302
0
                     int iColorParam) {
1303
0
  CPLXMLNode *psCssParam = NULL, *psGraphicFill = NULL;
1304
0
  char *psStrkName = NULL;
1305
0
  char *pszDashValue = NULL;
1306
1307
0
  if (!psStroke || !psStyle)
1308
0
    return MS_FAILURE;
1309
1310
  /* parse css parameters */
1311
0
  psCssParam = CPLGetXMLNode(psStroke, "CssParameter");
1312
  /*sld 1.1 used SvgParameter*/
1313
0
  if (psCssParam == NULL)
1314
0
    psCssParam = CPLGetXMLNode(psStroke, "SvgParameter");
1315
1316
0
  while (psCssParam && psCssParam->pszValue &&
1317
0
         (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
1318
0
          strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
1319
0
    psStrkName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
1320
1321
0
    if (psStrkName) {
1322
0
      if (strcasecmp(psStrkName, "stroke") == 0) {
1323
0
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
1324
0
          switch (iColorParam) {
1325
0
          case 0:
1326
0
            msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1327
0
                                    MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1328
0
            break;
1329
0
          case 1:
1330
0
            msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1331
0
                                    MS_STYLE_BINDING_OUTLINECOLOR,
1332
0
                                    MS_OBJ_STYLE);
1333
0
            break;
1334
0
          }
1335
0
        }
1336
0
      } else if (strcasecmp(psStrkName, "stroke-width") == 0) {
1337
0
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
1338
0
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1339
0
                                  MS_STYLE_BINDING_WIDTH, MS_OBJ_STYLE);
1340
0
        }
1341
0
      } else if (strcasecmp(psStrkName, "stroke-dasharray") == 0) {
1342
0
        if (psCssParam->psChild && psCssParam->psChild->psNext &&
1343
0
            psCssParam->psChild->psNext->pszValue) {
1344
0
          int nDash = 0, i;
1345
0
          char **aszValues = NULL;
1346
0
          int nMaxDash;
1347
0
          if (pszDashValue)
1348
0
            free(pszDashValue); /* free previous if multiple stroke-dasharray
1349
                                   attributes were found */
1350
0
          pszDashValue = msStrdup(psCssParam->psChild->psNext->pszValue);
1351
0
          aszValues = msStringSplit(pszDashValue, ' ', &nDash);
1352
0
          if (nDash > 0) {
1353
0
            nMaxDash = nDash;
1354
0
            if (nDash > MS_MAXPATTERNLENGTH)
1355
0
              nMaxDash = MS_MAXPATTERNLENGTH;
1356
1357
0
            psStyle->patternlength = nMaxDash;
1358
0
            for (i = 0; i < nMaxDash; i++)
1359
0
              psStyle->pattern[i] = atof(aszValues[i]);
1360
1361
0
            psStyle->linecap = MS_CJC_BUTT;
1362
0
          }
1363
0
          msFreeCharArray(aszValues, nDash);
1364
0
        }
1365
0
      } else if (strcasecmp(psStrkName, "stroke-opacity") == 0) {
1366
0
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
1367
0
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1368
0
                                  MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1369
0
        }
1370
0
      }
1371
0
    }
1372
0
    psCssParam = psCssParam->psNext;
1373
0
  }
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
0
  psGraphicFill = CPLGetXMLNode(psStroke, "GraphicFill");
1381
0
  if (psGraphicFill)
1382
0
    msSLDParseGraphicFillOrStroke(psGraphicFill, pszDashValue, psStyle, map);
1383
0
  psGraphicFill = CPLGetXMLNode(psStroke, "GraphicStroke");
1384
0
  if (psGraphicFill)
1385
0
    msSLDParseGraphicFillOrStroke(psGraphicFill, pszDashValue, psStyle, map);
1386
1387
0
  if (pszDashValue)
1388
0
    free(pszDashValue);
1389
1390
0
  return MS_SUCCESS;
1391
0
}
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,
1400
0
                            enum objType objtype) {
1401
0
  int status = MS_FAILURE;
1402
0
  const char *ops = "Add+Sub-Mul*Div/";
1403
0
  styleObj *psStyle = static_cast<styleObj *>(psObj);
1404
0
  labelObj *psLabel = static_cast<labelObj *>(psObj);
1405
0
  int lbinding;
1406
0
  expressionObj *exprBindings;
1407
0
  int *nexprbindings;
1408
0
  enum { MS_STYLE_BASE = 0, MS_LABEL_BASE = 100 };
1409
1410
0
  switch (objtype) {
1411
0
  case MS_OBJ_STYLE:
1412
0
    lbinding = binding + MS_STYLE_BASE;
1413
0
    exprBindings = psStyle->exprBindings;
1414
0
    nexprbindings = &psStyle->nexprbindings;
1415
0
    break;
1416
0
  case MS_OBJ_LABEL:
1417
0
    lbinding = binding + MS_LABEL_BASE;
1418
0
    exprBindings = psLabel->exprBindings;
1419
0
    nexprbindings = &psLabel->nexprbindings;
1420
0
    break;
1421
0
  default:
1422
0
    return MS_FAILURE;
1423
0
    break;
1424
0
  }
1425
1426
0
  switch (psRoot->eType) {
1427
0
  case CXT_Text:
1428
    // Parse a raw value
1429
0
    {
1430
0
      msStringBuffer *literal = msStringBufferAlloc();
1431
0
      msStringBufferAppend(literal, "(");
1432
0
      msStringBufferAppend(literal, psRoot->pszValue);
1433
0
      msStringBufferAppend(literal, ")");
1434
0
      msFreeExpression(&(exprBindings[binding]));
1435
0
      msInitExpression(&(exprBindings[binding]));
1436
0
      exprBindings[binding].string =
1437
0
          msStringBufferReleaseStringAndFree(literal);
1438
0
      exprBindings[binding].type = MS_STRING;
1439
0
    }
1440
0
    switch (lbinding) {
1441
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_OFFSET_X:
1442
0
      psStyle->offsetx = atoi(psRoot->pszValue);
1443
0
      status = MS_SUCCESS;
1444
0
      break;
1445
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_OFFSET_Y:
1446
0
      psStyle->offsety = atoi(psRoot->pszValue);
1447
0
      status = MS_SUCCESS;
1448
0
      break;
1449
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_ANGLE:
1450
0
      psStyle->angle = atof(psRoot->pszValue);
1451
0
      status = MS_SUCCESS;
1452
0
      break;
1453
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_SIZE:
1454
0
      psStyle->size = atof(psRoot->pszValue);
1455
0
      status = MS_SUCCESS;
1456
0
      break;
1457
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_WIDTH:
1458
0
      psStyle->width = atof(psRoot->pszValue);
1459
0
      status = MS_SUCCESS;
1460
0
      break;
1461
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_OPACITY:
1462
0
      psStyle->opacity = atof(psRoot->pszValue) * 100;
1463
0
      status = MS_SUCCESS;
1464
      // Apply opacity as the alpha channel color(s)
1465
0
      if (psStyle->opacity < 100) {
1466
0
        int alpha = MS_NINT(psStyle->opacity * 2.55);
1467
0
        psStyle->color.alpha = alpha;
1468
0
        psStyle->outlinecolor.alpha = alpha;
1469
0
        psStyle->mincolor.alpha = alpha;
1470
0
        psStyle->maxcolor.alpha = alpha;
1471
0
      }
1472
0
      break;
1473
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_COLOR:
1474
0
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
1475
0
        psStyle->color.red = msHexToInt(psRoot->pszValue + 1);
1476
0
        psStyle->color.green = msHexToInt(psRoot->pszValue + 3);
1477
0
        psStyle->color.blue = msHexToInt(psRoot->pszValue + 5);
1478
0
        status = MS_SUCCESS;
1479
0
      }
1480
0
      break;
1481
0
    case MS_STYLE_BASE + MS_STYLE_BINDING_OUTLINECOLOR:
1482
0
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
1483
0
        psStyle->outlinecolor.red = msHexToInt(psRoot->pszValue + 1);
1484
0
        psStyle->outlinecolor.green = msHexToInt(psRoot->pszValue + 3);
1485
0
        psStyle->outlinecolor.blue = msHexToInt(psRoot->pszValue + 5);
1486
0
        status = MS_SUCCESS;
1487
0
      }
1488
0
      break;
1489
1490
0
    case MS_LABEL_BASE + MS_LABEL_BINDING_SIZE:
1491
0
      psLabel->size = atof(psRoot->pszValue);
1492
0
      if (psLabel->size <= 0.0) {
1493
0
        psLabel->size = 10.0;
1494
0
      }
1495
0
      status = MS_SUCCESS;
1496
0
      break;
1497
0
    case MS_LABEL_BASE + MS_LABEL_BINDING_ANGLE:
1498
0
      psLabel->angle = atof(psRoot->pszValue);
1499
0
      status = MS_SUCCESS;
1500
0
      break;
1501
0
    case MS_LABEL_BASE + MS_LABEL_BINDING_COLOR:
1502
0
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
1503
0
        psLabel->color.red = msHexToInt(psRoot->pszValue + 1);
1504
0
        psLabel->color.green = msHexToInt(psRoot->pszValue + 3);
1505
0
        psLabel->color.blue = msHexToInt(psRoot->pszValue + 5);
1506
0
        status = MS_SUCCESS;
1507
0
      }
1508
0
      break;
1509
0
    case MS_LABEL_BASE + MS_LABEL_BINDING_OUTLINECOLOR:
1510
0
      if (strlen(psRoot->pszValue) == 7 && psRoot->pszValue[0] == '#') {
1511
0
        psLabel->outlinecolor.red = msHexToInt(psRoot->pszValue + 1);
1512
0
        psLabel->outlinecolor.green = msHexToInt(psRoot->pszValue + 3);
1513
0
        psLabel->outlinecolor.blue = msHexToInt(psRoot->pszValue + 5);
1514
0
        status = MS_SUCCESS;
1515
0
      }
1516
0
      break;
1517
0
    default:
1518
0
      break;
1519
0
    }
1520
0
    break;
1521
0
  case CXT_Element:
1522
0
    if (strcasecmp(psRoot->pszValue, "Literal") == 0 && psRoot->psChild) {
1523
      // Parse a <ogc:Literal> element
1524
0
      status =
1525
0
          msSLDParseOgcExpression(psRoot->psChild, psObj, binding, objtype);
1526
0
    } else if (strcasecmp(psRoot->pszValue, "PropertyName") == 0 &&
1527
0
               psRoot->psChild) {
1528
      // Parse a <ogc:PropertyName> element
1529
0
      msStringBuffer *property = msStringBufferAlloc();
1530
0
      const char *strDelim = "";
1531
1532
0
      switch (lbinding) {
1533
0
      case MS_STYLE_BASE + MS_STYLE_BINDING_COLOR:
1534
0
      case MS_STYLE_BASE + MS_STYLE_BINDING_OUTLINECOLOR:
1535
0
      case MS_LABEL_BASE + MS_LABEL_BINDING_COLOR:
1536
0
      case MS_LABEL_BASE + MS_LABEL_BINDING_OUTLINECOLOR:
1537
0
        strDelim = "\"";
1538
        /* FALLTHROUGH */
1539
0
      default:
1540
0
        msStringBufferAppend(property, strDelim);
1541
0
        msStringBufferAppend(property, "[");
1542
0
        msStringBufferAppend(property, psRoot->psChild->pszValue);
1543
0
        msStringBufferAppend(property, "]");
1544
0
        msStringBufferAppend(property, strDelim);
1545
0
        msInitExpression(&(exprBindings[binding]));
1546
0
        exprBindings[binding].string =
1547
0
            msStringBufferReleaseStringAndFree(property);
1548
0
        exprBindings[binding].type = MS_EXPRESSION;
1549
0
        (*nexprbindings)++;
1550
0
        break;
1551
0
      }
1552
0
      status = MS_SUCCESS;
1553
0
    } else if (strcasecmp(psRoot->pszValue, "Function") == 0 &&
1554
0
               psRoot->psChild && CPLGetXMLValue(psRoot, "name", NULL) &&
1555
0
               psRoot->psChild->psNext) {
1556
      // Parse a <ogc:Function> element
1557
0
      msStringBuffer *function = msStringBufferAlloc();
1558
1559
      // Parse function name
1560
0
      const char *funcname = CPLGetXMLValue(psRoot, "name", NULL);
1561
0
      msStringBufferAppend(function, funcname);
1562
0
      msStringBufferAppend(function, "(");
1563
0
      msInitExpression(&(exprBindings[binding]));
1564
1565
      // Parse arguments
1566
0
      const char *sep = "";
1567
0
      for (CPLXMLNode *argument = psRoot->psChild->psNext; argument;
1568
0
           argument = argument->psNext) {
1569
0
        status = msSLDParseOgcExpression(argument, psObj, binding, objtype);
1570
0
        if (status != MS_SUCCESS)
1571
0
          break;
1572
0
        msStringBufferAppend(function, sep);
1573
0
        msStringBufferAppend(function, exprBindings[binding].string);
1574
0
        msReplaceFreeableStr(&(exprBindings[binding].string), nullptr);
1575
0
        msInitExpression(&(exprBindings[binding]));
1576
0
        sep = ",";
1577
0
      }
1578
0
      msStringBufferAppend(function, ")");
1579
0
      exprBindings[binding].string =
1580
0
          msStringBufferReleaseStringAndFree(function);
1581
0
      exprBindings[binding].type = MS_EXPRESSION;
1582
0
      (*nexprbindings)++;
1583
0
      status = MS_SUCCESS;
1584
0
    } else if (strstr(ops, psRoot->pszValue) && psRoot->psChild &&
1585
0
               psRoot->psChild->psNext) {
1586
      // Parse an arithmetic element <ogc:Add>, <ogc:Sub>, <ogc:Mul>, <ogc:Div>
1587
0
      const char op[2] = {*(strstr(ops, psRoot->pszValue) + 3), '\0'};
1588
0
      msStringBuffer *expression = msStringBufferAlloc();
1589
1590
      // Parse first operand
1591
0
      msStringBufferAppend(expression, "(");
1592
0
      msInitExpression(&(exprBindings[binding]));
1593
0
      status =
1594
0
          msSLDParseOgcExpression(psRoot->psChild, psObj, binding, objtype);
1595
1596
      // Parse second operand
1597
0
      if (status == MS_SUCCESS) {
1598
0
        msStringBufferAppend(expression, exprBindings[binding].string);
1599
0
        msStringBufferAppend(expression, op);
1600
0
        msReplaceFreeableStr(&(exprBindings[binding].string), nullptr);
1601
0
        msInitExpression(&(exprBindings[binding]));
1602
0
        status = msSLDParseOgcExpression(psRoot->psChild->psNext, psObj,
1603
0
                                         binding, objtype);
1604
0
        if (status == MS_SUCCESS && exprBindings[binding].string) {
1605
0
          msStringBufferAppend(expression, exprBindings[binding].string);
1606
0
          msStringBufferAppend(expression, ")");
1607
0
          msReplaceFreeableStr(&(exprBindings[binding].string),
1608
0
                               msStringBufferReleaseStringAndFree(expression));
1609
0
          expression = NULL;
1610
0
          exprBindings[binding].type = MS_EXPRESSION;
1611
0
          (*nexprbindings)++;
1612
0
        }
1613
0
      }
1614
0
      if (expression != NULL) {
1615
0
        msStringBufferFree(expression);
1616
0
        msInitExpression(&(exprBindings[binding]));
1617
0
      }
1618
0
    }
1619
0
    break;
1620
0
  default:
1621
0
    break;
1622
0
  }
1623
1624
0
  return status;
1625
0
}
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,
1711
0
                                int bNewClass, const char *pszUserStyleName) {
1712
0
  CPLXMLNode *psFill, *psStroke;
1713
0
  CPLXMLNode *psDisplacement = NULL, *psDisplacementX = NULL,
1714
0
             *psDisplacementY = NULL;
1715
0
  int nOffsetX = -1, nOffsetY = -1;
1716
1717
0
  if (!psRoot || !psLayer)
1718
0
    return MS_FAILURE;
1719
1720
  // Get uom if any, defaults to MS_PIXELS
1721
0
  enum MS_UNITS sizeunits = MS_PIXELS;
1722
0
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
1723
0
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
1724
0
               "msSLDParsePolygonSymbolizer()");
1725
0
    return MS_FAILURE;
1726
0
  }
1727
1728
  /*parse displacement for SLD 1.1.0*/
1729
0
  psDisplacement = CPLGetXMLNode(psRoot, "Displacement");
1730
0
  if (psDisplacement) {
1731
0
    psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
1732
0
    psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
1733
    /* psCssParam->psChild->psNext->pszValue) */
1734
0
    if (psDisplacementX && psDisplacementX->psChild &&
1735
0
        psDisplacementX->psChild->pszValue && psDisplacementY &&
1736
0
        psDisplacementY->psChild && psDisplacementY->psChild->pszValue) {
1737
0
      nOffsetX = atoi(psDisplacementX->psChild->pszValue);
1738
0
      nOffsetY = atoi(psDisplacementY->psChild->pszValue);
1739
0
    }
1740
0
  }
1741
1742
0
  psFill = CPLGetXMLNode(psRoot, "Fill");
1743
0
  if (psFill) {
1744
0
    const int nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
1745
0
    if (nClassId < 0)
1746
0
      return MS_FAILURE;
1747
1748
0
    const int iStyle = psLayer->_class[nClassId]->numstyles;
1749
0
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
1750
0
    psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
1751
1752
0
    msSLDParsePolygonFill(psFill, psLayer->_class[nClassId]->styles[iStyle],
1753
0
                          psLayer->map);
1754
1755
0
    if (nOffsetX > 0 && nOffsetY > 0) {
1756
0
      psLayer->_class[nClassId]->styles[iStyle]->offsetx = nOffsetX;
1757
0
      psLayer->_class[nClassId]->styles[iStyle]->offsety = nOffsetY;
1758
0
    }
1759
0
  }
1760
  /* stroke which corresponds to the outline in mapserver */
1761
  /* is drawn after the fill */
1762
0
  psStroke = CPLGetXMLNode(psRoot, "Stroke");
1763
0
  if (psStroke) {
1764
    /* -------------------------------------------------------------------- */
1765
    /*      there was a fill so add a style to the last class created       */
1766
    /*      by the fill                                                     */
1767
    /* -------------------------------------------------------------------- */
1768
0
    int nClassId;
1769
0
    int iStyle;
1770
0
    if (psFill && psLayer->numclasses > 0) {
1771
0
      nClassId = psLayer->numclasses - 1;
1772
0
      iStyle = psLayer->_class[nClassId]->numstyles;
1773
0
      msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
1774
0
      psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
1775
0
    } else {
1776
0
      nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
1777
0
      if (nClassId < 0)
1778
0
        return MS_FAILURE;
1779
1780
0
      iStyle = psLayer->_class[nClassId]->numstyles;
1781
0
      msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
1782
0
      psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
1783
0
    }
1784
0
    msSLDParseStroke(psStroke, psLayer->_class[nClassId]->styles[iStyle],
1785
0
                     psLayer->map, 1);
1786
1787
0
    if (nOffsetX > 0 && nOffsetY > 0) {
1788
0
      psLayer->_class[nClassId]->styles[iStyle]->offsetx = nOffsetX;
1789
0
      psLayer->_class[nClassId]->styles[iStyle]->offsety = nOffsetY;
1790
0
    }
1791
0
  }
1792
1793
0
  return MS_SUCCESS;
1794
0
}
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
0
int msSLDParsePolygonFill(CPLXMLNode *psFill, styleObj *psStyle, mapObj *map) {
1803
0
  CPLXMLNode *psCssParam, *psGraphicFill;
1804
0
  char *psFillName = NULL;
1805
1806
0
  if (!psFill || !psStyle || !map)
1807
0
    return MS_FAILURE;
1808
1809
  /* sets the default fill color defined in the spec #808080 */
1810
0
  psStyle->color.red = 128;
1811
0
  psStyle->color.green = 128;
1812
0
  psStyle->color.blue = 128;
1813
1814
0
  psCssParam = CPLGetXMLNode(psFill, "CssParameter");
1815
  /*sld 1.1 used SvgParameter*/
1816
0
  if (psCssParam == NULL)
1817
0
    psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
1818
1819
0
  while (psCssParam && psCssParam->pszValue &&
1820
0
         (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
1821
0
          strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
1822
0
    psFillName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
1823
0
    if (psFillName) {
1824
0
      if (strcasecmp(psFillName, "fill") == 0) {
1825
0
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
1826
0
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1827
0
                                  MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1828
0
        }
1829
0
      } else if (strcasecmp(psFillName, "fill-opacity") == 0) {
1830
0
        if (psCssParam->psChild && psCssParam->psChild->psNext) {
1831
0
          msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1832
0
                                  MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1833
0
        }
1834
0
      }
1835
0
    }
1836
0
    psCssParam = psCssParam->psNext;
1837
0
  }
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
0
  psGraphicFill = CPLGetXMLNode(psFill, "GraphicFill");
1844
0
  if (psGraphicFill)
1845
0
    msSLDParseGraphicFillOrStroke(psGraphicFill, NULL, psStyle, map);
1846
0
  psGraphicFill = CPLGetXMLNode(psFill, "GraphicStroke");
1847
0
  if (psGraphicFill)
1848
0
    msSLDParseGraphicFillOrStroke(psGraphicFill, NULL, psStyle, map);
1849
1850
0
  return MS_SUCCESS;
1851
0
}
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,
1860
0
                                  styleObj *psStyle, mapObj *map) {
1861
0
  (void)pszDashValue_unused;
1862
0
  CPLXMLNode *psCssParam, *psGraphic, *psExternalGraphic, *psMark, *psSize,
1863
0
      *psGap, *psInitialGap;
1864
0
  CPLXMLNode *psWellKnownName, *psStroke, *psFill;
1865
0
  CPLXMLNode *psDisplacement = NULL, *psDisplacementX = NULL,
1866
0
             *psDisplacementY = NULL;
1867
0
  CPLXMLNode *psOpacity = NULL, *psRotation = NULL;
1868
0
  char *psName = NULL, *psValue = NULL;
1869
0
  char *pszSymbolName = NULL;
1870
0
  int bFilled = 0;
1871
1872
0
  if (!psRoot || !psStyle || !map)
1873
0
    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
0
  psGraphic = CPLGetXMLNode(psRoot, "Graphic");
1888
0
  if (psGraphic) {
1889
    /* extract symbol size */
1890
0
    psSize = CPLGetXMLNode(psGraphic, "Size");
1891
0
    if (psSize && psSize->psChild) {
1892
0
      msSLDParseOgcExpression(psSize->psChild, psStyle, MS_STYLE_BINDING_SIZE,
1893
0
                              MS_OBJ_STYLE);
1894
0
    } else {
1895
      /*do not set a default for external symbols #2305*/
1896
0
      psExternalGraphic = CPLGetXMLNode(psGraphic, "ExternalGraphic");
1897
0
      if (!psExternalGraphic)
1898
0
        psStyle->size = 6; /* default value */
1899
0
    }
1900
1901
    /*SLD 1.1.0 extract opacity, rotation, displacement*/
1902
0
    psOpacity = CPLGetXMLNode(psGraphic, "Opacity");
1903
0
    if (psOpacity && psOpacity->psChild) {
1904
0
      msSLDParseOgcExpression(psOpacity->psChild, psStyle,
1905
0
                              MS_STYLE_BINDING_OPACITY, MS_OBJ_STYLE);
1906
0
    }
1907
1908
0
    psRotation = CPLGetXMLNode(psGraphic, "Rotation");
1909
0
    if (psRotation && psRotation->psChild) {
1910
0
      msSLDParseOgcExpression(psRotation->psChild, psStyle,
1911
0
                              MS_STYLE_BINDING_ANGLE, MS_OBJ_STYLE);
1912
0
    }
1913
0
    psDisplacement = CPLGetXMLNode(psGraphic, "Displacement");
1914
0
    if (psDisplacement && psDisplacement->psChild) {
1915
0
      psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
1916
0
      if (psDisplacementX && psDisplacementX->psChild) {
1917
0
        msSLDParseOgcExpression(psDisplacementX->psChild, psStyle,
1918
0
                                MS_STYLE_BINDING_OFFSET_X, MS_OBJ_STYLE);
1919
0
      }
1920
0
      psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
1921
0
      if (psDisplacementY && psDisplacementY->psChild) {
1922
0
        msSLDParseOgcExpression(psDisplacementY->psChild, psStyle,
1923
0
                                MS_STYLE_BINDING_OFFSET_Y, MS_OBJ_STYLE);
1924
0
      }
1925
0
    }
1926
    /* extract symbol */
1927
0
    psMark = CPLGetXMLNode(psGraphic, "Mark");
1928
0
    if (psMark) {
1929
0
      pszSymbolName = NULL;
1930
0
      psWellKnownName = CPLGetXMLNode(psMark, "WellKnownName");
1931
0
      if (psWellKnownName && psWellKnownName->psChild &&
1932
0
          psWellKnownName->psChild->pszValue)
1933
0
        pszSymbolName = msStrdup(psWellKnownName->psChild->pszValue);
1934
1935
      /* default symbol is square */
1936
1937
0
      if (!pszSymbolName || !*pszSymbolName ||
1938
0
          (strcasecmp(pszSymbolName, "square") != 0 &&
1939
0
           strcasecmp(pszSymbolName, "circle") != 0 &&
1940
0
           strcasecmp(pszSymbolName, "triangle") != 0 &&
1941
0
           strcasecmp(pszSymbolName, "star") != 0 &&
1942
0
           strcasecmp(pszSymbolName, "cross") != 0 &&
1943
0
           strcasecmp(pszSymbolName, "x") != 0)) {
1944
0
        if (!pszSymbolName || !*pszSymbolName ||
1945
0
            msGetSymbolIndex(&map->symbolset, pszSymbolName, MS_FALSE) < 0) {
1946
0
          msFree(pszSymbolName);
1947
0
          pszSymbolName = msStrdup("square");
1948
0
        }
1949
0
      }
1950
1951
      /* check if the symbol should be filled or not */
1952
0
      psFill = CPLGetXMLNode(psMark, "Fill");
1953
0
      psStroke = CPLGetXMLNode(psMark, "Stroke");
1954
1955
0
      if (psFill || psStroke) {
1956
0
        if (psFill)
1957
0
          bFilled = 1;
1958
0
        else
1959
0
          bFilled = 0;
1960
1961
0
        if (psFill) {
1962
0
          psCssParam = CPLGetXMLNode(psFill, "CssParameter");
1963
          /*sld 1.1 used SvgParameter*/
1964
0
          if (psCssParam == NULL)
1965
0
            psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
1966
1967
0
          while (psCssParam && psCssParam->pszValue &&
1968
0
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
1969
0
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
1970
0
            psName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
1971
0
            if (psName && strcasecmp(psName, "fill") == 0) {
1972
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
1973
0
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
1974
0
                                        MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
1975
0
              }
1976
0
            } else if (psName && strcasecmp(psName, "fill-opacity") == 0) {
1977
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
1978
0
                psValue = psCssParam->psChild->psNext->pszValue;
1979
0
                if (psValue) {
1980
0
                  psStyle->color.alpha = (int)(atof(psValue) * 255);
1981
0
                }
1982
0
              }
1983
0
            }
1984
1985
0
            psCssParam = psCssParam->psNext;
1986
0
          }
1987
0
        }
1988
0
        if (psStroke) {
1989
0
          psCssParam = CPLGetXMLNode(psStroke, "CssParameter");
1990
          /*sld 1.1 used SvgParameter*/
1991
0
          if (psCssParam == NULL)
1992
0
            psCssParam = CPLGetXMLNode(psStroke, "SvgParameter");
1993
1994
0
          while (psCssParam && psCssParam->pszValue &&
1995
0
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
1996
0
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
1997
0
            psName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
1998
0
            if (psName && strcasecmp(psName, "stroke") == 0) {
1999
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
2000
0
                if (bFilled) {
2001
0
                  msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
2002
0
                                          MS_STYLE_BINDING_OUTLINECOLOR,
2003
0
                                          MS_OBJ_STYLE);
2004
0
                } else {
2005
0
                  msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
2006
0
                                          MS_STYLE_BINDING_COLOR, MS_OBJ_STYLE);
2007
0
                }
2008
0
              }
2009
0
            } else if (psName && strcasecmp(psName, "stroke-opacity") == 0) {
2010
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
2011
0
                psValue = psCssParam->psChild->psNext->pszValue;
2012
0
                if (psValue) {
2013
0
                  if (bFilled) {
2014
0
                    psStyle->outlinecolor.alpha = (int)(atof(psValue) * 255);
2015
0
                  } else {
2016
0
                    psStyle->color.alpha = (int)(atof(psValue) * 255);
2017
0
                  }
2018
0
                }
2019
0
              }
2020
0
            } else if (psName && strcasecmp(psName, "stroke-width") == 0) {
2021
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
2022
0
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psStyle,
2023
0
                                        MS_STYLE_BINDING_WIDTH, MS_OBJ_STYLE);
2024
0
              }
2025
0
            }
2026
2027
0
            psCssParam = psCssParam->psNext;
2028
0
          }
2029
0
        }
2030
0
      }
2031
      /* set the default color if color is not not already set */
2032
0
      if ((psStyle->color.red < 0 || psStyle->color.green == -1 ||
2033
0
           psStyle->color.blue == -1) &&
2034
0
          (psStyle->outlinecolor.red == -1 ||
2035
0
           psStyle->outlinecolor.green == -1 ||
2036
0
           psStyle->outlinecolor.blue == -1)) {
2037
0
        psStyle->color.red = 128;
2038
0
        psStyle->color.green = 128;
2039
0
        psStyle->color.blue = 128;
2040
0
      }
2041
2042
      /* Get the corresponding symbol id  */
2043
0
      psStyle->symbol = msSLDGetMarkSymbol(map, pszSymbolName, bFilled);
2044
0
      if (psStyle->symbol > 0 && psStyle->symbol < map->symbolset.numsymbols)
2045
0
        psStyle->symbolname =
2046
0
            msStrdup(map->symbolset.symbol[psStyle->symbol]->name);
2047
2048
0
    } else {
2049
0
      psExternalGraphic = CPLGetXMLNode(psGraphic, "ExternalGraphic");
2050
0
      if (psExternalGraphic)
2051
0
        msSLDParseExternalGraphic(psExternalGraphic, psStyle, map);
2052
0
    }
2053
0
    msFree(pszSymbolName);
2054
0
  }
2055
0
  psGap = CPLGetXMLNode(psRoot, "Gap");
2056
0
  if (psGap && psGap->psChild && psGap->psChild->pszValue) {
2057
0
    psStyle->gap = atof(psGap->psChild->pszValue);
2058
0
  }
2059
0
  psInitialGap = CPLGetXMLNode(psRoot, "InitialGap");
2060
0
  if (psInitialGap && psInitialGap->psChild &&
2061
0
      psInitialGap->psChild->pszValue) {
2062
0
    psStyle->initialgap = atof(psInitialGap->psChild->pszValue);
2063
0
  }
2064
2065
0
  return MS_SUCCESS;
2066
0
}
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
0
int msSLDGetMarkSymbol(mapObj *map, char *pszSymbolName, int bFilled) {
2076
0
  int nSymbolId = 0;
2077
0
  symbolObj *psSymbol = NULL;
2078
2079
0
  if (!map || !pszSymbolName)
2080
0
    return 0;
2081
2082
0
  if (strcasecmp(pszSymbolName, "square") == 0) {
2083
0
    if (bFilled)
2084
0
      nSymbolId = msGetSymbolIndex(&map->symbolset,
2085
0
                                   SLD_MARK_SYMBOL_SQUARE_FILLED, MS_FALSE);
2086
0
    else
2087
0
      nSymbolId =
2088
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_SQUARE, MS_FALSE);
2089
0
  } else if (strcasecmp(pszSymbolName, "circle") == 0) {
2090
2091
0
    if (bFilled)
2092
0
      nSymbolId = msGetSymbolIndex(&map->symbolset,
2093
0
                                   SLD_MARK_SYMBOL_CIRCLE_FILLED, MS_FALSE);
2094
0
    else
2095
0
      nSymbolId =
2096
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_CIRCLE, MS_FALSE);
2097
0
  } else if (strcasecmp(pszSymbolName, "triangle") == 0) {
2098
2099
0
    if (bFilled)
2100
0
      nSymbolId = msGetSymbolIndex(&map->symbolset,
2101
0
                                   SLD_MARK_SYMBOL_TRIANGLE_FILLED, MS_FALSE);
2102
0
    else
2103
0
      nSymbolId =
2104
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_TRIANGLE, MS_FALSE);
2105
0
  } else if (strcasecmp(pszSymbolName, "star") == 0) {
2106
2107
0
    if (bFilled)
2108
0
      nSymbolId = msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_STAR_FILLED,
2109
0
                                   MS_FALSE);
2110
0
    else
2111
0
      nSymbolId =
2112
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_STAR, MS_FALSE);
2113
0
  } else if (strcasecmp(pszSymbolName, "cross") == 0) {
2114
2115
0
    if (bFilled)
2116
0
      nSymbolId = msGetSymbolIndex(&map->symbolset,
2117
0
                                   SLD_MARK_SYMBOL_CROSS_FILLED, MS_FALSE);
2118
0
    else
2119
0
      nSymbolId =
2120
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_CROSS, MS_FALSE);
2121
0
  } else if (strcasecmp(pszSymbolName, "x") == 0) {
2122
2123
0
    if (bFilled)
2124
0
      nSymbolId =
2125
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_X_FILLED, MS_FALSE);
2126
0
    else
2127
0
      nSymbolId =
2128
0
          msGetSymbolIndex(&map->symbolset, SLD_MARK_SYMBOL_X, MS_FALSE);
2129
0
  } else {
2130
0
    nSymbolId = msGetSymbolIndex(&map->symbolset, pszSymbolName, MS_FALSE);
2131
0
  }
2132
2133
0
  if (nSymbolId <= 0) {
2134
0
    if ((psSymbol = msGrowSymbolSet(&(map->symbolset))) == NULL)
2135
0
      return 0; /* returns 0 for no symbol */
2136
2137
0
    nSymbolId = map->symbolset.numsymbols;
2138
0
    map->symbolset.numsymbols++;
2139
0
    initSymbol(psSymbol);
2140
0
    psSymbol->inmapfile = MS_TRUE;
2141
0
    psSymbol->sizex = 1;
2142
0
    psSymbol->sizey = 1;
2143
2144
0
    if (strcasecmp(pszSymbolName, "square") == 0) {
2145
0
      if (bFilled)
2146
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_SQUARE_FILLED);
2147
0
      else
2148
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_SQUARE);
2149
2150
0
      psSymbol->type = MS_SYMBOL_VECTOR;
2151
0
      if (bFilled)
2152
0
        psSymbol->filled = MS_TRUE;
2153
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2154
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2155
0
      psSymbol->numpoints++;
2156
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2157
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2158
0
      psSymbol->numpoints++;
2159
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2160
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2161
0
      psSymbol->numpoints++;
2162
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2163
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2164
0
      psSymbol->numpoints++;
2165
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2166
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2167
0
      psSymbol->numpoints++;
2168
0
    } else if (strcasecmp(pszSymbolName, "circle") == 0) {
2169
0
      if (bFilled)
2170
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CIRCLE_FILLED);
2171
0
      else
2172
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CIRCLE);
2173
2174
0
      psSymbol->type = MS_SYMBOL_ELLIPSE;
2175
0
      if (bFilled)
2176
0
        psSymbol->filled = MS_TRUE;
2177
2178
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2179
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2180
0
      psSymbol->sizex = 1;
2181
0
      psSymbol->sizey = 1;
2182
0
      psSymbol->numpoints++;
2183
0
    } else if (strcasecmp(pszSymbolName, "triangle") == 0) {
2184
0
      if (bFilled)
2185
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_TRIANGLE_FILLED);
2186
0
      else
2187
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_TRIANGLE);
2188
2189
0
      psSymbol->type = MS_SYMBOL_VECTOR;
2190
0
      if (bFilled)
2191
0
        psSymbol->filled = MS_TRUE;
2192
2193
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2194
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2195
0
      psSymbol->numpoints++;
2196
0
      psSymbol->points[psSymbol->numpoints].x = 0.5;
2197
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2198
0
      psSymbol->numpoints++;
2199
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2200
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2201
0
      psSymbol->numpoints++;
2202
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2203
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2204
0
      psSymbol->numpoints++;
2205
2206
0
    } else if (strcasecmp(pszSymbolName, "star") == 0) {
2207
0
      if (bFilled)
2208
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_STAR_FILLED);
2209
0
      else
2210
0
        psSymbol->name = msStrdup(SLD_MARK_SYMBOL_STAR);
2211
2212
0
      psSymbol->type = MS_SYMBOL_VECTOR;
2213
0
      if (bFilled)
2214
0
        psSymbol->filled = MS_TRUE;
2215
2216
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2217
0
      psSymbol->points[psSymbol->numpoints].y = 0.375;
2218
0
      psSymbol->numpoints++;
2219
0
      psSymbol->points[psSymbol->numpoints].x = 0.35;
2220
0
      psSymbol->points[psSymbol->numpoints].y = 0.375;
2221
0
      psSymbol->numpoints++;
2222
0
      psSymbol->points[psSymbol->numpoints].x = 0.5;
2223
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2224
0
      psSymbol->numpoints++;
2225
0
      psSymbol->points[psSymbol->numpoints].x = 0.65;
2226
0
      psSymbol->points[psSymbol->numpoints].y = 0.375;
2227
0
      psSymbol->numpoints++;
2228
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2229
0
      psSymbol->points[psSymbol->numpoints].y = 0.375;
2230
0
      psSymbol->numpoints++;
2231
0
      psSymbol->points[psSymbol->numpoints].x = 0.75;
2232
0
      psSymbol->points[psSymbol->numpoints].y = 0.625;
2233
0
      psSymbol->numpoints++;
2234
0
      psSymbol->points[psSymbol->numpoints].x = 0.875;
2235
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2236
0
      psSymbol->numpoints++;
2237
0
      psSymbol->points[psSymbol->numpoints].x = 0.5;
2238
0
      psSymbol->points[psSymbol->numpoints].y = 0.75;
2239
0
      psSymbol->numpoints++;
2240
0
      psSymbol->points[psSymbol->numpoints].x = 0.125;
2241
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2242
0
      psSymbol->numpoints++;
2243
0
      psSymbol->points[psSymbol->numpoints].x = 0.25;
2244
0
      psSymbol->points[psSymbol->numpoints].y = 0.625;
2245
0
      psSymbol->numpoints++;
2246
0
    }
2247
    /* cross is like plus (+) since there is also X symbol ?? */
2248
0
    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
0
      psSymbol->name = msStrdup(SLD_MARK_SYMBOL_CROSS);
2254
2255
0
      psSymbol->type = MS_SYMBOL_VECTOR;
2256
      /* if (bFilled) */
2257
      /* psSymbol->filled = MS_TRUE; */
2258
2259
0
      psSymbol->points[psSymbol->numpoints].x = 0.5;
2260
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2261
0
      psSymbol->numpoints++;
2262
0
      psSymbol->points[psSymbol->numpoints].x = 0.5;
2263
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2264
0
      psSymbol->numpoints++;
2265
0
      psSymbol->points[psSymbol->numpoints].x = -99;
2266
0
      psSymbol->points[psSymbol->numpoints].y = -99;
2267
0
      psSymbol->numpoints++;
2268
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2269
0
      psSymbol->points[psSymbol->numpoints].y = 0.5;
2270
0
      psSymbol->numpoints++;
2271
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2272
0
      psSymbol->points[psSymbol->numpoints].y = 0.5;
2273
0
      psSymbol->numpoints++;
2274
0
    } 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
0
      psSymbol->name = msStrdup(SLD_MARK_SYMBOL_X);
2280
2281
0
      psSymbol->type = MS_SYMBOL_VECTOR;
2282
      /* if (bFilled) */
2283
      /* psSymbol->filled = MS_TRUE; */
2284
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2285
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2286
0
      psSymbol->numpoints++;
2287
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2288
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2289
0
      psSymbol->numpoints++;
2290
0
      psSymbol->points[psSymbol->numpoints].x = -99;
2291
0
      psSymbol->points[psSymbol->numpoints].y = -99;
2292
0
      psSymbol->numpoints++;
2293
0
      psSymbol->points[psSymbol->numpoints].x = 0;
2294
0
      psSymbol->points[psSymbol->numpoints].y = 1;
2295
0
      psSymbol->numpoints++;
2296
0
      psSymbol->points[psSymbol->numpoints].x = 1;
2297
0
      psSymbol->points[psSymbol->numpoints].y = 0;
2298
0
      psSymbol->numpoints++;
2299
0
    }
2300
0
  }
2301
2302
0
  return nSymbolId;
2303
0
}
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
0
                          int nGap_ignored) {
2313
0
  (void)nGap_ignored;
2314
0
  int nSymbolId = 0;
2315
0
  symbolObj *psSymbol = NULL;
2316
2317
0
  if (map && pszFileName) {
2318
0
    if ((psSymbol = msGrowSymbolSet(&(map->symbolset))) == NULL)
2319
0
      return 0; /* returns 0 for no symbol */
2320
0
    nSymbolId = map->symbolset.numsymbols;
2321
0
    map->symbolset.numsymbols++;
2322
0
    initSymbol(psSymbol);
2323
0
    psSymbol->inmapfile = MS_TRUE;
2324
0
    psSymbol->type = MS_SYMBOL_PIXMAP;
2325
0
    psSymbol->name = msStrdup(extGraphicName);
2326
0
    psSymbol->imagepath = msStrdup(pszFileName);
2327
0
    psSymbol->full_pixmap_path = msStrdup(pszFileName);
2328
0
  }
2329
0
  return nSymbolId;
2330
0
}
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,
2347
0
                              int bNewClass, const char *pszUserStyleName) {
2348
0
  int nClassId = 0;
2349
0
  int iStyle = 0;
2350
2351
0
  if (!psRoot || !psLayer)
2352
0
    return MS_FAILURE;
2353
2354
0
  nClassId = getClassId(psLayer, bNewClass, pszUserStyleName);
2355
0
  if (nClassId < 0)
2356
0
    return MS_FAILURE;
2357
2358
  // Get uom if any, defaults to MS_PIXELS
2359
0
  enum MS_UNITS sizeunits = MS_PIXELS;
2360
0
  if (msSLDParseUomAttribute(psRoot, &sizeunits) != MS_SUCCESS) {
2361
0
    msSetError(MS_WMSERR, "Invalid uom attribute value.",
2362
0
               "msSLDParsePolygonSymbolizer()");
2363
0
    return MS_FAILURE;
2364
0
  }
2365
2366
0
  iStyle = psLayer->_class[nClassId]->numstyles;
2367
0
  msMaybeAllocateClassStyle(psLayer->_class[nClassId], iStyle);
2368
0
  psLayer->_class[nClassId]->styles[iStyle]->sizeunits = sizeunits;
2369
2370
0
  msSLDParseGraphicFillOrStroke(
2371
0
      psRoot, NULL, psLayer->_class[nClassId]->styles[iStyle], psLayer->map);
2372
2373
0
  return MS_SUCCESS;
2374
0
}
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,
2384
0
                              mapObj *map) {
2385
0
  char *pszFormat = NULL;
2386
0
  CPLXMLNode *psURL = NULL, *psFormat = NULL, *psTmp = NULL;
2387
0
  char *pszURL = NULL;
2388
2389
0
  if (!psExternalGraphic || !psStyle || !map)
2390
0
    return MS_FAILURE;
2391
2392
0
  psFormat = CPLGetXMLNode(psExternalGraphic, "Format");
2393
0
  if (psFormat && psFormat->psChild && psFormat->psChild->pszValue)
2394
0
    pszFormat = psFormat->psChild->pszValue;
2395
2396
  /* supports GIF and PNG and SVG */
2397
0
  if (pszFormat && (strcasecmp(pszFormat, "GIF") == 0 ||
2398
0
                    strcasecmp(pszFormat, "image/gif") == 0 ||
2399
0
                    strcasecmp(pszFormat, "PNG") == 0 ||
2400
0
                    strcasecmp(pszFormat, "image/png") == 0 ||
2401
0
                    strcasecmp(pszFormat, "image/svg+xml") == 0)) {
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
0
    psURL = CPLGetXMLNode(psExternalGraphic, "OnlineResource");
2407
0
    if (psURL && psURL->psChild) {
2408
0
      psTmp = psURL->psChild;
2409
0
      while (psTmp != NULL && psTmp->pszValue &&
2410
0
             strcasecmp(psTmp->pszValue, "xlink:href") != 0) {
2411
0
        psTmp = psTmp->psNext;
2412
0
      }
2413
0
      if (psTmp && psTmp->psChild) {
2414
0
        pszURL = (char *)psTmp->psChild->pszValue;
2415
2416
0
        char *symbolurl = NULL;
2417
        // Handle relative URL for ExternalGraphic
2418
0
        if (map->sldurl && !strstr(pszURL, "://")) {
2419
0
          char *baseurl = NULL;
2420
0
          char *relpath = NULL;
2421
0
          symbolurl = static_cast<char *>(malloc(sizeof(char) * MS_MAXPATHLEN));
2422
0
          if (pszURL[0] != '/') {
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
0
            baseurl = msGetPath(map->sldurl);
2428
0
            relpath = pszURL;
2429
0
          } 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
0
            baseurl = msStrdup(map->sldurl);
2435
0
            relpath = pszURL + 1;
2436
0
            char *sep = strstr(baseurl, "://");
2437
0
            if (sep)
2438
0
              sep += 3;
2439
0
            else
2440
0
              sep = baseurl;
2441
0
            sep = strchr(sep, '/');
2442
0
            if (!sep)
2443
0
              sep = baseurl + strlen(baseurl);
2444
0
            sep[1] = '\0';
2445
0
          }
2446
0
          msBuildPath(symbolurl, baseurl, relpath);
2447
0
          msFree(baseurl);
2448
0
        } else {
2449
          // Absolute URL
2450
          // e.g. symbol: http://example.com/path/to/assets/symbol.svg
2451
0
          symbolurl = msStrdup(pszURL);
2452
0
        }
2453
2454
        /* validate the ExternalGraphic parameter */
2455
0
        if (msValidateParameter(symbolurl,
2456
0
                                msLookupHashTable(&(map->web.validation),
2457
0
                                                  "sld_external_graphic"),
2458
0
                                NULL, NULL, NULL) != MS_SUCCESS) {
2459
0
          msSetError(MS_WEBERR,
2460
0
                     "SLD ExternalGraphic OnlineResource value fails to "
2461
0
                     "validate against sld_external_graphic VALIDATION",
2462
0
                     "mapserv()");
2463
0
          msFree(symbolurl);
2464
0
          return MS_FAILURE;
2465
0
        }
2466
2467
        /*external symbols using http will be automatically downloaded. The
2468
          file should be saved in a temporary directory (msAddImageSymbol)
2469
          #2305*/
2470
0
        psStyle->symbol = msGetSymbolIndex(&map->symbolset, symbolurl, MS_TRUE);
2471
0
        msFree(symbolurl);
2472
2473
0
        if (psStyle->symbol > 0 && psStyle->symbol < map->symbolset.numsymbols)
2474
0
          psStyle->symbolname =
2475
0
              msStrdup(map->symbolset.symbol[psStyle->symbol]->name);
2476
2477
        /* set the color parameter if not set. Does not make sense */
2478
        /* for pixmap but mapserver needs it. */
2479
0
        if (psStyle->color.red == -1 || psStyle->color.green ||
2480
0
            psStyle->color.blue) {
2481
0
          psStyle->color.red = 0;
2482
0
          psStyle->color.green = 0;
2483
0
          psStyle->color.blue = 0;
2484
0
        }
2485
0
      }
2486
0
    }
2487
0
  }
2488
2489
0
  return MS_SUCCESS;
2490
0
}
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,
2585
                             int bOtherSymboliser,
2586
0
                             const char *pszUserStyleName) {
2587
0
  int nStyleId = 0, nClassId = 0;
2588
2589
0
  if (!psRoot || !psLayer)
2590
0
    return MS_FAILURE;
2591
2592
0
  if (!bOtherSymboliser) {
2593
0
    if (msGrowLayerClasses(psLayer) == NULL)
2594
0
      return MS_FAILURE;
2595
0
    initClass(psLayer->_class[psLayer->numclasses]);
2596
0
    nClassId = psLayer->numclasses;
2597
0
    if (pszUserStyleName)
2598
0
      psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
2599
0
    psLayer->numclasses++;
2600
0
    msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
2601
0
    nStyleId = 0;
2602
0
  } else {
2603
0
    nClassId = psLayer->numclasses - 1;
2604
0
    if (nClassId >= 0) /* should always be true */
2605
0
      nStyleId = psLayer->_class[nClassId]->numstyles - 1;
2606
0
  }
2607
2608
0
  if (nStyleId >= 0 && nClassId >= 0) /* should always be true */
2609
0
    msSLDParseTextParams(psRoot, psLayer, psLayer->_class[nClassId]);
2610
2611
0
  return MS_SUCCESS;
2612
0
}
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,
2710
0
                               const char *pszUserStyleName) {
2711
0
  CPLXMLNode *psColorMap = NULL, *psColorEntry = NULL, *psOpacity = NULL;
2712
0
  char *pszColor = NULL, *pszQuantity = NULL;
2713
0
  char *pszPreviousColor = NULL, *pszPreviousQuality = NULL;
2714
0
  colorObj sColor;
2715
0
  char szExpression[100];
2716
0
  double dfOpacity = 1.0;
2717
0
  char *pszLabel = NULL, *pszPreviousLabel = NULL;
2718
0
  char *pch = NULL, *pchPrevious = NULL;
2719
2720
0
  CPLXMLNode *psNode = NULL, *psCategorize = NULL;
2721
0
  char *pszTmp = NULL;
2722
0
  int nValues = 0, nThresholds = 0;
2723
0
  int i, nMaxValues = 100, nMaxThreshold = 100;
2724
2725
0
  if (!psRoot || !psLayer)
2726
0
    return MS_FAILURE;
2727
2728
0
  psOpacity = CPLGetXMLNode(psRoot, "Opacity");
2729
0
  if (psOpacity) {
2730
0
    if (psOpacity->psChild && psOpacity->psChild->pszValue)
2731
0
      dfOpacity = atof(psOpacity->psChild->pszValue);
2732
2733
    /* values in sld goes from 0.0 (for transparent) to 1.0 (for full opacity);
2734
     */
2735
0
    if (dfOpacity >= 0.0 && dfOpacity <= 1.0)
2736
0
      msSetLayerOpacity(psLayer, (int)(dfOpacity * 100));
2737
0
    else {
2738
0
      msSetError(MS_WMSERR,
2739
0
                 "Invalid opacity value. Values should be between 0.0 and 1.0",
2740
0
                 "msSLDParseRasterSymbolizer()");
2741
0
      return MS_FAILURE;
2742
0
    }
2743
0
  }
2744
0
  psColorMap = CPLGetXMLNode(psRoot, "ColorMap");
2745
0
  if (psColorMap) {
2746
0
    psColorEntry = CPLGetXMLNode(psColorMap, "ColorMapEntry");
2747
2748
0
    if (psColorEntry) { /*SLD 1.0*/
2749
0
      while (psColorEntry && psColorEntry->pszValue &&
2750
0
             strcasecmp(psColorEntry->pszValue, "ColorMapEntry") == 0) {
2751
0
        pszColor = (char *)CPLGetXMLValue(psColorEntry, "color", NULL);
2752
0
        pszQuantity = (char *)CPLGetXMLValue(psColorEntry, "quantity", NULL);
2753
0
        pszLabel = (char *)CPLGetXMLValue(psColorEntry, "label", NULL);
2754
2755
0
        if (pszColor && pszQuantity) {
2756
0
          if (pszPreviousColor && pszPreviousQuality) {
2757
0
            if (strlen(pszPreviousColor) == 7 && pszPreviousColor[0] == '#' &&
2758
0
                strlen(pszColor) == 7 && pszColor[0] == '#') {
2759
0
              sColor.red = msHexToInt(pszPreviousColor + 1);
2760
0
              sColor.green = msHexToInt(pszPreviousColor + 3);
2761
0
              sColor.blue = msHexToInt(pszPreviousColor + 5);
2762
2763
              /* pszQuantity and pszPreviousQuality may be integer or float */
2764
0
              pchPrevious = strchr(pszPreviousQuality, '.');
2765
0
              pch = strchr(pszQuantity, '.');
2766
0
              if (pchPrevious == NULL && pch == NULL) {
2767
0
                snprintf(szExpression, sizeof(szExpression),
2768
0
                         "([pixel] >= %d AND [pixel] < %d)",
2769
0
                         atoi(pszPreviousQuality), atoi(pszQuantity));
2770
0
              } else if (pchPrevious != NULL && pch == NULL) {
2771
0
                snprintf(szExpression, sizeof(szExpression),
2772
0
                         "([pixel] >= %f AND [pixel] < %d)",
2773
0
                         atof(pszPreviousQuality), atoi(pszQuantity));
2774
0
              } else if (pchPrevious == NULL && pch != NULL) {
2775
0
                snprintf(szExpression, sizeof(szExpression),
2776
0
                         "([pixel] >= %d AND [pixel] < %f)",
2777
0
                         atoi(pszPreviousQuality), atof(pszQuantity));
2778
0
              } else {
2779
0
                snprintf(szExpression, sizeof(szExpression),
2780
0
                         "([pixel] >= %f AND [pixel] < %f)",
2781
0
                         atof(pszPreviousQuality), atof(pszQuantity));
2782
0
              }
2783
2784
0
              if (msGrowLayerClasses(psLayer) == NULL)
2785
0
                return MS_FAILURE;
2786
0
              else {
2787
0
                initClass(psLayer->_class[psLayer->numclasses]);
2788
0
                psLayer->numclasses++;
2789
0
                const int nClassId = psLayer->numclasses - 1;
2790
0
                if (pszUserStyleName)
2791
0
                  psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
2792
2793
                /*set the class name using the label. If label not defined
2794
                  set it with the quantity*/
2795
0
                if (pszPreviousLabel)
2796
0
                  psLayer->_class[nClassId]->name = msStrdup(pszPreviousLabel);
2797
0
                else
2798
0
                  psLayer->_class[nClassId]->name =
2799
0
                      msStrdup(pszPreviousQuality);
2800
2801
0
                msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
2802
2803
0
                psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
2804
0
                psLayer->_class[nClassId]->styles[0]->color.green =
2805
0
                    sColor.green;
2806
0
                psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
2807
2808
0
                if (!psLayer->classitem ||
2809
0
                    strcasecmp(psLayer->classitem, "[pixel]") != 0) {
2810
0
                  free(psLayer->classitem);
2811
0
                  psLayer->classitem = msStrdup("[pixel]");
2812
0
                }
2813
2814
0
                msLoadExpressionString(&psLayer->_class[nClassId]->expression,
2815
0
                                       szExpression);
2816
0
              }
2817
0
            } else {
2818
0
              msSetError(MS_WMSERR, "Invalid ColorMap Entry.",
2819
0
                         "msSLDParseRasterSymbolizer()");
2820
0
              return MS_FAILURE;
2821
0
            }
2822
0
          }
2823
2824
0
          pszPreviousColor = pszColor;
2825
0
          pszPreviousQuality = pszQuantity;
2826
0
          pszPreviousLabel = pszLabel;
2827
0
        }
2828
0
        psColorEntry = psColorEntry->psNext;
2829
0
      }
2830
      /* do the last Color Map Entry */
2831
0
      if (pszColor && pszQuantity) {
2832
0
        if (strlen(pszColor) == 7 && pszColor[0] == '#') {
2833
0
          sColor.red = msHexToInt(pszColor + 1);
2834
0
          sColor.green = msHexToInt(pszColor + 3);
2835
0
          sColor.blue = msHexToInt(pszColor + 5);
2836
2837
          /* pszQuantity may be integer or float */
2838
0
          pch = strchr(pszQuantity, '.');
2839
0
          if (pch == NULL) {
2840
0
            snprintf(szExpression, sizeof(szExpression), "([pixel] = %d)",
2841
0
                     atoi(pszQuantity));
2842
0
          } else {
2843
0
            snprintf(szExpression, sizeof(szExpression), "([pixel] = %f)",
2844
0
                     atof(pszQuantity));
2845
0
          }
2846
2847
0
          if (msGrowLayerClasses(psLayer) == NULL)
2848
0
            return MS_FAILURE;
2849
0
          else {
2850
0
            initClass(psLayer->_class[psLayer->numclasses]);
2851
0
            psLayer->numclasses++;
2852
0
            const int nClassId = psLayer->numclasses - 1;
2853
0
            if (pszUserStyleName)
2854
0
              psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
2855
2856
0
            msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
2857
0
            if (pszLabel)
2858
0
              psLayer->_class[nClassId]->name = msStrdup(pszLabel);
2859
0
            else
2860
0
              psLayer->_class[nClassId]->name = msStrdup(pszQuantity);
2861
0
            psLayer->_class[nClassId]->numstyles = 1;
2862
0
            psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
2863
0
            psLayer->_class[nClassId]->styles[0]->color.green = sColor.green;
2864
0
            psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
2865
2866
0
            if (!psLayer->classitem ||
2867
0
                strcasecmp(psLayer->classitem, "[pixel]") != 0) {
2868
0
              free(psLayer->classitem);
2869
0
              psLayer->classitem = msStrdup("[pixel]");
2870
0
            }
2871
2872
0
            msLoadExpressionString(&psLayer->_class[nClassId]->expression,
2873
0
                                   szExpression);
2874
0
          }
2875
0
        }
2876
0
      }
2877
0
    } else if ((psCategorize = CPLGetXMLNode(psColorMap, "Categorize"))) {
2878
0
      char **papszValues = (char **)msSmallMalloc(sizeof(char *) * nMaxValues);
2879
0
      char **papszThresholds =
2880
0
          (char **)msSmallMalloc(sizeof(char *) * nMaxThreshold);
2881
0
      psNode = CPLGetXMLNode(psCategorize, "Value");
2882
0
      while (psNode && psNode->pszValue && psNode->psChild &&
2883
0
             psNode->psChild->pszValue)
2884
2885
0
      {
2886
0
        if (strcasecmp(psNode->pszValue, "Value") == 0) {
2887
0
          papszValues[nValues] = psNode->psChild->pszValue;
2888
0
          nValues++;
2889
0
          if (nValues == nMaxValues) {
2890
0
            nMaxValues += 100;
2891
0
            papszValues = (char **)msSmallRealloc(papszValues,
2892
0
                                                  sizeof(char *) * nMaxValues);
2893
0
          }
2894
0
        } else if (strcasecmp(psNode->pszValue, "Threshold") == 0) {
2895
0
          papszThresholds[nThresholds] = psNode->psChild->pszValue;
2896
0
          nThresholds++;
2897
0
          if (nValues == nMaxThreshold) {
2898
0
            nMaxThreshold += 100;
2899
0
            papszThresholds = (char **)msSmallRealloc(
2900
0
                papszThresholds, sizeof(char *) * nMaxThreshold);
2901
0
          }
2902
0
        }
2903
0
        psNode = psNode->psNext;
2904
0
      }
2905
2906
0
      if (nThresholds > 0 && nValues == nThresholds + 1) {
2907
        /*free existing classes*/
2908
0
        for (i = 0; i < psLayer->numclasses; i++) {
2909
0
          if (psLayer->_class[i] != NULL) {
2910
0
            psLayer->_class[i]->layer = NULL;
2911
0
            if (freeClass(psLayer->_class[i]) == MS_SUCCESS) {
2912
0
              msFree(psLayer->_class[i]);
2913
0
              psLayer->_class[i] = NULL;
2914
0
            }
2915
0
          }
2916
0
        }
2917
0
        psLayer->numclasses = 0;
2918
0
        for (i = 0; i < nValues; i++) {
2919
0
          pszTmp = (papszValues[i]);
2920
0
          if (pszTmp && strlen(pszTmp) == 7 && pszTmp[0] == '#') {
2921
0
            sColor.red = msHexToInt(pszTmp + 1);
2922
0
            sColor.green = msHexToInt(pszTmp + 3);
2923
0
            sColor.blue = msHexToInt(pszTmp + 5);
2924
0
            if (i == 0) {
2925
0
              if (strchr(papszThresholds[i], '.'))
2926
0
                snprintf(szExpression, sizeof(szExpression), "([pixel] < %f)",
2927
0
                         atof(papszThresholds[i]));
2928
0
              else
2929
0
                snprintf(szExpression, sizeof(szExpression), "([pixel] < %d)",
2930
0
                         atoi(papszThresholds[i]));
2931
2932
0
            } else if (i < nValues - 1) {
2933
0
              if (strchr(papszThresholds[i], '.'))
2934
0
                snprintf(szExpression, sizeof(szExpression),
2935
0
                         "([pixel] >= %f AND [pixel] < %f)",
2936
0
                         atof(papszThresholds[i - 1]),
2937
0
                         atof(papszThresholds[i]));
2938
0
              else
2939
0
                snprintf(szExpression, sizeof(szExpression),
2940
0
                         "([pixel] >= %d AND [pixel] < %d)",
2941
0
                         atoi(papszThresholds[i - 1]),
2942
0
                         atoi(papszThresholds[i]));
2943
0
            } else {
2944
0
              if (strchr(papszThresholds[i - 1], '.'))
2945
0
                snprintf(szExpression, sizeof(szExpression), "([pixel] >= %f)",
2946
0
                         atof(papszThresholds[i - 1]));
2947
0
              else
2948
0
                snprintf(szExpression, sizeof(szExpression), "([pixel] >= %d)",
2949
0
                         atoi(papszThresholds[i - 1]));
2950
0
            }
2951
0
            if (msGrowLayerClasses(psLayer)) {
2952
0
              initClass(psLayer->_class[psLayer->numclasses]);
2953
0
              psLayer->numclasses++;
2954
0
              const int nClassId = psLayer->numclasses - 1;
2955
0
              if (pszUserStyleName)
2956
0
                psLayer->_class[nClassId]->group = msStrdup(pszUserStyleName);
2957
0
              msMaybeAllocateClassStyle(psLayer->_class[nClassId], 0);
2958
0
              psLayer->_class[nClassId]->numstyles = 1;
2959
0
              psLayer->_class[nClassId]->styles[0]->color.red = sColor.red;
2960
0
              psLayer->_class[nClassId]->styles[0]->color.green = sColor.green;
2961
0
              psLayer->_class[nClassId]->styles[0]->color.blue = sColor.blue;
2962
0
              if (psLayer->classitem &&
2963
0
                  strcasecmp(psLayer->classitem, "[pixel]") != 0)
2964
0
                free(psLayer->classitem);
2965
0
              psLayer->classitem = msStrdup("[pixel]");
2966
0
              msLoadExpressionString(&psLayer->_class[nClassId]->expression,
2967
0
                                     szExpression);
2968
0
            }
2969
0
          }
2970
0
        }
2971
0
      }
2972
0
      msFree(papszValues);
2973
0
      msFree(papszThresholds);
2974
2975
0
    } else {
2976
0
      msSetError(MS_WMSERR, "Invalid SLD document. msSLDParseRaster", "");
2977
0
      return MS_FAILURE;
2978
0
    }
2979
0
  }
2980
2981
0
  return MS_SUCCESS;
2982
0
}
2983
/************************************************************************/
2984
/*                           msSLDParseTextParams                       */
2985
/*                                                                      */
2986
/*      Parse text parameters like font, placement and color.           */
2987
/************************************************************************/
2988
int msSLDParseTextParams(CPLXMLNode *psRoot, layerObj *psLayer,
2989
0
                         classObj *psClass) {
2990
0
  char szFontName[100];
2991
2992
0
  CPLXMLNode *psLabel = NULL, *psFont = NULL;
2993
0
  CPLXMLNode *psCssParam = NULL;
2994
0
  char *pszName = NULL, *pszFontFamily = NULL, *pszFontStyle = NULL;
2995
0
  char *pszFontWeight = NULL;
2996
0
  CPLXMLNode *psLabelPlacement = NULL, *psPointPlacement = NULL,
2997
0
             *psLinePlacement = NULL;
2998
0
  CPLXMLNode *psFill = NULL, *psHalo = NULL, *psHaloRadius = NULL,
2999
0
             *psHaloFill = NULL;
3000
0
  labelObj *psLabelObj = NULL;
3001
0
  szFontName[0] = '\0';
3002
3003
0
  if (!psRoot || !psClass || !psLayer)
3004
0
    return MS_FAILURE;
3005
3006
0
  if (psClass->numlabels == 0) {
3007
0
    if (msGrowClassLabels(psClass) == NULL)
3008
0
      return (MS_FAILURE);
3009
0
    initLabel(psClass->labels[0]);
3010
0
    psClass->numlabels++;
3011
0
  }
3012
0
  psLabelObj = psClass->labels[0];
3013
3014
  /*set the angle by default to auto. the angle can be
3015
    modified Label Placement #2806*/
3016
0
  psLabelObj->anglemode = MS_AUTO;
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
0
  psLabel = CPLGetXMLNode(psRoot, "Label");
3025
0
  if (psLabel) {
3026
0
    const char *sep = "";
3027
0
    msStringBuffer *classtext = msStringBufferAlloc();
3028
0
    msStringBufferAppend(classtext, "(");
3029
0
    for (CPLXMLNode *psTmpNode = psLabel->psChild; psTmpNode;
3030
0
         psTmpNode = psTmpNode->psNext) {
3031
0
      if (psTmpNode->eType == CXT_Text && psTmpNode->pszValue) {
3032
0
        msStringBufferAppend(classtext, sep);
3033
0
        msStringBufferAppend(classtext, "\"");
3034
0
        msStringBufferAppend(classtext, psTmpNode->pszValue);
3035
0
        msStringBufferAppend(classtext, "\"");
3036
0
        sep = "+";
3037
0
      } else if (psTmpNode->eType == CXT_Element &&
3038
0
                 strcasecmp(psTmpNode->pszValue, "Literal") == 0 &&
3039
0
                 psTmpNode->psChild) {
3040
0
        msStringBufferAppend(classtext, sep);
3041
0
        msStringBufferAppend(classtext, "\"");
3042
0
        msStringBufferAppend(classtext, psTmpNode->psChild->pszValue);
3043
0
        msStringBufferAppend(classtext, "\"");
3044
0
        sep = "+";
3045
0
      } else if (psTmpNode->eType == CXT_Element &&
3046
0
                 strcasecmp(psTmpNode->pszValue, "PropertyName") == 0 &&
3047
0
                 psTmpNode->psChild) {
3048
0
        msStringBufferAppend(classtext, sep);
3049
0
        msStringBufferAppend(classtext, "\"[");
3050
0
        msStringBufferAppend(classtext, psTmpNode->psChild->pszValue);
3051
0
        msStringBufferAppend(classtext, "]\"");
3052
0
        sep = "+";
3053
0
      } else if (psTmpNode->eType == CXT_Element &&
3054
0
                 strcasecmp(psTmpNode->pszValue, "Function") == 0 &&
3055
0
                 psTmpNode->psChild) {
3056
0
        msStringBufferAppend(classtext, sep);
3057
0
        msStringBufferAppend(classtext, "tostring(");
3058
3059
0
        labelObj tempExpressionCollector;
3060
0
        initLabel(&tempExpressionCollector);
3061
0
        msSLDParseOgcExpression(psTmpNode, &tempExpressionCollector,
3062
0
                                MS_LABEL_BINDING_SIZE, MS_OBJ_LABEL);
3063
0
        msStringBufferAppend(
3064
0
            classtext,
3065
0
            tempExpressionCollector.exprBindings[MS_LABEL_BINDING_SIZE].string);
3066
0
        freeLabel(&tempExpressionCollector);
3067
3068
0
        msStringBufferAppend(classtext, ",\"%g\")");
3069
0
        sep = "+";
3070
0
      }
3071
0
    }
3072
0
    msStringBufferAppend(classtext, ")");
3073
0
    const char *expressionstring = msStringBufferGetString(classtext);
3074
0
    if (strlen(expressionstring) > 2) {
3075
0
      msLoadExpressionString(&psClass->text, (char *)expressionstring);
3076
0
    }
3077
0
    msStringBufferFree(classtext);
3078
3079
0
    {
3080
      /* font */
3081
0
      psFont = CPLGetXMLNode(psRoot, "Font");
3082
0
      if (psFont) {
3083
0
        psCssParam = CPLGetXMLNode(psFont, "CssParameter");
3084
        /*sld 1.1 used SvgParameter*/
3085
0
        if (psCssParam == NULL)
3086
0
          psCssParam = CPLGetXMLNode(psFont, "SvgParameter");
3087
3088
0
        while (psCssParam && psCssParam->pszValue &&
3089
0
               (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
3090
0
                strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
3091
0
          pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
3092
0
          if (pszName) {
3093
0
            if (strcasecmp(pszName, "font-family") == 0) {
3094
0
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
3095
0
                  psCssParam->psChild->psNext->pszValue)
3096
0
                pszFontFamily = psCssParam->psChild->psNext->pszValue;
3097
0
            }
3098
            /* normal, italic, oblique */
3099
0
            else if (strcasecmp(pszName, "font-style") == 0) {
3100
0
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
3101
0
                  psCssParam->psChild->psNext->pszValue)
3102
0
                pszFontStyle = psCssParam->psChild->psNext->pszValue;
3103
0
            }
3104
            /* normal or bold */
3105
0
            else if (strcasecmp(pszName, "font-weight") == 0) {
3106
0
              if (psCssParam->psChild && psCssParam->psChild->psNext &&
3107
0
                  psCssParam->psChild->psNext->pszValue)
3108
0
                pszFontWeight = psCssParam->psChild->psNext->pszValue;
3109
0
            }
3110
            /* default is 10 pix */
3111
0
            else if (strcasecmp(pszName, "font-size") == 0) {
3112
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
3113
0
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psLabelObj,
3114
0
                                        MS_LABEL_BINDING_SIZE, MS_OBJ_LABEL);
3115
0
              }
3116
0
            }
3117
0
          }
3118
0
          psCssParam = psCssParam->psNext;
3119
0
        }
3120
0
      }
3121
3122
      /* -------------------------------------------------------------------- */
3123
      /*      parse the label placement.                                      */
3124
      /* -------------------------------------------------------------------- */
3125
0
      psLabelPlacement = CPLGetXMLNode(psRoot, "LabelPlacement");
3126
0
      if (psLabelPlacement) {
3127
0
        psPointPlacement = CPLGetXMLNode(psLabelPlacement, "PointPlacement");
3128
0
        psLinePlacement = CPLGetXMLNode(psLabelPlacement, "LinePlacement");
3129
0
        if (psPointPlacement)
3130
0
          ParseTextPointPlacement(psPointPlacement, psClass);
3131
0
        if (psLinePlacement)
3132
0
          ParseTextLinePlacement(psLinePlacement, psClass);
3133
0
      }
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
0
      if (pszFontFamily) {
3143
0
        snprintf(szFontName, sizeof(szFontName), "%s", pszFontFamily);
3144
0
        if (pszFontWeight && strcasecmp(pszFontWeight, "normal") != 0) {
3145
0
          strlcat(szFontName, "-", sizeof(szFontName));
3146
0
          strlcat(szFontName, pszFontWeight, sizeof(szFontName));
3147
0
        }
3148
0
        if (pszFontStyle && strcasecmp(pszFontStyle, "normal") != 0) {
3149
0
          strlcat(szFontName, "-", sizeof(szFontName));
3150
0
          strlcat(szFontName, pszFontStyle, sizeof(szFontName));
3151
0
        }
3152
3153
0
        if ((msLookupHashTable(&(psLayer->map->fontset.fonts), szFontName) !=
3154
0
             NULL)) {
3155
0
          psLabelObj->font = msStrdup(szFontName);
3156
0
        }
3157
0
      }
3158
3159
      /* -------------------------------------------------------------------- */
3160
      /*      parse the halo parameter.                                       */
3161
      /* -------------------------------------------------------------------- */
3162
0
      psHalo = CPLGetXMLNode(psRoot, "Halo");
3163
0
      if (psHalo) {
3164
0
        psHaloRadius = CPLGetXMLNode(psHalo, "Radius");
3165
0
        if (psHaloRadius && psHaloRadius->psChild &&
3166
0
            psHaloRadius->psChild->pszValue)
3167
0
          psLabelObj->outlinewidth = atoi(psHaloRadius->psChild->pszValue);
3168
3169
0
        psHaloFill = CPLGetXMLNode(psHalo, "Fill");
3170
0
        if (psHaloFill) {
3171
0
          psCssParam = CPLGetXMLNode(psHaloFill, "CssParameter");
3172
          /*sld 1.1 used SvgParameter*/
3173
0
          if (psCssParam == NULL)
3174
0
            psCssParam = CPLGetXMLNode(psHaloFill, "SvgParameter");
3175
3176
0
          while (psCssParam && psCssParam->pszValue &&
3177
0
                 (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
3178
0
                  strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
3179
0
            pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
3180
0
            if (pszName) {
3181
0
              if (strcasecmp(pszName, "fill") == 0) {
3182
0
                if (psCssParam->psChild && psCssParam->psChild->psNext) {
3183
0
                  msSLDParseOgcExpression(
3184
0
                      psCssParam->psChild->psNext, psLabelObj,
3185
0
                      MS_LABEL_BINDING_OUTLINECOLOR, MS_OBJ_LABEL);
3186
0
                }
3187
0
              }
3188
0
            }
3189
0
            psCssParam = psCssParam->psNext;
3190
0
          }
3191
0
        }
3192
0
      }
3193
      /* -------------------------------------------------------------------- */
3194
      /*      Parse the color                                                 */
3195
      /* -------------------------------------------------------------------- */
3196
0
      psFill = CPLGetXMLNode(psRoot, "Fill");
3197
0
      if (psFill) {
3198
0
        psCssParam = CPLGetXMLNode(psFill, "CssParameter");
3199
        /*sld 1.1 used SvgParameter*/
3200
0
        if (psCssParam == NULL)
3201
0
          psCssParam = CPLGetXMLNode(psFill, "SvgParameter");
3202
3203
0
        while (psCssParam && psCssParam->pszValue &&
3204
0
               (strcasecmp(psCssParam->pszValue, "CssParameter") == 0 ||
3205
0
                strcasecmp(psCssParam->pszValue, "SvgParameter") == 0)) {
3206
0
          pszName = (char *)CPLGetXMLValue(psCssParam, "name", NULL);
3207
0
          if (pszName) {
3208
0
            if (strcasecmp(pszName, "fill") == 0) {
3209
0
              if (psCssParam->psChild && psCssParam->psChild->psNext) {
3210
0
                msSLDParseOgcExpression(psCssParam->psChild->psNext, psLabelObj,
3211
0
                                        MS_LABEL_BINDING_COLOR, MS_OBJ_LABEL);
3212
0
              }
3213
0
            }
3214
0
          }
3215
0
          psCssParam = psCssParam->psNext;
3216
0
        }
3217
0
      }
3218
0
    }
3219
0
  }
3220
3221
0
  return MS_SUCCESS;
3222
0
}
3223
3224
/************************************************************************/
3225
/*                         ParseTextPointPlacement                      */
3226
/*                                                                      */
3227
/*      point placement node for the text symbolizer.                  */
3228
/************************************************************************/
3229
0
int ParseTextPointPlacement(CPLXMLNode *psRoot, classObj *psClass) {
3230
0
  CPLXMLNode *psAnchor, *psAnchorX, *psAnchorY;
3231
0
  CPLXMLNode *psDisplacement, *psDisplacementX, *psDisplacementY;
3232
0
  CPLXMLNode *psRotation = NULL;
3233
0
  labelObj *psLabelObj = NULL;
3234
3235
0
  if (!psRoot || !psClass)
3236
0
    return MS_FAILURE;
3237
0
  if (psClass->numlabels == 0) {
3238
0
    if (msGrowClassLabels(psClass) == NULL)
3239
0
      return (MS_FAILURE);
3240
0
    initLabel(psClass->labels[0]);
3241
0
    psClass->numlabels++;
3242
0
  }
3243
0
  psLabelObj = psClass->labels[0];
3244
3245
  /* init the label with the default position */
3246
0
  psLabelObj->position = MS_CL;
3247
3248
  /* -------------------------------------------------------------------- */
3249
  /*      parse anchor point. see function msSLDParseTextSymbolizer       */
3250
  /*      for documentation.                                              */
3251
  /* -------------------------------------------------------------------- */
3252
0
  psAnchor = CPLGetXMLNode(psRoot, "AnchorPoint");
3253
0
  if (psAnchor) {
3254
0
    psAnchorX = CPLGetXMLNode(psAnchor, "AnchorPointX");
3255
0
    psAnchorY = CPLGetXMLNode(psAnchor, "AnchorPointY");
3256
    /* psCssParam->psChild->psNext->pszValue) */
3257
0
    if (psAnchorX && psAnchorX->psChild && psAnchorX->psChild->pszValue &&
3258
0
        psAnchorY && psAnchorY->psChild && psAnchorY->psChild->pszValue) {
3259
0
      const double dfAnchorX = atof(psAnchorX->psChild->pszValue);
3260
0
      const double dfAnchorY = atof(psAnchorY->psChild->pszValue);
3261
3262
0
      if ((dfAnchorX == 0 || dfAnchorX == 0.5 || dfAnchorX == 1) &&
3263
0
          (dfAnchorY == 0 || dfAnchorY == 0.5 || dfAnchorY == 1)) {
3264
0
        if (dfAnchorX == 0 && dfAnchorY == 0)
3265
0
          psLabelObj->position = MS_LL;
3266
0
        if (dfAnchorX == 0 && dfAnchorY == 0.5)
3267
0
          psLabelObj->position = MS_CL;
3268
0
        if (dfAnchorX == 0 && dfAnchorY == 1)
3269
0
          psLabelObj->position = MS_UL;
3270
3271
0
        if (dfAnchorX == 0.5 && dfAnchorY == 0)
3272
0
          psLabelObj->position = MS_LC;
3273
0
        if (dfAnchorX == 0.5 && dfAnchorY == 0.5)
3274
0
          psLabelObj->position = MS_CC;
3275
0
        if (dfAnchorX == 0.5 && dfAnchorY == 1)
3276
0
          psLabelObj->position = MS_UC;
3277
3278
0
        if (dfAnchorX == 1 && dfAnchorY == 0)
3279
0
          psLabelObj->position = MS_LR;
3280
0
        if (dfAnchorX == 1 && dfAnchorY == 0.5)
3281
0
          psLabelObj->position = MS_CR;
3282
0
        if (dfAnchorX == 1 && dfAnchorY == 1)
3283
0
          psLabelObj->position = MS_UR;
3284
0
      }
3285
0
    }
3286
0
  }
3287
3288
  /* -------------------------------------------------------------------- */
3289
  /*      Parse displacement                                              */
3290
  /* -------------------------------------------------------------------- */
3291
0
  psDisplacement = CPLGetXMLNode(psRoot, "Displacement");
3292
0
  if (psDisplacement) {
3293
0
    psDisplacementX = CPLGetXMLNode(psDisplacement, "DisplacementX");
3294
0
    psDisplacementY = CPLGetXMLNode(psDisplacement, "DisplacementY");
3295
    /* psCssParam->psChild->psNext->pszValue) */
3296
0
    if (psDisplacementX && psDisplacementX->psChild &&
3297
0
        psDisplacementX->psChild->pszValue && psDisplacementY &&
3298
0
        psDisplacementY->psChild && psDisplacementY->psChild->pszValue) {
3299
0
      psLabelObj->offsetx = atoi(psDisplacementX->psChild->pszValue);
3300
0
      psLabelObj->offsety = atoi(psDisplacementY->psChild->pszValue);
3301
0
    }
3302
0
  }
3303
3304
  /* -------------------------------------------------------------------- */
3305
  /*      parse rotation.                                                 */
3306
  /* -------------------------------------------------------------------- */
3307
0
  psRotation = CPLGetXMLNode(psRoot, "Rotation");
3308
0
  if (psRotation && psRotation->psChild) {
3309
0
    msSLDParseOgcExpression(psRotation->psChild, psLabelObj,
3310
0
                            MS_LABEL_BINDING_ANGLE, MS_OBJ_LABEL);
3311
0
  }
3312
3313
0
  return MS_SUCCESS;
3314
0
}
3315
3316
/************************************************************************/
3317
/*                          ParseTextLinePlacement                      */
3318
/*                                                                      */
3319
/*      Lineplacement node fro the text symbolizer.                     */
3320
/************************************************************************/
3321
0
int ParseTextLinePlacement(CPLXMLNode *psRoot, classObj *psClass) {
3322
0
  CPLXMLNode *psOffset = NULL, *psAligned = NULL;
3323
0
  labelObj *psLabelObj = NULL;
3324
3325
0
  if (!psRoot || !psClass)
3326
0
    return MS_FAILURE;
3327
3328
0
  if (psClass->numlabels == 0) {
3329
0
    if (msGrowClassLabels(psClass) == NULL)
3330
0
      return (MS_FAILURE);
3331
0
    initLabel(psClass->labels[0]);
3332
0
    psClass->numlabels++;
3333
0
  }
3334
0
  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
0
  psLabelObj->anglemode = MS_FOLLOW;
3340
3341
  /*sld 1.1.0 has a parameter IsAligned. default value is true*/
3342
0
  psAligned = CPLGetXMLNode(psRoot, "IsAligned");
3343
0
  if (psAligned && psAligned->psChild && psAligned->psChild->pszValue &&
3344
0
      strcasecmp(psAligned->psChild->pszValue, "false") == 0) {
3345
0
    psLabelObj->anglemode = MS_NONE;
3346
0
  }
3347
0
  psOffset = CPLGetXMLNode(psRoot, "PerpendicularOffset");
3348
0
  if (psOffset && psOffset->psChild && psOffset->psChild->pszValue) {
3349
0
    psLabelObj->offsetx = atoi(psOffset->psChild->pszValue);
3350
0
    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
0
    if (!psAligned) {
3358
0
      psLabelObj->anglemode = MS_NONE;
3359
0
      psLabelObj->offsety = psLabelObj->offsetx;
3360
0
    }
3361
0
  }
3362
3363
0
  return MS_SUCCESS;
3364
0
}
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
0
int msSLDSetColorObject(char *psHexColor, colorObj *psColor) {
3375
0
  if (psHexColor && psColor && strlen(psHexColor) == 7 &&
3376
0
      psHexColor[0] == '#') {
3377
3378
0
    psColor->red = msHexToInt(psHexColor + 1);
3379
0
    psColor->green = msHexToInt(psHexColor + 3);
3380
0
    psColor->blue = msHexToInt(psHexColor + 5);
3381
0
  }
3382
3383
0
  return MS_SUCCESS;
3384
0
}
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
0
char *msSLDGenerateSLD(mapObj *map, int iLayer, const char *pszVersion) {
3401
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3402
0
    defined(USE_SOS_SVR)
3403
3404
0
  char szTmp[500];
3405
0
  int i = 0;
3406
0
  char *pszTmp = NULL;
3407
0
  char *pszSLD = NULL;
3408
0
  char *schemalocation = NULL;
3409
0
  int sld_version = OWS_VERSION_NOTSET;
3410
3411
0
  sld_version = msOWSParseVersionString(pszVersion);
3412
3413
0
  if (sld_version == OWS_VERSION_NOTSET ||
3414
0
      (sld_version != OWS_1_0_0 && sld_version != OWS_1_1_0))
3415
0
    sld_version = OWS_1_0_0;
3416
3417
0
  if (map) {
3418
0
    schemalocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
3419
0
    if (sld_version == OWS_1_0_0)
3420
0
      snprintf(szTmp, sizeof(szTmp),
3421
0
               "<StyledLayerDescriptor version=\"1.0.0\" "
3422
0
               "xmlns=\"http://www.opengis.net/sld\" "
3423
0
               "xmlns:gml=\"http://www.opengis.net/gml\" "
3424
0
               "xmlns:ogc=\"http://www.opengis.net/ogc\" "
3425
0
               "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3426
0
               "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
3427
0
               "xsi:schemaLocation=\"http://www.opengis.net/sld "
3428
0
               "%s/sld/1.0.0/StyledLayerDescriptor.xsd\">\n",
3429
0
               schemalocation);
3430
0
    else
3431
0
      snprintf(szTmp, sizeof(szTmp),
3432
0
               "<StyledLayerDescriptor version=\"1.1.0\" "
3433
0
               "xsi:schemaLocation=\"http://www.opengis.net/sld "
3434
0
               "%s/sld/1.1.0/StyledLayerDescriptor.xsd\" "
3435
0
               "xmlns=\"http://www.opengis.net/sld\" "
3436
0
               "xmlns:ogc=\"http://www.opengis.net/ogc\" "
3437
0
               "xmlns:se=\"http://www.opengis.net/se\" "
3438
0
               "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3439
0
               "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n",
3440
0
               schemalocation);
3441
3442
0
    free(schemalocation);
3443
3444
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3445
0
    if (iLayer < 0 || iLayer > map->numlayers - 1) {
3446
0
      for (i = 0; i < map->numlayers; i++) {
3447
0
        pszTmp = msSLDGenerateSLDLayer(GET_LAYER(map, i), sld_version);
3448
0
        if (pszTmp) {
3449
0
          pszSLD = msStringConcatenate(pszSLD, pszTmp);
3450
0
          free(pszTmp);
3451
0
        }
3452
0
      }
3453
0
    } else {
3454
0
      pszTmp = msSLDGenerateSLDLayer(GET_LAYER(map, iLayer), sld_version);
3455
0
      if (pszTmp) {
3456
0
        pszSLD = msStringConcatenate(pszSLD, pszTmp);
3457
0
        free(pszTmp);
3458
0
      }
3459
0
    }
3460
0
    snprintf(szTmp, sizeof(szTmp), "%s", "</StyledLayerDescriptor>\n");
3461
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3462
0
  }
3463
3464
0
  return pszSLD;
3465
3466
#else
3467
  msSetError(MS_MISCERR, "OWS support is not available.",
3468
             "msSLDGenerateSLDLayer()");
3469
  return NULL;
3470
3471
#endif
3472
0
}
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,
3480
0
                         int bNeedMarkSybol, int nVersion) {
3481
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3482
0
    defined(USE_SOS_SVR)
3483
3484
0
  msStringBuffer *sldString = msStringBufferAlloc();
3485
0
  int nSymbol = -1;
3486
0
  symbolObj *psSymbol = NULL;
3487
0
  char szTmp[512];
3488
0
  char szFormat[4];
3489
0
  int i = 0, nLength = 0;
3490
0
  int bColorAvailable = 0;
3491
0
  int bGenerateDefaultSymbol = 0;
3492
0
  char *pszSymbolName = NULL;
3493
0
  char sNameSpace[10];
3494
0
  char sCssParam[30];
3495
3496
0
  sCssParam[0] = '\0';
3497
0
  if (nVersion > OWS_1_0_0)
3498
0
    strcpy(sCssParam, "se:SvgParameter");
3499
0
  else
3500
0
    strcpy(sCssParam, "CssParameter");
3501
3502
0
  sNameSpace[0] = '\0';
3503
0
  if (nVersion > OWS_1_0_0)
3504
0
    strcpy(sNameSpace, "se:");
3505
3506
0
  if (psStyle && psLayer && psLayer->map) {
3507
0
    nSymbol = -1;
3508
0
    if (psStyle->symbol > 0)
3509
0
      nSymbol = psStyle->symbol;
3510
0
    else if (psStyle->symbolname)
3511
0
      nSymbol = msGetSymbolIndex(&psLayer->map->symbolset, psStyle->symbolname,
3512
0
                                 MS_FALSE);
3513
3514
0
    bGenerateDefaultSymbol = 0;
3515
3516
0
    if (bNeedMarkSybol &&
3517
0
        (nSymbol <= 0 || nSymbol >= psLayer->map->symbolset.numsymbols))
3518
0
      bGenerateDefaultSymbol = 1;
3519
3520
0
    if (nSymbol > 0 && nSymbol < psLayer->map->symbolset.numsymbols) {
3521
0
      psSymbol = psLayer->map->symbolset.symbol[nSymbol];
3522
0
      if (psSymbol->type == MS_SYMBOL_VECTOR ||
3523
0
          psSymbol->type == MS_SYMBOL_ELLIPSE) {
3524
        /* Mark symbol */
3525
0
        if (psSymbol->name)
3526
3527
0
        {
3528
0
          if (strcasecmp(psSymbol->name, "square") == 0 ||
3529
0
              strcasecmp(psSymbol->name, "circle") == 0 ||
3530
0
              strcasecmp(psSymbol->name, "triangle") == 0 ||
3531
0
              strcasecmp(psSymbol->name, "star") == 0 ||
3532
0
              strcasecmp(psSymbol->name, "cross") == 0 ||
3533
0
              strcasecmp(psSymbol->name, "x") == 0)
3534
0
            pszSymbolName = msStrdup(psSymbol->name);
3535
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_square", 22) ==
3536
0
                   0)
3537
0
            pszSymbolName = msStrdup("square");
3538
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_triangle",
3539
0
                               24) == 0)
3540
0
            pszSymbolName = msStrdup("triangle");
3541
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_circle", 22) ==
3542
0
                   0)
3543
0
            pszSymbolName = msStrdup("circle");
3544
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_star", 20) == 0)
3545
0
            pszSymbolName = msStrdup("star");
3546
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_cross", 21) ==
3547
0
                   0)
3548
0
            pszSymbolName = msStrdup("cross");
3549
0
          else if (strncasecmp(psSymbol->name, "sld_mark_symbol_x", 17) == 0)
3550
0
            pszSymbolName = msStrdup("X");
3551
3552
0
          if (pszSymbolName) {
3553
0
            colorObj sTmpFillColor = {128, 128, 128, 255};
3554
0
            colorObj sTmpStrokeColor = {0, 0, 0, 255};
3555
0
            int hasFillColor = 0;
3556
0
            int hasStrokeColor = 0;
3557
3558
0
            snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3559
0
            msStringBufferAppend(sldString, szTmp);
3560
3561
0
            snprintf(szTmp, sizeof(szTmp), "<%sMark>\n", sNameSpace);
3562
0
            msStringBufferAppend(sldString, szTmp);
3563
3564
0
            snprintf(szTmp, sizeof(szTmp),
3565
0
                     "<%sWellKnownName>%s</%sWellKnownName>\n", sNameSpace,
3566
0
                     pszSymbolName, sNameSpace);
3567
0
            msStringBufferAppend(sldString, szTmp);
3568
3569
0
            if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
3570
0
                psStyle->color.blue != -1) {
3571
0
              sTmpFillColor.red = psStyle->color.red;
3572
0
              sTmpFillColor.green = psStyle->color.green;
3573
0
              sTmpFillColor.blue = psStyle->color.blue;
3574
0
              sTmpFillColor.alpha = psStyle->color.alpha;
3575
0
              hasFillColor = 1;
3576
0
            }
3577
0
            if (psStyle->outlinecolor.red != -1 &&
3578
0
                psStyle->outlinecolor.green != -1 &&
3579
0
                psStyle->outlinecolor.blue != -1) {
3580
0
              sTmpStrokeColor.red = psStyle->outlinecolor.red;
3581
0
              sTmpStrokeColor.green = psStyle->outlinecolor.green;
3582
0
              sTmpStrokeColor.blue = psStyle->outlinecolor.blue;
3583
0
              sTmpStrokeColor.alpha = psStyle->outlinecolor.alpha;
3584
0
              hasStrokeColor = 1;
3585
              // Make defaults implicit
3586
0
              if (sTmpStrokeColor.red == 0 && sTmpStrokeColor.green == 0 &&
3587
0
                  sTmpStrokeColor.blue == 0 && sTmpStrokeColor.alpha == 255 &&
3588
0
                  psStyle->width == 1) {
3589
0
                hasStrokeColor = 0;
3590
0
              }
3591
0
            }
3592
0
            if (!hasFillColor && !hasStrokeColor) {
3593
0
              sTmpFillColor.red = 128;
3594
0
              sTmpFillColor.green = 128;
3595
0
              sTmpFillColor.blue = 128;
3596
0
              sTmpFillColor.alpha = 255;
3597
0
              hasFillColor = 1;
3598
0
            }
3599
3600
0
            if (hasFillColor) {
3601
0
              snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3602
0
              msStringBufferAppend(sldString, szTmp);
3603
0
              snprintf(szTmp, sizeof(szTmp),
3604
0
                       "<%s name=\"fill\">#%02x%02x%02x</%s>\n", sCssParam,
3605
0
                       sTmpFillColor.red, sTmpFillColor.green,
3606
0
                       sTmpFillColor.blue, sCssParam);
3607
0
              msStringBufferAppend(sldString, szTmp);
3608
0
              if (sTmpFillColor.alpha != 255 && sTmpFillColor.alpha != -1) {
3609
0
                snprintf(szTmp, sizeof(szTmp),
3610
0
                         "<%s name=\"fill-opacity\">%.2f</%s>\n", sCssParam,
3611
0
                         (float)sTmpFillColor.alpha / 255.0, sCssParam);
3612
0
                msStringBufferAppend(sldString, szTmp);
3613
0
              }
3614
0
              snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3615
0
              msStringBufferAppend(sldString, szTmp);
3616
0
            }
3617
0
            if (hasStrokeColor) {
3618
0
              snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3619
0
              msStringBufferAppend(sldString, szTmp);
3620
0
              snprintf(szTmp, sizeof(szTmp),
3621
0
                       "<%s name=\"stroke\">#%02x%02x%02x</%s>\n", sCssParam,
3622
0
                       sTmpStrokeColor.red, sTmpStrokeColor.green,
3623
0
                       sTmpStrokeColor.blue, sCssParam);
3624
0
              msStringBufferAppend(sldString, szTmp);
3625
0
              if (psStyle->width > 0) {
3626
0
                snprintf(szTmp, sizeof(szTmp),
3627
0
                         "<%s name=\"stroke-width\">%g</%s>\n", sCssParam,
3628
0
                         psStyle->width, sCssParam);
3629
0
                msStringBufferAppend(sldString, szTmp);
3630
0
              }
3631
0
              if (sTmpStrokeColor.alpha != 255 && sTmpStrokeColor.alpha != -1) {
3632
0
                snprintf(szTmp, sizeof(szTmp),
3633
0
                         "<%s name=\"stroke-opacity\">%.2f</%s>\n", sCssParam,
3634
0
                         (float)sTmpStrokeColor.alpha / 255.0, sCssParam);
3635
0
                msStringBufferAppend(sldString, szTmp);
3636
0
              }
3637
0
              snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3638
0
              msStringBufferAppend(sldString, szTmp);
3639
0
            }
3640
3641
0
            snprintf(szTmp, sizeof(szTmp), "</%sMark>\n", sNameSpace);
3642
0
            msStringBufferAppend(sldString, szTmp);
3643
3644
0
            if (psStyle->size > 0) {
3645
0
              snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n",
3646
0
                       sNameSpace, psStyle->size, sNameSpace);
3647
0
              msStringBufferAppend(sldString, szTmp);
3648
0
            }
3649
3650
0
            if (fmod(psStyle->angle, 360)) {
3651
0
              snprintf(szTmp, sizeof(szTmp), "<%sRotation>%g</%sRotation>\n",
3652
0
                       sNameSpace, psStyle->angle, sNameSpace);
3653
0
              msStringBufferAppend(sldString, szTmp);
3654
0
            }
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
0
            if (psStyle->offsetx != 0 || psStyle->offsety != 0) {
3664
0
              snprintf(szTmp, sizeof(szTmp), "<%sDisplacement>\n", sNameSpace);
3665
0
              msStringBufferAppend(sldString, szTmp);
3666
0
              snprintf(szTmp, sizeof(szTmp),
3667
0
                       "<%sDisplacementX>%g</%sDisplacementX>\n", sNameSpace,
3668
0
                       psStyle->offsetx, sNameSpace);
3669
0
              msStringBufferAppend(sldString, szTmp);
3670
0
              snprintf(szTmp, sizeof(szTmp),
3671
0
                       "<%sDisplacementY>%g</%sDisplacementY>\n", sNameSpace,
3672
0
                       psStyle->offsety, sNameSpace);
3673
0
              msStringBufferAppend(sldString, szTmp);
3674
0
              snprintf(szTmp, sizeof(szTmp), "</%sDisplacement>\n", sNameSpace);
3675
0
              msStringBufferAppend(sldString, szTmp);
3676
0
            }
3677
3678
0
            snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3679
0
            msStringBufferAppend(sldString, szTmp);
3680
3681
0
            if (pszSymbolName)
3682
0
              free(pszSymbolName);
3683
0
          }
3684
0
        } else
3685
0
          bGenerateDefaultSymbol = 1;
3686
0
      } else if (psSymbol->type == MS_SYMBOL_PIXMAP ||
3687
0
                 psSymbol->type == MS_SYMBOL_SVG) {
3688
0
        if (psSymbol->name) {
3689
0
          const char *pszURL =
3690
0
              msLookupHashTable(&(psLayer->metadata), "WMS_SLD_SYMBOL_URL");
3691
0
          if (!pszURL)
3692
0
            pszURL = msLookupHashTable(&(psLayer->map->web.metadata),
3693
0
                                       "WMS_SLD_SYMBOL_URL");
3694
3695
0
          if (pszURL) {
3696
0
            snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3697
0
            msStringBufferAppend(sldString, szTmp);
3698
3699
0
            snprintf(szTmp, sizeof(szTmp), "<%sExternalGraphic>\n", sNameSpace);
3700
0
            msStringBufferAppend(sldString, szTmp);
3701
3702
0
            snprintf(szTmp, sizeof(szTmp),
3703
0
                     "<%sOnlineResource "
3704
0
                     "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
3705
0
                     "xlink:type=\"simple\" xlink:href=\"%s%s\"/>\n",
3706
0
                     sNameSpace, pszURL, psSymbol->imagepath);
3707
0
            msStringBufferAppend(sldString, szTmp);
3708
            /* TODO : extract format from symbol */
3709
3710
0
            szFormat[0] = '\0';
3711
0
            nLength = strlen(psSymbol->imagepath);
3712
0
            if (nLength > 3) {
3713
0
              for (i = 0; i <= 2; i++)
3714
0
                szFormat[2 - i] = psSymbol->imagepath[nLength - 1 - i];
3715
0
              szFormat[3] = '\0';
3716
0
            }
3717
0
            if (strlen(szFormat) > 0 && ((strcasecmp(szFormat, "GIF") == 0) ||
3718
0
                                         (strcasecmp(szFormat, "PNG") == 0))) {
3719
0
              if (strcasecmp(szFormat, "GIF") == 0)
3720
0
                snprintf(szTmp, sizeof(szTmp),
3721
0
                         "<%sFormat>image/gif</%sFormat>\n", sNameSpace,
3722
0
                         sNameSpace);
3723
0
              else
3724
0
                snprintf(szTmp, sizeof(szTmp),
3725
0
                         "<%sFormat>image/png</%sFormat>\n", sNameSpace,
3726
0
                         sNameSpace);
3727
0
            } else
3728
0
              snprintf(szTmp, sizeof(szTmp), "<%sFormat>%s</%sFormat>\n",
3729
0
                       sNameSpace,
3730
0
                       (psSymbol->type == MS_SYMBOL_SVG) ? "image/svg+xml"
3731
0
                                                         : "image/gif",
3732
0
                       sNameSpace);
3733
3734
0
            msStringBufferAppend(sldString, szTmp);
3735
3736
0
            snprintf(szTmp, sizeof(szTmp), "</%sExternalGraphic>\n",
3737
0
                     sNameSpace);
3738
0
            msStringBufferAppend(sldString, szTmp);
3739
3740
0
            if (psStyle->size > 0)
3741
0
              snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n",
3742
0
                       sNameSpace, psStyle->size, sNameSpace);
3743
0
            msStringBufferAppend(sldString, szTmp);
3744
3745
0
            snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3746
0
            msStringBufferAppend(sldString, szTmp);
3747
0
          }
3748
0
        }
3749
0
      }
3750
0
    }
3751
0
    if (bGenerateDefaultSymbol) { /* generate a default square symbol */
3752
0
      snprintf(szTmp, sizeof(szTmp), "<%sGraphic>\n", sNameSpace);
3753
0
      msStringBufferAppend(sldString, szTmp);
3754
3755
0
      snprintf(szTmp, sizeof(szTmp), "<%sMark>\n", sNameSpace);
3756
0
      msStringBufferAppend(sldString, szTmp);
3757
3758
0
      snprintf(szTmp, sizeof(szTmp), "<%sWellKnownName>%s</%sWellKnownName>\n",
3759
0
               sNameSpace, "square", sNameSpace);
3760
0
      msStringBufferAppend(sldString, szTmp);
3761
3762
0
      bColorAvailable = 0;
3763
0
      if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
3764
0
          psStyle->color.blue != -1) {
3765
0
        snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3766
0
        msStringBufferAppend(sldString, szTmp);
3767
0
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%02x%02x%02x</%s>\n",
3768
0
                 sCssParam, psStyle->color.red, psStyle->color.green,
3769
0
                 psStyle->color.blue, sCssParam);
3770
0
        msStringBufferAppend(sldString, szTmp);
3771
0
        snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3772
0
        msStringBufferAppend(sldString, szTmp);
3773
0
        bColorAvailable = 1;
3774
0
      }
3775
0
      if (psStyle->outlinecolor.red != -1 &&
3776
0
          psStyle->outlinecolor.green != -1 &&
3777
0
          psStyle->outlinecolor.blue != -1) {
3778
0
        snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3779
0
        msStringBufferAppend(sldString, szTmp);
3780
0
        snprintf(szTmp, sizeof(szTmp),
3781
0
                 "<%s name=\"Stroke\">#%02x%02x%02x</%s>\n", sCssParam,
3782
0
                 psStyle->outlinecolor.red, psStyle->outlinecolor.green,
3783
0
                 psStyle->outlinecolor.blue, sCssParam);
3784
0
        msStringBufferAppend(sldString, szTmp);
3785
0
        snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3786
0
        msStringBufferAppend(sldString, szTmp);
3787
0
        bColorAvailable = 1;
3788
0
      }
3789
0
      if (!bColorAvailable) {
3790
        /* default color */
3791
0
        snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
3792
0
        msStringBufferAppend(sldString, szTmp);
3793
0
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">%s</%s>\n", sCssParam,
3794
0
                 "#808080", sCssParam);
3795
0
        msStringBufferAppend(sldString, szTmp);
3796
0
        snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
3797
0
        msStringBufferAppend(sldString, szTmp);
3798
0
      }
3799
3800
0
      snprintf(szTmp, sizeof(szTmp), "</%sMark>\n", sNameSpace);
3801
0
      msStringBufferAppend(sldString, szTmp);
3802
3803
0
      if (psStyle->size > 0)
3804
0
        snprintf(szTmp, sizeof(szTmp), "<%sSize>%g</%sSize>\n", sNameSpace,
3805
0
                 psStyle->size, sNameSpace);
3806
0
      else
3807
0
        snprintf(szTmp, sizeof(szTmp), "<%sSize>%d</%sSize>\n", sNameSpace, 1,
3808
0
                 sNameSpace);
3809
0
      msStringBufferAppend(sldString, szTmp);
3810
3811
0
      snprintf(szTmp, sizeof(szTmp), "</%sGraphic>\n", sNameSpace);
3812
0
      msStringBufferAppend(sldString, szTmp);
3813
0
    }
3814
0
  }
3815
3816
0
  return msStringBufferReleaseStringAndFree(sldString);
3817
3818
#else
3819
  return NULL;
3820
3821
#endif
3822
0
}
3823
3824
/************************************************************************/
3825
/*                           msSLDGenerateLineSLD                       */
3826
/*                                                                      */
3827
/*      Generate SLD for a Line layer.                                  */
3828
/************************************************************************/
3829
0
char *msSLDGenerateLineSLD(styleObj *psStyle, layerObj *psLayer, int nVersion) {
3830
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3831
0
    defined(USE_SOS_SVR)
3832
3833
0
  char *pszSLD = NULL;
3834
0
  char szTmp[100];
3835
0
  char szHexColor[7];
3836
0
  int nSymbol = -1;
3837
0
  int i = 0;
3838
0
  double dfSize = 1.0;
3839
0
  char *pszDashArray = NULL;
3840
0
  char *pszGraphicSLD = NULL;
3841
0
  char sCssParam[30];
3842
0
  char sNameSpace[10];
3843
3844
0
  if (msCheckParentPointer(psLayer->map, "map") == MS_FAILURE)
3845
0
    return NULL;
3846
3847
0
  sCssParam[0] = '\0';
3848
0
  if (nVersion > OWS_1_0_0)
3849
0
    strcpy(sCssParam, "se:SvgParameter");
3850
0
  else
3851
0
    strcpy(sCssParam, "CssParameter");
3852
3853
0
  sNameSpace[0] = '\0';
3854
0
  if (nVersion > OWS_1_0_0)
3855
0
    strcpy(sNameSpace, "se:");
3856
3857
0
  snprintf(szTmp, sizeof(szTmp), "<%sLineSymbolizer>\n", sNameSpace);
3858
3859
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3860
3861
0
  snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
3862
3863
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3864
3865
0
  pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
3866
0
  if (pszGraphicSLD) {
3867
0
    snprintf(szTmp, sizeof(szTmp), "<%sGraphicStroke>\n", sNameSpace);
3868
3869
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3870
3871
0
    pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
3872
3873
0
    if (nVersion >= OWS_1_1_0) {
3874
0
      if (psStyle->gap > 0) {
3875
0
        snprintf(szTmp, sizeof(szTmp), "<%sGap>%.2f</%sGap>\n", sNameSpace,
3876
0
                 psStyle->gap, sNameSpace);
3877
0
      }
3878
0
      if (psStyle->initialgap > 0) {
3879
0
        snprintf(szTmp, sizeof(szTmp), "<%sInitialGap>%.2f</%sInitialGap>\n",
3880
0
                 sNameSpace, psStyle->initialgap, sNameSpace);
3881
0
      }
3882
0
    }
3883
3884
0
    snprintf(szTmp, sizeof(szTmp), "</%sGraphicStroke>\n", sNameSpace);
3885
3886
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3887
3888
0
    free(pszGraphicSLD);
3889
0
    pszGraphicSLD = NULL;
3890
0
  }
3891
3892
0
  if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
3893
0
      psStyle->color.blue != -1)
3894
0
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x", psStyle->color.red,
3895
0
             psStyle->color.green, psStyle->color.blue);
3896
0
  else
3897
0
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x",
3898
0
             psStyle->outlinecolor.red, psStyle->outlinecolor.green,
3899
0
             psStyle->outlinecolor.blue);
3900
3901
0
  snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke\">#%s</%s>\n", sCssParam,
3902
0
           szHexColor, sCssParam);
3903
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3904
3905
0
  if (psStyle->color.alpha != 255 && psStyle->color.alpha != -1) {
3906
0
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-opacity\">%.2f</%s>\n",
3907
0
             sCssParam, (float)psStyle->color.alpha / 255.0, sCssParam);
3908
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3909
0
  }
3910
3911
0
  nSymbol = -1;
3912
3913
0
  if (psStyle->symbol >= 0)
3914
0
    nSymbol = psStyle->symbol;
3915
0
  else if (psStyle->symbolname)
3916
0
    nSymbol = msGetSymbolIndex(&psLayer->map->symbolset, psStyle->symbolname,
3917
0
                               MS_FALSE);
3918
3919
0
  if (nSymbol < 0)
3920
0
    dfSize = 1.0;
3921
0
  else {
3922
0
    if (psStyle->size > 0)
3923
0
      dfSize = psStyle->size;
3924
0
    else if (psStyle->width > 0)
3925
0
      dfSize = psStyle->width;
3926
0
    else
3927
0
      dfSize = 1;
3928
0
  }
3929
3930
0
  snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-width\">%.2f</%s>\n",
3931
0
           sCssParam, dfSize, sCssParam);
3932
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3933
3934
  /* -------------------------------------------------------------------- */
3935
  /*      dash array                                                      */
3936
  /* -------------------------------------------------------------------- */
3937
3938
0
  if (psStyle->patternlength > 0) {
3939
0
    for (i = 0; i < psStyle->patternlength; i++) {
3940
0
      snprintf(szTmp, sizeof(szTmp), "%.2f ", psStyle->pattern[i]);
3941
0
      pszDashArray = msStringConcatenate(pszDashArray, szTmp);
3942
0
    }
3943
    // remove the final trailing space from the last pattern value
3944
0
    msStringTrimBlanks(pszDashArray);
3945
3946
0
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-dasharray\">%s</%s>\n",
3947
0
             sCssParam, pszDashArray, sCssParam);
3948
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
3949
0
    msFree(pszDashArray);
3950
0
  }
3951
3952
0
  snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
3953
3954
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3955
3956
0
  snprintf(szTmp, sizeof(szTmp), "</%sLineSymbolizer>\n", sNameSpace);
3957
3958
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3959
3960
0
  return pszSLD;
3961
3962
#else
3963
  return NULL;
3964
#endif
3965
0
}
3966
3967
/************************************************************************/
3968
/*                         msSLDGeneratePolygonSLD                      */
3969
/*                                                                      */
3970
/*       Generate SLD for a Polygon layer.                              */
3971
/************************************************************************/
3972
char *msSLDGeneratePolygonSLD(styleObj *psStyle, layerObj *psLayer,
3973
0
                              int nVersion) {
3974
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
3975
0
    defined(USE_SOS_SVR)
3976
3977
0
  char szTmp[100];
3978
0
  char *pszSLD = NULL;
3979
0
  char szHexColor[7];
3980
0
  double dfSize;
3981
0
  char sCssParam[30];
3982
0
  char sNameSpace[10];
3983
3984
0
  sCssParam[0] = '\0';
3985
0
  if (nVersion > OWS_1_0_0)
3986
0
    strcpy(sCssParam, "se:SvgParameter");
3987
0
  else
3988
0
    strcpy(sCssParam, "CssParameter");
3989
3990
0
  sNameSpace[0] = '\0';
3991
0
  if (nVersion > OWS_1_0_0)
3992
0
    strcpy(sNameSpace, "se:");
3993
3994
0
  snprintf(szTmp, sizeof(szTmp), "<%sPolygonSymbolizer>\n", sNameSpace);
3995
3996
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
3997
  /* fill */
3998
0
  if (psStyle->color.red != -1 && psStyle->color.green != -1 &&
3999
0
      psStyle->color.blue != -1) {
4000
4001
0
    snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4002
4003
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4004
4005
0
    char *pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
4006
0
    if (pszGraphicSLD) {
4007
0
      snprintf(szTmp, sizeof(szTmp), "<%sGraphicFill>\n", sNameSpace);
4008
4009
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4010
4011
0
      pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
4012
4013
0
      snprintf(szTmp, sizeof(szTmp), "</%sGraphicFill>\n", sNameSpace);
4014
4015
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4016
4017
0
      free(pszGraphicSLD);
4018
0
    }
4019
4020
0
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x", psStyle->color.red,
4021
0
             psStyle->color.green, psStyle->color.blue);
4022
4023
0
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%s</%s>\n", sCssParam,
4024
0
             szHexColor, sCssParam);
4025
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4026
4027
0
    if (psStyle->color.alpha != 255 && psStyle->color.alpha != -1) {
4028
0
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill-opacity\">%.2f</%s>\n",
4029
0
               sCssParam, (float)psStyle->color.alpha / 255, sCssParam);
4030
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4031
0
    }
4032
4033
0
    snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4034
4035
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4036
0
  }
4037
  /* stroke */
4038
0
  if (psStyle->outlinecolor.red != -1 && psStyle->outlinecolor.green != -1 &&
4039
0
      psStyle->outlinecolor.blue != -1) {
4040
0
    snprintf(szTmp, sizeof(szTmp), "<%sStroke>\n", sNameSpace);
4041
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
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
0
    if (psStyle->color.red == -1 && psStyle->color.green == -1 &&
4046
0
        psStyle->color.blue == -1) {
4047
0
      char *pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 0, nVersion);
4048
0
      if (pszGraphicSLD) {
4049
0
        snprintf(szTmp, sizeof(szTmp), "<%sGraphicFill>\n", sNameSpace);
4050
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4051
4052
0
        pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
4053
0
        snprintf(szTmp, sizeof(szTmp), "</%sGraphicFill>\n", sNameSpace);
4054
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4055
4056
0
        free(pszGraphicSLD);
4057
0
      }
4058
0
    }
4059
4060
0
    snprintf(szHexColor, sizeof(szHexColor), "%02x%02x%02x",
4061
0
             psStyle->outlinecolor.red, psStyle->outlinecolor.green,
4062
0
             psStyle->outlinecolor.blue);
4063
4064
0
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke\">#%s</%s>\n", sCssParam,
4065
0
             szHexColor, sCssParam);
4066
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4067
4068
0
    dfSize = 1.0;
4069
0
    if (psStyle->size > 0)
4070
0
      dfSize = psStyle->size;
4071
0
    else if (psStyle->width > 0)
4072
0
      dfSize = psStyle->width;
4073
4074
0
    snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-width\">%.2f</%s>\n",
4075
0
             sCssParam, dfSize, sCssParam);
4076
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4077
4078
0
    if (psStyle->outlinecolor.alpha != 255 &&
4079
0
        psStyle->outlinecolor.alpha != -1) {
4080
0
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"stroke-opacity\">%.2f</%s>\n",
4081
0
               sCssParam, psStyle->outlinecolor.alpha / 255.0, sCssParam);
4082
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4083
0
    }
4084
4085
0
    snprintf(szTmp, sizeof(szTmp), "</%sStroke>\n", sNameSpace);
4086
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4087
0
  }
4088
4089
0
  snprintf(szTmp, sizeof(szTmp), "</%sPolygonSymbolizer>\n", sNameSpace);
4090
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
4091
4092
0
  return pszSLD;
4093
4094
#else
4095
  return NULL;
4096
#endif
4097
0
}
4098
4099
/************************************************************************/
4100
/*                          msSLDGeneratePointSLD                       */
4101
/*                                                                      */
4102
/*      Generate SLD for a Point layer.                                 */
4103
/************************************************************************/
4104
char *msSLDGeneratePointSLD(styleObj *psStyle, layerObj *psLayer,
4105
0
                            int nVersion) {
4106
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4107
0
    defined(USE_SOS_SVR)
4108
0
  char *pszSLD = NULL;
4109
0
  char *pszGraphicSLD = NULL;
4110
0
  char szTmp[100];
4111
0
  char sNameSpace[10];
4112
4113
0
  sNameSpace[0] = '\0';
4114
0
  if (nVersion > OWS_1_0_0)
4115
0
    strcpy(sNameSpace, "se:");
4116
4117
0
  snprintf(szTmp, sizeof(szTmp), "<%sPointSymbolizer>\n", sNameSpace);
4118
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
4119
4120
0
  pszGraphicSLD = msSLDGetGraphicSLD(psStyle, psLayer, 1, nVersion);
4121
0
  if (pszGraphicSLD) {
4122
0
    pszSLD = msStringConcatenate(pszSLD, pszGraphicSLD);
4123
0
    free(pszGraphicSLD);
4124
0
  }
4125
4126
0
  snprintf(szTmp, sizeof(szTmp), "</%sPointSymbolizer>\n", sNameSpace);
4127
0
  pszSLD = msStringConcatenate(pszSLD, szTmp);
4128
4129
0
  return pszSLD;
4130
4131
#else
4132
  return NULL;
4133
4134
#endif
4135
0
}
4136
4137
/************************************************************************/
4138
/*                           msSLDGenerateTextSLD                       */
4139
/*                                                                      */
4140
/*      Generate a TextSymboliser SLD xml based on the class's label    */
4141
/*      object.                                                         */
4142
/************************************************************************/
4143
0
char *msSLDGenerateTextSLD(classObj *psClass, layerObj *psLayer, int nVersion) {
4144
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4145
0
    defined(USE_SOS_SVR)
4146
0
  char *pszSLD = NULL;
4147
4148
0
  char szTmp[1000];
4149
0
  char **aszFontsParts = NULL;
4150
0
  int nFontParts = 0;
4151
0
  char szHexColor[7];
4152
0
  double dfAnchorX = 0.5, dfAnchorY = 0.5;
4153
0
  int i = 0;
4154
0
  int lid;
4155
0
  char sCssParam[30];
4156
0
  char sNameSpace[10];
4157
0
  labelObj *psLabelObj = NULL;
4158
4159
0
  sCssParam[0] = '\0';
4160
0
  if (nVersion > OWS_1_0_0)
4161
0
    strcpy(sCssParam, "se:SvgParameter");
4162
0
  else
4163
0
    strcpy(sCssParam, "CssParameter");
4164
4165
0
  sNameSpace[0] = '\0';
4166
0
  if (nVersion > OWS_1_0_0)
4167
0
    strcpy(sNameSpace, "se:");
4168
4169
0
  if (!psLayer || !psClass)
4170
0
    return pszSLD;
4171
4172
0
  for (lid = 0; lid < psClass->numlabels; lid++) {
4173
0
    char *psLabelText;
4174
0
    expressionObj psLabelExpr;
4175
0
    parseObj p;
4176
4177
0
    msInitExpression(&psLabelExpr);
4178
0
    psLabelObj = psClass->labels[lid];
4179
4180
0
    if (psLabelObj->text.string) {
4181
0
      psLabelExpr.string = msStrdup(psLabelObj->text.string);
4182
0
      psLabelExpr.type = psLabelObj->text.type;
4183
0
    } else if (psClass->text.string) {
4184
0
      psLabelExpr.string = msStrdup(psClass->text.string);
4185
0
      psLabelExpr.type = psClass->text.type;
4186
0
    } else if (psLayer->labelitem) {
4187
0
      psLabelExpr.string = msStrdup(psLayer->labelitem);
4188
0
      psLabelExpr.type = MS_STRING;
4189
0
    } else {
4190
0
      msFreeExpression(&psLabelExpr);
4191
0
      continue; // Can't find text content for this <Label>
4192
0
    }
4193
4194
0
    if (psLabelExpr.type == MS_STRING) {
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
0
      char *result;
4200
0
      result = msStrdup("\"");
4201
0
      result = msStringConcatenate(result, psLabelExpr.string);
4202
0
      result = msStringConcatenate(result, "\"");
4203
0
      msTokenizeExpression(&psLabelExpr, NULL, NULL);
4204
0
      for (tokenListNodeObjPtr t = psLabelExpr.tokens; t; t = t->next) {
4205
0
        if (t->token == MS_TOKEN_BINDING_DOUBLE ||
4206
0
            t->token == MS_TOKEN_BINDING_INTEGER ||
4207
0
            t->token == MS_TOKEN_BINDING_STRING) {
4208
0
          const size_t nSizeTarget = strlen(t->tokenval.bindval.item) + 3;
4209
0
          char *target = static_cast<char *>(msSmallMalloc(nSizeTarget));
4210
0
          const size_t nSizeReplacement = strlen(t->tokenval.bindval.item) + 9;
4211
0
          char *replacement =
4212
0
              static_cast<char *>(msSmallMalloc(nSizeReplacement));
4213
0
          snprintf(target, nSizeTarget, "[%s]", t->tokenval.bindval.item);
4214
0
          snprintf(replacement, nSizeReplacement, "\"+\"[%s]\"+\"",
4215
0
                   t->tokenval.bindval.item);
4216
0
          result = msReplaceSubstring(result, target, replacement);
4217
0
          msFree(target);
4218
0
          msFree(replacement);
4219
0
        }
4220
0
      }
4221
0
      msFreeExpression(&psLabelExpr);
4222
0
      psLabelExpr.string = msStrdup(result);
4223
0
      psLabelExpr.type = MS_EXPRESSION;
4224
0
      msFree(result);
4225
0
    }
4226
4227
    // Parse label expression to generate SLD tags from MapFile syntax
4228
0
    msTokenizeExpression(&psLabelExpr, NULL, NULL);
4229
0
    p.expr = &psLabelExpr;
4230
0
    p.shape = NULL;
4231
0
    p.type = MS_PARSE_TYPE_SLD;
4232
0
    p.result.strval = NULL;
4233
0
    yyparse(&p);
4234
    // Not totally sure what we should do if p.result.strval. For now
4235
    // we export the raw MapServer expression.
4236
0
    psLabelText =
4237
0
        msStrdup(p.result.strval ? p.result.strval : psLabelExpr.string);
4238
0
    msFree(p.result.strval);
4239
0
    msFreeExpression(&psLabelExpr);
4240
4241
0
    snprintf(szTmp, sizeof(szTmp), "<%sTextSymbolizer>\n", sNameSpace);
4242
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4243
4244
0
    snprintf(szTmp, sizeof(szTmp), "<%sLabel>\n%s</%sLabel>\n", sNameSpace,
4245
0
             psLabelText, sNameSpace);
4246
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
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
0
    if (psLabelObj->font) {
4256
0
      aszFontsParts = msStringSplit(psLabelObj->font, '-', &nFontParts);
4257
0
      if (nFontParts > 0) {
4258
0
        snprintf(szTmp, sizeof(szTmp), "<%sFont>\n", sNameSpace);
4259
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4260
4261
        /* assuming first one is font-family */
4262
0
        snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-family\">%s</%s>\n",
4263
0
                 sCssParam, aszFontsParts[0], sCssParam);
4264
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4265
0
        for (i = 1; i < nFontParts; i++) {
4266
0
          if (strcasecmp(aszFontsParts[i], "italic") == 0 ||
4267
0
              strcasecmp(aszFontsParts[i], "oblique") == 0) {
4268
0
            snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-style\">%s</%s>\n",
4269
0
                     sCssParam, aszFontsParts[i], sCssParam);
4270
0
            pszSLD = msStringConcatenate(pszSLD, szTmp);
4271
0
          } else if (strcasecmp(aszFontsParts[i], "bold") == 0) {
4272
0
            snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-weight\">%s</%s>\n",
4273
0
                     sCssParam, aszFontsParts[i], sCssParam);
4274
0
            pszSLD = msStringConcatenate(pszSLD, szTmp);
4275
0
          }
4276
0
        }
4277
        /* size */
4278
0
        if (psLabelObj->size > 0) {
4279
0
          snprintf(szTmp, sizeof(szTmp), "<%s name=\"font-size\">%d</%s>\n",
4280
0
                   sCssParam, psLabelObj->size, sCssParam);
4281
0
          pszSLD = msStringConcatenate(pszSLD, szTmp);
4282
0
        }
4283
0
        snprintf(szTmp, sizeof(szTmp), "</%sFont>\n", sNameSpace);
4284
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4285
0
      }
4286
0
      msFreeCharArray(aszFontsParts, nFontParts);
4287
0
    }
4288
4289
    /* label placement */
4290
0
    snprintf(szTmp, sizeof(szTmp), "<%sLabelPlacement>\n<%sPointPlacement>\n",
4291
0
             sNameSpace, sNameSpace);
4292
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4293
4294
0
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPoint>\n", sNameSpace);
4295
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4296
4297
0
    if (psLabelObj->position == MS_LL) {
4298
0
      dfAnchorX = 0;
4299
0
      dfAnchorY = 0;
4300
0
    } else if (psLabelObj->position == MS_CL) {
4301
0
      dfAnchorX = 0;
4302
0
      dfAnchorY = 0.5;
4303
0
    } else if (psLabelObj->position == MS_UL) {
4304
0
      dfAnchorX = 0;
4305
0
      dfAnchorY = 1;
4306
0
    }
4307
4308
0
    else if (psLabelObj->position == MS_LC) {
4309
0
      dfAnchorX = 0.5;
4310
0
      dfAnchorY = 0;
4311
0
    } else if (psLabelObj->position == MS_CC) {
4312
0
      dfAnchorX = 0.5;
4313
0
      dfAnchorY = 0.5;
4314
0
    } else if (psLabelObj->position == MS_UC) {
4315
0
      dfAnchorX = 0.5;
4316
0
      dfAnchorY = 1;
4317
0
    }
4318
4319
0
    else if (psLabelObj->position == MS_LR) {
4320
0
      dfAnchorX = 1;
4321
0
      dfAnchorY = 0;
4322
0
    } else if (psLabelObj->position == MS_CR) {
4323
0
      dfAnchorX = 1;
4324
0
      dfAnchorY = 0.5;
4325
0
    } else if (psLabelObj->position == MS_UR) {
4326
0
      dfAnchorX = 1;
4327
0
      dfAnchorY = 1;
4328
0
    }
4329
0
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPointX>%.1f</%sAnchorPointX>\n",
4330
0
             sNameSpace, dfAnchorX, sNameSpace);
4331
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4332
0
    snprintf(szTmp, sizeof(szTmp), "<%sAnchorPointY>%.1f</%sAnchorPointY>\n",
4333
0
             sNameSpace, dfAnchorY, sNameSpace);
4334
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4335
4336
0
    snprintf(szTmp, sizeof(szTmp), "</%sAnchorPoint>\n", sNameSpace);
4337
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4338
4339
    /* displacement */
4340
0
    if (psLabelObj->offsetx > 0 || psLabelObj->offsety > 0) {
4341
0
      snprintf(szTmp, sizeof(szTmp), "<%sDisplacement>\n", sNameSpace);
4342
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4343
4344
0
      if (psLabelObj->offsetx > 0) {
4345
0
        snprintf(szTmp, sizeof(szTmp),
4346
0
                 "<%sDisplacementX>%d</%sDisplacementX>\n", sNameSpace,
4347
0
                 psLabelObj->offsetx, sNameSpace);
4348
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4349
0
      }
4350
0
      if (psLabelObj->offsety > 0) {
4351
0
        snprintf(szTmp, sizeof(szTmp),
4352
0
                 "<%sDisplacementY>%d</%sDisplacementY>\n", sNameSpace,
4353
0
                 psLabelObj->offsety, sNameSpace);
4354
0
        pszSLD = msStringConcatenate(pszSLD, szTmp);
4355
0
      }
4356
4357
0
      snprintf(szTmp, sizeof(szTmp), "</%sDisplacement>\n", sNameSpace);
4358
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4359
0
    }
4360
4361
    /* rotation */
4362
0
    if (psLabelObj->angle > 0) {
4363
0
      snprintf(szTmp, sizeof(szTmp), "<%sRotation>%.2f</%sRotation>\n",
4364
0
               sNameSpace, psLabelObj->angle, sNameSpace);
4365
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4366
0
    }
4367
4368
0
    snprintf(szTmp, sizeof(szTmp), "</%sPointPlacement>\n</%sLabelPlacement>\n",
4369
0
             sNameSpace, sNameSpace);
4370
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4371
4372
0
    if (psLabelObj->outlinecolor.red != -1 &&
4373
0
        psLabelObj->outlinecolor.green != -1 &&
4374
0
        psLabelObj->outlinecolor.blue != -1) {
4375
0
      snprintf(szTmp, sizeof(szTmp), "<%sHalo>\n", sNameSpace);
4376
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4377
0
      snprintf(szTmp, sizeof(szTmp), "<%sRadius>%d</%sRadius>\n", sNameSpace,
4378
0
               psLabelObj->outlinewidth, sNameSpace);
4379
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4380
0
      snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4381
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4382
0
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%02x%02x%02x</%s>\n",
4383
0
               sCssParam, psLabelObj->outlinecolor.red,
4384
0
               psLabelObj->outlinecolor.green, psLabelObj->outlinecolor.blue,
4385
0
               sCssParam);
4386
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4387
0
      snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4388
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4389
0
      snprintf(szTmp, sizeof(szTmp), "</%sHalo>\n", sNameSpace);
4390
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4391
0
    }
4392
4393
    /* color */
4394
0
    if (psLabelObj->color.red != -1 && psLabelObj->color.green != -1 &&
4395
0
        psLabelObj->color.blue != -1) {
4396
0
      snprintf(szTmp, sizeof(szTmp), "<%sFill>\n", sNameSpace);
4397
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4398
4399
0
      snprintf(szHexColor, sizeof(szHexColor), "%02hhx%02hhx%02hhx",
4400
0
               (unsigned char)psLabelObj->color.red,
4401
0
               (unsigned char)psLabelObj->color.green,
4402
0
               (unsigned char)psLabelObj->color.blue);
4403
4404
0
      snprintf(szTmp, sizeof(szTmp), "<%s name=\"fill\">#%s</%s>\n", sCssParam,
4405
0
               szHexColor, sCssParam);
4406
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4407
4408
0
      snprintf(szTmp, sizeof(szTmp), "</%sFill>\n", sNameSpace);
4409
0
      pszSLD = msStringConcatenate(pszSLD, szTmp);
4410
0
    }
4411
4412
0
    snprintf(szTmp, sizeof(szTmp), "</%sTextSymbolizer>\n", sNameSpace);
4413
0
    pszSLD = msStringConcatenate(pszSLD, szTmp);
4414
4415
0
    msFree(psLabelText);
4416
0
  }
4417
0
  return pszSLD;
4418
4419
#else
4420
  return NULL;
4421
#endif
4422
0
}
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,
4428
0
                            int nVersion) {
4429
0
  char *pszEncoded = msEncodeHTMLEntities(pszName);
4430
0
  msStringBufferAppend(sb, (nVersion > OWS_1_0_0) ? "<se:Name>" : "<Name>");
4431
0
  msStringBufferAppend(sb, pszEncoded);
4432
0
  msStringBufferAppend(sb,
4433
0
                       (nVersion > OWS_1_0_0) ? "</se:Name>\n" : "</Name>\n");
4434
0
  msFree(pszEncoded);
4435
0
}
4436
4437
static void msSLDGenerateUserStyle(msStringBuffer *sb, layerObj *psLayer,
4438
0
                                   int nVersion, const char *pszTargetGroup) {
4439
0
  const char *pszWfsFilter;
4440
4441
0
  msStringBufferAppend(sb, "<UserStyle>\n");
4442
4443
0
  if (pszTargetGroup) {
4444
0
    msSLDAppendName(sb, pszTargetGroup, nVersion);
4445
0
    if (psLayer->classgroup &&
4446
0
        strcmp(psLayer->classgroup, pszTargetGroup) == 0) {
4447
0
      msStringBufferAppend(sb, nVersion > OWS_1_0_0
4448
0
                                   ? "<se:IsDefault>true</se:IsDefault>\n"
4449
0
                                   : "<IsDefault>true</IsDefault>\n");
4450
0
    }
4451
0
  }
4452
4453
0
  msStringBufferAppend(sb, nVersion > OWS_1_0_0 ? "<se:FeatureTypeStyle>\n"
4454
0
                                                : "<FeatureTypeStyle>\n");
4455
4456
0
  pszWfsFilter = msLookupHashTable(&(psLayer->metadata), "wfs_filter");
4457
0
  if (psLayer->numclasses > 0) {
4458
0
    int i;
4459
0
    for (i = 0; i < psLayer->numclasses; i++) {
4460
0
      char *pszFilter;
4461
0
      double dfMinScale = -1, dfMaxScale = -1;
4462
4463
      /* Only write down classes that match the group of interest */
4464
0
      if (psLayer->_class[i]->group) {
4465
0
        if (pszTargetGroup == NULL ||
4466
0
            strcmp(psLayer->_class[i]->group, pszTargetGroup) != 0) {
4467
0
          continue;
4468
0
        }
4469
0
      } else if (pszTargetGroup != NULL) {
4470
0
        continue;
4471
0
      }
4472
4473
0
      msStringBufferAppend(sb,
4474
0
                           nVersion > OWS_1_0_0 ? "<se:Rule>\n" : "<Rule>\n");
4475
4476
      /* if class has a name, use it as the RULE name */
4477
0
      if (psLayer->_class[i]->name) {
4478
0
        msSLDAppendName(sb, psLayer->_class[i]->name, nVersion);
4479
0
      }
4480
      /* -------------------------------------------------------------------- */
4481
      /*      get the Filter if there is a class expression.                  */
4482
      /* -------------------------------------------------------------------- */
4483
0
      pszFilter = msSLDGetFilter(psLayer->_class[i], pszWfsFilter);
4484
4485
0
      if (pszFilter) {
4486
0
        msStringBufferAppend(sb, pszFilter);
4487
0
        free(pszFilter);
4488
0
      }
4489
      /* -------------------------------------------------------------------- */
4490
      /*      generate the min/max scale.                                     */
4491
      /* -------------------------------------------------------------------- */
4492
0
      dfMinScale = -1.0;
4493
0
      if (psLayer->_class[i]->minscaledenom > 0)
4494
0
        dfMinScale = psLayer->_class[i]->minscaledenom;
4495
0
      else if (psLayer->minscaledenom > 0)
4496
0
        dfMinScale = psLayer->minscaledenom;
4497
0
      else if (psLayer->map && psLayer->map->web.minscaledenom > 0)
4498
0
        dfMinScale = psLayer->map->web.minscaledenom;
4499
0
      if (dfMinScale > 0) {
4500
0
        char szTmp[100];
4501
0
        if (nVersion > OWS_1_0_0)
4502
0
          snprintf(szTmp, sizeof(szTmp),
4503
0
                   "<se:MinScaleDenominator>%f</se:MinScaleDenominator>\n",
4504
0
                   dfMinScale);
4505
0
        else
4506
0
          snprintf(szTmp, sizeof(szTmp),
4507
0
                   "<MinScaleDenominator>%f</MinScaleDenominator>\n",
4508
0
                   dfMinScale);
4509
4510
0
        msStringBufferAppend(sb, szTmp);
4511
0
      }
4512
4513
0
      dfMaxScale = -1.0;
4514
0
      if (psLayer->_class[i]->maxscaledenom > 0)
4515
0
        dfMaxScale = psLayer->_class[i]->maxscaledenom;
4516
0
      else if (psLayer->maxscaledenom > 0)
4517
0
        dfMaxScale = psLayer->maxscaledenom;
4518
0
      else if (psLayer->map && psLayer->map->web.maxscaledenom > 0)
4519
0
        dfMaxScale = psLayer->map->web.maxscaledenom;
4520
0
      if (dfMaxScale > 0) {
4521
0
        char szTmp[100];
4522
0
        if (nVersion > OWS_1_0_0)
4523
0
          snprintf(szTmp, sizeof(szTmp),
4524
0
                   "<se:MaxScaleDenominator>%f</se:MaxScaleDenominator>\n",
4525
0
                   dfMaxScale);
4526
0
        else
4527
0
          snprintf(szTmp, sizeof(szTmp),
4528
0
                   "<MaxScaleDenominator>%f</MaxScaleDenominator>\n",
4529
0
                   dfMaxScale);
4530
4531
0
        msStringBufferAppend(sb, szTmp);
4532
0
      }
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
0
      if (psLayer->type == MS_LAYER_LINE) {
4542
0
        int j;
4543
0
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
4544
0
          styleObj *psStyle = psLayer->_class[i]->styles[j];
4545
0
          char *pszSLD = msSLDGenerateLineSLD(psStyle, psLayer, nVersion);
4546
0
          if (pszSLD) {
4547
0
            msStringBufferAppend(sb, pszSLD);
4548
0
            free(pszSLD);
4549
0
          }
4550
0
        }
4551
4552
0
      } else if (psLayer->type == MS_LAYER_POLYGON) {
4553
0
        int j;
4554
0
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
4555
0
          styleObj *psStyle = psLayer->_class[i]->styles[j];
4556
0
          char *pszSLD = msSLDGeneratePolygonSLD(psStyle, psLayer, nVersion);
4557
0
          if (pszSLD) {
4558
0
            msStringBufferAppend(sb, pszSLD);
4559
0
            free(pszSLD);
4560
0
          }
4561
0
        }
4562
4563
0
      } else if (psLayer->type == MS_LAYER_POINT) {
4564
0
        int j;
4565
0
        for (j = 0; j < psLayer->_class[i]->numstyles; j++) {
4566
0
          styleObj *psStyle = psLayer->_class[i]->styles[j];
4567
0
          char *pszSLD = msSLDGeneratePointSLD(psStyle, psLayer, nVersion);
4568
0
          if (pszSLD) {
4569
0
            msStringBufferAppend(sb, pszSLD);
4570
0
            free(pszSLD);
4571
0
          }
4572
0
        }
4573
0
      }
4574
0
      {
4575
        /* label if it exists */
4576
0
        char *pszSLD =
4577
0
            msSLDGenerateTextSLD(psLayer->_class[i], psLayer, nVersion);
4578
0
        if (pszSLD) {
4579
0
          msStringBufferAppend(sb, pszSLD);
4580
0
          free(pszSLD);
4581
0
        }
4582
0
      }
4583
0
      msStringBufferAppend(sb,
4584
0
                           nVersion > OWS_1_0_0 ? "</se:Rule>\n" : "</Rule>\n");
4585
0
    }
4586
0
  }
4587
4588
0
  msStringBufferAppend(sb, nVersion > OWS_1_0_0 ? "</se:FeatureTypeStyle>\n"
4589
0
                                                : "</FeatureTypeStyle>\n");
4590
0
  msStringBufferAppend(sb, "</UserStyle>\n");
4591
0
}
4592
4593
#endif
4594
4595
/************************************************************************/
4596
/*                          msSLDGenerateSLDLayer                       */
4597
/*                                                                      */
4598
/*      Generate an SLD XML string based on the layer's classes.        */
4599
/************************************************************************/
4600
0
char *msSLDGenerateSLDLayer(layerObj *psLayer, int nVersion) {
4601
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4602
0
    defined(USE_SOS_SVR)
4603
4604
0
  const char *pszWMSLayerName = NULL;
4605
0
  msStringBuffer *sb = msStringBufferAlloc();
4606
4607
0
  if (psLayer && (psLayer->status == MS_ON || psLayer->status == MS_DEFAULT) &&
4608
0
      (psLayer->type == MS_LAYER_POINT || psLayer->type == MS_LAYER_LINE ||
4609
0
       psLayer->type == MS_LAYER_POLYGON)) {
4610
4611
0
    int i;
4612
0
    int numClassGroupNames = 0;
4613
0
    char **papszClassGroupNames =
4614
0
        (char **)msSmallMalloc(sizeof(char *) * psLayer->numclasses);
4615
0
    for (i = 0; i < psLayer->numclasses; i++) {
4616
0
      const char *group = psLayer->_class[i]->group;
4617
0
      int j;
4618
0
      for (j = 0; j < numClassGroupNames; j++) {
4619
0
        if (group == NULL) {
4620
0
          if (papszClassGroupNames[j] == NULL)
4621
0
            break;
4622
0
        } else if (papszClassGroupNames[j] != NULL &&
4623
0
                   strcmp(papszClassGroupNames[j], group) == 0) {
4624
0
          break;
4625
0
        }
4626
0
      }
4627
0
      if (j >= numClassGroupNames) {
4628
0
        papszClassGroupNames[numClassGroupNames] =
4629
0
            group ? msStrdup(group) : NULL;
4630
0
        numClassGroupNames++;
4631
0
      }
4632
0
    }
4633
4634
0
    msStringBufferAppend(sb, "<NamedLayer>\n");
4635
4636
0
    pszWMSLayerName = msOWSLookupMetadata(&(psLayer->metadata), "MO", "name");
4637
0
    msSLDAppendName(sb,
4638
0
                    pszWMSLayerName ? pszWMSLayerName
4639
0
                    : psLayer->name ? psLayer->name
4640
0
                                    : "NamedLayer",
4641
0
                    nVersion);
4642
4643
0
    for (i = 0; i < numClassGroupNames; i++) {
4644
0
      msSLDGenerateUserStyle(sb, psLayer, nVersion, papszClassGroupNames[i]);
4645
0
      msFree(papszClassGroupNames[i]);
4646
0
    }
4647
0
    msFree(papszClassGroupNames);
4648
4649
0
    msStringBufferAppend(sb, "</NamedLayer>\n");
4650
0
  }
4651
0
  return msStringBufferReleaseStringAndFree(sb);
4652
4653
#else
4654
  msSetError(MS_MISCERR, "OWS support is not available.",
4655
             "msSLDGenerateSLDLayer()");
4656
  return NULL;
4657
#endif
4658
0
}
4659
4660
0
static const char *msSLDGetComparisonValue(const char *pszExpression) {
4661
0
  if (!pszExpression)
4662
0
    return NULL;
4663
4664
0
  if (strstr(pszExpression, "<=") || strstr(pszExpression, " le "))
4665
0
    return "PropertyIsLessThanOrEqualTo";
4666
0
  else if (strstr(pszExpression, "=~"))
4667
0
    return "PropertyIsLike";
4668
0
  else if (strstr(pszExpression, "~*"))
4669
0
    return "PropertyIsLike";
4670
0
  else if (strstr(pszExpression, ">=") || strstr(pszExpression, " ge "))
4671
0
    return "PropertyIsGreaterThanOrEqualTo";
4672
0
  else if (strstr(pszExpression, "!=") || strstr(pszExpression, " ne ") ||
4673
0
           strstr(pszExpression, "<>"))
4674
0
    return "PropertyIsNotEqualTo";
4675
0
  else if (strstr(pszExpression, "=") || strstr(pszExpression, " eq "))
4676
0
    return "PropertyIsEqualTo";
4677
0
  else if (strstr(pszExpression, "<") || strstr(pszExpression, " lt "))
4678
0
    return "PropertyIsLessThan";
4679
0
  else if (strstr(pszExpression, ">") || strstr(pszExpression, " gt "))
4680
0
    return "PropertyIsGreaterThan";
4681
4682
0
  return NULL;
4683
0
}
4684
4685
0
static const char *msSLDGetLogicalOperator(const char *pszExpression) {
4686
4687
0
  if (!pszExpression)
4688
0
    return NULL;
4689
4690
0
  if (strcasestr(pszExpression, " AND ") || strcasestr(pszExpression, "AND("))
4691
0
    return "And";
4692
4693
0
  if (strcasestr(pszExpression, " OR ") || strcasestr(pszExpression, "OR("))
4694
0
    return "Or";
4695
4696
0
  if (strcasestr(pszExpression, "NOT ") || strcasestr(pszExpression, "NOT("))
4697
0
    return "Not";
4698
4699
0
  return NULL;
4700
0
}
4701
4702
0
static char *msSLDGetRightExpressionOfOperator(const char *pszExpression) {
4703
0
  const char *pszAnd = NULL, *pszOr = NULL, *pszNot = NULL;
4704
4705
0
  pszAnd = strcasestr(pszExpression, " AND ");
4706
4707
0
  if (!pszAnd) {
4708
0
    pszAnd = strcasestr(pszExpression, "AND(");
4709
0
  }
4710
4711
0
  if (pszAnd)
4712
0
    return msStrdup(pszAnd + 4);
4713
0
  else {
4714
0
    pszOr = strcasestr(pszExpression, " OR ");
4715
4716
0
    if (!pszOr) {
4717
0
      pszOr = strcasestr(pszExpression, "OR(");
4718
0
    }
4719
4720
0
    if (pszOr)
4721
0
      return msStrdup(pszOr + 3);
4722
0
    else {
4723
0
      pszNot = strcasestr(pszExpression, "NOT ");
4724
4725
0
      if (!pszNot)
4726
0
        pszNot = strcasestr(pszExpression, "NOT(");
4727
4728
0
      if (pszNot)
4729
0
        return msStrdup(pszNot + 4);
4730
0
    }
4731
0
  }
4732
0
  return NULL;
4733
0
}
4734
4735
0
static char *msSLDGetLeftExpressionOfOperator(const char *pszExpression) {
4736
0
  char *pszReturn = NULL;
4737
0
  int nLength = 0, iReturn = 0;
4738
4739
0
  if (!pszExpression || (nLength = strlen(pszExpression)) <= 0)
4740
0
    return NULL;
4741
4742
0
  pszReturn = (char *)malloc(sizeof(char) * (nLength + 1));
4743
0
  pszReturn[0] = '\0';
4744
0
  if (strcasestr(pszExpression, " AND ")) {
4745
0
    for (int i = 0; i < nLength - 5; i++) {
4746
0
      if (pszExpression[i] == ' ' && (toupper(pszExpression[i + 1]) == 'A') &&
4747
0
          (toupper(pszExpression[i + 2]) == 'N') &&
4748
0
          (toupper(pszExpression[i + 3]) == 'D') &&
4749
0
          (pszExpression[i + 4] == ' '))
4750
0
        break;
4751
0
      else {
4752
0
        pszReturn[iReturn++] = pszExpression[i];
4753
0
      }
4754
0
      pszReturn[iReturn] = '\0';
4755
0
    }
4756
0
  } else if (strcasestr(pszExpression, "AND(")) {
4757
0
    for (int i = 0; i < nLength - 4; i++) {
4758
0
      if ((toupper(pszExpression[i]) == 'A') &&
4759
0
          (toupper(pszExpression[i + 1]) == 'N') &&
4760
0
          (toupper(pszExpression[i + 2]) == 'D') &&
4761
0
          (pszExpression[i + 3] == '('))
4762
0
        break;
4763
0
      else {
4764
0
        pszReturn[iReturn++] = pszExpression[i];
4765
0
      }
4766
0
      pszReturn[iReturn] = '\0';
4767
0
    }
4768
0
  } else if (strcasestr(pszExpression, " OR ")) {
4769
0
    for (int i = 0; i < nLength - 4; i++) {
4770
0
      if (pszExpression[i] == ' ' && (toupper(pszExpression[i + 1]) == 'O') &&
4771
0
          (toupper(pszExpression[i + 2]) == 'R') && pszExpression[i + 3] == ' ')
4772
0
        break;
4773
0
      else {
4774
0
        pszReturn[iReturn++] = pszExpression[i];
4775
0
      }
4776
0
      pszReturn[iReturn] = '\0';
4777
0
    }
4778
0
  } else if (strcasestr(pszExpression, "OR(")) {
4779
0
    for (int i = 0; i < nLength - 3; i++) {
4780
0
      if ((toupper(pszExpression[i]) == 'O') &&
4781
0
          (toupper(pszExpression[i + 1]) == 'R') && pszExpression[i + 2] == '(')
4782
0
        break;
4783
0
      else {
4784
0
        pszReturn[iReturn++] = pszExpression[i];
4785
0
      }
4786
0
      pszReturn[iReturn] = '\0';
4787
0
    }
4788
0
  } else {
4789
0
    msFree(pszReturn);
4790
0
    return NULL;
4791
0
  }
4792
4793
0
  return pszReturn;
4794
0
}
4795
4796
0
static int msSLDNumberOfLogicalOperators(const char *pszExpression) {
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
0
  if (!pszExpression)
4803
0
    return 0;
4804
4805
0
  int nAndCount = 0;
4806
0
  int nNotCount = 0;
4807
0
  int nOrCount = 0;
4808
4809
0
  int nArgs = 0;
4810
0
  char **papszArgs;
4811
0
  papszArgs = msStringTokenize(pszExpression, " ", &nArgs, MS_TRUE);
4812
4813
0
  for (int i = 0; i < nArgs; i++) {
4814
0
    if (strlen(papszArgs[i]) == 0) {
4815
0
      free(papszArgs[i]);
4816
0
      continue;
4817
0
    }
4818
4819
0
    if ((strncasecmp(papszArgs[i], "AND", 3) == 0) ||
4820
0
        (strcasestr(papszArgs[i], "AND("))) {
4821
0
      nAndCount += 1;
4822
0
    }
4823
4824
0
    if ((strncasecmp(papszArgs[i], "OR", 3) == 0) ||
4825
0
        (strcasestr(papszArgs[i], "OR("))) {
4826
0
      nOrCount += 1;
4827
0
    }
4828
4829
0
    if ((strncasecmp(papszArgs[i], "NOT", 3) == 0) ||
4830
0
        (strcasestr(papszArgs[i], "NOT("))) {
4831
0
      nNotCount += 1;
4832
0
    }
4833
4834
0
    free(papszArgs[i]);
4835
0
  }
4836
0
  free(papszArgs);
4837
4838
0
  if (nAndCount == 0 && nNotCount == 0 && nOrCount == 0) {
4839
0
    return 0;
4840
0
  }
4841
4842
0
  int sum = nAndCount + nNotCount + nOrCount;
4843
0
  if (sum == 1) {
4844
0
    return 1;
4845
0
  } else {
4846
0
    return 2;
4847
0
  }
4848
0
}
4849
4850
static char *msSLDGetAttributeNameOrValue(const char *pszExpression,
4851
                                          const char *pszComparisonValue,
4852
0
                                          int bReturnName) {
4853
0
  char **aszValues = NULL;
4854
0
  char *pszAttributeName = NULL;
4855
0
  char *pszAttributeValue = NULL;
4856
0
  char cCompare = '=';
4857
0
  char szCompare[3] = {0};
4858
0
  char szCompare2[3] = {0};
4859
0
  char szCompare3[3] = {0};
4860
0
  int bOneCharCompare = -1, nTokens = 0, nLength = 0;
4861
0
  int iValue = 0, i = 0, iValueIndex = 0;
4862
0
  int bStartCopy = 0, iAtt = 0;
4863
0
  char *pszFinalAttributeName = NULL, *pszFinalAttributeValue = NULL;
4864
0
  int bSingleQuote = 0, bDoubleQuote = 0;
4865
4866
0
  if (!pszExpression || !pszComparisonValue || strlen(pszExpression) == 0)
4867
0
    return NULL;
4868
4869
0
  szCompare[0] = '\0';
4870
0
  szCompare2[0] = '\0';
4871
0
  szCompare3[0] = '\0';
4872
4873
0
  if (strcasecmp(pszComparisonValue, "PropertyIsEqualTo") == 0) {
4874
0
    cCompare = '=';
4875
0
    szCompare[0] = 'e';
4876
0
    szCompare[1] = 'q';
4877
0
    szCompare[2] = '\0';
4878
4879
0
    bOneCharCompare = 1;
4880
0
  }
4881
0
  if (strcasecmp(pszComparisonValue, "PropertyIsNotEqualTo") == 0) {
4882
0
    szCompare[0] = 'n';
4883
0
    szCompare[1] = 'e';
4884
0
    szCompare[2] = '\0';
4885
4886
0
    szCompare2[0] = '!';
4887
0
    szCompare2[1] = '=';
4888
0
    szCompare2[2] = '\0';
4889
4890
0
    szCompare3[0] = '<';
4891
0
    szCompare3[1] = '>';
4892
0
    szCompare3[2] = '\0';
4893
4894
0
    bOneCharCompare = 0;
4895
0
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
4896
0
    szCompare[0] = '=';
4897
0
    szCompare[1] = '~';
4898
0
    szCompare[2] = '\0';
4899
4900
0
    szCompare2[0] = '~';
4901
0
    szCompare2[1] = '*';
4902
0
    szCompare2[2] = '\0';
4903
4904
0
    bOneCharCompare = 0;
4905
0
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLessThan") == 0) {
4906
0
    cCompare = '<';
4907
0
    szCompare[0] = 'l';
4908
0
    szCompare[1] = 't';
4909
0
    szCompare[2] = '\0';
4910
0
    bOneCharCompare = 1;
4911
0
  } else if (strcasecmp(pszComparisonValue, "PropertyIsLessThanOrEqualTo") ==
4912
0
             0) {
4913
0
    szCompare[0] = 'l';
4914
0
    szCompare[1] = 'e';
4915
0
    szCompare[2] = '\0';
4916
4917
0
    szCompare2[0] = '<';
4918
0
    szCompare2[1] = '=';
4919
0
    szCompare2[2] = '\0';
4920
4921
0
    bOneCharCompare = 0;
4922
0
  } else if (strcasecmp(pszComparisonValue, "PropertyIsGreaterThan") == 0) {
4923
0
    cCompare = '>';
4924
0
    szCompare[0] = 'g';
4925
0
    szCompare[1] = 't';
4926
0
    szCompare[2] = '\0';
4927
0
    bOneCharCompare = 1;
4928
0
  } else if (strcasecmp(pszComparisonValue, "PropertyIsGreaterThanOrEqualTo") ==
4929
0
             0) {
4930
0
    szCompare[0] = 'g';
4931
0
    szCompare[1] = 'e';
4932
0
    szCompare[2] = '\0';
4933
4934
0
    szCompare2[0] = '>';
4935
0
    szCompare2[1] = '=';
4936
0
    szCompare2[2] = '\0';
4937
4938
0
    bOneCharCompare = 0;
4939
0
  }
4940
4941
0
  if (bOneCharCompare == 1) {
4942
0
    aszValues = msStringSplit(pszExpression, cCompare, &nTokens);
4943
0
    if (nTokens > 1) {
4944
      // for operators such as >
4945
0
      pszAttributeName = msStrdup(aszValues[0]);
4946
0
      pszAttributeValue = msStrdup(aszValues[1]);
4947
0
    } else {
4948
      // for operators such as gt
4949
0
      nLength = strlen(pszExpression);
4950
0
      pszAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
4951
0
      iValue = 0;
4952
0
      for (i = 0; i < nLength - 2; i++) {
4953
0
        if (pszExpression[i] != szCompare[0] &&
4954
0
            pszExpression[i] != toupper(szCompare[0])) {
4955
0
          pszAttributeName[iValue++] = pszExpression[i];
4956
0
        } else {
4957
0
          if ((pszExpression[i + 1] == szCompare[1] ||
4958
0
               pszExpression[i + 1] == toupper(szCompare[1])) &&
4959
0
              (pszExpression[i + 2] == ' ')) {
4960
0
            iValueIndex = i + 3;
4961
0
            pszAttributeValue = msStrdup(pszExpression + iValueIndex);
4962
0
            break;
4963
0
          } else
4964
0
            pszAttributeName[iValue++] = pszExpression[i];
4965
0
        }
4966
0
      }
4967
0
      pszAttributeName[iValue] = '\0';
4968
0
    }
4969
0
    msFreeCharArray(aszValues, nTokens);
4970
0
  } else if (bOneCharCompare == 0) {
4971
0
    nLength = strlen(pszExpression);
4972
0
    pszAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
4973
0
    iValue = 0;
4974
0
    for (int i = 0; i < nLength; i++) {
4975
      // Check for comparison operator
4976
0
      if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2)) ||
4977
0
          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
4978
0
          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2))) {
4979
        // Extract value after the operator
4980
0
        int offset = 2; // All operators are 2 characters long
4981
0
        pszAttributeValue = msStrdup(&pszExpression[i + offset]);
4982
0
        break;
4983
0
      } else {
4984
        // Copy character to attribute name
4985
0
        pszAttributeName[iValue++] = pszExpression[i];
4986
0
      }
4987
0
    }
4988
0
    pszAttributeName[iValue] = '\0';
4989
0
  }
4990
4991
  /* -------------------------------------------------------------------- */
4992
  /*      Return the name of the attribute : It is supposed to be         */
4993
  /*      inside []                                                       */
4994
  /* -------------------------------------------------------------------- */
4995
0
  if (bReturnName) {
4996
0
    if (!pszAttributeName) {
4997
0
      msFree(pszAttributeValue);
4998
0
      return NULL;
4999
0
    }
5000
5001
0
    nLength = strlen(pszAttributeName);
5002
0
    pszFinalAttributeName = (char *)malloc(sizeof(char) * (nLength + 1));
5003
0
    bStartCopy = 0;
5004
0
    iAtt = 0;
5005
0
    for (i = 0; i < nLength; i++) {
5006
0
      if (pszAttributeName[i] == ' ' && bStartCopy == 0)
5007
0
        continue;
5008
5009
0
      if (pszAttributeName[i] == '[') {
5010
0
        bStartCopy = 1;
5011
0
        continue;
5012
0
      }
5013
0
      if (pszAttributeName[i] == ']')
5014
0
        break;
5015
0
      if (bStartCopy) {
5016
0
        pszFinalAttributeName[iAtt++] = pszAttributeName[i];
5017
0
      }
5018
0
      pszFinalAttributeName[iAtt] = '\0';
5019
0
    }
5020
5021
0
    msFree(pszAttributeName);
5022
0
    msFree(pszAttributeValue);
5023
0
    return pszFinalAttributeName;
5024
0
  } else {
5025
5026
0
    if (!pszAttributeValue) {
5027
0
      msFree(pszAttributeName);
5028
0
      return NULL;
5029
0
    }
5030
0
    nLength = strlen(pszAttributeValue);
5031
0
    pszFinalAttributeValue = (char *)malloc(sizeof(char) * (nLength + 1));
5032
0
    pszFinalAttributeValue[0] = '\0';
5033
0
    bStartCopy = 0;
5034
0
    iAtt = 0;
5035
0
    for (i = 0; i < nLength; i++) {
5036
0
      if (pszAttributeValue[i] == ' ' && bStartCopy == 0)
5037
0
        continue;
5038
5039
0
      if (pszAttributeValue[i] == '\'' && bStartCopy == 0) {
5040
0
        bSingleQuote = 1;
5041
0
        bStartCopy = 1;
5042
0
        continue;
5043
0
      } else if (pszAttributeValue[i] == '"' && bStartCopy == 0) {
5044
0
        bDoubleQuote = 1;
5045
0
        bStartCopy = 1;
5046
0
        continue;
5047
0
      } else
5048
0
        bStartCopy = 1;
5049
5050
0
      if (bStartCopy) {
5051
0
        if (pszAttributeValue[i] == '\'' && bSingleQuote)
5052
0
          break;
5053
0
        else if (pszAttributeValue[i] == '"' && bDoubleQuote)
5054
0
          break;
5055
0
        else if (pszAttributeValue[i] == ')') {
5056
          // remove any spaces prior to the closing bracket
5057
0
          msStringTrimBlanks(pszFinalAttributeValue);
5058
0
          break;
5059
0
        }
5060
0
        pszFinalAttributeValue[iAtt++] = pszAttributeValue[i];
5061
0
      }
5062
0
      pszFinalAttributeValue[iAtt] = '\0';
5063
0
    }
5064
5065
    /*trim  for regular expressions*/
5066
0
    if (strlen(pszFinalAttributeValue) > 2 &&
5067
0
        strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
5068
0
      int len = strlen(pszFinalAttributeValue);
5069
0
      msStringTrimBlanks(pszFinalAttributeValue);
5070
0
      if (pszFinalAttributeValue[0] == '/' &&
5071
0
          (pszFinalAttributeValue[len - 1] == '/' ||
5072
0
           (pszFinalAttributeValue[len - 1] == 'i' &&
5073
0
            pszFinalAttributeValue[len - 2] == '/'))) {
5074
0
        if (pszFinalAttributeValue[len - 1] == '/')
5075
0
          pszFinalAttributeValue[len - 1] = '\0';
5076
0
        else
5077
0
          pszFinalAttributeValue[len - 2] = '\0';
5078
5079
0
        memmove(pszFinalAttributeValue,
5080
0
                pszFinalAttributeValue +
5081
0
                    ((pszFinalAttributeValue[1] == '^') ? 2 : 1),
5082
0
                len - 1);
5083
5084
        /*replace wild card string .* with * */
5085
0
        pszFinalAttributeValue =
5086
0
            msReplaceSubstring(pszFinalAttributeValue, ".*", "*");
5087
0
      }
5088
0
    }
5089
0
    msFree(pszAttributeName);
5090
0
    msFree(pszAttributeValue);
5091
0
    return pszFinalAttributeValue;
5092
0
  }
5093
0
}
5094
5095
static char *msSLDGetAttributeName(const char *pszExpression,
5096
0
                                   const char *pszComparisonValue) {
5097
0
  return msSLDGetAttributeNameOrValue(pszExpression, pszComparisonValue, 1);
5098
0
}
5099
5100
static char *msSLDGetAttributeValue(const char *pszExpression,
5101
0
                                    const char *pszComparisonValue) {
5102
0
  return msSLDGetAttributeNameOrValue(pszExpression, pszComparisonValue, 0);
5103
0
}
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,
5117
0
                                               FilterEncodingNode *psNode) {
5118
0
  int nOperators = 0;
5119
5120
0
  if (!pszExpression || strlen(pszExpression) == 0)
5121
0
    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
0
  nOperators = msSLDNumberOfLogicalOperators(pszExpression);
5133
0
  if (nOperators == 0) {
5134
0
    if (!psNode)
5135
0
      psNode = FLTCreateFilterEncodingNode();
5136
5137
0
    const char *pszComparisonValue = msSLDGetComparisonValue(pszExpression);
5138
0
    char *pszAttributeName =
5139
0
        msSLDGetAttributeName(pszExpression, pszComparisonValue);
5140
0
    char *pszAttributeValue =
5141
0
        msSLDGetAttributeValue(pszExpression, pszComparisonValue);
5142
0
    if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
5143
0
      psNode->eType = FILTER_NODE_TYPE_COMPARISON;
5144
0
      psNode->pszValue = msStrdup(pszComparisonValue);
5145
5146
0
      psNode->psLeftNode = FLTCreateFilterEncodingNode();
5147
0
      psNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
5148
0
      psNode->psLeftNode->pszValue = msStrdup(pszAttributeName);
5149
5150
0
      psNode->psRightNode = FLTCreateFilterEncodingNode();
5151
0
      psNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
5152
0
      psNode->psRightNode->pszValue = msStrdup(pszAttributeValue);
5153
5154
0
      if (strcasecmp(pszComparisonValue, "PropertyIsLike") == 0) {
5155
0
        psNode->pOther = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
5156
0
        ((FEPropertyIsLike *)psNode->pOther)->bCaseInsensitive = 0;
5157
0
        ((FEPropertyIsLike *)psNode->pOther)->pszWildCard = msStrdup("*");
5158
0
        ((FEPropertyIsLike *)psNode->pOther)->pszSingleChar = msStrdup("#");
5159
0
        ((FEPropertyIsLike *)psNode->pOther)->pszEscapeChar = msStrdup("!");
5160
0
      }
5161
0
    }
5162
0
    free(pszAttributeName);
5163
0
    free(pszAttributeValue);
5164
0
    return psNode;
5165
5166
0
  } else if (nOperators == 1) {
5167
0
    const char *pszOperator = msSLDGetLogicalOperator(pszExpression);
5168
0
    if (pszOperator) {
5169
0
      if (!psNode)
5170
0
        psNode = FLTCreateFilterEncodingNode();
5171
5172
0
      psNode->eType = FILTER_NODE_TYPE_LOGICAL;
5173
0
      psNode->pszValue = msStrdup(pszOperator);
5174
5175
0
      char *pszLeftExpression = msSLDGetLeftExpressionOfOperator(pszExpression);
5176
0
      char *pszRightExpression =
5177
0
          msSLDGetRightExpressionOfOperator(pszExpression);
5178
5179
0
      if (pszLeftExpression || pszRightExpression) {
5180
0
        if (pszLeftExpression) {
5181
0
          const char *pszComparisonValue =
5182
0
              msSLDGetComparisonValue(pszLeftExpression);
5183
0
          char *pszAttributeName =
5184
0
              msSLDGetAttributeName(pszLeftExpression, pszComparisonValue);
5185
0
          char *pszAttributeValue =
5186
0
              msSLDGetAttributeValue(pszLeftExpression, pszComparisonValue);
5187
5188
0
          if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
5189
0
            psNode->psLeftNode = FLTCreateFilterEncodingNode();
5190
0
            psNode->psLeftNode->eType = FILTER_NODE_TYPE_COMPARISON;
5191
0
            psNode->psLeftNode->pszValue = msStrdup(pszComparisonValue);
5192
5193
0
            psNode->psLeftNode->psLeftNode = FLTCreateFilterEncodingNode();
5194
0
            psNode->psLeftNode->psLeftNode->eType =
5195
0
                FILTER_NODE_TYPE_PROPERTYNAME;
5196
0
            psNode->psLeftNode->psLeftNode->pszValue =
5197
0
                msStrdup(pszAttributeName);
5198
5199
0
            psNode->psLeftNode->psRightNode = FLTCreateFilterEncodingNode();
5200
0
            psNode->psLeftNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
5201
0
            psNode->psLeftNode->psRightNode->pszValue =
5202
0
                msStrdup(pszAttributeValue);
5203
0
          }
5204
5205
0
          free(pszAttributeName);
5206
0
          free(pszAttributeValue);
5207
0
          free(pszLeftExpression);
5208
0
        }
5209
0
        if (pszRightExpression) {
5210
0
          const char *pszComparisonValue =
5211
0
              msSLDGetComparisonValue(pszRightExpression);
5212
0
          char *pszAttributeName =
5213
0
              msSLDGetAttributeName(pszRightExpression, pszComparisonValue);
5214
0
          char *pszAttributeValue =
5215
0
              msSLDGetAttributeValue(pszRightExpression, pszComparisonValue);
5216
5217
0
          if (pszComparisonValue && pszAttributeName && pszAttributeValue) {
5218
0
            psNode->psRightNode = FLTCreateFilterEncodingNode();
5219
0
            psNode->psRightNode->eType = FILTER_NODE_TYPE_COMPARISON;
5220
0
            psNode->psRightNode->pszValue = msStrdup(pszComparisonValue);
5221
5222
0
            psNode->psRightNode->psLeftNode = FLTCreateFilterEncodingNode();
5223
0
            psNode->psRightNode->psLeftNode->eType =
5224
0
                FILTER_NODE_TYPE_PROPERTYNAME;
5225
0
            psNode->psRightNode->psLeftNode->pszValue =
5226
0
                msStrdup(pszAttributeName);
5227
5228
0
            psNode->psRightNode->psRightNode = FLTCreateFilterEncodingNode();
5229
0
            psNode->psRightNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
5230
0
            psNode->psRightNode->psRightNode->pszValue =
5231
0
                msStrdup(pszAttributeValue);
5232
0
          }
5233
0
          free(pszAttributeName);
5234
0
          free(pszAttributeValue);
5235
0
          free(pszRightExpression);
5236
0
        }
5237
0
      }
5238
0
    }
5239
5240
0
    return psNode;
5241
0
  } else {
5242
0
    return NULL;
5243
0
  }
5244
0
}
5245
5246
0
static char *msSLDBuildFilterEncoding(const FilterEncodingNode *psNode) {
5247
0
  char *pszTmp = NULL;
5248
0
  char szTmp[200];
5249
0
  char *pszExpression = NULL;
5250
5251
0
  if (!psNode)
5252
0
    return NULL;
5253
5254
0
  if (psNode->eType == FILTER_NODE_TYPE_COMPARISON && psNode->pszValue &&
5255
0
      psNode->psLeftNode && psNode->psLeftNode->pszValue &&
5256
0
      psNode->psRightNode && psNode->psRightNode->pszValue) {
5257
0
    snprintf(szTmp, sizeof(szTmp),
5258
0
             "<ogc:%s><ogc:PropertyName>%s</ogc:PropertyName><ogc:Literal>%s</"
5259
0
             "ogc:Literal></ogc:%s>",
5260
0
             psNode->pszValue, psNode->psLeftNode->pszValue,
5261
0
             psNode->psRightNode->pszValue, psNode->pszValue);
5262
0
    pszExpression = msStrdup(szTmp);
5263
0
  } else if (psNode->eType == FILTER_NODE_TYPE_LOGICAL && psNode->pszValue &&
5264
0
             ((psNode->psLeftNode && psNode->psLeftNode->pszValue) ||
5265
0
              (psNode->psRightNode && psNode->psRightNode->pszValue))) {
5266
0
    snprintf(szTmp, sizeof(szTmp), "<ogc:%s>", psNode->pszValue);
5267
0
    pszExpression = msStringConcatenate(pszExpression, szTmp);
5268
0
    if (psNode->psLeftNode) {
5269
0
      pszTmp = msSLDBuildFilterEncoding(psNode->psLeftNode);
5270
0
      if (pszTmp) {
5271
0
        pszExpression = msStringConcatenate(pszExpression, pszTmp);
5272
0
        free(pszTmp);
5273
0
      }
5274
0
    }
5275
0
    if (psNode->psRightNode) {
5276
0
      pszTmp = msSLDBuildFilterEncoding(psNode->psRightNode);
5277
0
      if (pszTmp) {
5278
0
        pszExpression = msStringConcatenate(pszExpression, pszTmp);
5279
0
        free(pszTmp);
5280
0
      }
5281
0
    }
5282
0
    snprintf(szTmp, sizeof(szTmp), "</ogc:%s>", psNode->pszValue);
5283
0
    pszExpression = msStringConcatenate(pszExpression, szTmp);
5284
0
  }
5285
0
  return pszExpression;
5286
0
}
5287
5288
static char *msSLDParseLogicalExpression(const char *pszExpression,
5289
0
                                         const char *pszWfsFilter) {
5290
0
  FilterEncodingNode *psNode = NULL;
5291
0
  char *pszFLTExpression = NULL;
5292
0
  char *pszTmp = NULL;
5293
5294
0
  if (!pszExpression || strlen(pszExpression) == 0)
5295
0
    return NULL;
5296
5297
0
  psNode = BuildExpressionTree(pszExpression, NULL);
5298
5299
0
  if (psNode) {
5300
0
    pszFLTExpression = msSLDBuildFilterEncoding(psNode);
5301
0
    if (pszFLTExpression) {
5302
0
      pszTmp = msStringConcatenate(pszTmp, "<ogc:Filter>");
5303
0
      if (pszWfsFilter) {
5304
0
        pszTmp = msStringConcatenate(pszTmp, "<ogc:And>");
5305
0
        pszTmp = msStringConcatenate(pszTmp, (char *)pszWfsFilter);
5306
0
      }
5307
0
      pszTmp = msStringConcatenate(pszTmp, pszFLTExpression);
5308
5309
0
      if (pszWfsFilter)
5310
0
        pszTmp = msStringConcatenate(pszTmp, "</ogc:And>");
5311
5312
0
      pszTmp = msStringConcatenate(pszTmp, "</ogc:Filter>\n");
5313
5314
0
      free(pszFLTExpression);
5315
0
      pszFLTExpression = pszTmp;
5316
0
    }
5317
0
    FLTFreeFilterEncodingNode(psNode);
5318
0
  }
5319
5320
0
  return pszFLTExpression;
5321
0
}
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
0
static char *msSLDConvertRegexExpToOgcIsLike(const char *pszRegex) {
5353
0
  char szBuffer[1024];
5354
0
  int iBuffer = 0, i = 0;
5355
0
  int nLength = 0;
5356
5357
0
  if (!pszRegex || strlen(pszRegex) == 0)
5358
0
    return NULL;
5359
5360
0
  szBuffer[0] = '\0';
5361
0
  nLength = strlen(pszRegex);
5362
5363
0
  while (i < nLength) {
5364
0
    if (pszRegex[i] != '.') {
5365
0
      szBuffer[iBuffer++] = pszRegex[i];
5366
0
      i++;
5367
0
    } else {
5368
0
      if (i < nLength - 1 && pszRegex[i + 1] == '*') {
5369
0
        szBuffer[iBuffer++] = '*';
5370
0
        i = i + 2;
5371
0
      } else {
5372
0
        szBuffer[iBuffer++] = pszRegex[i];
5373
0
        i++;
5374
0
      }
5375
0
    }
5376
0
  }
5377
0
  szBuffer[iBuffer] = '\0';
5378
5379
0
  return msStrdup(szBuffer);
5380
0
}
5381
5382
/************************************************************************/
5383
/*                          XMLEscape                                   */
5384
/************************************************************************/
5385
0
static std::string XMLEscape(const char *pszValue) {
5386
0
  char *pszEscaped = CPLEscapeString(pszValue, -1, CPLES_XML);
5387
0
  std::string osRet(pszEscaped);
5388
0
  CPLFree(pszEscaped);
5389
0
  return osRet;
5390
0
}
5391
5392
static std::string GetPropertyIsEqualTo(const char *pszPropertyName,
5393
0
                                        const char *pszLiteral) {
5394
0
  std::string osFilter("<ogc:PropertyIsEqualTo><ogc:PropertyName>");
5395
0
  osFilter += XMLEscape(pszPropertyName);
5396
0
  osFilter += "</ogc:PropertyName><ogc:Literal>";
5397
0
  osFilter += XMLEscape(pszLiteral);
5398
0
  osFilter += "</ogc:Literal></ogc:PropertyIsEqualTo>";
5399
0
  return osFilter;
5400
0
}
5401
5402
static std::string GetPropertyIsLike(const char *pszPropertyName,
5403
0
                                     const char *pszLiteral) {
5404
0
  std::string osFilter("<ogc:PropertyIsLike wildCard=\"*\" singleChar=\".\" "
5405
0
                       "escape=\"\\\"><ogc:PropertyName>");
5406
0
  osFilter += XMLEscape(pszPropertyName);
5407
0
  osFilter += "</ogc:PropertyName><ogc:Literal>";
5408
0
  osFilter += XMLEscape(pszLiteral);
5409
0
  osFilter += "</ogc:Literal></ogc:PropertyIsLike>";
5410
0
  return osFilter;
5411
0
}
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
0
char *msSLDGetFilter(classObj *psClass, const char *pszWfsFilter) {
5421
0
  char *pszFilter = NULL;
5422
0
  char *pszOgcFilter = NULL;
5423
5424
0
  if (psClass && psClass->expression.string) {
5425
5426
0
    char *pszExpression = msStrdup(psClass->expression.string);
5427
5428
    /* string expression */
5429
0
    if (psClass->expression.type == MS_STRING) {
5430
5431
      // ensure any trailing whitespace is removed
5432
0
      msStringTrimBlanks(pszExpression);
5433
0
      if (psClass->layer && psClass->layer->classitem) {
5434
0
        std::string osFilter("<ogc:Filter>");
5435
0
        if (pszWfsFilter) {
5436
0
          osFilter += "<ogc:And>";
5437
0
          osFilter += pszWfsFilter;
5438
0
        }
5439
0
        osFilter +=
5440
0
            GetPropertyIsEqualTo(psClass->layer->classitem, pszExpression);
5441
0
        if (pszWfsFilter) {
5442
0
          osFilter += "</ogc:And>";
5443
0
        }
5444
0
        osFilter += "</ogc:Filter>\n";
5445
0
        pszFilter = msStrdup(osFilter.c_str());
5446
0
      }
5447
0
    } else if (psClass->expression.type == MS_EXPRESSION) {
5448
0
      msStringTrimBlanks(pszExpression);
5449
0
      pszFilter = msSLDParseLogicalExpression(pszExpression, pszWfsFilter);
5450
0
    } else if (psClass->expression.type == MS_LIST) {
5451
0
      if (psClass->layer && psClass->layer->classitem) {
5452
5453
0
        char **listExpressionValues = NULL;
5454
0
        int numListExpressionValues = 0;
5455
0
        int i = 0;
5456
0
        int tokenCount = 0;
5457
0
        std::string osOrFilters;
5458
5459
0
        listExpressionValues =
5460
0
            msStringSplit(pszExpression, ',', &numListExpressionValues);
5461
5462
        // loop through all values in the list and create a PropertyIsEqualTo
5463
        // for each value
5464
0
        for (i = 0; i < numListExpressionValues; i++) {
5465
0
          if (listExpressionValues[i] && listExpressionValues[i][0] != '\0') {
5466
0
            osOrFilters += GetPropertyIsEqualTo(psClass->layer->classitem,
5467
0
                                                listExpressionValues[i]);
5468
0
            osOrFilters += '\n';
5469
0
            tokenCount++;
5470
0
          }
5471
0
        }
5472
5473
0
        std::string osFilter("<ogc:Filter>");
5474
5475
        // no need for an OR clause if there is only one item in the list
5476
0
        if (tokenCount == 1) {
5477
0
          osFilter += osOrFilters;
5478
0
        } else if (tokenCount > 1) {
5479
0
          osFilter += "<ogc:Or>";
5480
0
          osFilter += osOrFilters;
5481
0
          osFilter += "</ogc:Or>";
5482
0
        }
5483
0
        osFilter += "</ogc:Filter>";
5484
5485
        // don't filter when the list is empty
5486
0
        if (tokenCount > 0) {
5487
0
          pszFilter = msStrdup(osFilter.c_str());
5488
0
        }
5489
0
        msFreeCharArray(listExpressionValues, numListExpressionValues);
5490
0
      }
5491
0
    } else if (psClass->expression.type == MS_REGEX) {
5492
0
      if (psClass->layer && psClass->layer->classitem) {
5493
0
        pszOgcFilter =
5494
0
            msSLDConvertRegexExpToOgcIsLike(psClass->expression.string);
5495
5496
0
        std::string osFilter("<ogc:Filter>");
5497
0
        if (pszWfsFilter) {
5498
0
          osFilter += "<ogc:And>";
5499
0
          osFilter += pszWfsFilter;
5500
0
        }
5501
0
        osFilter += GetPropertyIsLike(psClass->layer->classitem, pszOgcFilter);
5502
5503
0
        free(pszOgcFilter);
5504
5505
0
        if (pszWfsFilter) {
5506
0
          osFilter += "</ogc:And>";
5507
0
        }
5508
0
        osFilter += "</ogc:Filter>\n";
5509
0
        pszFilter = msStrdup(osFilter.c_str());
5510
0
      }
5511
0
    }
5512
5513
0
    msFree(pszExpression);
5514
0
  } else if (pszWfsFilter) {
5515
0
    std::string osFilter("<ogc:Filter>");
5516
0
    osFilter += pszWfsFilter;
5517
0
    osFilter += "</ogc:Filter>\n";
5518
0
    pszFilter = msStrdup(osFilter.c_str());
5519
0
  }
5520
0
  return pszFilter;
5521
0
}