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
KmlRenderer(int width,int height,outputFormatObj * format,colorObj * color)44 KmlRenderer::KmlRenderer(int width, int height, outputFormatObj *format, colorObj* color/*=NULL*/)
45 : Width(width), Height(height), MapCellsize(1.0), XmlDoc(NULL), LayerNode(NULL), GroundOverlayNode(NULL),
46 PlacemarkNode(NULL), GeomNode(NULL),
47 Items(NULL), NumItems(0), FirstLayer(MS_TRUE), map(NULL), currentLayer(NULL),
48 mElevationFromAttribute( false ), mElevationAttributeIndex( -1 ), mCurrentElevationValue(0.0)
49
50 {
51 /*private variables*/
52 pszLayerDescMetadata = NULL;
53 papszLayerIncludeItems = NULL;
54 nIncludeItems=0;
55 papszLayerExcludeItems = NULL;
56 nExcludeItems=0;
57 pszLayerNameAttributeMetadata = NULL; /*metadata to use for a name for each feature*/
58
59 LineStyle = NULL;
60 numLineStyle = 0;
61
62 xmlNodePtr styleNode;
63 xmlNodePtr listStyleNode;
64 /* Create document.*/
65 XmlDoc = xmlNewDoc(BAD_CAST "1.0");
66
67 xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "kml");
68
69 /* Name spaces*/
70 xmlSetNs(rootNode, xmlNewNs(rootNode, BAD_CAST "http://www.opengis.net/kml/2.2", NULL));
71
72 xmlDocSetRootElement(XmlDoc, rootNode);
73
74 DocNode = xmlNewChild(rootNode, NULL, BAD_CAST "Document", NULL);
75
76 styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
77 xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_check");
78 listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
79 xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "check");
80
81 styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
82 xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_checkHideChildren");
83 listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
84 xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "checkHideChildren");
85
86 styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
87 xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_checkOffOnly");
88 listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
89 xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "checkOffOnly");
90
91 styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
92 xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_radioFolder");
93 listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
94 xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "radioFolder");
95
96
97 StyleHashTable = msCreateHashTable();
98
99 }
100
~KmlRenderer()101 KmlRenderer::~KmlRenderer()
102 {
103 if (XmlDoc)
104 xmlFreeDoc(XmlDoc);
105
106 if (StyleHashTable)
107 msFreeHashTable(StyleHashTable);
108
109 if(LineStyle)
110 msFree(LineStyle);
111
112 xmlCleanupParser();
113 }
114
createImage(int,int,outputFormatObj *,colorObj *)115 imageObj* KmlRenderer::createImage(int, int, outputFormatObj*, colorObj*)
116 {
117 return NULL;
118 }
119
saveImage(imageObj *,FILE * fp,outputFormatObj * format)120 int KmlRenderer::saveImage(imageObj *, FILE *fp, outputFormatObj *format)
121 {
122 /* -------------------------------------------------------------------- */
123 /* Write out the document. */
124 /* -------------------------------------------------------------------- */
125
126 int bufSize = 0;
127 xmlChar *buf = NULL;
128 msIOContext *context = NULL;
129 int chunkSize = 4096;
130 int bZip = MS_FALSE;
131
132 if( msIO_needBinaryStdout() == MS_FAILURE )
133 return MS_FAILURE;
134
135 xmlDocDumpFormatMemoryEnc(XmlDoc, &buf, &bufSize, "UTF-8", 1);
136
137 if (format && format->driver && strcasecmp(format->driver, "kmz") == 0) {
138 bZip = MS_TRUE;
139 }
140
141 if (bZip) {
142 VSILFILE *fpZip;
143 int bytes_read;
144 char buffer[1024];
145 char *zip_filename =NULL;
146 void *hZip=NULL;
147
148 zip_filename = msTmpFile(NULL, NULL, "/vsimem/kmlzip/", "kmz" );
149 hZip = CPLCreateZip( zip_filename, NULL );
150 CPLCreateFileInZip( hZip, "mapserver.kml", NULL );
151 for (int i=0; i<bufSize; i+=chunkSize) {
152 int size = chunkSize;
153 if (i + size > bufSize)
154 size = bufSize - i;
155 CPLWriteFileInZip( hZip, buf+i, size);
156 }
157 CPLCloseFileInZip( hZip );
158 CPLCloseZip( hZip );
159
160 context = msIO_getHandler(fp);
161 fpZip = VSIFOpenL( zip_filename, "r" );
162
163 while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fpZip )) > 0 ) {
164 if (context)
165 msIO_contextWrite(context, buffer, bytes_read);
166 else
167 msIO_fwrite( buffer, 1, bytes_read, fp );
168 }
169 VSIFCloseL( fpZip );
170 msFree( zip_filename);
171 xmlFree(buf);
172 return(MS_SUCCESS);
173 }
174
175 context = msIO_getHandler(fp);
176
177
178 for (int i=0; i<bufSize; i+=chunkSize) {
179 int size = chunkSize;
180 if (i + size > bufSize)
181 size = bufSize - i;
182
183 if (context)
184 msIO_contextWrite(context, buf+i, size);
185 else
186 msIO_fwrite(buf+i, 1, size, fp);
187 }
188
189 xmlFree(buf);
190
191 return(MS_SUCCESS);
192 }
193
194
195 /************************************************************************/
196 /* processLayer */
197 /* */
198 /* Set parameters that make sense to a kml output. */
199 /************************************************************************/
processLayer(layerObj * layer,outputFormatObj * format)200 void KmlRenderer::processLayer(layerObj *layer, outputFormatObj *format)
201 {
202 int i;
203 const char *asRaster = NULL;
204 int nMaxFeatures = -1;
205 const char *pszTmp;
206 char szTmp[10];
207
208 if (!layer)
209 return;
210
211 /*turn of labelcache*/
212 layer->labelcache = MS_OFF;
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++)
217 if(layer->_class[i]->numlabels > 0) layer->_class[i]->labels[0]->position = MS_XY;
218
219 /*we do not want to draw multiple styles.
220 the new rendering architecture does not allow
221 to know if we are dealing with a multi-style.
222 So here we remove all styles beside the first one*/
223
224 for(i=0; i<layer->numclasses; i++) {
225 while (layer->_class[i]->numstyles > 1)
226 msDeleteStyle(layer->_class[i], layer->_class[i]->numstyles-1);
227 }
228
229 /*if layer has a metadata KML_OUTPUTASRASTER set to true, add a processing directive
230 to use an agg driver*/
231 asRaster = msLookupHashTable(&layer->metadata, "kml_outputasraster");
232 if (!asRaster)
233 asRaster = msLookupHashTable(&(layer->map->web.metadata), "kml_outputasraster");
234 if (asRaster && (strcasecmp(asRaster, "true") == 0 ||
235 strcasecmp(asRaster, "yes") == 0))
236 msLayerAddProcessing(layer, "RENDERER=png24");
237
238
239 /*set a maxfeaturestodraw, if not already set*/
240
241 pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
242 if (pszTmp)
243 nMaxFeatures = atoi(pszTmp);
244 else {
245 pszTmp = msLookupHashTable(&layer->map->web.metadata, "maxfeaturestodraw");
246 if (pszTmp)
247 nMaxFeatures = atoi(pszTmp);
248 }
249 if (nMaxFeatures < 0 && format)
250 nMaxFeatures = atoi(msGetOutputFormatOption( format, "maxfeaturestodraw", "-1"));
251
252 if (nMaxFeatures < 0 && format) {
253 snprintf(szTmp, sizeof(szTmp), "%d", KML_MAXFEATURES_TODRAW);
254 msSetOutputFormatOption( format, "maxfeaturestodraw", szTmp);
255 }
256
257 }
258
259 /************************************************************************/
260 /* getLayerName */
261 /* */
262 /* Internal utility function to build name used fo rthe layer. */
263 /************************************************************************/
getLayerName(layerObj * layer)264 char* KmlRenderer::getLayerName(layerObj *layer)
265 {
266 char stmp[20];
267 const char *name=NULL;;
268
269 if (!layer)
270 return NULL;
271
272
273 name = msLookupHashTable(&layer->metadata, "ows_name");
274 if (name && strlen(name) > 0)
275 return msStrdup(name);
276
277 if (layer->name && strlen(layer->name) > 0)
278 return msStrdup(layer->name);
279
280 sprintf(stmp, "Layer%d",layer->index);
281 return msStrdup(stmp);
282
283 }
284
getAliasName(layerObj * lp,char * pszItemName,const char * namespaces)285 const char* KmlRenderer::getAliasName(layerObj *lp, char *pszItemName, const char *namespaces)
286 {
287 const char *pszAlias = NULL;
288 if (lp && pszItemName && strlen(pszItemName) > 0) {
289 char szTmp[256];
290 snprintf(szTmp, sizeof(szTmp), "%s_alias", pszItemName);
291 pszAlias = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
292 }
293 return pszAlias;
294 }
295
startNewLayer(imageObj * img,layerObj * layer)296 int KmlRenderer::startNewLayer(imageObj *img, layerObj *layer)
297 {
298 char *layerName=NULL;
299 const char *value=NULL;
300
301 LayerNode = xmlNewNode(NULL, BAD_CAST "Folder");
302
303 layerName = getLayerName(layer);
304 xmlNewChild(LayerNode, NULL, BAD_CAST "name", BAD_CAST layerName);
305 msFree(layerName);
306
307 const char *layerVisibility = layer->status != MS_OFF ? "1" : "0";
308 xmlNewChild(LayerNode, NULL, BAD_CAST "visibility", BAD_CAST layerVisibility);
309
310 const char *layerDsiplayFolder = msLookupHashTable(&(layer->metadata), "kml_folder_display");
311 if (layerDsiplayFolder == NULL)
312 layerDsiplayFolder = msLookupHashTable(&(layer->map->web.metadata), "kml_folder_display");
313 if (!layerDsiplayFolder || strlen(layerDsiplayFolder)<=0) {
314 xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_check");
315 }
316
317 else {
318 if (strcasecmp(layerDsiplayFolder, "checkHideChildren") == 0)
319 xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_checkHideChildren");
320 else if (strcasecmp(layerDsiplayFolder, "checkOffOnly") == 0)
321 xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_checkOffOnly");
322 else if (strcasecmp(layerDsiplayFolder, "radioFolder") == 0)
323 xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_radioFolder");
324 else
325 xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_check");
326 }
327
328
329 /*Init few things on the first layer*/
330 if (FirstLayer) {
331 FirstLayer = MS_FALSE;
332 map = layer->map;
333
334 if (layer->map->mappath)
335 snprintf(MapPath, sizeof(MapPath), "%s", layer->map->mappath);
336
337 /*First rendered layer - check mapfile projection*/
338 checkProjection(layer->map);
339
340 /*check for image path and image url*/
341 if (layer->map->debug && (layer->map->web.imageurl == NULL || layer->map->web.imagepath == NULL))
342 msDebug("KmlRenderer::startNewLayer: imagepath and imageurl should be set in the web object\n");
343
344
345 /*map rect for ground overlay*/
346 MapExtent = layer->map->extent;
347 MapCellsize = layer->map->cellsize;
348 BgColor = layer->map->imagecolor;
349
350 xmlNewChild(DocNode, NULL, BAD_CAST "name", BAD_CAST layer->map->name);
351 aggFormat = msSelectOutputFormat( layer->map, "png24");
352 aggFormat->transparent = MS_TRUE;
353
354 }
355
356 currentLayer = layer;
357
358 if (!msLayerIsOpen(layer)) {
359 if (msLayerOpen(layer) != MS_SUCCESS) {
360 msSetError(MS_MISCERR, "msLayerOpen failed", "KmlRenderer::startNewLayer" );
361 return MS_FAILURE;
362 }
363 }
364
365 /*pre process the layer to set things that make sense for kml output*/
366 if (img)
367 processLayer(layer, img->format);
368 else
369 processLayer(layer, NULL);
370
371 if (msLookupHashTable(&layer->metadata, "kml_description"))
372 pszLayerDescMetadata = msLookupHashTable(&layer->metadata, "kml_description");
373 else if (msLookupHashTable(&layer->metadata, "ows_description"))
374 pszLayerDescMetadata = msLookupHashTable(&layer->metadata, "ows_description");
375
376 value=msLookupHashTable(&layer->metadata, "kml_include_items");
377 if (!value)
378 value=msLookupHashTable(&layer->metadata, "ows_include_items");
379 if (value)
380 papszLayerIncludeItems = msStringSplit(value, ',', &nIncludeItems);
381
382 value=msLookupHashTable(&layer->metadata, "kml_exclude_items");
383 if (!value)
384 value=msLookupHashTable(&layer->metadata, "ows_exclude_items");
385 if (value)
386 papszLayerExcludeItems = msStringSplit(value, ',', &nExcludeItems);
387
388
389 if (msLookupHashTable(&layer->metadata, "kml_name_item"))
390 pszLayerNameAttributeMetadata = msLookupHashTable(&layer->metadata, "kml_name_item");
391
392 /*get all attributes*/
393 if(msLayerWhichItems(layer, MS_TRUE, NULL) != MS_SUCCESS) {
394 return MS_FAILURE;
395 }
396
397
398 NumItems = layer->numitems;
399 if (NumItems) {
400 Items = (char **)msSmallCalloc(NumItems, sizeof(char *));
401 for (int i=0; i<NumItems; i++)
402 Items[i] = msStrdup(layer->items[i]);
403 }
404
405
406 const char* elevationAttribute = msLookupHashTable(&layer->metadata, "kml_elevation_attribute");
407 if( elevationAttribute ) {
408 mElevationFromAttribute = true;
409 for( int i = 0; i < layer->numitems; ++i ) {
410 if( strcasecmp( layer->items[i], elevationAttribute ) == 0 ) {
411 mElevationAttributeIndex = i;
412 }
413 }
414 }
415
416 setupRenderingParams(&layer->metadata);
417 return MS_SUCCESS;
418 }
419
closeNewLayer(imageObj * img,layerObj * layer)420 int KmlRenderer::closeNewLayer(imageObj *img, layerObj *layer)
421 {
422 flushPlacemark();
423
424 xmlAddChild(DocNode, LayerNode);
425
426 if(Items) {
427 msFreeCharArray(Items, NumItems);
428 Items = NULL;
429 NumItems = 0;
430 }
431
432 if (pszLayerDescMetadata)
433 pszLayerDescMetadata = NULL;
434 if (pszLayerNameAttributeMetadata)
435 pszLayerNameAttributeMetadata = NULL;
436
437 if (papszLayerIncludeItems && nIncludeItems>0)
438 msFreeCharArray(papszLayerIncludeItems, nIncludeItems);
439 papszLayerIncludeItems=NULL;
440
441 if (papszLayerExcludeItems && nExcludeItems>0)
442 msFreeCharArray(papszLayerExcludeItems, nExcludeItems);
443 papszLayerExcludeItems=NULL;
444
445 return MS_SUCCESS;
446 }
447
mergeRasterBuffer(imageObj * image,rasterBufferObj * rb)448 int KmlRenderer::mergeRasterBuffer(imageObj *image, rasterBufferObj *rb)
449 {
450 assert(rb && rb->type == MS_BUFFER_BYTE_RGBA);
451 char *tmpFileName = NULL;
452 char *tmpUrl = NULL;
453 FILE *tmpFile = NULL;
454
455 tmpFileName = msTmpFile(NULL, MapPath, image->imagepath, "png");
456 tmpFile = fopen(tmpFileName,"wb");
457 if (tmpFile) {
458
459 if (!aggFormat->vtable)
460 msInitializeRendererVTable(aggFormat);
461
462 msSaveRasterBuffer(map,rb,tmpFile,aggFormat);
463 tmpUrl = msStrdup( image->imageurl);
464 tmpUrl = msStringConcatenate(tmpUrl, (char *)(msGetBasename(tmpFileName)));
465 tmpUrl = msStringConcatenate(tmpUrl, ".png");
466
467 createGroundOverlayNode(LayerNode, tmpUrl, currentLayer);
468 msFree(tmpFileName);
469 msFree(tmpUrl);
470 fclose(tmpFile);
471 return MS_SUCCESS;
472 } else {
473 msSetError(MS_IOERR,"Failed to create file for kml overlay","KmlRenderer::mergeRasterBuffer()");
474 return MS_FAILURE;
475 }
476 }
477
setupRenderingParams(hashTableObj * layerMetadata)478 void KmlRenderer::setupRenderingParams(hashTableObj *layerMetadata)
479 {
480 AltitudeMode = 0;
481 Extrude = 0;
482 Tessellate = 0;
483
484 const char *altitudeModeVal = msLookupHashTable(layerMetadata, "kml_altitudeMode");
485 if (altitudeModeVal) {
486 if(strcasecmp(altitudeModeVal, "absolute") == 0)
487 AltitudeMode = absolute;
488 else if(strcasecmp(altitudeModeVal, "relativeToGround") == 0)
489 AltitudeMode = relativeToGround;
490 else if(strcasecmp(altitudeModeVal, "clampToGround") == 0)
491 AltitudeMode = clampToGround;
492 }
493
494 const char *extrudeVal = msLookupHashTable(layerMetadata, "kml_extrude");
495 if (altitudeModeVal) {
496 Extrude = atoi(extrudeVal);
497 }
498
499 const char *tessellateVal = msLookupHashTable(layerMetadata, "kml_tessellate");
500 if (tessellateVal) {
501 Tessellate = atoi(tessellateVal);
502 }
503
504 }
505
checkProjection(mapObj * map)506 int KmlRenderer::checkProjection(mapObj *map)
507 {
508 projectionObj *projection= &map->projection;
509 if (projection && projection->numargs > 0 && msProjIsGeographicCRS(projection)) {
510 return MS_SUCCESS;
511 } else {
512 char epsg_string[100];
513 rectObj sRect;
514 projectionObj out;
515
516 /* for layer the do not have any projection set, set them with the current map
517 projection*/
518 if (projection && projection->numargs > 0) {
519 layerObj *lp = NULL;
520 int i =0;
521 char *pszMapProjectString = msGetProjectionString(projection);
522 if (pszMapProjectString) {
523 for(i=0; i<map->numlayers; i++) {
524 lp = GET_LAYER(map, i);
525 if (lp->projection.numargs == 0 && lp->transform == MS_TRUE) {
526 msFreeProjection(&lp->projection);
527 msLoadProjectionString(&lp->projection, pszMapProjectString);
528 }
529 }
530 msFree(pszMapProjectString);
531 }
532 }
533 strcpy(epsg_string, "epsg:4326" );
534 msInitProjection(&out);
535 msProjectionInheritContextFrom(&out, projection);
536 msLoadProjectionString(&out, epsg_string);
537
538 sRect = map->extent;
539 msProjectRect(projection, &out, &sRect);
540 msFreeProjectionExceptContext(projection);
541 msLoadProjectionString(projection, epsg_string);
542
543 /*change also units and extents*/
544 map->extent = sRect;
545 map->units = MS_DD;
546
547
548 if (map->debug)
549 msDebug("KmlRenderer::checkProjection: Mapfile projection set to epsg:4326\n");
550
551 return MS_SUCCESS;
552 }
553 }
554
createPlacemarkNode(xmlNodePtr parentNode,char * styleUrl)555 xmlNodePtr KmlRenderer::createPlacemarkNode(xmlNodePtr parentNode, char *styleUrl)
556 {
557 xmlNodePtr placemarkNode = xmlNewChild(parentNode, NULL, BAD_CAST "Placemark", NULL);
558 /*always add a name. It will be replaced by a text value if available*/
559 char tmpid[100];
560 char *stmp=NULL, *layerName=NULL;
561 if (CurrentShapeName && strlen(CurrentShapeName)>0) {
562 xmlNewChild(placemarkNode, NULL, BAD_CAST "name", BAD_CAST CurrentShapeName);
563 } else {
564 sprintf(tmpid, ".%d", CurrentShapeIndex);
565 layerName = getLayerName(currentLayer);
566 stmp = msStringConcatenate(stmp, layerName);
567 stmp = msStringConcatenate(stmp, tmpid);
568 xmlNewChild(placemarkNode, NULL, BAD_CAST "name", BAD_CAST stmp);
569 }
570 msFree(layerName);
571 msFree(stmp);
572 if (styleUrl)
573 xmlNewChild(placemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
574
575 return placemarkNode;
576 }
577
renderLine(imageObj *,shapeObj * p,strokeStyleObj * style)578 void KmlRenderer::renderLine(imageObj*, shapeObj *p, strokeStyleObj *style)
579 {
580 if (p->numlines == 0)
581 return;
582
583 if (PlacemarkNode == NULL)
584 PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
585
586 if (!PlacemarkNode)
587 return;
588
589 addLineStyleToList(style);
590 SymbologyFlag[Line] = 1;
591
592 /*p->index > CurrentDrawnShapeIndexneed to be reviewd. Added since the hight
593 level code caches shapes when rendering lines*/
594 if (CurrentDrawnShapeIndex == -1 || p->index > CurrentDrawnShapeIndex) {
595 xmlNodePtr geomNode = getGeomParentNode("LineString");
596 addAddRenderingSpecifications(geomNode);
597 addCoordsNode(geomNode, p->line[0].point, p->line[0].numpoints);
598
599 /* more than one line => MultiGeometry*/
600 if (p->numlines > 1) {
601 geomNode = getGeomParentNode("LineString"); // returns MultiGeom Node
602 for (int i=1; i<p->numlines; i++) {
603 xmlNodePtr lineStringNode = xmlNewChild(geomNode, NULL, BAD_CAST "LineString", NULL);
604 addAddRenderingSpecifications(lineStringNode);
605 addCoordsNode(lineStringNode, p->line[i].point, p->line[i].numpoints);
606 }
607 }
608
609 CurrentDrawnShapeIndex = p->index;
610 }
611
612 }
613
renderPolygon(imageObj *,shapeObj * p,colorObj * color)614 void KmlRenderer::renderPolygon(imageObj*, shapeObj *p, colorObj *color)
615 {
616 if (PlacemarkNode == NULL)
617 PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
618
619 if (!PlacemarkNode)
620 return;
621
622 memcpy(&PolygonColor, color, sizeof(colorObj));
623 SymbologyFlag[Polygon] = 1;
624
625
626 if (p->index != CurrentDrawnShapeIndex) {
627
628 xmlNodePtr geomParentNode = getGeomParentNode("Polygon");
629
630 for (int i=0; i<p->numlines; i++) {
631 xmlNodePtr bdryNode = NULL;
632
633 if (i==0) /* __TODO__ check ring order*/
634 bdryNode = xmlNewChild(geomParentNode, NULL, BAD_CAST "outerBoundaryIs", NULL);
635 else
636 bdryNode = xmlNewChild(geomParentNode, NULL, BAD_CAST "innerBoundaryIs", NULL);
637
638 xmlNodePtr ringNode = xmlNewChild(bdryNode, NULL, BAD_CAST "LinearRing", NULL);
639 addAddRenderingSpecifications(ringNode);
640 addCoordsNode(ringNode, p->line[i].point, p->line[i].numpoints);
641 }
642
643 CurrentDrawnShapeIndex = p->index;
644
645 }
646
647 }
648
addCoordsNode(xmlNodePtr parentNode,pointObj * pts,int numPts)649 void KmlRenderer::addCoordsNode(xmlNodePtr parentNode, pointObj *pts, int numPts)
650 {
651 char lineBuf[128];
652
653 xmlNodePtr coordsNode = xmlNewChild(parentNode, NULL, BAD_CAST "coordinates", NULL);
654 xmlNodeAddContent(coordsNode, BAD_CAST "\n");
655
656 for (int i=0; i<numPts; i++) {
657 if( mElevationFromAttribute ) {
658 sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, mCurrentElevationValue);
659 } else if (AltitudeMode == relativeToGround || AltitudeMode == absolute) {
660 #ifdef USE_POINT_Z_M
661 sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, pts[i].z);
662 #else
663 msSetError(MS_MISCERR, "Z coordinates support not available (mapserver not compiled with USE_POINT_Z_M option)", "KmlRenderer::addCoordsNode()");
664 #endif
665 } else
666 sprintf(lineBuf, "\t%.8f,%.8f\n", pts[i].x, pts[i].y);
667
668 xmlNodeAddContent(coordsNode, BAD_CAST lineBuf);
669 }
670 xmlNodeAddContent(coordsNode, BAD_CAST "\t");
671 }
672
renderGlyphs(imageObj * img,pointObj * labelpnt,char * text,double angle,colorObj * clr,colorObj * olcolor,int olwidth)673 void KmlRenderer::renderGlyphs(imageObj *img, pointObj *labelpnt, char *text, double angle, colorObj *clr, colorObj *olcolor, int olwidth)
674 {
675 xmlNodePtr node;
676
677 if (PlacemarkNode == NULL)
678 PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
679
680 if (!PlacemarkNode)
681 return;
682
683 memcpy(&LabelColor, clr, sizeof(colorObj));
684 SymbologyFlag[Label] = 1;
685
686 /*there is alaws a default name (layer.shapeid). Replace it*/
687 for (node = PlacemarkNode->children; node; node = node->next) {
688 if (node->type != XML_ELEMENT_NODE)
689 continue;
690
691 if (strcmp((char *)node->name, "name") == 0) {
692 xmlNodeSetContent(node, BAD_CAST text);
693 break;
694 }
695 }
696
697 /*xmlNewChild(PlacemarkNode, NULL, BAD_CAST "name", BAD_CAST text);*/
698
699 xmlNodePtr geomNode = getGeomParentNode("Point");
700 addAddRenderingSpecifications(geomNode);
701
702 pointObj pt;
703 pt.x = labelpnt->x;
704 pt.y = labelpnt->y;
705 addCoordsNode(geomNode, &pt, 1);
706 }
707
addAddRenderingSpecifications(xmlNodePtr node)708 void KmlRenderer::addAddRenderingSpecifications(xmlNodePtr node)
709 {
710 /*
711 <extrude>0</extrude> <!-- boolean -->
712 <tessellate>0</tessellate> <!-- boolean -->
713 <altitudeMode>clampToGround</altitudeMode>
714 */
715
716 if (Extrude)
717 xmlNewChild(node, NULL, BAD_CAST "extrude", BAD_CAST "1");
718
719 if (Tessellate)
720 xmlNewChild(node, NULL, BAD_CAST "tessellate", BAD_CAST "1");
721
722 if (AltitudeMode == absolute)
723 xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "absolute");
724 else if (AltitudeMode == relativeToGround)
725 xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "relativeToGround");
726 else if (AltitudeMode == clampToGround)
727 xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "clampToGround");
728 }
729
730
731 imageObj *agg2CreateImage(int width, int height, outputFormatObj *format, colorObj * bg);
732
createIconImage(char * fileName,symbolObj * symbol,symbolStyleObj * symstyle)733 int KmlRenderer::createIconImage(char *fileName, symbolObj *symbol, symbolStyleObj *symstyle)
734 {
735 pointObj p;
736 int status;
737
738 imageObj *tmpImg = NULL;
739
740 tmpImg = agg2CreateImage((int)(symbol->sizex*symstyle->scale),
741 (int)(symbol->sizey*symstyle->scale),
742 aggFormat, NULL);
743 tmpImg->format = aggFormat;
744 if (!aggFormat->vtable)
745 msInitializeRendererVTable(aggFormat);
746
747 p.x = symbol->sizex * symstyle->scale / 2;
748 p.y = symbol->sizey *symstyle->scale / 2;
749 #ifdef USE_POINT_Z_M
750 p.z = 0.0;
751 #endif
752
753 status = msDrawMarkerSymbol(map,tmpImg, &p, symstyle->style, 1);
754 if( status != MS_SUCCESS )
755 return status;
756
757 return msSaveImage(map, tmpImg, fileName);
758 }
759
renderSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)760 void KmlRenderer::renderSymbol(imageObj *img, double x, double y, symbolObj *symbol, symbolStyleObj *style)
761 {
762 if (PlacemarkNode == NULL)
763 PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
764
765 if (!PlacemarkNode)
766 return;
767
768 snprintf(SymbolUrl, sizeof(SymbolUrl), "%s", lookupSymbolUrl(img, symbol, style));
769 SymbologyFlag[Symbol] = 1;
770
771 xmlNodePtr geomNode = getGeomParentNode("Point");
772 addAddRenderingSpecifications(geomNode);
773
774 pointObj pt;
775 pt.x = x;
776 pt.y = y;
777 addCoordsNode(geomNode, &pt, 1);
778 }
779
renderPixmapSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)780 void KmlRenderer::renderPixmapSymbol(imageObj *img, double x, double y, symbolObj *symbol,
781 symbolStyleObj *style)
782 {
783 renderSymbol(img, x, y, symbol, style);
784 }
785
renderVectorSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)786 void KmlRenderer::renderVectorSymbol(imageObj *img, double x, double y, symbolObj *symbol,
787 symbolStyleObj *style)
788 {
789 renderSymbol(img, x, y, symbol, style);
790 }
791
renderEllipseSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)792 void KmlRenderer::renderEllipseSymbol(imageObj *img, double x, double y, symbolObj *symbol,
793 symbolStyleObj *style)
794 {
795 renderSymbol(img, x, y, symbol, style);
796 }
797
renderTruetypeSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)798 void KmlRenderer::renderTruetypeSymbol(imageObj *img, double x, double y, symbolObj *symbol,
799 symbolStyleObj *style)
800 {
801 renderSymbol(img, x, y, symbol, style);
802 }
803
createGroundOverlayNode(xmlNodePtr parentNode,char * imageHref,layerObj * layer)804 xmlNodePtr KmlRenderer::createGroundOverlayNode(xmlNodePtr parentNode, char *imageHref, layerObj *layer)
805 {
806 /*
807 <?xml version="1.0" encoding="UTF-8"?>
808 <kml xmlns="http://www.opengis.net/kml/2.2">
809 <GroundOverlay>
810 <name>GroundOverlay.kml</name>
811 <color>7fffffff</color>
812 <drawOrder>1</drawOrder>
813 <Icon>
814 <href>http://www.google.com/intl/en/images/logo.gif</href>
815 <refreshMode>onInterval</refreshMode>
816 <refreshInterval>86400</refreshInterval>
817 <viewBoundScale>0.75</viewBoundScale>
818 </Icon>
819 <LatLonBox>
820 <north>37.83234</north>
821 <south>37.832122</south>
822 <east>-122.373033</east>
823 <west>-122.373724</west>
824 <rotation>45</rotation>
825 </LatLonBox>
826 </GroundOverlay>
827 </kml>
828 */
829 char layerHexColor[32];
830 xmlNodePtr groundOverlayNode = xmlNewChild(parentNode, NULL, BAD_CAST "GroundOverlay", NULL);
831 char *layerName = getLayerName(layer);
832 xmlNewChild(groundOverlayNode, NULL, BAD_CAST "name", BAD_CAST layerName);
833 if (layer->compositer && layer->compositer->opacity > 0 && layer->compositer->opacity < 100) {
834 sprintf(layerHexColor, "%02xffffff", (unsigned int)MS_NINT(layer->compositer->opacity*2.55));
835 xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color", BAD_CAST layerHexColor);
836 } else
837 xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color", BAD_CAST "ffffffff");
838 char stmp[20];
839 sprintf(stmp, "%d",layer->index);
840 xmlNewChild(groundOverlayNode, NULL, BAD_CAST "drawOrder", BAD_CAST stmp);
841
842 if (imageHref) {
843 xmlNodePtr iconNode = xmlNewChild(groundOverlayNode, NULL, BAD_CAST "Icon", NULL);
844 xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST imageHref);
845 }
846
847 char crdStr[64];
848 rectObj mapextent;
849 if (map->gt.need_geotransform == MS_TRUE)
850 mapextent = currentLayer->map->saved_extent;
851 else
852 mapextent = currentLayer->map->extent;
853
854 xmlNodePtr latLonBoxNode = xmlNewChild(groundOverlayNode, NULL, BAD_CAST "LatLonBox", NULL);
855 sprintf(crdStr, "%.8f", mapextent.maxy);
856 xmlNewChild(latLonBoxNode, NULL, BAD_CAST "north", BAD_CAST crdStr);
857
858 sprintf(crdStr, "%.8f", mapextent.miny);
859 xmlNewChild(latLonBoxNode, NULL, BAD_CAST "south", BAD_CAST crdStr);
860
861 sprintf(crdStr, "%.8f", mapextent.minx);
862 xmlNewChild(latLonBoxNode, NULL, BAD_CAST "west", BAD_CAST crdStr);
863
864 sprintf(crdStr, "%.8f", mapextent.maxx);
865 xmlNewChild(latLonBoxNode, NULL, BAD_CAST "east", BAD_CAST crdStr);
866
867 xmlNewChild(latLonBoxNode, NULL, BAD_CAST "rotation", BAD_CAST "0.0");
868
869 return groundOverlayNode;
870 }
871
startShape(imageObj *,shapeObj * shape)872 void KmlRenderer::startShape(imageObj *, shapeObj *shape)
873 {
874 if (PlacemarkNode)
875 flushPlacemark();
876
877 CurrentShapeIndex=-1;
878 CurrentDrawnShapeIndex = -1;
879 CurrentShapeName=NULL;
880
881 /*should be done at endshape but the plugin architecture does not call endshape yet*/
882 if(LineStyle) {
883 msFree(LineStyle);
884
885 LineStyle = NULL;
886 numLineStyle = 0;
887 }
888
889 CurrentShapeIndex = shape->index;
890 if (pszLayerNameAttributeMetadata) {
891 for (int i=0; i<currentLayer->numitems; i++) {
892 if (strcasecmp(currentLayer->items[i], pszLayerNameAttributeMetadata) == 0 && shape->values[i]) {
893 CurrentShapeName = msStrdup(shape->values[i]);
894 break;
895 }
896 }
897 }
898 PlacemarkNode = NULL;
899 GeomNode = NULL;
900
901 DescriptionNode = createDescriptionNode(shape);
902
903 if( mElevationFromAttribute && shape->numvalues > mElevationAttributeIndex &&
904 mElevationAttributeIndex >= 0 && shape->values[mElevationAttributeIndex]) {
905 mCurrentElevationValue = atof( shape->values[mElevationAttributeIndex] );
906 }
907
908
909 memset(SymbologyFlag, 0, NumSymbologyFlag);
910 }
911
endShape(imageObj *,shapeObj *)912 void KmlRenderer::endShape(imageObj*, shapeObj*)
913 {
914 CurrentShapeIndex = -1;
915 if (CurrentShapeName)
916 msFree(CurrentShapeName);
917 CurrentShapeName = NULL;
918 }
919
getGeomParentNode(const char * geomName)920 xmlNodePtr KmlRenderer::getGeomParentNode(const char *geomName)
921 {
922 /*we do not need a multi-geometry for point layers*/
923 if (currentLayer->type != MS_LAYER_POINT && currentLayer->type != MS_LAYER_ANNOTATION && GeomNode) {
924 /*placemark geometry already defined, we need multigeometry node*/
925 xmlNodePtr multiGeomNode = xmlNewNode(NULL, BAD_CAST "MultiGeometry");
926 xmlAddChild(multiGeomNode, GeomNode);
927 GeomNode = multiGeomNode;
928
929 xmlNodePtr geomNode = xmlNewChild(multiGeomNode, NULL, BAD_CAST geomName, NULL);
930 return geomNode;
931 } else {
932 GeomNode = xmlNewNode(NULL, BAD_CAST geomName);
933 return GeomNode;
934 }
935 }
936
lookupSymbolUrl(imageObj * img,symbolObj * symbol,symbolStyleObj * symstyle)937 const char* KmlRenderer::lookupSymbolUrl(imageObj *img, symbolObj *symbol, symbolStyleObj *symstyle)
938 {
939 char symbolHexColor[32];
940 /*
941 <Style id="randomColorIcon">
942 <IconStyle>
943 <color>ff00ff00</color>
944 <colorMode>random</colorMode>
945 <scale>1.1</scale>
946 <Icon>
947 <href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
948 </Icon>
949 </IconStyle>
950 </Style>
951 */
952
953 sprintf(symbolHexColor,"%02x%02x%02x%02x", symstyle->style->color.alpha, symstyle->style->color.blue,
954 symstyle->style->color.green, symstyle->style->color.red);
955 snprintf(SymbolName, sizeof(SymbolName), "symbol_%s_%.1f_%s", symbol->name, symstyle->scale, symbolHexColor);
956
957 const char *symbolUrl = msLookupHashTable(StyleHashTable, SymbolName);
958 if (!symbolUrl) {
959 char iconFileName[MS_MAXPATHLEN];
960 char iconUrl[MS_MAXPATHLEN];
961
962 if (img->imagepath) {
963 char *tmpFileName = msTmpFile(NULL, MapPath, img->imagepath, "png");
964 snprintf(iconFileName, sizeof(iconFileName), "%s", tmpFileName);
965 msFree(tmpFileName);
966 } else {
967 sprintf(iconFileName, "symbol_%s_%.1f.%s", symbol->name, symstyle->scale, "png");
968 }
969
970 if (createIconImage(iconFileName, symbol, symstyle) != MS_SUCCESS) {
971 msSetError(MS_IOERR, "Error creating icon file '%s'", "KmlRenderer::lookupSymbolStyle()", iconFileName);
972 return NULL;
973 }
974
975 if (img->imageurl)
976 sprintf(iconUrl, "%s%s.%s", img->imageurl, msGetBasename(iconFileName), "png");
977 else
978 snprintf(iconUrl, sizeof(iconUrl), "%s", iconFileName);
979
980 hashObj *hash = msInsertHashTable(StyleHashTable, SymbolName, iconUrl);
981 symbolUrl = hash->data;
982 }
983
984 return symbolUrl;
985 }
986
lookupPlacemarkStyle()987 const char* KmlRenderer::lookupPlacemarkStyle()
988 {
989 char lineHexColor[32];
990 char polygonHexColor[32];
991 char labelHexColor[32];
992 char *styleName=NULL;
993
994 styleName = msStringConcatenate(styleName, "style");
995
996 if (SymbologyFlag[Line]) {
997 /*
998 <LineStyle id="ID">
999 <!-- inherited from ColorStyle -->
1000 <color>ffffffff</color> <!-- kml:color -->
1001 <colorMode>normal</colorMode> <!-- colorModeEnum: normal or random -->
1002
1003 <!-- specific to LineStyle -->
1004 <width>1</width> <!-- float -->
1005 </LineStyle>
1006 */
1007
1008 for (int i=0; i<numLineStyle; i++) {
1009 if (currentLayer && currentLayer->compositer && currentLayer->compositer->opacity > 0 && currentLayer->compositer->opacity < 100 &&
1010 LineStyle[i].color->alpha == 255)
1011 LineStyle[i].color->alpha = MS_NINT(currentLayer->compositer->opacity*2.55);
1012
1013 sprintf(lineHexColor,"%02x%02x%02x%02x", LineStyle[i].color->alpha, LineStyle[0].color->blue,
1014 LineStyle[i].color->green, LineStyle[i].color->red);
1015
1016 char lineStyleName[32];
1017 sprintf(lineStyleName, "_line_%s_w%.1f", lineHexColor, LineStyle[i].width);
1018 styleName = msStringConcatenate(styleName, lineStyleName);
1019 }
1020 }
1021
1022 if (SymbologyFlag[Polygon]) {
1023 /*
1024 <PolyStyle id="ID">
1025 <!-- inherited from ColorStyle -->
1026 <color>ffffffff</color> <!-- kml:color -->
1027 <colorMode>normal</colorMode> <!-- kml:colorModeEnum: normal or random -->
1028
1029 <!-- specific to PolyStyle -->
1030 <fill>1</fill> <!-- boolean -->
1031 <outline>1</outline> <!-- boolean -->
1032 </PolyStyle>
1033 */
1034
1035 if (currentLayer && currentLayer->compositer && currentLayer->compositer->opacity > 0 && currentLayer->compositer->opacity < 100 &&
1036 PolygonColor.alpha == 255)
1037 PolygonColor.alpha = MS_NINT(currentLayer->compositer->opacity*2.55);
1038 sprintf(polygonHexColor,"%02x%02x%02x%02x", PolygonColor.alpha, PolygonColor.blue, PolygonColor.green, PolygonColor.red);
1039
1040 char polygonStyleName[64];
1041 sprintf(polygonStyleName, "_polygon_%s", polygonHexColor);
1042 styleName = msStringConcatenate(styleName, polygonStyleName);
1043 }
1044
1045 if (SymbologyFlag[Label]) {
1046 /*
1047 <LabelStyle id="ID">
1048 <!-- inherited from ColorStyle -->
1049 <color>ffffffff</color> <!-- kml:color -->
1050 <colorMode>normal</colorMode> <!-- kml:colorModeEnum: normal or random -->
1051
1052 <!-- specific to LabelStyle -->
1053 <scale>1</scale> <!-- float -->
1054 </LabelStyle>
1055 */
1056
1057 if (currentLayer && currentLayer->compositer && currentLayer->compositer->opacity > 0 && currentLayer->compositer->opacity < 100 &&
1058 LabelColor.alpha == 255)
1059 LabelColor.alpha = MS_NINT(currentLayer->compositer->opacity*2.55);
1060 sprintf(labelHexColor,"%02x%02x%02x%02x", LabelColor.alpha, LabelColor.blue, LabelColor.green, LabelColor.red);
1061
1062 // __TODO__ add label scale
1063
1064 char labelStyleName[64];
1065 sprintf(labelStyleName, "_label_%s", labelHexColor);
1066 styleName = msStringConcatenate(styleName, labelStyleName);
1067 }
1068
1069 if (SymbologyFlag[Symbol]) {
1070 /*
1071 <Style id="randomColorIcon">
1072 <IconStyle>
1073 <color>ff00ff00</color>
1074 <colorMode>random</colorMode>
1075 <scale>1.1</scale>
1076 <Icon>
1077 <href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
1078 </Icon>
1079 </IconStyle>
1080 </Style>
1081 */
1082
1083 /* __TODO__ add label scale */
1084
1085 styleName = msStringConcatenate(styleName, "_");
1086 styleName = msStringConcatenate(styleName, SymbolName);
1087 }
1088
1089 const char *styleUrl = msLookupHashTable(StyleHashTable, styleName);
1090 if (!styleUrl) {
1091 char *styleValue=NULL;
1092 styleValue = msStringConcatenate(styleValue, "#");
1093 styleValue = msStringConcatenate(styleValue, styleName);
1094 hashObj *hash = msInsertHashTable(StyleHashTable, styleName, styleValue);
1095 styleUrl = hash->data;
1096 msFree(styleValue);
1097
1098 /* Insert new Style node into Document node*/
1099 xmlNodePtr styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
1100 xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST styleName);
1101
1102 if (SymbologyFlag[Polygon]) {
1103 xmlNodePtr polyStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "PolyStyle", NULL);
1104 xmlNewChild(polyStyleNode, NULL, BAD_CAST "color", BAD_CAST polygonHexColor);
1105 }
1106
1107 if (SymbologyFlag[Line]) {
1108 for (int i=0; i<numLineStyle; i++) {
1109 xmlNodePtr lineStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "LineStyle", NULL);
1110 sprintf(lineHexColor,"%02x%02x%02x%02x", LineStyle[i].color->alpha, LineStyle[i].color->blue,
1111 LineStyle[i].color->green, LineStyle[i].color->red);
1112 xmlNewChild(lineStyleNode, NULL, BAD_CAST "color", BAD_CAST lineHexColor);
1113
1114 char width[16];
1115 sprintf(width, "%.1f", LineStyle[i].width);
1116 xmlNewChild(lineStyleNode, NULL, BAD_CAST "width", BAD_CAST width);
1117 }
1118 }
1119
1120 if (SymbologyFlag[Symbol]) {
1121 xmlNodePtr iconStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
1122
1123 xmlNodePtr iconNode = xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
1124 xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST SymbolUrl);
1125
1126 /*char scale[16];
1127 sprintf(scale, "%.1f", style->scale);
1128 xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1129 } else {
1130 const char *value=msLookupHashTable(¤tLayer->metadata, "kml_default_symbol_href");
1131 if (value && strlen(value) > 0) {
1132 xmlNodePtr iconStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
1133
1134 xmlNodePtr iconNode = xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
1135 xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST value);
1136 }
1137 }
1138
1139 if (SymbologyFlag[Label]) {
1140 xmlNodePtr labelStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "LabelStyle", NULL);
1141 xmlNewChild(labelStyleNode, NULL, BAD_CAST "color", BAD_CAST labelHexColor);
1142
1143 /*char scale[16];
1144 sprintf(scale, "%.1f", style->scale);
1145 xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
1146 }
1147 }
1148
1149 if (styleName)
1150 msFree(styleName);
1151
1152 return styleUrl;
1153 }
1154
flushPlacemark()1155 void KmlRenderer::flushPlacemark()
1156 {
1157 if (PlacemarkNode) {
1158 const char *styleUrl = lookupPlacemarkStyle();
1159 xmlNewChild(PlacemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
1160
1161 if (DescriptionNode)
1162 xmlAddChild(PlacemarkNode, DescriptionNode);
1163
1164 if (GeomNode)
1165 xmlAddChild(PlacemarkNode, GeomNode);
1166 }
1167 }
1168
1169
createDescriptionNode(shapeObj * shape)1170 xmlNodePtr KmlRenderer::createDescriptionNode(shapeObj *shape)
1171 {
1172 /*
1173 <description>
1174 <![CDATA[
1175 special characters here
1176 ]]>
1177 <description>
1178 */
1179
1180
1181 /*description nodes for vector layers:
1182 - if kml_description is set, use it
1183 - if not, dump the attributes */
1184
1185 if (pszLayerDescMetadata) {
1186 char *pszTmp=NULL;
1187 char *pszTmpDesc = NULL;
1188 size_t bufferSize = 0;
1189 pszTmpDesc = msStrdup(pszLayerDescMetadata);
1190
1191 for (int i=0; i<currentLayer->numitems; i++) {
1192 bufferSize = strlen(currentLayer->items[i]) + 3;
1193 pszTmp = (char *)msSmallMalloc(bufferSize);
1194 snprintf(pszTmp, bufferSize, "%%%s%%",currentLayer->items[i]);
1195 if (strcasestr(pszTmpDesc, pszTmp))
1196 pszTmpDesc = msCaseReplaceSubstring(pszTmpDesc, pszTmp, shape->values[i]);
1197 msFree(pszTmp);
1198 }
1199 xmlNodePtr descriptionNode = xmlNewNode(NULL, BAD_CAST "description");
1200 xmlNodeAddContent(descriptionNode, BAD_CAST pszTmpDesc);
1201 msFree(pszTmpDesc);
1202 return descriptionNode;
1203 } else if ((papszLayerIncludeItems && nIncludeItems > 0) ||
1204 (papszLayerExcludeItems && nExcludeItems > 0)) {
1205 /* -------------------------------------------------------------------- */
1206 /* preffered way is to use the ExtendedData tag (#3728) */
1207 /* http://code.google.com/apis/kml/documentation/extendeddata.html */
1208 /* -------------------------------------------------------------------- */
1209
1210 xmlNodePtr extendedDataNode = xmlNewNode(NULL, BAD_CAST "ExtendedData");
1211 xmlNodePtr dataNode = NULL;
1212 const char*pszAlias=NULL;
1213 int bIncludeAll = MS_FALSE;
1214
1215 if(papszLayerIncludeItems && nIncludeItems == 1 &&
1216 strcasecmp(papszLayerIncludeItems[0], "all") == 0)
1217 bIncludeAll = MS_TRUE;
1218
1219 for (int i=0; i<currentLayer->numitems; i++) {
1220 int j=0,k=0;
1221
1222 /*TODO optimize to calculate this only once per layer*/
1223 for (j=0; j<nIncludeItems; j++) {
1224 if (strcasecmp(currentLayer->items[i], papszLayerIncludeItems[j]) == 0)
1225 break;
1226 }
1227 if (j<nIncludeItems || bIncludeAll) {
1228 if (papszLayerExcludeItems && nExcludeItems > 0) {
1229 for (k=0; k<nExcludeItems; k++) {
1230 if (strcasecmp(currentLayer->items[i], papszLayerExcludeItems[k]) == 0)
1231 break;
1232 }
1233 }
1234 if (nExcludeItems == 0 || k == nExcludeItems) {
1235 dataNode = xmlNewNode(NULL, BAD_CAST "Data");
1236 xmlNewProp(dataNode, BAD_CAST "name", BAD_CAST currentLayer->items[i]);
1237 pszAlias = getAliasName(currentLayer, currentLayer->items[i], "GO");
1238 if (pszAlias)
1239 xmlNewChild(dataNode, NULL, BAD_CAST "displayName", BAD_CAST pszAlias);
1240 else
1241 xmlNewChild(dataNode, NULL, BAD_CAST "displayName", BAD_CAST currentLayer->items[i]);
1242 if (shape->values[i] && strlen(shape->values[i]))
1243 xmlNewChild(dataNode, NULL, BAD_CAST "value", BAD_CAST shape->values[i]);
1244 else
1245 xmlNewChild(dataNode, NULL, BAD_CAST "value", NULL);
1246 xmlAddChild(extendedDataNode, dataNode);
1247 }
1248 }
1249 }
1250
1251 return extendedDataNode;
1252
1253
1254 }
1255
1256 return NULL;
1257 }
1258
addLineStyleToList(strokeStyleObj * style)1259 void KmlRenderer::addLineStyleToList(strokeStyleObj *style)
1260 {
1261 /*actually this is not necessary. kml only uses the last LineStyle
1262 so we should not bother keeping them all*/
1263 int i =0;
1264 for (i=0; i<numLineStyle; i++) {
1265 if (style->width == LineStyle[i].width &&
1266 LineStyle[i].color->alpha == style->color->alpha &&
1267 LineStyle[i].color->red == style->color->red &&
1268 LineStyle[i].color->green == style->color->green &&
1269 LineStyle[i].color->blue == style->color->blue)
1270 break;
1271 }
1272 if (i == numLineStyle) {
1273 numLineStyle++;
1274 if (LineStyle == NULL)
1275 LineStyle = (strokeStyleObj *)msSmallMalloc(sizeof(strokeStyleObj));
1276 else
1277 LineStyle = (strokeStyleObj *)msSmallRealloc(LineStyle, sizeof(strokeStyleObj)*numLineStyle);
1278
1279 memcpy(&LineStyle[numLineStyle-1], style, sizeof(strokeStyleObj));
1280 }
1281
1282 }
1283
1284 #endif
1285