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

geographika / mapserver / 17709373190

14 Sep 2025 09:32AM UTC coverage: 41.466% (+0.09%) from 41.375%
17709373190

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

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

54.02
/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
#include "cpl_string.h"
42

43
#define KML_MAXFEATURES_TODRAW 1000
44

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

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

63
  LineStyle = NULL;
64
  numLineStyle = 0;
65

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

71
  xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "kml");
3✔
72

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

77
  xmlDocSetRootElement(XmlDoc, rootNode);
3✔
78

79
  DocNode = xmlNewChild(rootNode, NULL, BAD_CAST "Document", NULL);
3✔
80

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

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

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

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

105
  StyleHashTable = msCreateHashTable();
3✔
106
}
3✔
107

108
KmlRenderer::~KmlRenderer() {
6✔
109
  if (XmlDoc)
3✔
110
    xmlFreeDoc(XmlDoc);
3✔
111

112
  if (StyleHashTable)
3✔
113
    msFreeHashTable(StyleHashTable);
3✔
114

115
  if (LineStyle)
3✔
116
    msFree(LineStyle);
2✔
117

118
  xmlCleanupParser();
3✔
119
}
6✔
120

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

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

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

136
  if (msIO_needBinaryStdout() == MS_FAILURE)
3✔
137
    return MS_FAILURE;
138

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

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

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

152
    const char *timestamp;
153
    timestamp = msGetOutputFormatOption(format, "TIMESTAMP", "NOW");
1✔
154
    char **papszOptions = nullptr;
155
    papszOptions = CSLAddNameValue(papszOptions, "TIMESTAMP", timestamp);
1✔
156

157
    zip_filename = msTmpFile(NULL, NULL, "/vsimem/kmlzip/", "kmz");
1✔
158
    hZip = CPLCreateZip(zip_filename, NULL);
1✔
159
    CPLCreateFileInZip(hZip, "mapserver.kml", papszOptions);
1✔
160
    for (int i = 0; i < bufSize; i += chunkSize) {
16✔
161
      int size = chunkSize;
162
      if (i + size > bufSize)
15✔
163
        size = bufSize - i;
1✔
164
      CPLWriteFileInZip(hZip, buf + i, size);
15✔
165
    }
166
    CPLCloseFileInZip(hZip);
1✔
167
    CPLCloseZip(hZip);
1✔
168
    CSLDestroy(papszOptions);
1✔
169

170
    context = msIO_getHandler(fp);
1✔
171
    fpZip = VSIFOpenL(zip_filename, "r");
1✔
172

173
    while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fpZip)) > 0) {
21✔
174
      if (context)
20✔
175
        msIO_contextWrite(context, buffer, bytes_read);
20✔
176
      else
177
        msIO_fwrite(buffer, 1, bytes_read, fp);
×
178
    }
179
    VSIFCloseL(fpZip);
1✔
180
    msFree(zip_filename);
1✔
181
    xmlFree(buf);
1✔
182
    return (MS_SUCCESS);
183
  }
184

185
  context = msIO_getHandler(fp);
2✔
186

187
  for (int i = 0; i < bufSize; i += chunkSize) {
19✔
188
    int size = chunkSize;
189
    if (i + size > bufSize)
17✔
190
      size = bufSize - i;
2✔
191

192
    if (context)
17✔
193
      msIO_contextWrite(context, buf + i, size);
17✔
194
    else
195
      msIO_fwrite(buf + i, 1, size, fp);
×
196
  }
197

198
  xmlFree(buf);
2✔
199

200
  return (MS_SUCCESS);
201
}
202

203
/************************************************************************/
204
/*                               processLayer                           */
205
/*                                                                      */
206
/*      Set parameters that make sense to a kml output.                 */
207
/************************************************************************/
208
void KmlRenderer::processLayer(layerObj *layer, outputFormatObj *format) {
3✔
209
  int i;
210
  const char *asRaster = NULL;
211
  int nMaxFeatures = -1;
212
  const char *pszTmp;
213
  char szTmp[10];
214

215
  if (!layer)
3✔
216
    return;
×
217

218
  /*turn of labelcache*/
219
  layer->labelcache = MS_OFF;
3✔
220

221
  /*if there are labels we want the coordinates to
222
    be the center of the element.*/
223
  for (i = 0; i < layer->numclasses; i++)
6✔
224
    if (layer->_class[i]->numlabels > 0)
3✔
225
      layer->_class[i]->labels[0]->position = MS_XY;
1✔
226

227
  /*we do not want to draw multiple styles.
228
    the new rendering architecture does not allow
229
    to know if we are dealing with a multi-style.
230
    So here we remove all styles beside the first one*/
231

232
  for (i = 0; i < layer->numclasses; i++) {
6✔
233
    while (layer->_class[i]->numstyles > 1)
3✔
234
      msDeleteStyle(layer->_class[i], layer->_class[i]->numstyles - 1);
×
235
  }
236

237
  /*if layer has a metadata KML_OUTPUTASRASTER set to true, add a processing
238
    directive to use an agg driver*/
239
  asRaster = msLookupHashTable(&layer->metadata, "kml_outputasraster");
3✔
240
  if (!asRaster)
3✔
241
    asRaster =
242
        msLookupHashTable(&(layer->map->web.metadata), "kml_outputasraster");
3✔
243
  if (asRaster &&
3✔
244
      (strcasecmp(asRaster, "true") == 0 || strcasecmp(asRaster, "yes") == 0))
×
245
    msLayerAddProcessing(layer, "RENDERER=png24");
×
246

247
  /*set a maxfeaturestodraw, if not already set*/
248

249
  pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
3✔
250
  if (pszTmp)
3✔
251
    nMaxFeatures = atoi(pszTmp);
252
  else {
253
    pszTmp = msLookupHashTable(&layer->map->web.metadata, "maxfeaturestodraw");
3✔
254
    if (pszTmp)
3✔
255
      nMaxFeatures = atoi(pszTmp);
256
  }
257
  if (nMaxFeatures < 0 && format)
3✔
258
    nMaxFeatures =
259
        atoi(msGetOutputFormatOption(format, "maxfeaturestodraw", "-1"));
3✔
260

261
  if (nMaxFeatures < 0 && format) {
3✔
262
    snprintf(szTmp, sizeof(szTmp), "%d", KML_MAXFEATURES_TODRAW);
263
    msSetOutputFormatOption(format, "maxfeaturestodraw", szTmp);
3✔
264
  }
265
}
266

267
/************************************************************************/
268
/*                               getLayerName                           */
269
/*                                                                      */
270
/*      Internal utility function to build name used for the layer.     */
271
/************************************************************************/
272
char *KmlRenderer::getLayerName(layerObj *layer) {
174✔
273
  char stmp[20];
274
  const char *name = NULL;
275
  ;
276

277
  if (!layer)
174✔
278
    return NULL;
279

280
  name = msLookupHashTable(&layer->metadata, "ows_name");
174✔
281
  if (name && strlen(name) > 0)
174✔
282
    return msStrdup(name);
×
283

284
  if (layer->name && strlen(layer->name) > 0)
174✔
285
    return msStrdup(layer->name);
174✔
286

287
  sprintf(stmp, "Layer%d", layer->index);
×
288
  return msStrdup(stmp);
×
289
}
290

291
const char *KmlRenderer::getAliasName(layerObj *lp, char *pszItemName,
×
292
                                      const char *namespaces) {
293
  const char *pszAlias = NULL;
294
  if (lp && pszItemName && strlen(pszItemName) > 0) {
×
295
    char szTmp[256];
296
    snprintf(szTmp, sizeof(szTmp), "%s_alias", pszItemName);
297
    pszAlias = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
×
298
  }
299
  return pszAlias;
×
300
}
301

302
int KmlRenderer::startNewLayer(imageObj *img, layerObj *layer) {
3✔
303
  char *layerName = NULL;
304
  const char *value = NULL;
305

306
  LayerNode = xmlNewNode(NULL, BAD_CAST "Folder");
3✔
307

308
  layerName = getLayerName(layer);
3✔
309
  xmlNewChild(LayerNode, NULL, BAD_CAST "name", BAD_CAST layerName);
3✔
310
  msFree(layerName);
3✔
311

312
  const char *layerVisibility = layer->status != MS_OFF ? "1" : "0";
3✔
313
  xmlNewChild(LayerNode, NULL, BAD_CAST "visibility", BAD_CAST layerVisibility);
3✔
314

315
  const char *layerDsiplayFolder =
316
      msLookupHashTable(&(layer->metadata), "kml_folder_display");
3✔
317
  if (layerDsiplayFolder == NULL)
3✔
318
    layerDsiplayFolder =
319
        msLookupHashTable(&(layer->map->web.metadata), "kml_folder_display");
3✔
320
  if (!layerDsiplayFolder || strlen(layerDsiplayFolder) <= 0) {
3✔
321
    xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
3✔
322
                BAD_CAST "#LayerFolder_check");
323
  }
324

325
  else {
326
    if (strcasecmp(layerDsiplayFolder, "checkHideChildren") == 0)
×
327
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
328
                  BAD_CAST "#LayerFolder_checkHideChildren");
329
    else if (strcasecmp(layerDsiplayFolder, "checkOffOnly") == 0)
×
330
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
331
                  BAD_CAST "#LayerFolder_checkOffOnly");
332
    else if (strcasecmp(layerDsiplayFolder, "radioFolder") == 0)
×
333
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
334
                  BAD_CAST "#LayerFolder_radioFolder");
335
    else
336
      xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl",
×
337
                  BAD_CAST "#LayerFolder_check");
338
  }
339

340
  /*Init few things on the first layer*/
341
  if (FirstLayer) {
3✔
342
    FirstLayer = MS_FALSE;
3✔
343
    map = layer->map;
3✔
344

345
    if (layer->map->mappath)
3✔
346
      snprintf(MapPath, sizeof(MapPath), "%s", layer->map->mappath);
3✔
347

348
    /*First rendered layer - check mapfile projection*/
349
    checkProjection(layer->map);
3✔
350

351
    /*check for image path and image url*/
352
    if (layer->map->debug &&
3✔
353
        (layer->map->web.imageurl == NULL || layer->map->web.imagepath == NULL))
×
354
      msDebug("KmlRenderer::startNewLayer: imagepath and imageurl should be "
×
355
              "set in the web object\n");
356

357
    /*map rect for ground overlay*/
358
    MapExtent = layer->map->extent;
3✔
359
    MapCellsize = layer->map->cellsize;
3✔
360
    BgColor = layer->map->imagecolor;
3✔
361

362
    xmlNewChild(DocNode, NULL, BAD_CAST "name", BAD_CAST layer->map->name);
3✔
363
    aggFormat = msSelectOutputFormat(layer->map, "png24");
3✔
364
    aggFormat->transparent = MS_TRUE;
3✔
365
  }
366

367
  currentLayer = layer;
3✔
368

369
  if (!msLayerIsOpen(layer)) {
3✔
370
    if (msLayerOpen(layer) != MS_SUCCESS) {
3✔
371
      msSetError(MS_MISCERR, "msLayerOpen failed",
×
372
                 "KmlRenderer::startNewLayer");
373
      return MS_FAILURE;
×
374
    }
375
  }
376

377
  /*pre process the layer to set things that make sense for kml output*/
378
  if (img)
3✔
379
    processLayer(layer, img->format);
3✔
380
  else
381
    processLayer(layer, NULL);
×
382

383
  if (msLookupHashTable(&layer->metadata, "kml_description"))
3✔
384
    pszLayerDescMetadata =
×
385
        msLookupHashTable(&layer->metadata, "kml_description");
×
386
  else if (msLookupHashTable(&layer->metadata, "ows_description"))
3✔
387
    pszLayerDescMetadata =
×
388
        msLookupHashTable(&layer->metadata, "ows_description");
×
389

390
  value = msLookupHashTable(&layer->metadata, "kml_include_items");
3✔
391
  if (!value)
3✔
392
    value = msLookupHashTable(&layer->metadata, "ows_include_items");
3✔
393
  if (value)
3✔
394
    papszLayerIncludeItems = msStringSplit(value, ',', &nIncludeItems);
×
395

396
  value = msLookupHashTable(&layer->metadata, "kml_exclude_items");
3✔
397
  if (!value)
3✔
398
    value = msLookupHashTable(&layer->metadata, "ows_exclude_items");
3✔
399
  if (value)
3✔
400
    papszLayerExcludeItems = msStringSplit(value, ',', &nExcludeItems);
×
401

402
  if (msLookupHashTable(&layer->metadata, "kml_name_item"))
3✔
403
    pszLayerNameAttributeMetadata =
×
404
        msLookupHashTable(&layer->metadata, "kml_name_item");
×
405

406
  /*get all attributes*/
407
  if (msLayerWhichItems(layer, MS_TRUE, NULL) != MS_SUCCESS) {
3✔
408
    return MS_FAILURE;
409
  }
410

411
  NumItems = layer->numitems;
3✔
412
  if (NumItems) {
3✔
413
    Items = (char **)msSmallCalloc(NumItems, sizeof(char *));
3✔
414
    for (int i = 0; i < NumItems; i++)
40✔
415
      Items[i] = msStrdup(layer->items[i]);
37✔
416
  }
417

418
  const char *elevationAttribute =
419
      msLookupHashTable(&layer->metadata, "kml_elevation_attribute");
3✔
420
  if (elevationAttribute) {
3✔
421
    mElevationFromAttribute = true;
×
422
    for (int i = 0; i < layer->numitems; ++i) {
×
423
      if (strcasecmp(layer->items[i], elevationAttribute) == 0) {
×
424
        mElevationAttributeIndex = i;
×
425
      }
426
    }
427
  }
428

429
  setupRenderingParams(&layer->metadata);
3✔
430
  return MS_SUCCESS;
3✔
431
}
432

433
int KmlRenderer::closeNewLayer(imageObj *, layerObj *) {
3✔
434
  flushPlacemark();
3✔
435

436
  xmlAddChild(DocNode, LayerNode);
3✔
437

438
  if (Items) {
3✔
439
    msFreeCharArray(Items, NumItems);
3✔
440
    Items = NULL;
3✔
441
    NumItems = 0;
3✔
442
  }
443

444
  if (pszLayerDescMetadata)
3✔
445
    pszLayerDescMetadata = NULL;
×
446
  if (pszLayerNameAttributeMetadata)
3✔
447
    pszLayerNameAttributeMetadata = NULL;
×
448

449
  if (papszLayerIncludeItems && nIncludeItems > 0)
3✔
450
    msFreeCharArray(papszLayerIncludeItems, nIncludeItems);
×
451
  papszLayerIncludeItems = NULL;
3✔
452

453
  if (papszLayerExcludeItems && nExcludeItems > 0)
3✔
454
    msFreeCharArray(papszLayerExcludeItems, nExcludeItems);
×
455
  papszLayerExcludeItems = NULL;
3✔
456

457
  return MS_SUCCESS;
3✔
458
}
459

460
int KmlRenderer::mergeRasterBuffer(imageObj *image, rasterBufferObj *rb) {
×
461
  assert(rb && rb->type == MS_BUFFER_BYTE_RGBA);
462
  char *tmpFileName = NULL;
463
  char *tmpUrl = NULL;
464
  FILE *tmpFile = NULL;
465

466
  tmpFileName = msTmpFile(NULL, MapPath, image->imagepath, "png");
×
467
  tmpFile = fopen(tmpFileName, "wb");
×
468
  if (tmpFile) {
×
469

470
    if (!aggFormat->vtable)
×
471
      msInitializeRendererVTable(aggFormat);
×
472

473
    msSaveRasterBuffer(map, rb, tmpFile, aggFormat);
×
474
    tmpUrl = msStrdup(image->imageurl);
×
475
    tmpUrl = msStringConcatenate(tmpUrl, (char *)(msGetBasename(tmpFileName)));
×
476
    tmpUrl = msStringConcatenate(tmpUrl, ".png");
×
477

478
    createGroundOverlayNode(LayerNode, tmpUrl, currentLayer);
×
479
    msFree(tmpFileName);
×
480
    msFree(tmpUrl);
×
481
    fclose(tmpFile);
×
482
    return MS_SUCCESS;
×
483
  } else {
484
    msSetError(MS_IOERR, "Failed to create file for kml overlay",
×
485
               "KmlRenderer::mergeRasterBuffer()");
486
    return MS_FAILURE;
×
487
  }
488
}
489

490
void KmlRenderer::setupRenderingParams(hashTableObj *layerMetadata) {
3✔
491
  AltitudeMode = 0;
3✔
492
  Extrude = 0;
3✔
493
  Tessellate = 0;
3✔
494

495
  const char *altitudeModeVal =
496
      msLookupHashTable(layerMetadata, "kml_altitudeMode");
3✔
497
  if (altitudeModeVal) {
3✔
498
    if (strcasecmp(altitudeModeVal, "absolute") == 0)
×
499
      AltitudeMode = absolute;
×
500
    else if (strcasecmp(altitudeModeVal, "relativeToGround") == 0)
×
501
      AltitudeMode = relativeToGround;
×
502
    else if (strcasecmp(altitudeModeVal, "clampToGround") == 0)
×
503
      AltitudeMode = clampToGround;
×
504
  }
505

506
  const char *extrudeVal = msLookupHashTable(layerMetadata, "kml_extrude");
3✔
507
  if (altitudeModeVal) {
3✔
508
    Extrude = atoi(extrudeVal);
×
509
  }
510

511
  const char *tessellateVal =
512
      msLookupHashTable(layerMetadata, "kml_tessellate");
3✔
513
  if (tessellateVal) {
3✔
514
    Tessellate = atoi(tessellateVal);
×
515
  }
516
}
3✔
517

518
int KmlRenderer::checkProjection(mapObj *map) {
3✔
519
  projectionObj *projection = &map->projection;
3✔
520
  if (projection && projection->numargs > 0 &&
6✔
521
      msProjIsGeographicCRS(projection)) {
3✔
522
    return MS_SUCCESS;
523
  } else {
524
    char epsg_string[100];
525
    rectObj sRect;
526
    projectionObj out;
527

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

550
    sRect = map->extent;
×
551
    msProjectRect(projection, &out, &sRect);
×
552
    msFreeProjectionExceptContext(projection);
×
553
    msLoadProjectionString(projection, epsg_string);
×
554

555
    /*change also units and extents*/
556
    map->extent = sRect;
×
557
    map->units = MS_DD;
×
558

559
    if (map->debug)
×
560
      msDebug("KmlRenderer::checkProjection: Mapfile projection set to "
×
561
              "epsg:4326\n");
562

563
    return MS_SUCCESS;
564
  }
565
}
566

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

589
  return placemarkNode;
171✔
590
}
591

592
void KmlRenderer::renderLine(imageObj *, shapeObj *p, strokeStyleObj *style) {
144✔
593
  if (p->numlines == 0)
144✔
594
    return;
595

596
  if (PlacemarkNode == NULL)
144✔
597
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
144✔
598

599
  if (!PlacemarkNode)
144✔
600
    return;
601

602
  addLineStyleToList(style);
144✔
603
  SymbologyFlag[Line] = 1;
144✔
604

605
  /*p->index > CurrentDrawnShapeIndexneed to be reviewd. Added since the height
606
    level code caches shapes when rendering lines*/
607
  if (CurrentDrawnShapeIndex == -1 || p->index > CurrentDrawnShapeIndex) {
144✔
608
    xmlNodePtr geomNode = getGeomParentNode("LineString");
144✔
609
    addAddRenderingSpecifications(geomNode);
144✔
610
    addCoordsNode(geomNode, p->line[0].point, p->line[0].numpoints);
144✔
611

612
    /* more than one line => MultiGeometry*/
613
    if (p->numlines > 1) {
144✔
614
      geomNode = getGeomParentNode("LineString"); // returns MultiGeom Node
×
615
      for (int i = 1; i < p->numlines; i++) {
×
616
        xmlNodePtr lineStringNode =
617
            xmlNewChild(geomNode, NULL, BAD_CAST "LineString", NULL);
×
618
        addAddRenderingSpecifications(lineStringNode);
×
619
        addCoordsNode(lineStringNode, p->line[i].point, p->line[i].numpoints);
×
620
      }
621
    }
622

623
    CurrentDrawnShapeIndex = p->index;
144✔
624
  }
625
}
626

627
void KmlRenderer::renderPolygon(imageObj *, shapeObj *p, colorObj *color) {
×
628
  if (PlacemarkNode == NULL)
×
629
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
×
630

631
  if (!PlacemarkNode)
×
632
    return;
633

634
  memcpy(&PolygonColor, color, sizeof(colorObj));
×
635
  SymbologyFlag[Polygon] = 1;
×
636

637
  if (p->index != CurrentDrawnShapeIndex) {
×
638

639
    xmlNodePtr geomParentNode = getGeomParentNode("Polygon");
×
640

641
    for (int i = 0; i < p->numlines; i++) {
×
642
      xmlNodePtr bdryNode = NULL;
643

644
      if (i == 0) /* __TODO__ check ring order*/
×
645
        bdryNode =
646
            xmlNewChild(geomParentNode, NULL, BAD_CAST "outerBoundaryIs", NULL);
×
647
      else
648
        bdryNode =
649
            xmlNewChild(geomParentNode, NULL, BAD_CAST "innerBoundaryIs", NULL);
×
650

651
      xmlNodePtr ringNode =
652
          xmlNewChild(bdryNode, NULL, BAD_CAST "LinearRing", NULL);
×
653
      addAddRenderingSpecifications(ringNode);
×
654
      addCoordsNode(ringNode, p->line[i].point, p->line[i].numpoints);
×
655
    }
656

657
    CurrentDrawnShapeIndex = p->index;
×
658
  }
659
}
660

661
void KmlRenderer::addCoordsNode(xmlNodePtr parentNode, pointObj *pts,
171✔
662
                                int numPts) {
663
  char lineBuf[128];
664

665
  xmlNodePtr coordsNode =
666
      xmlNewChild(parentNode, NULL, BAD_CAST "coordinates", NULL);
171✔
667
  xmlNodeAddContent(coordsNode, BAD_CAST "\n");
171✔
668

669
  for (int i = 0; i < numPts; i++) {
3,698✔
670
    if (mElevationFromAttribute) {
3,527✔
671
      sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y,
×
672
              mCurrentElevationValue);
673
    } else if (AltitudeMode == relativeToGround || AltitudeMode == absolute) {
3,527✔
674
      sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, pts[i].z);
×
675
    } else
676
      sprintf(lineBuf, "\t%.8f,%.8f\n", pts[i].x, pts[i].y);
3,527✔
677

678
    xmlNodeAddContent(coordsNode, BAD_CAST lineBuf);
3,527✔
679
  }
680
  xmlNodeAddContent(coordsNode, BAD_CAST "\t");
171✔
681
}
171✔
682

683
void KmlRenderer::renderGlyphs(imageObj *, const textSymbolObj *ts,
27✔
684
                               colorObj *clr, colorObj * /*oc*/, int /*ow*/) {
685
  if (ts->annotext == NULL || ts->textpath->numglyphs == 0)
27✔
686
    return;
×
687

688
  if (PlacemarkNode == NULL)
27✔
689
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
27✔
690

691
  if (!PlacemarkNode)
27✔
692
    return;
693

694
  memcpy(&LabelColor, clr, sizeof(colorObj));
27✔
695
  SymbologyFlag[Label] = 1;
27✔
696

697
  /*there is alaws a default name (layer.shapeid). Replace it*/
698
  for (xmlNodePtr node = PlacemarkNode->children; node; node = node->next) {
27✔
699
    if (node->type != XML_ELEMENT_NODE)
27✔
700
      continue;
×
701

702
    if (strcmp((char *)node->name, "name") == 0) {
27✔
703
      xmlNodeSetContent(node, BAD_CAST ts->annotext);
27✔
704
      break;
705
    }
706
  }
707

708
  /*xmlNewChild(PlacemarkNode, NULL, BAD_CAST "name", BAD_CAST text);*/
709

710
  xmlNodePtr geomNode = getGeomParentNode("Point");
27✔
711
  addAddRenderingSpecifications(geomNode);
27✔
712

713
  pointObj pt;
714
  pt.x = ts->textpath->glyphs[0].pnt.x;
27✔
715
  pt.y = ts->textpath->glyphs[0].pnt.y;
27✔
716
  pt.z = 0.0;
27✔
717
  addCoordsNode(geomNode, &pt, 1);
27✔
718
}
719

720
void KmlRenderer::addAddRenderingSpecifications(xmlNodePtr node) {
171✔
721
  /*
722
    <extrude>0</extrude>                   <!-- boolean -->
723
    <tessellate>0</tessellate>             <!-- boolean -->
724
    <altitudeMode>clampToGround</altitudeMode>
725
  */
726

727
  if (Extrude)
171✔
728
    xmlNewChild(node, NULL, BAD_CAST "extrude", BAD_CAST "1");
×
729

730
  if (Tessellate)
171✔
731
    xmlNewChild(node, NULL, BAD_CAST "tessellate", BAD_CAST "1");
×
732

733
  if (AltitudeMode == absolute)
171✔
734
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "absolute");
×
735
  else if (AltitudeMode == relativeToGround)
171✔
736
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode",
×
737
                BAD_CAST "relativeToGround");
738
  else if (AltitudeMode == clampToGround)
171✔
739
    xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "clampToGround");
×
740
}
171✔
741

742
imageObj *agg2CreateImage(int width, int height, outputFormatObj *format,
743
                          colorObj *bg);
744

745
int KmlRenderer::createIconImage(char *fileName, symbolObj *symbol,
×
746
                                 symbolStyleObj *symstyle) {
747
  pointObj p;
748
  int status;
749

750
  imageObj *tmpImg = NULL;
751

752
  tmpImg =
753
      agg2CreateImage((int)(symbol->sizex * symstyle->scale),
×
754
                      (int)(symbol->sizey * symstyle->scale), aggFormat, NULL);
×
755
  tmpImg->format = aggFormat;
×
756
  if (!aggFormat->vtable)
×
757
    msInitializeRendererVTable(aggFormat);
×
758

759
  p.x = symbol->sizex * symstyle->scale / 2;
×
760
  p.y = symbol->sizey * symstyle->scale / 2;
×
761
  p.z = 0.0;
×
762

763
  status = msDrawMarkerSymbol(map, tmpImg, &p, symstyle->style, 1);
×
764
  if (status != MS_SUCCESS)
×
765
    return status;
766

767
  return msSaveImage(map, tmpImg, fileName);
×
768
}
769

770
void KmlRenderer::renderSymbol(imageObj *img, double x, double y,
×
771
                               symbolObj *symbol, symbolStyleObj *style) {
772
  if (PlacemarkNode == NULL)
×
773
    PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
×
774

775
  if (!PlacemarkNode)
×
776
    return;
×
777

778
  snprintf(SymbolUrl, sizeof(SymbolUrl), "%s",
×
779
           lookupSymbolUrl(img, symbol, style));
780
  SymbologyFlag[Symbol] = 1;
×
781

782
  xmlNodePtr geomNode = getGeomParentNode("Point");
×
783
  addAddRenderingSpecifications(geomNode);
×
784

785
  pointObj pt;
786
  pt.x = x;
×
787
  pt.y = y;
×
788
  pt.z = 0.0;
×
789
  addCoordsNode(geomNode, &pt, 1);
×
790
}
791

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

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

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

808
void KmlRenderer::renderTruetypeSymbol(imageObj *img, double x, double y,
×
809
                                       symbolObj *symbol,
810
                                       symbolStyleObj *style) {
811
  renderSymbol(img, x, y, symbol, style);
×
812
}
×
813

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

857
  if (imageHref) {
×
858
    xmlNodePtr iconNode =
859
        xmlNewChild(groundOverlayNode, NULL, BAD_CAST "Icon", NULL);
×
860
    xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST imageHref);
×
861
  }
862

863
  char crdStr[64];
864
  rectObj mapextent;
865
  if (map->gt.need_geotransform == MS_TRUE)
×
866
    mapextent = currentLayer->map->saved_extent;
×
867
  else
868
    mapextent = currentLayer->map->extent;
×
869

870
  xmlNodePtr latLonBoxNode =
871
      xmlNewChild(groundOverlayNode, NULL, BAD_CAST "LatLonBox", NULL);
×
872
  sprintf(crdStr, "%.8f", mapextent.maxy);
873
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "north", BAD_CAST crdStr);
×
874

875
  sprintf(crdStr, "%.8f", mapextent.miny);
876
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "south", BAD_CAST crdStr);
×
877

878
  sprintf(crdStr, "%.8f", mapextent.minx);
879
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "west", BAD_CAST crdStr);
×
880

881
  sprintf(crdStr, "%.8f", mapextent.maxx);
882
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "east", BAD_CAST crdStr);
×
883

884
  xmlNewChild(latLonBoxNode, NULL, BAD_CAST "rotation", BAD_CAST "0.0");
×
885

886
  return groundOverlayNode;
×
887
}
888

889
void KmlRenderer::startShape(imageObj *, shapeObj *shape) {
172✔
890
  if (PlacemarkNode)
172✔
891
    flushPlacemark();
168✔
892

893
  CurrentShapeIndex = -1;
172✔
894
  CurrentDrawnShapeIndex = -1;
172✔
895
  CurrentShapeName = NULL;
172✔
896

897
  /*should be done at endshape but the plugin architecture does not call
898
   * endshape yet*/
899
  if (LineStyle) {
172✔
900
    msFree(LineStyle);
142✔
901

902
    LineStyle = NULL;
142✔
903
    numLineStyle = 0;
142✔
904
  }
905

906
  CurrentShapeIndex = shape->index;
172✔
907
  if (pszLayerNameAttributeMetadata) {
172✔
908
    for (int i = 0; i < currentLayer->numitems; i++) {
×
909
      if (strcasecmp(currentLayer->items[i], pszLayerNameAttributeMetadata) ==
×
910
              0 &&
×
911
          shape->values[i]) {
×
912
        CurrentShapeName = msStrdup(shape->values[i]);
×
913
        break;
×
914
      }
915
    }
916
  }
917
  PlacemarkNode = NULL;
172✔
918
  GeomNode = NULL;
172✔
919

920
  DescriptionNode = createDescriptionNode(shape);
172✔
921

922
  if (mElevationFromAttribute && shape->numvalues > mElevationAttributeIndex &&
172✔
923
      mElevationAttributeIndex >= 0 &&
×
924
      shape->values[mElevationAttributeIndex]) {
×
925
    mCurrentElevationValue = atof(shape->values[mElevationAttributeIndex]);
×
926
  }
927

928
  memset(SymbologyFlag, 0, NumSymbologyFlag);
172✔
929
}
172✔
930

931
void KmlRenderer::endShape(imageObj *, shapeObj *) {
172✔
932
  CurrentShapeIndex = -1;
172✔
933
  if (CurrentShapeName)
172✔
934
    msFree(CurrentShapeName);
×
935
  CurrentShapeName = NULL;
172✔
936
}
172✔
937

938
xmlNodePtr KmlRenderer::getGeomParentNode(const char *geomName) {
171✔
939
  /*we do not need a multi-geometry for point layers*/
940
  if (currentLayer->type != MS_LAYER_POINT &&
171✔
941
      currentLayer->type != MS_LAYER_ANNOTATION && GeomNode) {
144✔
942
    /*placemark geometry already defined, we need multigeometry node*/
943
    xmlNodePtr multiGeomNode = xmlNewNode(NULL, BAD_CAST "MultiGeometry");
×
944
    xmlAddChild(multiGeomNode, GeomNode);
×
945
    GeomNode = multiGeomNode;
×
946

947
    xmlNodePtr geomNode =
948
        xmlNewChild(multiGeomNode, NULL, BAD_CAST geomName, NULL);
×
949
    return geomNode;
×
950
  } else {
951
    GeomNode = xmlNewNode(NULL, BAD_CAST geomName);
171✔
952
    return GeomNode;
171✔
953
  }
954
}
955

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

972
  sprintf(symbolHexColor, "%02x%02x%02x%02x", symstyle->style->color.alpha,
×
973
          symstyle->style->color.blue, symstyle->style->color.green,
974
          symstyle->style->color.red);
×
975
  snprintf(SymbolName, sizeof(SymbolName), "symbol_%s_%.1f_%s", symbol->name,
×
976
           symstyle->scale, symbolHexColor);
977

978
  const char *symbolUrl = msLookupHashTable(StyleHashTable, SymbolName);
×
979
  if (!symbolUrl) {
×
980
    char iconFileName[MS_MAXPATHLEN];
981
    char iconUrl[MS_MAXPATHLEN];
982

983
    if (img->imagepath) {
×
984
      char *tmpFileName = msTmpFile(NULL, MapPath, img->imagepath, "png");
×
985
      snprintf(iconFileName, sizeof(iconFileName), "%s", tmpFileName);
986
      msFree(tmpFileName);
×
987
    } else {
988
      sprintf(iconFileName, "symbol_%s_%.1f.%s", symbol->name, symstyle->scale,
×
989
              "png");
990
    }
991

992
    if (createIconImage(iconFileName, symbol, symstyle) != MS_SUCCESS) {
×
993
      msSetError(MS_IOERR, "Error creating icon file '%s'",
×
994
                 "KmlRenderer::lookupSymbolStyle()", iconFileName);
995
      return NULL;
×
996
    }
997

998
    if (img->imageurl)
×
999
      sprintf(iconUrl, "%s%s.%s", img->imageurl, msGetBasename(iconFileName),
×
1000
              "png");
1001
    else
1002
      snprintf(iconUrl, sizeof(iconUrl), "%s", iconFileName);
1003

1004
    hashObj *hash = msInsertHashTable(StyleHashTable, SymbolName, iconUrl);
×
1005
    symbolUrl = hash->data;
×
1006
  }
1007

1008
  return symbolUrl;
1009
}
1010

1011
const char *KmlRenderer::lookupPlacemarkStyle() {
171✔
1012
  char lineHexColor[32];
1013
  char polygonHexColor[32];
1014
  char labelHexColor[32];
1015
  char *styleName = NULL;
1016

1017
  styleName = msStringConcatenate(styleName, "style");
171✔
1018

1019
  if (SymbologyFlag[Line]) {
171✔
1020
    /*
1021
      <LineStyle id="ID">
1022
      <!-- inherited from ColorStyle -->
1023
      <color>ffffffff</color>            <!-- kml:color -->
1024
      <colorMode>normal</colorMode>      <!-- colorModeEnum: normal or random
1025
      -->
1026

1027
      <!-- specific to LineStyle -->
1028
      <width>1</width>                   <!-- float -->
1029
      </LineStyle>
1030
    */
1031

1032
    for (int i = 0; i < numLineStyle; i++) {
288✔
1033
      if (currentLayer && currentLayer->compositer &&
144✔
1034
          currentLayer->compositer->opacity > 0 &&
×
1035
          currentLayer->compositer->opacity < 100 &&
×
1036
          LineStyle[i].color->alpha == 255)
×
1037
        LineStyle[i].color->alpha =
×
1038
            MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1039

1040
      sprintf(lineHexColor, "%02x%02x%02x%02x", LineStyle[i].color->alpha,
144✔
1041
              LineStyle[0].color->blue, LineStyle[i].color->green,
144✔
1042
              LineStyle[i].color->red);
144✔
1043

1044
      char lineStyleName[64];
1045
      snprintf(lineStyleName, sizeof(lineStyleName), "_line_%s_w%.1f",
144✔
1046
               lineHexColor, LineStyle[i].width);
144✔
1047
      styleName = msStringConcatenate(styleName, lineStyleName);
144✔
1048
    }
1049
  }
1050

1051
  if (SymbologyFlag[Polygon]) {
171✔
1052
    /*
1053
      <PolyStyle id="ID">
1054
      <!-- inherited from ColorStyle -->
1055
      <color>ffffffff</color>            <!-- kml:color -->
1056
      <colorMode>normal</colorMode>      <!-- kml:colorModeEnum: normal or
1057
      random -->
1058

1059
      <!-- specific to PolyStyle -->
1060
      <fill>1</fill>                     <!-- boolean -->
1061
      <outline>1</outline>               <!-- boolean -->
1062
      </PolyStyle>
1063
    */
1064

1065
    if (currentLayer && currentLayer->compositer &&
×
1066
        currentLayer->compositer->opacity > 0 &&
×
1067
        currentLayer->compositer->opacity < 100 && PolygonColor.alpha == 255)
×
1068
      PolygonColor.alpha = MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1069
    sprintf(polygonHexColor, "%02x%02x%02x%02x", PolygonColor.alpha,
×
1070
            PolygonColor.blue, PolygonColor.green, PolygonColor.red);
1071

1072
    char polygonStyleName[64];
1073
    sprintf(polygonStyleName, "_polygon_%s", polygonHexColor);
1074
    styleName = msStringConcatenate(styleName, polygonStyleName);
×
1075
  }
1076

1077
  if (SymbologyFlag[Label]) {
171✔
1078
    /*
1079
      <LabelStyle id="ID">
1080
      <!-- inherited from ColorStyle -->
1081
      <color>ffffffff</color>            <!-- kml:color -->
1082
      <colorMode>normal</colorMode>      <!-- kml:colorModeEnum: normal or
1083
      random -->
1084

1085
      <!-- specific to LabelStyle -->
1086
      <scale>1</scale>                   <!-- float -->
1087
      </LabelStyle>
1088
    */
1089

1090
    if (currentLayer && currentLayer->compositer &&
27✔
1091
        currentLayer->compositer->opacity > 0 &&
×
1092
        currentLayer->compositer->opacity < 100 && LabelColor.alpha == 255)
×
1093
      LabelColor.alpha = MS_NINT(currentLayer->compositer->opacity * 2.55);
×
1094
    sprintf(labelHexColor, "%02x%02x%02x%02x", LabelColor.alpha,
27✔
1095
            LabelColor.blue, LabelColor.green, LabelColor.red);
1096

1097
    // __TODO__ add label scale
1098

1099
    char labelStyleName[64];
1100
    sprintf(labelStyleName, "_label_%s", labelHexColor);
1101
    styleName = msStringConcatenate(styleName, labelStyleName);
27✔
1102
  }
1103

1104
  if (SymbologyFlag[Symbol]) {
171✔
1105
    /*
1106
    <Style id="randomColorIcon">
1107
            <IconStyle>
1108
            <color>ff00ff00</color>
1109
            <colorMode>random</colorMode>
1110
            <scale>1.1</scale>
1111
            <Icon>
1112
            <href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
1113
            </Icon>
1114
            </IconStyle>
1115
    </Style>
1116
    */
1117

1118
    /* __TODO__ add label scale */
1119

1120
    styleName = msStringConcatenate(styleName, "_");
×
1121
    styleName = msStringConcatenate(styleName, SymbolName);
×
1122
  }
1123

1124
  const char *styleUrl = msLookupHashTable(StyleHashTable, styleName);
171✔
1125
  if (!styleUrl) {
171✔
1126
    char *styleValue = NULL;
1127
    styleValue = msStringConcatenate(styleValue, "#");
3✔
1128
    styleValue = msStringConcatenate(styleValue, styleName);
3✔
1129
    hashObj *hash = msInsertHashTable(StyleHashTable, styleName, styleValue);
3✔
1130
    styleUrl = hash->data;
3✔
1131
    msFree(styleValue);
3✔
1132

1133
    /* Insert new Style node into Document node*/
1134
    xmlNodePtr styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
3✔
1135
    xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST styleName);
3✔
1136

1137
    if (SymbologyFlag[Polygon]) {
3✔
1138
      xmlNodePtr polyStyleNode =
1139
          xmlNewChild(styleNode, NULL, BAD_CAST "PolyStyle", NULL);
×
1140
      xmlNewChild(polyStyleNode, NULL, BAD_CAST "color",
×
1141
                  BAD_CAST polygonHexColor);
1142
    }
1143

1144
    if (SymbologyFlag[Line]) {
3✔
1145
      for (int i = 0; i < numLineStyle; i++) {
4✔
1146
        xmlNodePtr lineStyleNode =
1147
            xmlNewChild(styleNode, NULL, BAD_CAST "LineStyle", NULL);
2✔
1148
        sprintf(lineHexColor, "%02x%02x%02x%02x", LineStyle[i].color->alpha,
2✔
1149
                LineStyle[i].color->blue, LineStyle[i].color->green,
1150
                LineStyle[i].color->red);
2✔
1151
        xmlNewChild(lineStyleNode, NULL, BAD_CAST "color",
2✔
1152
                    BAD_CAST lineHexColor);
1153

1154
        char width[16];
1155
        sprintf(width, "%.1f", LineStyle[i].width);
2✔
1156
        xmlNewChild(lineStyleNode, NULL, BAD_CAST "width", BAD_CAST width);
2✔
1157
      }
1158
    }
1159

1160
    if (SymbologyFlag[Symbol]) {
3✔
1161
      xmlNodePtr iconStyleNode =
1162
          xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
×
1163

1164
      xmlNodePtr iconNode =
1165
          xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
×
1166
      xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST SymbolUrl);
×
1167

1168
      /*char scale[16];
1169
        sprintf(scale, "%.1f", style->scale);
1170
        xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1171
    } else {
1172
      const char *value =
1173
          msLookupHashTable(&currentLayer->metadata, "kml_default_symbol_href");
3✔
1174
      if (value && strlen(value) > 0) {
3✔
1175
        xmlNodePtr iconStyleNode =
1176
            xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
×
1177

1178
        xmlNodePtr iconNode =
1179
            xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
×
1180
        xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST value);
×
1181
      }
1182
    }
1183

1184
    if (SymbologyFlag[Label]) {
3✔
1185
      xmlNodePtr labelStyleNode =
1186
          xmlNewChild(styleNode, NULL, BAD_CAST "LabelStyle", NULL);
1✔
1187
      xmlNewChild(labelStyleNode, NULL, BAD_CAST "color",
1✔
1188
                  BAD_CAST labelHexColor);
1189

1190
      /*char scale[16];
1191
        sprintf(scale, "%.1f", style->scale);
1192
        xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1193
    }
1194
  }
1195

1196
  if (styleName)
171✔
1197
    msFree(styleName);
171✔
1198

1199
  return styleUrl;
171✔
1200
}
1201

1202
void KmlRenderer::flushPlacemark() {
171✔
1203
  if (PlacemarkNode) {
171✔
1204
    const char *styleUrl = lookupPlacemarkStyle();
171✔
1205
    xmlNewChild(PlacemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
171✔
1206

1207
    if (DescriptionNode)
171✔
1208
      xmlAddChild(PlacemarkNode, DescriptionNode);
×
1209

1210
    if (GeomNode)
171✔
1211
      xmlAddChild(PlacemarkNode, GeomNode);
171✔
1212
  }
1213
}
171✔
1214

1215
xmlNodePtr KmlRenderer::createDescriptionNode(shapeObj *shape) {
172✔
1216
  /*
1217
    <description>
1218
    <![CDATA[
1219
    special characters here
1220
    ]]>
1221
    <description>
1222
  */
1223

1224
  /*description nodes for vector layers:
1225
    - if kml_description is set, use it
1226
    - if not, dump the attributes */
1227

1228
  if (pszLayerDescMetadata) {
172✔
1229
    char *pszTmp = NULL;
1230
    char *pszTmpDesc = NULL;
1231
    size_t bufferSize = 0;
1232
    pszTmpDesc = msStrdup(pszLayerDescMetadata);
×
1233

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

1254
    xmlNodePtr extendedDataNode = xmlNewNode(NULL, BAD_CAST "ExtendedData");
×
1255
    xmlNodePtr dataNode = NULL;
1256
    const char *pszAlias = NULL;
1257
    int bIncludeAll = MS_FALSE;
1258

1259
    if (papszLayerIncludeItems && nIncludeItems == 1 &&
×
1260
        strcasecmp(papszLayerIncludeItems[0], "all") == 0)
×
1261
      bIncludeAll = MS_TRUE;
1262

1263
    for (int i = 0; i < currentLayer->numitems; i++) {
×
1264
      int j = 0, k = 0;
1265

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

1300
    return extendedDataNode;
1301
  }
1302

1303
  return NULL;
1304
}
1305

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

1326
    memcpy(&LineStyle[numLineStyle - 1], style, sizeof(strokeStyleObj));
144✔
1327
  }
1328
}
144✔
1329

1330
#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