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

geographika / mapserver / 13697752106

06 Mar 2025 11:34AM UTC coverage: 41.364% (-0.03%) from 41.392%
13697752106

push

github

geographika
Mark zip output tests as TO_FIX as output is different on each run

61755 of 149297 relevant lines covered (41.36%)

24825.13 hits per line

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

50.99
/src/mapkmlrenderer.cpp
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Google Earth KML output
6
 * Author:   David Kana and the MapServer team
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2009 Regents of the University of Minnesota.
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
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 *****************************************************************************/
29

30
#include "mapserver-config.h"
31
#ifdef USE_KML
32

33
#include "mapserver.h"
34
#include "maperror.h"
35
#include "mapkmlrenderer.h"
36
#include "mapio.h"
37
#include "mapows.h"
38

39
#include "cpl_conv.h"
40
#include "cpl_vsi.h"
41

42
#define KML_MAXFEATURES_TODRAW 1000
43

44
KmlRenderer::KmlRenderer(int width, int height, outputFormatObj * /*format*/,
2✔
45
                         colorObj * /*color*/)
2✔
46
    : Width(width), Height(height), MapCellsize(1.0), XmlDoc(NULL),
2✔
47
      LayerNode(NULL), GroundOverlayNode(NULL), PlacemarkNode(NULL),
2✔
48
      GeomNode(NULL), Items(NULL), NumItems(0), FirstLayer(MS_TRUE), map(NULL),
2✔
49
      currentLayer(NULL), mElevationFromAttribute(false),
2✔
50
      mElevationAttributeIndex(-1), mCurrentElevationValue(0.0)
2✔
51

52
{
53
  /*private variables*/
54
  pszLayerDescMetadata = NULL;
55
  papszLayerIncludeItems = NULL;
56
  nIncludeItems = 0;
57
  papszLayerExcludeItems = NULL;
58
  nExcludeItems = 0;
59
  pszLayerNameAttributeMetadata =
60
      NULL; /*metadata to use for a name for each feature*/
61

62
  LineStyle = NULL;
63
  numLineStyle = 0;
64

65
  xmlNodePtr styleNode;
66
  xmlNodePtr listStyleNode;
67
  /*  Create document.*/
68
  XmlDoc = xmlNewDoc(BAD_CAST "1.0");
2✔
69

70
  xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "kml");
2✔
71

72
  /*  Name spaces*/
73
  xmlSetNs(rootNode,
2✔
74
           xmlNewNs(rootNode, BAD_CAST "http://www.opengis.net/kml/2.2", NULL));
75

76
  xmlDocSetRootElement(XmlDoc, rootNode);
2✔
77

78
  DocNode = xmlNewChild(rootNode, NULL, BAD_CAST "Document", NULL);
2✔
79

80
  styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
2✔
81
  xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_check");
2✔
82
  listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
2✔
83
  xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "check");
2✔
84

85
  styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
2✔
86
  xmlNewProp(styleNode, BAD_CAST "id",
2✔
87
             BAD_CAST "LayerFolder_checkHideChildren");
88
  listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
2✔
89
  xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType",
2✔
90
              BAD_CAST "checkHideChildren");
91

92
  styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
2✔
93
  xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_checkOffOnly");
2✔
94
  listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
2✔
95
  xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType",
2✔
96
              BAD_CAST "checkOffOnly");
97

98
  styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
2✔
99
  xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_radioFolder");
2✔
100
  listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
2✔
101
  xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType",
2✔
102
              BAD_CAST "radioFolder");
103

104
  StyleHashTable = msCreateHashTable();
2✔
105
}
2✔
106

107
KmlRenderer::~KmlRenderer() {
4✔
108
  if (XmlDoc)
2✔
109
    xmlFreeDoc(XmlDoc);
2✔
110

111
  if (StyleHashTable)
2✔
112
    msFreeHashTable(StyleHashTable);
2✔
113

114
  if (LineStyle)
2✔
115
    msFree(LineStyle);
1✔
116

117
  xmlCleanupParser();
2✔
118
}
4✔
119

120
imageObj *KmlRenderer::createImage(int, int, outputFormatObj *, colorObj *) {
×
121
  return NULL;
×
122
}
123

124
int KmlRenderer::saveImage(imageObj *, FILE *fp, outputFormatObj *format) {
2✔
125
  /* -------------------------------------------------------------------- */
126
  /*      Write out the document.                                         */
127
  /* -------------------------------------------------------------------- */
128

129
  int bufSize = 0;
2✔
130
  xmlChar *buf = NULL;
2✔
131
  msIOContext *context = NULL;
132
  int chunkSize = 4096;
133
  int bZip = MS_FALSE;
134

135
  if (msIO_needBinaryStdout() == MS_FAILURE)
2✔
136
    return MS_FAILURE;
137

138
  xmlDocDumpFormatMemoryEnc(XmlDoc, &buf, &bufSize, "UTF-8", 1);
2✔
139

140
  if (format && format->driver && strcasecmp(format->driver, "kmz") == 0) {
2✔
141
    bZip = MS_TRUE;
142
  }
143

144
  if (bZip) {
145
    VSILFILE *fpZip;
146
    int bytes_read;
147
    char buffer[1024];
148
    char *zip_filename = NULL;
149
    void *hZip = NULL;
150

151
    zip_filename = msTmpFile(NULL, NULL, "/vsimem/kmlzip/", "kmz");
×
152
    hZip = CPLCreateZip(zip_filename, NULL);
×
153
    CPLCreateFileInZip(hZip, "mapserver.kml", NULL);
×
154
    for (int i = 0; i < bufSize; i += chunkSize) {
×
155
      int size = chunkSize;
156
      if (i + size > bufSize)
×
157
        size = bufSize - i;
×
158
      CPLWriteFileInZip(hZip, buf + i, size);
×
159
    }
160
    CPLCloseFileInZip(hZip);
×
161
    CPLCloseZip(hZip);
×
162

163
    context = msIO_getHandler(fp);
×
164
    fpZip = VSIFOpenL(zip_filename, "r");
×
165

166
    while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fpZip)) > 0) {
×
167
      if (context)
×
168
        msIO_contextWrite(context, buffer, bytes_read);
×
169
      else
170
        msIO_fwrite(buffer, 1, bytes_read, fp);
×
171
    }
172
    VSIFCloseL(fpZip);
×
173
    msFree(zip_filename);
×
174
    xmlFree(buf);
×
175
    return (MS_SUCCESS);
176
  }
177

178
  context = msIO_getHandler(fp);
2✔
179

180
  for (int i = 0; i < bufSize; i += chunkSize) {
19✔
181
    int size = chunkSize;
182
    if (i + size > bufSize)
17✔
183
      size = bufSize - i;
2✔
184

185
    if (context)
17✔
186
      msIO_contextWrite(context, buf + i, size);
17✔
187
    else
188
      msIO_fwrite(buf + i, 1, size, fp);
×
189
  }
190

191
  xmlFree(buf);
2✔
192

193
  return (MS_SUCCESS);
194
}
195

196
/************************************************************************/
197
/*                               processLayer                           */
198
/*                                                                      */
199
/*      Set parameters that make sense to a kml output.                 */
200
/************************************************************************/
201
void KmlRenderer::processLayer(layerObj *layer, outputFormatObj *format) {
2✔
202
  int i;
203
  const char *asRaster = NULL;
204
  int nMaxFeatures = -1;
205
  const char *pszTmp;
206
  char szTmp[10];
207

208
  if (!layer)
2✔
209
    return;
×
210

211
  /*turn of labelcache*/
212
  layer->labelcache = MS_OFF;
2✔
213

214
  /*if there are labels we want the coordinates to
215
    be the center of the element.*/
216
  for (i = 0; i < layer->numclasses; i++)
4✔
217
    if (layer->_class[i]->numlabels > 0)
2✔
218
      layer->_class[i]->labels[0]->position = MS_XY;
1✔
219

220
  /*we do not want to draw multiple styles.
221
    the new rendering architecture does not allow
222
    to know if we are dealing with a multi-style.
223
    So here we remove all styles beside the first one*/
224

225
  for (i = 0; i < layer->numclasses; i++) {
4✔
226
    while (layer->_class[i]->numstyles > 1)
2✔
227
      msDeleteStyle(layer->_class[i], layer->_class[i]->numstyles - 1);
×
228
  }
229

230
  /*if layer has a metadata KML_OUTPUTASRASTER set to true, add a processing
231
    directive to use an agg driver*/
232
  asRaster = msLookupHashTable(&layer->metadata, "kml_outputasraster");
2✔
233
  if (!asRaster)
2✔
234
    asRaster =
235
        msLookupHashTable(&(layer->map->web.metadata), "kml_outputasraster");
2✔
236
  if (asRaster &&
2✔
237
      (strcasecmp(asRaster, "true") == 0 || strcasecmp(asRaster, "yes") == 0))
×
238
    msLayerAddProcessing(layer, "RENDERER=png24");
×
239

240
  /*set a maxfeaturestodraw, if not already set*/
241

242
  pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
2✔
243
  if (pszTmp)
2✔
244
    nMaxFeatures = atoi(pszTmp);
245
  else {
246
    pszTmp = msLookupHashTable(&layer->map->web.metadata, "maxfeaturestodraw");
2✔
247
    if (pszTmp)
2✔
248
      nMaxFeatures = atoi(pszTmp);
249
  }
250
  if (nMaxFeatures < 0 && format)
2✔
251
    nMaxFeatures =
252
        atoi(msGetOutputFormatOption(format, "maxfeaturestodraw", "-1"));
2✔
253

254
  if (nMaxFeatures < 0 && format) {
2✔
255
    snprintf(szTmp, sizeof(szTmp), "%d", KML_MAXFEATURES_TODRAW);
256
    msSetOutputFormatOption(format, "maxfeaturestodraw", szTmp);
2✔
257
  }
258
}
259

260
/************************************************************************/
261
/*                               getLayerName                           */
262
/*                                                                      */
263
/*      Internal utility function to build name used for the layer.     */
264
/************************************************************************/
265
char *KmlRenderer::getLayerName(layerObj *layer) {
101✔
266
  char stmp[20];
267
  const char *name = NULL;
268
  ;
269

270
  if (!layer)
101✔
271
    return NULL;
272

273
  name = msLookupHashTable(&layer->metadata, "ows_name");
101✔
274
  if (name && strlen(name) > 0)
101✔
275
    return msStrdup(name);
×
276

277
  if (layer->name && strlen(layer->name) > 0)
101✔
278
    return msStrdup(layer->name);
101✔
279

280
  sprintf(stmp, "Layer%d", layer->index);
×
281
  return msStrdup(stmp);
×
282
}
283

284
const char *KmlRenderer::getAliasName(layerObj *lp, char *pszItemName,
×
285
                                      const char *namespaces) {
286
  const char *pszAlias = NULL;
287
  if (lp && pszItemName && strlen(pszItemName) > 0) {
×
288
    char szTmp[256];
289
    snprintf(szTmp, sizeof(szTmp), "%s_alias", pszItemName);
290
    pszAlias = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
×
291
  }
292
  return pszAlias;
×
293
}
294

295
int KmlRenderer::startNewLayer(imageObj *img, layerObj *layer) {
2✔
296
  char *layerName = NULL;
297
  const char *value = NULL;
298

299
  LayerNode = xmlNewNode(NULL, BAD_CAST "Folder");
2✔
300

301
  layerName = getLayerName(layer);
2✔
302
  xmlNewChild(LayerNode, NULL, BAD_CAST "name", BAD_CAST layerName);
2✔
303
  msFree(layerName);
2✔
304

305
  const char *layerVisibility = layer->status != MS_OFF ? "1" : "0";
2✔
306
  xmlNewChild(LayerNode, NULL, BAD_CAST "visibility", BAD_CAST layerVisibility);
2✔
307

308
  const char *layerDsiplayFolder =
309
      msLookupHashTable(&(layer->metadata), "kml_folder_display");
2✔
310
  if (layerDsiplayFolder == NULL)
2✔
311
    layerDsiplayFolder =
312
        msLookupHashTable(&(layer->map->web.metadata), "kml_folder_display");
2✔
313
  if (!layerDsiplayFolder || strlen(layerDsiplayFolder) <= 0) {
2✔
314
    xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
2✔
315
                BAD_CAST "#LayerFolder_check");
316
  }
317

318
  else {
319
    if (strcasecmp(layerDsiplayFolder, "checkHideChildren") == 0)
×
320
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
321
                  BAD_CAST "#LayerFolder_checkHideChildren");
322
    else if (strcasecmp(layerDsiplayFolder, "checkOffOnly") == 0)
×
323
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
324
                  BAD_CAST "#LayerFolder_checkOffOnly");
325
    else if (strcasecmp(layerDsiplayFolder, "radioFolder") == 0)
×
326
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
327
                  BAD_CAST "#LayerFolder_radioFolder");
328
    else
329
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
330
                  BAD_CAST "#LayerFolder_check");
331
  }
332

333
  /*Init few things on the first layer*/
334
  if (FirstLayer) {
2✔
335
    FirstLayer = MS_FALSE;
2✔
336
    map = layer->map;
2✔
337

338
    if (layer->map->mappath)
2✔
339
      snprintf(MapPath, sizeof(MapPath), "%s", layer->map->mappath);
2✔
340

341
    /*First rendered layer - check mapfile projection*/
342
    checkProjection(layer->map);
2✔
343

344
    /*check for image path and image url*/
345
    if (layer->map->debug &&
2✔
346
        (layer->map->web.imageurl == NULL || layer->map->web.imagepath == NULL))
×
347
      msDebug("KmlRenderer::startNewLayer: imagepath and imageurl should be "
×
348
              "set in the web object\n");
349

350
    /*map rect for ground overlay*/
351
    MapExtent = layer->map->extent;
2✔
352
    MapCellsize = layer->map->cellsize;
2✔
353
    BgColor = layer->map->imagecolor;
2✔
354

355
    xmlNewChild(DocNode, NULL, BAD_CAST "name", BAD_CAST layer->map->name);
2✔
356
    aggFormat = msSelectOutputFormat(layer->map, "png24");
2✔
357
    aggFormat->transparent = MS_TRUE;
2✔
358
  }
359

360
  currentLayer = layer;
2✔
361

362
  if (!msLayerIsOpen(layer)) {
2✔
363
    if (msLayerOpen(layer) != MS_SUCCESS) {
2✔
364
      msSetError(MS_MISCERR, "msLayerOpen failed",
×
365
                 "KmlRenderer::startNewLayer");
366
      return MS_FAILURE;
×
367
    }
368
  }
369

370
  /*pre process the layer to set things that make sense for kml output*/
371
  if (img)
2✔
372
    processLayer(layer, img->format);
2✔
373
  else
374
    processLayer(layer, NULL);
×
375

376
  if (msLookupHashTable(&layer->metadata, "kml_description"))
2✔
377
    pszLayerDescMetadata =
×
378
        msLookupHashTable(&layer->metadata, "kml_description");
×
379
  else if (msLookupHashTable(&layer->metadata, "ows_description"))
2✔
380
    pszLayerDescMetadata =
×
381
        msLookupHashTable(&layer->metadata, "ows_description");
×
382

383
  value = msLookupHashTable(&layer->metadata, "kml_include_items");
2✔
384
  if (!value)
2✔
385
    value = msLookupHashTable(&layer->metadata, "ows_include_items");
2✔
386
  if (value)
2✔
387
    papszLayerIncludeItems = msStringSplit(value, ',', &nIncludeItems);
×
388

389
  value = msLookupHashTable(&layer->metadata, "kml_exclude_items");
2✔
390
  if (!value)
2✔
391
    value = msLookupHashTable(&layer->metadata, "ows_exclude_items");
2✔
392
  if (value)
2✔
393
    papszLayerExcludeItems = msStringSplit(value, ',', &nExcludeItems);
×
394

395
  if (msLookupHashTable(&layer->metadata, "kml_name_item"))
2✔
396
    pszLayerNameAttributeMetadata =
×
397
        msLookupHashTable(&layer->metadata, "kml_name_item");
×
398

399
  /*get all attributes*/
400
  if (msLayerWhichItems(layer, MS_TRUE, NULL) != MS_SUCCESS) {
2✔
401
    return MS_FAILURE;
402
  }
403

404
  NumItems = layer->numitems;
2✔
405
  if (NumItems) {
2✔
406
    Items = (char **)msSmallCalloc(NumItems, sizeof(char *));
2✔
407
    for (int i = 0; i < NumItems; i++)
29✔
408
      Items[i] = msStrdup(layer->items[i]);
27✔
409
  }
410

411
  const char *elevationAttribute =
412
      msLookupHashTable(&layer->metadata, "kml_elevation_attribute");
2✔
413
  if (elevationAttribute) {
2✔
414
    mElevationFromAttribute = true;
×
415
    for (int i = 0; i < layer->numitems; ++i) {
×
416
      if (strcasecmp(layer->items[i], elevationAttribute) == 0) {
×
417
        mElevationAttributeIndex = i;
×
418
      }
419
    }
420
  }
421

422
  setupRenderingParams(&layer->metadata);
2✔
423
  return MS_SUCCESS;
2✔
424
}
425

426
int KmlRenderer::closeNewLayer(imageObj *, layerObj *) {
2✔
427
  flushPlacemark();
2✔
428

429
  xmlAddChild(DocNode, LayerNode);
2✔
430

431
  if (Items) {
2✔
432
    msFreeCharArray(Items, NumItems);
2✔
433
    Items = NULL;
2✔
434
    NumItems = 0;
2✔
435
  }
436

437
  if (pszLayerDescMetadata)
2✔
438
    pszLayerDescMetadata = NULL;
×
439
  if (pszLayerNameAttributeMetadata)
2✔
440
    pszLayerNameAttributeMetadata = NULL;
×
441

442
  if (papszLayerIncludeItems && nIncludeItems > 0)
2✔
443
    msFreeCharArray(papszLayerIncludeItems, nIncludeItems);
×
444
  papszLayerIncludeItems = NULL;
2✔
445

446
  if (papszLayerExcludeItems && nExcludeItems > 0)
2✔
447
    msFreeCharArray(papszLayerExcludeItems, nExcludeItems);
×
448
  papszLayerExcludeItems = NULL;
2✔
449

450
  return MS_SUCCESS;
2✔
451
}
452

453
int KmlRenderer::mergeRasterBuffer(imageObj *image, rasterBufferObj *rb) {
×
454
  assert(rb && rb->type == MS_BUFFER_BYTE_RGBA);
455
  char *tmpFileName = NULL;
456
  char *tmpUrl = NULL;
457
  FILE *tmpFile = NULL;
458

459
  tmpFileName = msTmpFile(NULL, MapPath, image->imagepath, "png");
×
460
  tmpFile = fopen(tmpFileName, "wb");
×
461
  if (tmpFile) {
×
462

463
    if (!aggFormat->vtable)
×
464
      msInitializeRendererVTable(aggFormat);
×
465

466
    msSaveRasterBuffer(map, rb, tmpFile, aggFormat);
×
467
    tmpUrl = msStrdup(image->imageurl);
×
468
    tmpUrl = msStringConcatenate(tmpUrl, (char *)(msGetBasename(tmpFileName)));
×
469
    tmpUrl = msStringConcatenate(tmpUrl, ".png");
×
470

471
    createGroundOverlayNode(LayerNode, tmpUrl, currentLayer);
×
472
    msFree(tmpFileName);
×
473
    msFree(tmpUrl);
×
474
    fclose(tmpFile);
×
475
    return MS_SUCCESS;
×
476
  } else {
477
    msSetError(MS_IOERR, "Failed to create file for kml overlay",
×
478
               "KmlRenderer::mergeRasterBuffer()");
479
    return MS_FAILURE;
×
480
  }
481
}
482

483
void KmlRenderer::setupRenderingParams(hashTableObj *layerMetadata) {
2✔
484
  AltitudeMode = 0;
2✔
485
  Extrude = 0;
2✔
486
  Tessellate = 0;
2✔
487

488
  const char *altitudeModeVal =
489
      msLookupHashTable(layerMetadata, "kml_altitudeMode");
2✔
490
  if (altitudeModeVal) {
2✔
491
    if (strcasecmp(altitudeModeVal, "absolute") == 0)
×
492
      AltitudeMode = absolute;
×
493
    else if (strcasecmp(altitudeModeVal, "relativeToGround") == 0)
×
494
      AltitudeMode = relativeToGround;
×
495
    else if (strcasecmp(altitudeModeVal, "clampToGround") == 0)
×
496
      AltitudeMode = clampToGround;
×
497
  }
498

499
  const char *extrudeVal = msLookupHashTable(layerMetadata, "kml_extrude");
2✔
500
  if (altitudeModeVal) {
2✔
501
    Extrude = atoi(extrudeVal);
×
502
  }
503

504
  const char *tessellateVal =
505
      msLookupHashTable(layerMetadata, "kml_tessellate");
2✔
506
  if (tessellateVal) {
2✔
507
    Tessellate = atoi(tessellateVal);
×
508
  }
509
}
2✔
510

511
int KmlRenderer::checkProjection(mapObj *map) {
2✔
512
  projectionObj *projection = &map->projection;
2✔
513
  if (projection && projection->numargs > 0 &&
4✔
514
      msProjIsGeographicCRS(projection)) {
2✔
515
    return MS_SUCCESS;
516
  } else {
517
    char epsg_string[100];
518
    rectObj sRect;
519
    projectionObj out;
520

521
    /* for layer the do not have any projection set, set them with the current
522
       map projection*/
523
    if (projection && projection->numargs > 0) {
×
524
      layerObj *lp = NULL;
525
      int i = 0;
526
      char *pszMapProjectString = msGetProjectionString(projection);
×
527
      if (pszMapProjectString) {
×
528
        for (i = 0; i < map->numlayers; i++) {
×
529
          lp = GET_LAYER(map, i);
×
530
          if (lp->projection.numargs == 0 && lp->transform == MS_TRUE) {
×
531
            msFreeProjection(&lp->projection);
×
532
            msLoadProjectionString(&lp->projection, pszMapProjectString);
×
533
          }
534
        }
535
        msFree(pszMapProjectString);
×
536
      }
537
    }
538
    strcpy(epsg_string, "epsg:4326");
539
    msInitProjection(&out);
×
540
    msProjectionInheritContextFrom(&out, projection);
×
541
    msLoadProjectionString(&out, epsg_string);
×
542

543
    sRect = map->extent;
×
544
    msProjectRect(projection, &out, &sRect);
×
545
    msFreeProjectionExceptContext(projection);
×
546
    msLoadProjectionString(projection, epsg_string);
×
547

548
    /*change also units and extents*/
549
    map->extent = sRect;
×
550
    map->units = MS_DD;
×
551

552
    if (map->debug)
×
553
      msDebug("KmlRenderer::checkProjection: Mapfile projection set to "
×
554
              "epsg:4326\n");
555

556
    return MS_SUCCESS;
557
  }
558
}
559

560
xmlNodePtr KmlRenderer::createPlacemarkNode(xmlNodePtr parentNode,
99✔
561
                                            char *styleUrl) {
562
  xmlNodePtr placemarkNode =
563
      xmlNewChild(parentNode, NULL, BAD_CAST "Placemark", NULL);
99✔
564
  /*always add a name. It will be replaced by a text value if available*/
565
  char tmpid[100];
566
  char *stmp = NULL, *layerName = NULL;
567
  if (CurrentShapeName && strlen(CurrentShapeName) > 0) {
99✔
568
    xmlNewChild(placemarkNode, NULL, BAD_CAST "name",
×
569
                BAD_CAST CurrentShapeName);
570
  } else {
571
    sprintf(tmpid, ".%d", CurrentShapeIndex);
99✔
572
    layerName = getLayerName(currentLayer);
99✔
573
    stmp = msStringConcatenate(stmp, layerName);
99✔
574
    stmp = msStringConcatenate(stmp, tmpid);
99✔
575
    xmlNewChild(placemarkNode, NULL, BAD_CAST "name", BAD_CAST stmp);
99✔
576
  }
577
  msFree(layerName);
99✔
578
  msFree(stmp);
99✔
579
  if (styleUrl)
99✔
580
    xmlNewChild(placemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
×
581

582
  return placemarkNode;
99✔
583
}
584

585
void KmlRenderer::renderLine(imageObj *, shapeObj *p, strokeStyleObj *style) {
72✔
586
  if (p->numlines == 0)
72✔
587
    return;
588

589
  if (PlacemarkNode == NULL)
72✔
590
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
72✔
591

592
  if (!PlacemarkNode)
72✔
593
    return;
594

595
  addLineStyleToList(style);
72✔
596
  SymbologyFlag[Line] = 1;
72✔
597

598
  /*p->index > CurrentDrawnShapeIndexneed to be reviewd. Added since the height
599
    level code caches shapes when rendering lines*/
600
  if (CurrentDrawnShapeIndex == -1 || p->index > CurrentDrawnShapeIndex) {
72✔
601
    xmlNodePtr geomNode = getGeomParentNode("LineString");
72✔
602
    addAddRenderingSpecifications(geomNode);
72✔
603
    addCoordsNode(geomNode, p->line[0].point, p->line[0].numpoints);
72✔
604

605
    /* more than one line => MultiGeometry*/
606
    if (p->numlines > 1) {
72✔
607
      geomNode = getGeomParentNode("LineString"); // returns MultiGeom Node
×
608
      for (int i = 1; i < p->numlines; i++) {
×
609
        xmlNodePtr lineStringNode =
610
            xmlNewChild(geomNode, NULL, BAD_CAST "LineString", NULL);
×
611
        addAddRenderingSpecifications(lineStringNode);
×
612
        addCoordsNode(lineStringNode, p->line[i].point, p->line[i].numpoints);
×
613
      }
614
    }
615

616
    CurrentDrawnShapeIndex = p->index;
72✔
617
  }
618
}
619

620
void KmlRenderer::renderPolygon(imageObj *, shapeObj *p, colorObj *color) {
×
621
  if (PlacemarkNode == NULL)
×
622
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
×
623

624
  if (!PlacemarkNode)
×
625
    return;
626

627
  memcpy(&PolygonColor, color, sizeof(colorObj));
×
628
  SymbologyFlag[Polygon] = 1;
×
629

630
  if (p->index != CurrentDrawnShapeIndex) {
×
631

632
    xmlNodePtr geomParentNode = getGeomParentNode("Polygon");
×
633

634
    for (int i = 0; i < p->numlines; i++) {
×
635
      xmlNodePtr bdryNode = NULL;
636

637
      if (i == 0) /* __TODO__ check ring order*/
×
638
        bdryNode =
639
            xmlNewChild(geomParentNode, NULL, BAD_CAST "outerBoundaryIs", NULL);
×
640
      else
641
        bdryNode =
642
            xmlNewChild(geomParentNode, NULL, BAD_CAST "innerBoundaryIs", NULL);
×
643

644
      xmlNodePtr ringNode =
645
          xmlNewChild(bdryNode, NULL, BAD_CAST "LinearRing", NULL);
×
646
      addAddRenderingSpecifications(ringNode);
×
647
      addCoordsNode(ringNode, p->line[i].point, p->line[i].numpoints);
×
648
    }
649

650
    CurrentDrawnShapeIndex = p->index;
×
651
  }
652
}
653

654
void KmlRenderer::addCoordsNode(xmlNodePtr parentNode, pointObj *pts,
99✔
655
                                int numPts) {
656
  char lineBuf[128];
657

658
  xmlNodePtr coordsNode =
659
      xmlNewChild(parentNode, NULL, BAD_CAST "coordinates", NULL);
99✔
660
  xmlNodeAddContent(coordsNode, BAD_CAST "\n");
99✔
661

662
  for (int i = 0; i < numPts; i++) {
1,876✔
663
    if (mElevationFromAttribute) {
1,777✔
664
      sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y,
×
665
              mCurrentElevationValue);
666
    } else if (AltitudeMode == relativeToGround || AltitudeMode == absolute) {
1,777✔
667
      sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, pts[i].z);
×
668
    } else
669
      sprintf(lineBuf, "\t%.8f,%.8f\n", pts[i].x, pts[i].y);
1,777✔
670

671
    xmlNodeAddContent(coordsNode, BAD_CAST lineBuf);
1,777✔
672
  }
673
  xmlNodeAddContent(coordsNode, BAD_CAST "\t");
99✔
674
}
99✔
675

676
void KmlRenderer::renderGlyphs(imageObj *, const textSymbolObj *ts,
27✔
677
                               colorObj *clr, colorObj * /*oc*/, int /*ow*/) {
678
  if (ts->annotext == NULL || ts->textpath->numglyphs == 0)
27✔
679
    return;
×
680

681
  if (PlacemarkNode == NULL)
27✔
682
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
27✔
683

684
  if (!PlacemarkNode)
27✔
685
    return;
686

687
  memcpy(&LabelColor, clr, sizeof(colorObj));
27✔
688
  SymbologyFlag[Label] = 1;
27✔
689

690
  /*there is alaws a default name (layer.shapeid). Replace it*/
691
  for (xmlNodePtr node = PlacemarkNode->children; node; node = node->next) {
27✔
692
    if (node->type != XML_ELEMENT_NODE)
27✔
693
      continue;
×
694

695
    if (strcmp((char *)node->name, "name") == 0) {
27✔
696
      xmlNodeSetContent(node, BAD_CAST ts->annotext);
27✔
697
      break;
698
    }
699
  }
700

701
  /*xmlNewChild(PlacemarkNode, NULL, BAD_CAST "name", BAD_CAST text);*/
702

703
  xmlNodePtr geomNode = getGeomParentNode("Point");
27✔
704
  addAddRenderingSpecifications(geomNode);
27✔
705

706
  pointObj pt;
707
  pt.x = ts->textpath->glyphs[0].pnt.x;
27✔
708
  pt.y = ts->textpath->glyphs[0].pnt.y;
27✔
709
  pt.z = 0.0;
27✔
710
  addCoordsNode(geomNode, &pt, 1);
27✔
711
}
712

713
void KmlRenderer::addAddRenderingSpecifications(xmlNodePtr node) {
99✔
714
  /*
715
    <extrude>0</extrude>                   <!-- boolean -->
716
    <tessellate>0</tessellate>             <!-- boolean -->
717
    <altitudeMode>clampToGround</altitudeMode>
718
  */
719

720
  if (Extrude)
99✔
721
    xmlNewChild(node, NULL, BAD_CAST "extrude", BAD_CAST "1");
×
722

723
  if (Tessellate)
99✔
724
    xmlNewChild(node, NULL, BAD_CAST "tessellate", BAD_CAST "1");
×
725

726
  if (AltitudeMode == absolute)
99✔
727
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "absolute");
×
728
  else if (AltitudeMode == relativeToGround)
99✔
729
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode",
×
730
                BAD_CAST "relativeToGround");
731
  else if (AltitudeMode == clampToGround)
99✔
732
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "clampToGround");
×
733
}
99✔
734

735
imageObj *agg2CreateImage(int width, int height, outputFormatObj *format,
736
                          colorObj *bg);
737

738
int KmlRenderer::createIconImage(char *fileName, symbolObj *symbol,
×
739
                                 symbolStyleObj *symstyle) {
740
  pointObj p;
741
  int status;
742

743
  imageObj *tmpImg = NULL;
744

745
  tmpImg =
746
      agg2CreateImage((int)(symbol->sizex * symstyle->scale),
×
747
                      (int)(symbol->sizey * symstyle->scale), aggFormat, NULL);
×
748
  tmpImg->format = aggFormat;
×
749
  if (!aggFormat->vtable)
×
750
    msInitializeRendererVTable(aggFormat);
×
751

752
  p.x = symbol->sizex * symstyle->scale / 2;
×
753
  p.y = symbol->sizey * symstyle->scale / 2;
×
754
  p.z = 0.0;
×
755

756
  status = msDrawMarkerSymbol(map, tmpImg, &p, symstyle->style, 1);
×
757
  if (status != MS_SUCCESS)
×
758
    return status;
759

760
  return msSaveImage(map, tmpImg, fileName);
×
761
}
762

763
void KmlRenderer::renderSymbol(imageObj *img, double x, double y,
×
764
                               symbolObj *symbol, symbolStyleObj *style) {
765
  if (PlacemarkNode == NULL)
×
766
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
×
767

768
  if (!PlacemarkNode)
×
769
    return;
×
770

771
  snprintf(SymbolUrl, sizeof(SymbolUrl), "%s",
×
772
           lookupSymbolUrl(img, symbol, style));
773
  SymbologyFlag[Symbol] = 1;
×
774

775
  xmlNodePtr geomNode = getGeomParentNode("Point");
×
776
  addAddRenderingSpecifications(geomNode);
×
777

778
  pointObj pt;
779
  pt.x = x;
×
780
  pt.y = y;
×
781
  pt.z = 0.0;
×
782
  addCoordsNode(geomNode, &pt, 1);
×
783
}
784

785
void KmlRenderer::renderPixmapSymbol(imageObj *img, double x, double y,
×
786
                                     symbolObj *symbol, symbolStyleObj *style) {
787
  renderSymbol(img, x, y, symbol, style);
×
788
}
×
789

790
void KmlRenderer::renderVectorSymbol(imageObj *img, double x, double y,
×
791
                                     symbolObj *symbol, symbolStyleObj *style) {
792
  renderSymbol(img, x, y, symbol, style);
×
793
}
×
794

795
void KmlRenderer::renderEllipseSymbol(imageObj *img, double x, double y,
×
796
                                      symbolObj *symbol,
797
                                      symbolStyleObj *style) {
798
  renderSymbol(img, x, y, symbol, style);
×
799
}
×
800

801
void KmlRenderer::renderTruetypeSymbol(imageObj *img, double x, double y,
×
802
                                       symbolObj *symbol,
803
                                       symbolStyleObj *style) {
804
  renderSymbol(img, x, y, symbol, style);
×
805
}
×
806

807
xmlNodePtr KmlRenderer::createGroundOverlayNode(xmlNodePtr parentNode,
×
808
                                                char *imageHref,
809
                                                layerObj *layer) {
810
  /*
811
    <?xml version="1.0" encoding="UTF-8"?>
812
    <kml xmlns="http://www.opengis.net/kml/2.2">
813
    <GroundOverlay>
814
    <name>GroundOverlay.kml</name>
815
    <color>7fffffff</color>
816
    <drawOrder>1</drawOrder>
817
    <Icon>
818
    <href>http://www.google.com/intl/en/images/logo.gif</href>
819
    <refreshMode>onInterval</refreshMode>
820
    <refreshInterval>86400</refreshInterval>
821
    <viewBoundScale>0.75</viewBoundScale>
822
    </Icon>
823
    <LatLonBox>
824
    <north>37.83234</north>
825
    <south>37.832122</south>
826
    <east>-122.373033</east>
827
    <west>-122.373724</west>
828
    <rotation>45</rotation>
829
    </LatLonBox>
830
    </GroundOverlay>
831
    </kml>
832
  */
833
  char layerHexColor[32];
834
  xmlNodePtr groundOverlayNode =
835
      xmlNewChild(parentNode, NULL, BAD_CAST "GroundOverlay", NULL);
×
836
  char *layerName = getLayerName(layer);
×
837
  xmlNewChild(groundOverlayNode, NULL, BAD_CAST "name", BAD_CAST layerName);
×
838
  if (layer->compositer && layer->compositer->opacity > 0 &&
×
839
      layer->compositer->opacity < 100) {
840
    sprintf(layerHexColor, "%02xffffff",
×
841
            (unsigned int)MS_NINT(layer->compositer->opacity * 2.55));
×
842
    xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color",
×
843
                BAD_CAST layerHexColor);
844
  } else
845
    xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color", BAD_CAST "ffffffff");
×
846
  char stmp[20];
847
  sprintf(stmp, "%d", layer->index);
×
848
  xmlNewChild(groundOverlayNode, NULL, BAD_CAST "drawOrder", BAD_CAST stmp);
×
849

850
  if (imageHref) {
×
851
    xmlNodePtr iconNode =
852
        xmlNewChild(groundOverlayNode, NULL, BAD_CAST "Icon", NULL);
×
853
    xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST imageHref);
×
854
  }
855

856
  char crdStr[64];
857
  rectObj mapextent;
858
  if (map->gt.need_geotransform == MS_TRUE)
×
859
    mapextent = currentLayer->map->saved_extent;
×
860
  else
861
    mapextent = currentLayer->map->extent;
×
862

863
  xmlNodePtr latLonBoxNode =
864
      xmlNewChild(groundOverlayNode, NULL, BAD_CAST "LatLonBox", NULL);
×
865
  sprintf(crdStr, "%.8f", mapextent.maxy);
866
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "north", BAD_CAST crdStr);
×
867

868
  sprintf(crdStr, "%.8f", mapextent.miny);
869
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "south", BAD_CAST crdStr);
×
870

871
  sprintf(crdStr, "%.8f", mapextent.minx);
872
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "west", BAD_CAST crdStr);
×
873

874
  sprintf(crdStr, "%.8f", mapextent.maxx);
875
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "east", BAD_CAST crdStr);
×
876

877
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "rotation", BAD_CAST "0.0");
×
878

879
  return groundOverlayNode;
×
880
}
881

882
void KmlRenderer::startShape(imageObj *, shapeObj *shape) {
100✔
883
  if (PlacemarkNode)
100✔
884
    flushPlacemark();
97✔
885

886
  CurrentShapeIndex = -1;
100✔
887
  CurrentDrawnShapeIndex = -1;
100✔
888
  CurrentShapeName = NULL;
100✔
889

890
  /*should be done at endshape but the plugin architecture does not call
891
   * endshape yet*/
892
  if (LineStyle) {
100✔
893
    msFree(LineStyle);
71✔
894

895
    LineStyle = NULL;
71✔
896
    numLineStyle = 0;
71✔
897
  }
898

899
  CurrentShapeIndex = shape->index;
100✔
900
  if (pszLayerNameAttributeMetadata) {
100✔
901
    for (int i = 0; i < currentLayer->numitems; i++) {
×
902
      if (strcasecmp(currentLayer->items[i], pszLayerNameAttributeMetadata) ==
×
903
              0 &&
×
904
          shape->values[i]) {
×
905
        CurrentShapeName = msStrdup(shape->values[i]);
×
906
        break;
×
907
      }
908
    }
909
  }
910
  PlacemarkNode = NULL;
100✔
911
  GeomNode = NULL;
100✔
912

913
  DescriptionNode = createDescriptionNode(shape);
100✔
914

915
  if (mElevationFromAttribute && shape->numvalues > mElevationAttributeIndex &&
100✔
916
      mElevationAttributeIndex >= 0 &&
×
917
      shape->values[mElevationAttributeIndex]) {
×
918
    mCurrentElevationValue = atof(shape->values[mElevationAttributeIndex]);
×
919
  }
920

921
  memset(SymbologyFlag, 0, NumSymbologyFlag);
100✔
922
}
100✔
923

924
void KmlRenderer::endShape(imageObj *, shapeObj *) {
100✔
925
  CurrentShapeIndex = -1;
100✔
926
  if (CurrentShapeName)
100✔
927
    msFree(CurrentShapeName);
×
928
  CurrentShapeName = NULL;
100✔
929
}
100✔
930

931
xmlNodePtr KmlRenderer::getGeomParentNode(const char *geomName) {
99✔
932
  /*we do not need a multi-geometry for point layers*/
933
  if (currentLayer->type != MS_LAYER_POINT &&
99✔
934
      currentLayer->type != MS_LAYER_ANNOTATION && GeomNode) {
72✔
935
    /*placemark geometry already defined, we need multigeometry node*/
936
    xmlNodePtr multiGeomNode = xmlNewNode(NULL, BAD_CAST "MultiGeometry");
×
937
    xmlAddChild(multiGeomNode, GeomNode);
×
938
    GeomNode = multiGeomNode;
×
939

940
    xmlNodePtr geomNode =
941
        xmlNewChild(multiGeomNode, NULL, BAD_CAST geomName, NULL);
×
942
    return geomNode;
×
943
  } else {
944
    GeomNode = xmlNewNode(NULL, BAD_CAST geomName);
99✔
945
    return GeomNode;
99✔
946
  }
947
}
948

949
const char *KmlRenderer::lookupSymbolUrl(imageObj *img, symbolObj *symbol,
×
950
                                         symbolStyleObj *symstyle) {
951
  char symbolHexColor[32];
952
  /*
953
      <Style id="randomColorIcon">
954
      <IconStyle>
955
      <color>ff00ff00</color>
956
      <colorMode>random</colorMode>
957
      <scale>1.1</scale>
958
      <Icon>
959
      <href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
960
      </Icon>
961
      </IconStyle>
962
      </Style>
963
  */
964

965
  sprintf(symbolHexColor, "%02x%02x%02x%02x", symstyle->style->color.alpha,
×
966
          symstyle->style->color.blue, symstyle->style->color.green,
967
          symstyle->style->color.red);
×
968
  snprintf(SymbolName, sizeof(SymbolName), "symbol_%s_%.1f_%s", symbol->name,
×
969
           symstyle->scale, symbolHexColor);
970

971
  const char *symbolUrl = msLookupHashTable(StyleHashTable, SymbolName);
×
972
  if (!symbolUrl) {
×
973
    char iconFileName[MS_MAXPATHLEN];
974
    char iconUrl[MS_MAXPATHLEN];
975

976
    if (img->imagepath) {
×
977
      char *tmpFileName = msTmpFile(NULL, MapPath, img->imagepath, "png");
×
978
      snprintf(iconFileName, sizeof(iconFileName), "%s", tmpFileName);
979
      msFree(tmpFileName);
×
980
    } else {
981
      sprintf(iconFileName, "symbol_%s_%.1f.%s", symbol->name, symstyle->scale,
×
982
              "png");
983
    }
984

985
    if (createIconImage(iconFileName, symbol, symstyle) != MS_SUCCESS) {
×
986
      msSetError(MS_IOERR, "Error creating icon file '%s'",
×
987
                 "KmlRenderer::lookupSymbolStyle()", iconFileName);
988
      return NULL;
×
989
    }
990

991
    if (img->imageurl)
×
992
      sprintf(iconUrl, "%s%s.%s", img->imageurl, msGetBasename(iconFileName),
×
993
              "png");
994
    else
995
      snprintf(iconUrl, sizeof(iconUrl), "%s", iconFileName);
996

997
    hashObj *hash = msInsertHashTable(StyleHashTable, SymbolName, iconUrl);
×
998
    symbolUrl = hash->data;
×
999
  }
1000

1001
  return symbolUrl;
1002
}
1003

1004
const char *KmlRenderer::lookupPlacemarkStyle() {
99✔
1005
  char lineHexColor[32];
1006
  char polygonHexColor[32];
1007
  char labelHexColor[32];
1008
  char *styleName = NULL;
1009

1010
  styleName = msStringConcatenate(styleName, "style");
99✔
1011

1012
  if (SymbologyFlag[Line]) {
99✔
1013
    /*
1014
      <LineStyle id="ID">
1015
      <!-- inherited from ColorStyle -->
1016
      <color>ffffffff</color>            <!-- kml:color -->
1017
      <colorMode>normal</colorMode>      <!-- colorModeEnum: normal or random
1018
      -->
1019

1020
      <!-- specific to LineStyle -->
1021
      <width>1</width>                   <!-- float -->
1022
      </LineStyle>
1023
    */
1024

1025
    for (int i = 0; i < numLineStyle; i++) {
144✔
1026
      if (currentLayer && currentLayer->compositer &&
72✔
1027
          currentLayer->compositer->opacity > 0 &&
×
1028
          currentLayer->compositer->opacity < 100 &&
×
1029
          LineStyle[i].color->alpha == 255)
×
1030
        LineStyle[i].color->alpha =
×
1031
            MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1032

1033
      sprintf(lineHexColor, "%02x%02x%02x%02x", LineStyle[i].color->alpha,
72✔
1034
              LineStyle[0].color->blue, LineStyle[i].color->green,
72✔
1035
              LineStyle[i].color->red);
72✔
1036

1037
      char lineStyleName[64];
1038
      snprintf(lineStyleName, sizeof(lineStyleName), "_line_%s_w%.1f",
72✔
1039
               lineHexColor, LineStyle[i].width);
72✔
1040
      styleName = msStringConcatenate(styleName, lineStyleName);
72✔
1041
    }
1042
  }
1043

1044
  if (SymbologyFlag[Polygon]) {
99✔
1045
    /*
1046
      <PolyStyle id="ID">
1047
      <!-- inherited from ColorStyle -->
1048
      <color>ffffffff</color>            <!-- kml:color -->
1049
      <colorMode>normal</colorMode>      <!-- kml:colorModeEnum: normal or
1050
      random -->
1051

1052
      <!-- specific to PolyStyle -->
1053
      <fill>1</fill>                     <!-- boolean -->
1054
      <outline>1</outline>               <!-- boolean -->
1055
      </PolyStyle>
1056
    */
1057

1058
    if (currentLayer && currentLayer->compositer &&
×
1059
        currentLayer->compositer->opacity > 0 &&
×
1060
        currentLayer->compositer->opacity < 100 && PolygonColor.alpha == 255)
×
1061
      PolygonColor.alpha = MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1062
    sprintf(polygonHexColor, "%02x%02x%02x%02x", PolygonColor.alpha,
×
1063
            PolygonColor.blue, PolygonColor.green, PolygonColor.red);
1064

1065
    char polygonStyleName[64];
1066
    sprintf(polygonStyleName, "_polygon_%s", polygonHexColor);
1067
    styleName = msStringConcatenate(styleName, polygonStyleName);
×
1068
  }
1069

1070
  if (SymbologyFlag[Label]) {
99✔
1071
    /*
1072
      <LabelStyle id="ID">
1073
      <!-- inherited from ColorStyle -->
1074
      <color>ffffffff</color>            <!-- kml:color -->
1075
      <colorMode>normal</colorMode>      <!-- kml:colorModeEnum: normal or
1076
      random -->
1077

1078
      <!-- specific to LabelStyle -->
1079
      <scale>1</scale>                   <!-- float -->
1080
      </LabelStyle>
1081
    */
1082

1083
    if (currentLayer && currentLayer->compositer &&
27✔
1084
        currentLayer->compositer->opacity > 0 &&
×
1085
        currentLayer->compositer->opacity < 100 && LabelColor.alpha == 255)
×
1086
      LabelColor.alpha = MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1087
    sprintf(labelHexColor, "%02x%02x%02x%02x", LabelColor.alpha,
27✔
1088
            LabelColor.blue, LabelColor.green, LabelColor.red);
1089

1090
    // __TODO__ add label scale
1091

1092
    char labelStyleName[64];
1093
    sprintf(labelStyleName, "_label_%s", labelHexColor);
1094
    styleName = msStringConcatenate(styleName, labelStyleName);
27✔
1095
  }
1096

1097
  if (SymbologyFlag[Symbol]) {
99✔
1098
    /*
1099
    <Style id="randomColorIcon">
1100
            <IconStyle>
1101
            <color>ff00ff00</color>
1102
            <colorMode>random</colorMode>
1103
            <scale>1.1</scale>
1104
            <Icon>
1105
            <href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
1106
            </Icon>
1107
            </IconStyle>
1108
    </Style>
1109
    */
1110

1111
    /* __TODO__ add label scale */
1112

1113
    styleName = msStringConcatenate(styleName, "_");
×
1114
    styleName = msStringConcatenate(styleName, SymbolName);
×
1115
  }
1116

1117
  const char *styleUrl = msLookupHashTable(StyleHashTable, styleName);
99✔
1118
  if (!styleUrl) {
99✔
1119
    char *styleValue = NULL;
1120
    styleValue = msStringConcatenate(styleValue, "#");
2✔
1121
    styleValue = msStringConcatenate(styleValue, styleName);
2✔
1122
    hashObj *hash = msInsertHashTable(StyleHashTable, styleName, styleValue);
2✔
1123
    styleUrl = hash->data;
2✔
1124
    msFree(styleValue);
2✔
1125

1126
    /* Insert new Style node into Document node*/
1127
    xmlNodePtr styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
2✔
1128
    xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST styleName);
2✔
1129

1130
    if (SymbologyFlag[Polygon]) {
2✔
1131
      xmlNodePtr polyStyleNode =
1132
          xmlNewChild(styleNode, NULL, BAD_CAST "PolyStyle", NULL);
×
1133
      xmlNewChild(polyStyleNode, NULL, BAD_CAST "color",
×
1134
                  BAD_CAST polygonHexColor);
1135
    }
1136

1137
    if (SymbologyFlag[Line]) {
2✔
1138
      for (int i = 0; i < numLineStyle; i++) {
2✔
1139
        xmlNodePtr lineStyleNode =
1140
            xmlNewChild(styleNode, NULL, BAD_CAST "LineStyle", NULL);
1✔
1141
        sprintf(lineHexColor, "%02x%02x%02x%02x", LineStyle[i].color->alpha,
1✔
1142
                LineStyle[i].color->blue, LineStyle[i].color->green,
1143
                LineStyle[i].color->red);
1✔
1144
        xmlNewChild(lineStyleNode, NULL, BAD_CAST "color",
1✔
1145
                    BAD_CAST lineHexColor);
1146

1147
        char width[16];
1148
        sprintf(width, "%.1f", LineStyle[i].width);
1✔
1149
        xmlNewChild(lineStyleNode, NULL, BAD_CAST "width", BAD_CAST width);
1✔
1150
      }
1151
    }
1152

1153
    if (SymbologyFlag[Symbol]) {
2✔
1154
      xmlNodePtr iconStyleNode =
1155
          xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
×
1156

1157
      xmlNodePtr iconNode =
1158
          xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
×
1159
      xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST SymbolUrl);
×
1160

1161
      /*char scale[16];
1162
        sprintf(scale, "%.1f", style->scale);
1163
        xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1164
    } else {
1165
      const char *value =
1166
          msLookupHashTable(&currentLayer->metadata, "kml_default_symbol_href");
2✔
1167
      if (value && strlen(value) > 0) {
2✔
1168
        xmlNodePtr iconStyleNode =
1169
            xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
×
1170

1171
        xmlNodePtr iconNode =
1172
            xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
×
1173
        xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST value);
×
1174
      }
1175
    }
1176

1177
    if (SymbologyFlag[Label]) {
2✔
1178
      xmlNodePtr labelStyleNode =
1179
          xmlNewChild(styleNode, NULL, BAD_CAST "LabelStyle", NULL);
1✔
1180
      xmlNewChild(labelStyleNode, NULL, BAD_CAST "color",
1✔
1181
                  BAD_CAST labelHexColor);
1182

1183
      /*char scale[16];
1184
        sprintf(scale, "%.1f", style->scale);
1185
        xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1186
    }
1187
  }
1188

1189
  if (styleName)
99✔
1190
    msFree(styleName);
99✔
1191

1192
  return styleUrl;
99✔
1193
}
1194

1195
void KmlRenderer::flushPlacemark() {
99✔
1196
  if (PlacemarkNode) {
99✔
1197
    const char *styleUrl = lookupPlacemarkStyle();
99✔
1198
    xmlNewChild(PlacemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
99✔
1199

1200
    if (DescriptionNode)
99✔
1201
      xmlAddChild(PlacemarkNode, DescriptionNode);
×
1202

1203
    if (GeomNode)
99✔
1204
      xmlAddChild(PlacemarkNode, GeomNode);
99✔
1205
  }
1206
}
99✔
1207

1208
xmlNodePtr KmlRenderer::createDescriptionNode(shapeObj *shape) {
100✔
1209
  /*
1210
    <description>
1211
    <![CDATA[
1212
    special characters here
1213
    ]]>
1214
    <description>
1215
  */
1216

1217
  /*description nodes for vector layers:
1218
    - if kml_description is set, use it
1219
    - if not, dump the attributes */
1220

1221
  if (pszLayerDescMetadata) {
100✔
1222
    char *pszTmp = NULL;
1223
    char *pszTmpDesc = NULL;
1224
    size_t bufferSize = 0;
1225
    pszTmpDesc = msStrdup(pszLayerDescMetadata);
×
1226

1227
    for (int i = 0; i < currentLayer->numitems; i++) {
×
1228
      bufferSize = strlen(currentLayer->items[i]) + 3;
×
1229
      pszTmp = (char *)msSmallMalloc(bufferSize);
×
1230
      snprintf(pszTmp, bufferSize, "%%%s%%", currentLayer->items[i]);
×
1231
      if (strcasestr(pszTmpDesc, pszTmp))
×
1232
        pszTmpDesc =
1233
            msCaseReplaceSubstring(pszTmpDesc, pszTmp, shape->values[i]);
×
1234
      msFree(pszTmp);
×
1235
    }
1236
    xmlNodePtr descriptionNode = xmlNewNode(NULL, BAD_CAST "description");
×
1237
    xmlNodeAddContent(descriptionNode, BAD_CAST pszTmpDesc);
×
1238
    msFree(pszTmpDesc);
×
1239
    return descriptionNode;
×
1240
  } else if ((papszLayerIncludeItems && nIncludeItems > 0) ||
100✔
1241
             (papszLayerExcludeItems && nExcludeItems > 0)) {
100✔
1242
    /* -------------------------------------------------------------------- */
1243
    /*      preferred way is to use the ExtendedData tag (#3728)            */
1244
    /*      http://code.google.com/apis/kml/documentation/extendeddata.html */
1245
    /* -------------------------------------------------------------------- */
1246

1247
    xmlNodePtr extendedDataNode = xmlNewNode(NULL, BAD_CAST "ExtendedData");
×
1248
    xmlNodePtr dataNode = NULL;
1249
    const char *pszAlias = NULL;
1250
    int bIncludeAll = MS_FALSE;
1251

1252
    if (papszLayerIncludeItems && nIncludeItems == 1 &&
×
1253
        strcasecmp(papszLayerIncludeItems[0], "all") == 0)
×
1254
      bIncludeAll = MS_TRUE;
1255

1256
    for (int i = 0; i < currentLayer->numitems; i++) {
×
1257
      int j = 0, k = 0;
1258

1259
      /*TODO optimize to calculate this only once per layer*/
1260
      for (j = 0; j < nIncludeItems; j++) {
×
1261
        if (strcasecmp(currentLayer->items[i], papszLayerIncludeItems[j]) == 0)
×
1262
          break;
1263
      }
1264
      if (j < nIncludeItems || bIncludeAll) {
×
1265
        if (papszLayerExcludeItems && nExcludeItems > 0) {
×
1266
          for (k = 0; k < nExcludeItems; k++) {
×
1267
            if (strcasecmp(currentLayer->items[i], papszLayerExcludeItems[k]) ==
×
1268
                0)
1269
              break;
1270
          }
1271
        }
1272
        if (nExcludeItems == 0 || k == nExcludeItems) {
×
1273
          dataNode = xmlNewNode(NULL, BAD_CAST "Data");
×
1274
          xmlNewProp(dataNode, BAD_CAST "name",
×
1275
                     BAD_CAST currentLayer->items[i]);
×
1276
          pszAlias = getAliasName(currentLayer, currentLayer->items[i], "GO");
×
1277
          if (pszAlias)
×
1278
            xmlNewChild(dataNode, NULL, BAD_CAST "displayName",
×
1279
                        BAD_CAST pszAlias);
1280
          else
1281
            xmlNewChild(dataNode, NULL, BAD_CAST "displayName",
×
1282
                        BAD_CAST currentLayer->items[i]);
×
1283
          if (shape->values[i] && strlen(shape->values[i]))
×
1284
            xmlNewChild(dataNode, NULL, BAD_CAST "value",
×
1285
                        BAD_CAST shape->values[i]);
1286
          else
1287
            xmlNewChild(dataNode, NULL, BAD_CAST "value", NULL);
×
1288
          xmlAddChild(extendedDataNode, dataNode);
×
1289
        }
1290
      }
1291
    }
1292

1293
    return extendedDataNode;
1294
  }
1295

1296
  return NULL;
1297
}
1298

1299
void KmlRenderer::addLineStyleToList(strokeStyleObj *style) {
72✔
1300
  /*actually this is not necessary. kml only uses the last LineStyle
1301
    so we should not bother keeping them all*/
1302
  int i = 0;
1303
  for (i = 0; i < numLineStyle; i++) {
72✔
1304
    if (style->width == LineStyle[i].width &&
×
1305
        LineStyle[i].color->alpha == style->color->alpha &&
×
1306
        LineStyle[i].color->red == style->color->red &&
×
1307
        LineStyle[i].color->green == style->color->green &&
×
1308
        LineStyle[i].color->blue == style->color->blue)
×
1309
      break;
1310
  }
1311
  if (i == numLineStyle) {
72✔
1312
    numLineStyle++;
72✔
1313
    if (LineStyle == NULL)
72✔
1314
      LineStyle = (strokeStyleObj *)msSmallMalloc(sizeof(strokeStyleObj));
72✔
1315
    else
1316
      LineStyle = (strokeStyleObj *)msSmallRealloc(
×
1317
          LineStyle, sizeof(strokeStyleObj) * numLineStyle);
×
1318

1319
    memcpy(&LineStyle[numLineStyle - 1], style, sizeof(strokeStyleObj));
72✔
1320
  }
1321
}
72✔
1322

1323
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc