Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 4e50373df6
Fetching contributors…

Cannot retrieve contributors at this time

1302 lines (1048 sloc) 42.5 kb
/******************************************************************************
* $Id$
*
* Project: MapServer
* Purpose: Google Earth KML output
* Author: David Kana and the MapServer team
*
******************************************************************************
* Copyright (c) 1996-2009 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#ifdef USE_KML
#include "mapserver.h"
#include "maperror.h"
#include "mapkmlrenderer.h"
#include "mapio.h"
#if defined(USE_OGR)
# include "cpl_conv.h"
# include "cpl_vsi.h"
#endif
#define KML_MAXFEATURES_TODRAW 1000
KmlRenderer::KmlRenderer(int width, int height, outputFormatObj *format, colorObj* color/*=NULL*/)
: Width(width), Height(height), MapCellsize(1.0), XmlDoc(NULL), LayerNode(NULL), GroundOverlayNode(NULL),
PlacemarkNode(NULL), GeomNode(NULL),
Items(NULL), NumItems(0), FirstLayer(MS_TRUE), map(NULL), currentLayer(NULL),
mElevationFromAttribute( false ), mElevationAttributeIndex( -1 ), mCurrentElevationValue(0.0)
{
/*private variables*/
pszLayerDescMetadata = NULL;
papszLayerIncludeItems = NULL;
nIncludeItems=0;
papszLayerExcludeItems = NULL;
nExcludeItems=0;
pszLayerNameAttributeMetadata = NULL; /*metadata to use for a name for each feature*/
LineStyle = NULL;
numLineStyle = 0;
xmlNodePtr styleNode;
xmlNodePtr listStyleNode;
/* Create document.*/
XmlDoc = xmlNewDoc(BAD_CAST "1.0");
xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "kml");
/* Name spaces*/
xmlSetNs(rootNode, xmlNewNs(rootNode, BAD_CAST "http://www.opengis.net/kml/2.2", NULL));
xmlDocSetRootElement(XmlDoc, rootNode);
DocNode = xmlNewChild(rootNode, NULL, BAD_CAST "Document", NULL);
styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_check");
listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "check");
styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_checkHideChildren");
listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "checkHideChildren");
styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_checkOffOnly");
listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "checkOffOnly");
styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST "LayerFolder_radioFolder");
listStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "ListStyle", NULL);
xmlNewChild(listStyleNode, NULL, BAD_CAST "listItemType", BAD_CAST "radioFolder");
StyleHashTable = msCreateHashTable();
}
KmlRenderer::~KmlRenderer()
{
if (XmlDoc)
xmlFreeDoc(XmlDoc);
if (StyleHashTable)
msFreeHashTable(StyleHashTable);
if(LineStyle)
msFree(LineStyle);
xmlCleanupParser();
}
imageObj* KmlRenderer::createImage(int, int, outputFormatObj*, colorObj*)
{
return NULL;
}
int KmlRenderer::saveImage(imageObj *, FILE *fp, outputFormatObj *format)
{
/* -------------------------------------------------------------------- */
/* Write out the document. */
/* -------------------------------------------------------------------- */
int bufSize = 0;
xmlChar *buf = NULL;
msIOContext *context = NULL;
int chunkSize = 4096;
#if defined(CPL_ZIP_API_OFFERED)
int bZip = MS_FALSE;
#endif
if( msIO_needBinaryStdout() == MS_FAILURE )
return MS_FAILURE;
xmlDocDumpFormatMemoryEnc(XmlDoc, &buf, &bufSize, "UTF-8", 1);
#if defined(USE_OGR)
if (format && format->driver && strcasecmp(format->driver, "kmz") == 0) {
#if defined(CPL_ZIP_API_OFFERED)
bZip = MS_TRUE;
#else
msSetError( MS_MISCERR, "kmz format support unavailable, perhaps you need to upgrade to GDAL/OGR 1.8?",
"KmlRenderer::saveImage()");
xmlFree(buf);
return MS_FAILURE;
#endif
}
#if defined(CPL_ZIP_API_OFFERED)
if (bZip) {
VSILFILE *fpZip;
int bytes_read;
char buffer[1024];
char *zip_filename =NULL;
void *hZip=NULL;
zip_filename = msTmpFile(NULL, NULL, "/vsimem/kmlzip/", "kmz" );
hZip = CPLCreateZip( zip_filename, NULL );
CPLCreateFileInZip( hZip, "mapserver.kml", NULL );
for (int i=0; i<bufSize; i+=chunkSize) {
int size = chunkSize;
if (i + size > bufSize)
size = bufSize - i;
CPLWriteFileInZip( hZip, buf+i, size);
}
CPLCloseFileInZip( hZip );
CPLCloseZip( hZip );
context = msIO_getHandler(fp);
fpZip = VSIFOpenL( zip_filename, "r" );
while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fpZip )) > 0 ) {
if (context)
msIO_contextWrite(context, buffer, bytes_read);
else
msIO_fwrite( buffer, 1, bytes_read, fp );
}
VSIFCloseL( fpZip );
msFree( zip_filename);
xmlFree(buf);
return(MS_SUCCESS);
}
#endif
#endif
context = msIO_getHandler(fp);
for (int i=0; i<bufSize; i+=chunkSize) {
int size = chunkSize;
if (i + size > bufSize)
size = bufSize - i;
if (context)
msIO_contextWrite(context, buf+i, size);
else
msIO_fwrite(buf+i, 1, size, fp);
}
xmlFree(buf);
return(MS_SUCCESS);
}
/************************************************************************/
/* processLayer */
/* */
/* Set parameters that make sense to a kml output. */
/************************************************************************/
void KmlRenderer::processLayer(layerObj *layer, outputFormatObj *format)
{
int i;
const char *asRaster = NULL;
int nMaxFeatures = -1;
const char *pszTmp;
char szTmp[10];
if (!layer)
return;
/*turn of labelcache*/
layer->labelcache = MS_OFF;
/*if there are labels we want the coordinates to
be the center of the element.*/
for(i=0; i<layer->numclasses; i++)
if(layer->_class[i]->numlabels > 0) layer->_class[i]->labels[0]->position = MS_XY;
/*we do not want to draw multiple styles.
the new rendering architecture does not allow
to know if we are dealing with a multi-style.
So here we remove all styles beside the first one*/
for(i=0; i<layer->numclasses; i++) {
while (layer->_class[i]->numstyles > 1)
msDeleteStyle(layer->_class[i], layer->_class[i]->numstyles-1);
}
/*if layer has a metadata KML_OUTPUTASRASTER set to true, add a processing directive
to use an agg driver*/
asRaster = msLookupHashTable(&layer->metadata, "kml_outputasraster");
if (!asRaster)
asRaster = msLookupHashTable(&(layer->map->web.metadata), "kml_outputasraster");
if (asRaster && (strcasecmp(asRaster, "true") == 0 ||
strcasecmp(asRaster, "yes") == 0))
msLayerAddProcessing(layer, "RENDERER=png24");
/*set a maxfeaturestodraw, if not already set*/
pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
if (pszTmp)
nMaxFeatures = atoi(pszTmp);
else {
pszTmp = msLookupHashTable(&layer->map->web.metadata, "maxfeaturestodraw");
if (pszTmp)
nMaxFeatures = atoi(pszTmp);
}
if (nMaxFeatures < 0 && format)
nMaxFeatures = atoi(msGetOutputFormatOption( format, "maxfeaturestodraw", "-1"));
if (nMaxFeatures < 0 && format) {
snprintf(szTmp, sizeof(szTmp), "%d", KML_MAXFEATURES_TODRAW);
msSetOutputFormatOption( format, "maxfeaturestodraw", szTmp);
}
}
/************************************************************************/
/* getLayerName */
/* */
/* Internal utility function to build name used fo rthe layer. */
/************************************************************************/
char* KmlRenderer::getLayerName(layerObj *layer)
{
char stmp[20];
const char *name=NULL;;
if (!layer)
return NULL;
name = msLookupHashTable(&layer->metadata, "ows_name");
if (name && strlen(name) > 0)
return msStrdup(name);
if (layer->name && strlen(layer->name) > 0)
return msStrdup(layer->name);
sprintf(stmp, "Layer%d",layer->index);
return msStrdup(stmp);
}
const char* KmlRenderer::getAliasName(layerObj *lp, char *pszItemName, const char *namespaces)
{
const char *pszAlias = NULL;
if (lp && pszItemName && strlen(pszItemName) > 0) {
char szTmp[256];
snprintf(szTmp, sizeof(szTmp), "%s_alias", pszItemName);
pszAlias = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
}
return pszAlias;
}
int KmlRenderer::startNewLayer(imageObj *img, layerObj *layer)
{
char *layerName=NULL;
const char *value=NULL;
LayerNode = xmlNewNode(NULL, BAD_CAST "Folder");
layerName = getLayerName(layer);
xmlNewChild(LayerNode, NULL, BAD_CAST "name", BAD_CAST layerName);
msFree(layerName);
const char *layerVisibility = layer->status != MS_OFF ? "1" : "0";
xmlNewChild(LayerNode, NULL, BAD_CAST "visibility", BAD_CAST layerVisibility);
const char *layerDsiplayFolder = msLookupHashTable(&(layer->metadata), "kml_folder_display");
if (layerDsiplayFolder == NULL)
layerDsiplayFolder = msLookupHashTable(&(layer->map->web.metadata), "kml_folder_display");
if (!layerDsiplayFolder || strlen(layerDsiplayFolder)<=0) {
xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_check");
}
else {
if (strcasecmp(layerDsiplayFolder, "checkHideChildren") == 0)
xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_checkHideChildren");
else if (strcasecmp(layerDsiplayFolder, "checkOffOnly") == 0)
xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_checkOffOnly");
else if (strcasecmp(layerDsiplayFolder, "radioFolder") == 0)
xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_radioFolder");
else
xmlNewChild(LayerNode, NULL, BAD_CAST "styleUrl", BAD_CAST "#LayerFolder_check");
}
/*Init few things on the first layer*/
if (FirstLayer) {
FirstLayer = MS_FALSE;
map = layer->map;
if (layer->map->mappath)
snprintf(MapPath, sizeof(MapPath), "%s", layer->map->mappath);
/*First rendered layer - check mapfile projection*/
checkProjection(layer->map);
/*check for image path and image url*/
if (layer->map->debug && (layer->map->web.imageurl == NULL || layer->map->web.imagepath == NULL))
msDebug("KmlRenderer::startNewLayer: imagepath and imageurl sould be set in the web object\n");
/*map rect for ground overlay*/
MapExtent = layer->map->extent;
MapCellsize = layer->map->cellsize;
BgColor = layer->map->imagecolor;
xmlNewChild(DocNode, NULL, BAD_CAST "name", BAD_CAST layer->map->name);
aggFormat = msSelectOutputFormat( layer->map, "png24");
aggFormat->transparent = MS_TRUE;
}
currentLayer = layer;
if (!msLayerIsOpen(layer)) {
if (msLayerOpen(layer) != MS_SUCCESS) {
msSetError(MS_MISCERR, "msLayerOpen failed", "KmlRenderer::startNewLayer" );
}
}
/*pre process the layer to set things that make sense for kml output*/
if (img)
processLayer(layer, img->format);
else
processLayer(layer, NULL);
if (msLookupHashTable(&layer->metadata, "kml_description"))
pszLayerDescMetadata = msLookupHashTable(&layer->metadata, "kml_description");
else if (msLookupHashTable(&layer->metadata, "ows_description"))
pszLayerDescMetadata = msLookupHashTable(&layer->metadata, "ows_description");
value=msLookupHashTable(&layer->metadata, "kml_include_items");
if (!value)
value=msLookupHashTable(&layer->metadata, "ows_include_items");
if (value)
papszLayerIncludeItems = msStringSplit(value, ',', &nIncludeItems);
value=msLookupHashTable(&layer->metadata, "kml_exclude_items");
if (!value)
value=msLookupHashTable(&layer->metadata, "ows_exclude_items");
if (value)
papszLayerExcludeItems = msStringSplit(value, ',', &nExcludeItems);
if (msLookupHashTable(&layer->metadata, "kml_name_item"))
pszLayerNameAttributeMetadata = msLookupHashTable(&layer->metadata, "kml_name_item");
/*get all attributes*/
msLayerWhichItems(layer, MS_TRUE, NULL);
NumItems = layer->numitems;
if (NumItems) {
Items = (char **)msSmallCalloc(NumItems, sizeof(char *));
for (int i=0; i<NumItems; i++)
Items[i] = msStrdup(layer->items[i]);
}
char* elevationAttribute = msLookupHashTable(&layer->metadata, "kml_elevation_attribute");
if( elevationAttribute ) {
mElevationFromAttribute = true;
for( int i = 0; i < layer->numitems; ++i ) {
if( strcasecmp( layer->items[i], elevationAttribute ) == 0 ) {
mElevationAttributeIndex = i;
}
}
}
setupRenderingParams(&layer->metadata);
return MS_SUCCESS;
}
int KmlRenderer::closeNewLayer(imageObj *img, layerObj *layer)
{
flushPlacemark();
xmlAddChild(DocNode, LayerNode);
if(Items) {
msFreeCharArray(Items, NumItems);
Items = NULL;
NumItems = 0;
}
if (pszLayerDescMetadata)
pszLayerDescMetadata = NULL;
if (pszLayerNameAttributeMetadata)
pszLayerNameAttributeMetadata = NULL;
if (papszLayerIncludeItems && nIncludeItems>0)
msFreeCharArray(papszLayerIncludeItems, nIncludeItems);
papszLayerIncludeItems=NULL;
if (papszLayerExcludeItems && nExcludeItems>0)
msFreeCharArray(papszLayerExcludeItems, nExcludeItems);
papszLayerExcludeItems=NULL;
return MS_SUCCESS;
}
int KmlRenderer::mergeRasterBuffer(imageObj *image, rasterBufferObj *rb)
{
assert(rb && rb->type == MS_BUFFER_BYTE_RGBA);
char *tmpFileName = NULL;
char *tmpUrl = NULL;
FILE *tmpFile = NULL;
tmpFileName = msTmpFile(NULL, MapPath, image->imagepath, "png");
tmpFile = fopen(tmpFileName,"wb");
if (tmpFile) {
if (!aggFormat->vtable)
msInitializeRendererVTable(aggFormat);
msSaveRasterBuffer(map,rb,tmpFile,aggFormat);
tmpUrl = msStrdup( image->imageurl);
tmpUrl = msStringConcatenate(tmpUrl, (char *)(msGetBasename(tmpFileName)));
tmpUrl = msStringConcatenate(tmpUrl, ".png");
createGroundOverlayNode(LayerNode, tmpUrl, currentLayer);
msFree(tmpFileName);
msFree(tmpUrl);
fclose(tmpFile);
return MS_SUCCESS;
} else {
msSetError(MS_IOERR,"Failed to create file for kml overlay","KmlRenderer::mergeRasterBuffer()");
return MS_FAILURE;
}
}
void KmlRenderer::setupRenderingParams(hashTableObj *layerMetadata)
{
AltitudeMode = 0;
Extrude = 0;
Tessellate = 0;
char *altitudeModeVal = msLookupHashTable(layerMetadata, "kml_altitudeMode");
if (altitudeModeVal) {
if(strcasecmp(altitudeModeVal, "absolute") == 0)
AltitudeMode = absolute;
else if(strcasecmp(altitudeModeVal, "relativeToGround") == 0)
AltitudeMode = relativeToGround;
else if(strcasecmp(altitudeModeVal, "clampToGround") == 0)
AltitudeMode = clampToGround;
}
char *extrudeVal = msLookupHashTable(layerMetadata, "kml_extrude");
if (altitudeModeVal) {
Extrude = atoi(extrudeVal);
}
char *tessellateVal = msLookupHashTable(layerMetadata, "kml_tessellate");
if (tessellateVal) {
Tessellate = atoi(tessellateVal);
}
}
int KmlRenderer::checkProjection(mapObj *map)
{
projectionObj *projection= &map->projection;
#ifdef USE_PROJ
if (projection && projection->numargs > 0 && pj_is_latlong(projection->proj)) {
return MS_SUCCESS;
} else {
char epsg_string[100];
rectObj sRect;
projectionObj out;
/* for layer the do not have any projection set, set them with the current map
projection*/
if (projection && projection->numargs > 0) {
layerObj *lp = NULL;
int i =0;
char *pszMapProjectString = msGetProjectionString(projection);
if (pszMapProjectString) {
for(i=0; i<map->numlayers; i++) {
lp = GET_LAYER(map, i);
if (lp->projection.numargs == 0 && lp->transform == MS_TRUE) {
msFreeProjection(&lp->projection);
msLoadProjectionString(&lp->projection, pszMapProjectString);
}
}
msFree(pszMapProjectString);
}
}
strcpy(epsg_string, "epsg:4326" );
msInitProjection(&out);
msLoadProjectionString(&out, epsg_string);
sRect = map->extent;
msProjectRect(projection, &out, &sRect);
msFreeProjection(projection);
msLoadProjectionString(projection, epsg_string);
/*change also units and extents*/
map->extent = sRect;
map->units = MS_DD;
if (map->debug)
msDebug("KmlRenderer::checkProjection: Mapfile projection set to epsg:4326\n");
return MS_SUCCESS;
}
#else
msSetError(MS_MISCERR, "Projection support not enabled", "KmlRenderer::checkProjection" );
return MS_FAILURE;
#endif
}
xmlNodePtr KmlRenderer::createPlacemarkNode(xmlNodePtr parentNode, char *styleUrl)
{
xmlNodePtr placemarkNode = xmlNewChild(parentNode, NULL, BAD_CAST "Placemark", NULL);
/*always add a name. It will be replaced by a text value if available*/
char tmpid[100];
char *stmp=NULL, *layerName=NULL;
if (CurrentShapeName && strlen(CurrentShapeName)>0) {
xmlNewChild(placemarkNode, NULL, BAD_CAST "name", BAD_CAST CurrentShapeName);
} else {
sprintf(tmpid, ".%d", CurrentShapeIndex);
layerName = getLayerName(currentLayer);
stmp = msStringConcatenate(stmp, layerName);
stmp = msStringConcatenate(stmp, tmpid);
xmlNewChild(placemarkNode, NULL, BAD_CAST "name", BAD_CAST stmp);
}
msFree(layerName);
msFree(stmp);
if (styleUrl)
xmlNewChild(placemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
return placemarkNode;
}
void KmlRenderer::renderLine(imageObj*, shapeObj *p, strokeStyleObj *style)
{
if (p->numlines == 0)
return;
if (PlacemarkNode == NULL)
PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
if (!PlacemarkNode)
return;
addLineStyleToList(style);
SymbologyFlag[Line] = 1;
/*p->index > CurrentDrawnShapeIndexneed to be reviewd. Added since the hight
level code caches shapes when rendering lines*/
if (CurrentDrawnShapeIndex == -1 || p->index > CurrentDrawnShapeIndex) {
xmlNodePtr geomNode = getGeomParentNode("LineString");
addAddRenderingSpecifications(geomNode);
addCoordsNode(geomNode, p->line[0].point, p->line[0].numpoints);
/* more than one line => MultiGeometry*/
if (p->numlines > 1) {
geomNode = getGeomParentNode("LineString"); // returns MultiGeom Node
for (int i=1; i<p->numlines; i++) {
xmlNodePtr lineStringNode = xmlNewChild(geomNode, NULL, BAD_CAST "LineString", NULL);
addAddRenderingSpecifications(lineStringNode);
addCoordsNode(lineStringNode, p->line[i].point, p->line[i].numpoints);
}
}
CurrentDrawnShapeIndex = p->index;
}
}
void KmlRenderer::renderPolygon(imageObj*, shapeObj *p, colorObj *color)
{
if (PlacemarkNode == NULL)
PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
if (!PlacemarkNode)
return;
memcpy(&PolygonColor, color, sizeof(colorObj));
SymbologyFlag[Polygon] = 1;
if (p->index != CurrentDrawnShapeIndex) {
xmlNodePtr geomParentNode = getGeomParentNode("Polygon");
for (int i=0; i<p->numlines; i++) {
xmlNodePtr bdryNode = NULL;
if (i==0) /* __TODO__ check ring order*/
bdryNode = xmlNewChild(geomParentNode, NULL, BAD_CAST "outerBoundaryIs", NULL);
else
bdryNode = xmlNewChild(geomParentNode, NULL, BAD_CAST "innerBoundaryIs", NULL);
xmlNodePtr ringNode = xmlNewChild(bdryNode, NULL, BAD_CAST "LinearRing", NULL);
addAddRenderingSpecifications(ringNode);
addCoordsNode(ringNode, p->line[i].point, p->line[i].numpoints);
}
CurrentDrawnShapeIndex = p->index;
}
}
void KmlRenderer::addCoordsNode(xmlNodePtr parentNode, pointObj *pts, int numPts)
{
char lineBuf[128];
xmlNodePtr coordsNode = xmlNewChild(parentNode, NULL, BAD_CAST "coordinates", NULL);
xmlNodeAddContent(coordsNode, BAD_CAST "\n");
for (int i=0; i<numPts; i++) {
if( mElevationFromAttribute ) {
sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, mCurrentElevationValue);
} else if (AltitudeMode == relativeToGround || AltitudeMode == absolute) {
#ifdef USE_POINT_Z_M
sprintf(lineBuf, "\t%.8f,%.8f,%.8f\n", pts[i].x, pts[i].y, pts[i].z);
#else
msSetError(MS_MISCERR, "Z coordinates support not available (mapserver not compiled with USE_POINT_Z_M option)", "KmlRenderer::addCoordsNode()");
#endif
} else
sprintf(lineBuf, "\t%.8f,%.8f\n", pts[i].x, pts[i].y);
xmlNodeAddContent(coordsNode, BAD_CAST lineBuf);
}
xmlNodeAddContent(coordsNode, BAD_CAST "\t");
}
void KmlRenderer::renderGlyphs(imageObj*, double x, double y, labelStyleObj *style, char *text)
{
xmlNodePtr node;
if (PlacemarkNode == NULL)
PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
if (!PlacemarkNode)
return;
memcpy(&LabelStyle, style, sizeof(labelStyleObj));
SymbologyFlag[Label] = 1;
/*there is alaws a default name (layer.shapeid). Replace it*/
for (node = PlacemarkNode->children; node; node = node->next) {
if (node->type != XML_ELEMENT_NODE)
continue;
if (strcmp((char *)node->name, "name") == 0) {
xmlNodeSetContent(node, BAD_CAST text);
break;
}
}
/*xmlNewChild(PlacemarkNode, NULL, BAD_CAST "name", BAD_CAST text);*/
xmlNodePtr geomNode = getGeomParentNode("Point");
addAddRenderingSpecifications(geomNode);
pointObj pt;
pt.x = x;
pt.y = y;
addCoordsNode(geomNode, &pt, 1);
}
void KmlRenderer::addAddRenderingSpecifications(xmlNodePtr node)
{
/*
<extrude>0</extrude> <!-- boolean -->
<tessellate>0</tessellate> <!-- boolean -->
<altitudeMode>clampToGround</altitudeMode>
*/
if (Extrude)
xmlNewChild(node, NULL, BAD_CAST "extrude", BAD_CAST "1");
if (Tessellate)
xmlNewChild(node, NULL, BAD_CAST "tessellate", BAD_CAST "1");
if (AltitudeMode == absolute)
xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "absolute");
else if (AltitudeMode == relativeToGround)
xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "relativeToGround");
else if (AltitudeMode == clampToGround)
xmlNewChild(node, NULL, BAD_CAST "altitudeMode", BAD_CAST "clampToGround");
}
imageObj *agg2CreateImage(int width, int height, outputFormatObj *format, colorObj * bg);
int KmlRenderer::createIconImage(char *fileName, symbolObj *symbol, symbolStyleObj *symstyle)
{
pointObj p;
imageObj *tmpImg = NULL;
tmpImg = agg2CreateImage((int)(symbol->sizex*symstyle->scale),
(int)(symbol->sizey*symstyle->scale),
aggFormat, NULL);
tmpImg->format = aggFormat;
if (!aggFormat->vtable)
msInitializeRendererVTable(aggFormat);
p.x = symbol->sizex * symstyle->scale / 2;
p.y = symbol->sizey *symstyle->scale / 2;
#ifdef USE_POINT_Z_M
p.z = 0.0;
#endif
msDrawMarkerSymbol(&map->symbolset,tmpImg, &p, symstyle->style, 1);
return msSaveImage(map, tmpImg, fileName);
}
void KmlRenderer::renderSymbol(imageObj *img, double x, double y, symbolObj *symbol, symbolStyleObj *style)
{
if (PlacemarkNode == NULL)
PlacemarkNode = createPlacemarkNode(LayerNode, NULL);
if (!PlacemarkNode)
return;
snprintf(SymbolUrl, sizeof(SymbolUrl), "%s", lookupSymbolUrl(img, symbol, style));
SymbologyFlag[Symbol] = 1;
xmlNodePtr geomNode = getGeomParentNode("Point");
addAddRenderingSpecifications(geomNode);
pointObj pt;
pt.x = x;
pt.y = y;
addCoordsNode(geomNode, &pt, 1);
}
void KmlRenderer::renderPixmapSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style)
{
renderSymbol(img, x, y, symbol, style);
}
void KmlRenderer::renderVectorSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style)
{
renderSymbol(img, x, y, symbol, style);
}
void KmlRenderer::renderEllipseSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style)
{
renderSymbol(img, x, y, symbol, style);
}
void KmlRenderer::renderTruetypeSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style)
{
renderSymbol(img, x, y, symbol, style);
}
xmlNodePtr KmlRenderer::createGroundOverlayNode(xmlNodePtr parentNode, char *imageHref, layerObj *layer)
{
/*
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<GroundOverlay>
<name>GroundOverlay.kml</name>
<color>7fffffff</color>
<drawOrder>1</drawOrder>
<Icon>
<href>http://www.google.com/intl/en/images/logo.gif</href>
<refreshMode>onInterval</refreshMode>
<refreshInterval>86400</refreshInterval>
<viewBoundScale>0.75</viewBoundScale>
</Icon>
<LatLonBox>
<north>37.83234</north>
<south>37.832122</south>
<east>-122.373033</east>
<west>-122.373724</west>
<rotation>45</rotation>
</LatLonBox>
</GroundOverlay>
</kml>
*/
char layerHexColor[32];
xmlNodePtr groundOverlayNode = xmlNewChild(parentNode, NULL, BAD_CAST "GroundOverlay", NULL);
char *layerName = getLayerName(layer);
xmlNewChild(groundOverlayNode, NULL, BAD_CAST "name", BAD_CAST layerName);
if (layer->opacity > 0 && layer->opacity < 100) {
sprintf(layerHexColor, "%02xffffff", (unsigned int)MS_NINT(layer->opacity*2.55));
xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color", BAD_CAST layerHexColor);
} else
xmlNewChild(groundOverlayNode, NULL, BAD_CAST "color", BAD_CAST "ffffffff");
char stmp[20];
sprintf(stmp, "%d",layer->index);
xmlNewChild(groundOverlayNode, NULL, BAD_CAST "drawOrder", BAD_CAST stmp);
if (imageHref) {
xmlNodePtr iconNode = xmlNewChild(groundOverlayNode, NULL, BAD_CAST "Icon", NULL);
xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST imageHref);
}
char crdStr[64];
rectObj mapextent;
if (map->gt.need_geotransform == MS_TRUE)
mapextent = currentLayer->map->saved_extent;
else
mapextent = currentLayer->map->extent;
xmlNodePtr latLonBoxNode = xmlNewChild(groundOverlayNode, NULL, BAD_CAST "LatLonBox", NULL);
sprintf(crdStr, "%.8f", mapextent.maxy);
xmlNewChild(latLonBoxNode, NULL, BAD_CAST "north", BAD_CAST crdStr);
sprintf(crdStr, "%.8f", mapextent.miny);
xmlNewChild(latLonBoxNode, NULL, BAD_CAST "south", BAD_CAST crdStr);
sprintf(crdStr, "%.8f", mapextent.minx);
xmlNewChild(latLonBoxNode, NULL, BAD_CAST "west", BAD_CAST crdStr);
sprintf(crdStr, "%.8f", mapextent.maxx);
xmlNewChild(latLonBoxNode, NULL, BAD_CAST "east", BAD_CAST crdStr);
xmlNewChild(latLonBoxNode, NULL, BAD_CAST "rotation", BAD_CAST "0.0");
return groundOverlayNode;
}
void KmlRenderer::startShape(imageObj *, shapeObj *shape)
{
if (PlacemarkNode)
flushPlacemark();
CurrentShapeIndex=-1;
CurrentDrawnShapeIndex = -1;
CurrentShapeName=NULL;
/*should be done at endshape but the plugin architecture does not call endshape yet*/
if(LineStyle) {
msFree(LineStyle);
LineStyle = NULL;
numLineStyle = 0;
}
if (shape) {
CurrentShapeIndex = shape->index;
if (pszLayerNameAttributeMetadata) {
for (int i=0; i<currentLayer->numitems; i++) {
if (strcasecmp(currentLayer->items[i], pszLayerNameAttributeMetadata) == 0 && shape->values[i]) {
CurrentShapeName = msStrdup(shape->values[i]);
break;
}
}
}
}
PlacemarkNode = NULL;
GeomNode = NULL;
DescriptionNode = createDescriptionNode(shape);
if( mElevationFromAttribute && shape->numvalues > mElevationAttributeIndex &&
mElevationAttributeIndex >= 0 && shape->values[mElevationAttributeIndex]) {
mCurrentElevationValue = atof( shape->values[mElevationAttributeIndex] );
}
memset(SymbologyFlag, 0, NumSymbologyFlag);
}
void KmlRenderer::endShape(imageObj*, shapeObj*)
{
CurrentShapeIndex = -1;
if (CurrentShapeName)
msFree(CurrentShapeName);
CurrentShapeName = NULL;
}
xmlNodePtr KmlRenderer::getGeomParentNode(const char *geomName)
{
/*we do not need a multi-geometry for point layers*/
if (currentLayer->type != MS_LAYER_POINT && currentLayer->type != MS_LAYER_ANNOTATION && GeomNode) {
/*placemark geometry already defined, we need multigeometry node*/
xmlNodePtr multiGeomNode = xmlNewNode(NULL, BAD_CAST "MultiGeometry");
xmlAddChild(multiGeomNode, GeomNode);
GeomNode = multiGeomNode;
xmlNodePtr geomNode = xmlNewChild(multiGeomNode, NULL, BAD_CAST geomName, NULL);
return geomNode;
} else {
GeomNode = xmlNewNode(NULL, BAD_CAST geomName);
return GeomNode;
}
}
char* KmlRenderer::lookupSymbolUrl(imageObj *img, symbolObj *symbol, symbolStyleObj *symstyle)
{
char symbolHexColor[32];
/*
<Style id="randomColorIcon">
<IconStyle>
<color>ff00ff00</color>
<colorMode>random</colorMode>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
</Icon>
</IconStyle>
</Style>
*/
sprintf(symbolHexColor,"%02x%02x%02x%02x", symstyle->style->color.alpha, symstyle->style->color.blue,
symstyle->style->color.green, symstyle->style->color.red);
snprintf(SymbolName, sizeof(SymbolName), "symbol_%s_%.1f_%s", symbol->name, symstyle->scale, symbolHexColor);
char *symbolUrl = msLookupHashTable(StyleHashTable, SymbolName);
if (!symbolUrl) {
char iconFileName[MS_MAXPATHLEN];
char iconUrl[MS_MAXPATHLEN];
if (img->imagepath) {
char *tmpFileName = msTmpFile(NULL, MapPath, img->imagepath, "png");
snprintf(iconFileName, sizeof(iconFileName), "%s", tmpFileName);
msFree(tmpFileName);
} else {
sprintf(iconFileName, "symbol_%s_%.1f.%s", symbol->name, symstyle->scale, "png");
}
if (createIconImage(iconFileName, symbol, symstyle) != MS_SUCCESS) {
char errMsg[512];
sprintf(errMsg, "Error creating icon file '%s'", iconFileName);
msSetError(MS_IOERR, errMsg, "KmlRenderer::lookupSymbolStyle()" );
return NULL;
}
if (img->imageurl)
sprintf(iconUrl, "%s%s.%s", img->imageurl, msGetBasename(iconFileName), "png");
else
snprintf(iconUrl, sizeof(iconUrl), "%s", iconFileName);
hashObj *hash = msInsertHashTable(StyleHashTable, SymbolName, iconUrl);
symbolUrl = hash->data;
}
return symbolUrl;
}
char* KmlRenderer::lookupPlacemarkStyle()
{
char lineHexColor[32];
char polygonHexColor[32];
char labelHexColor[32];
char *styleName=NULL;
styleName = msStringConcatenate(styleName, "style");
if (SymbologyFlag[Line]) {
/*
<LineStyle id="ID">
<!-- inherited from ColorStyle -->
<color>ffffffff</color> <!-- kml:color -->
<colorMode>normal</colorMode> <!-- colorModeEnum: normal or random -->
<!-- specific to LineStyle -->
<width>1</width> <!-- float -->
</LineStyle>
*/
for (int i=0; i<numLineStyle; i++) {
if (currentLayer && currentLayer->opacity > 0 && currentLayer->opacity < 100 &&
LineStyle[i].color->alpha == 255)
LineStyle[i].color->alpha = MS_NINT(currentLayer->opacity*2.55);
sprintf(lineHexColor,"%02x%02x%02x%02x", LineStyle[i].color->alpha, LineStyle[0].color->blue,
LineStyle[i].color->green, LineStyle[i].color->red);
char lineStyleName[32];
sprintf(lineStyleName, "_line_%s_w%.1f", lineHexColor, LineStyle[i].width);
styleName = msStringConcatenate(styleName, lineStyleName);
}
}
if (SymbologyFlag[Polygon]) {
/*
<PolyStyle id="ID">
<!-- inherited from ColorStyle -->
<color>ffffffff</color> <!-- kml:color -->
<colorMode>normal</colorMode> <!-- kml:colorModeEnum: normal or random -->
<!-- specific to PolyStyle -->
<fill>1</fill> <!-- boolean -->
<outline>1</outline> <!-- boolean -->
</PolyStyle>
*/
if (currentLayer && currentLayer->opacity > 0 && currentLayer->opacity < 100 &&
PolygonColor.alpha == 255)
PolygonColor.alpha = MS_NINT(currentLayer->opacity*2.55);
sprintf(polygonHexColor,"%02x%02x%02x%02x", PolygonColor.alpha, PolygonColor.blue, PolygonColor.green, PolygonColor.red);
char polygonStyleName[64];
sprintf(polygonStyleName, "_polygon_%s", polygonHexColor);
styleName = msStringConcatenate(styleName, polygonStyleName);
}
if (SymbologyFlag[Label]) {
/*
<LabelStyle id="ID">
<!-- inherited from ColorStyle -->
<color>ffffffff</color> <!-- kml:color -->
<colorMode>normal</colorMode> <!-- kml:colorModeEnum: normal or random -->
<!-- specific to LabelStyle -->
<scale>1</scale> <!-- float -->
</LabelStyle>
*/
if (currentLayer && currentLayer->opacity > 0 && currentLayer->opacity < 100 &&
LabelStyle.color->alpha == 255)
LabelStyle.color->alpha = MS_NINT(currentLayer->opacity*2.55);
sprintf(labelHexColor,"%02x%02x%02x%02x", LabelStyle.color->alpha, LabelStyle.color->blue, LabelStyle.color->green, LabelStyle.color->red);
// __TODO__ add label scale
char labelStyleName[64];
sprintf(labelStyleName, "_label_%s", labelHexColor);
styleName = msStringConcatenate(styleName, labelStyleName);
}
if (SymbologyFlag[Symbol]) {
/*
<Style id="randomColorIcon">
<IconStyle>
<color>ff00ff00</color>
<colorMode>random</colorMode>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
</Icon>
</IconStyle>
</Style>
*/
/* __TODO__ add label scale */
styleName = msStringConcatenate(styleName, "_");
styleName = msStringConcatenate(styleName, SymbolName);
}
char *styleUrl = msLookupHashTable(StyleHashTable, styleName);
if (!styleUrl) {
char *styleValue=NULL;
styleValue = msStringConcatenate(styleValue, "#");
styleValue = msStringConcatenate(styleValue, styleName);
hashObj *hash = msInsertHashTable(StyleHashTable, styleName, styleValue);
styleUrl = hash->data;
msFree(styleValue);
/* Insert new Style node into Document node*/
xmlNodePtr styleNode = xmlNewChild(DocNode, NULL, BAD_CAST "Style", NULL);
xmlNewProp(styleNode, BAD_CAST "id", BAD_CAST styleName);
if (SymbologyFlag[Polygon]) {
xmlNodePtr polyStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "PolyStyle", NULL);
xmlNewChild(polyStyleNode, NULL, BAD_CAST "color", BAD_CAST polygonHexColor);
}
if (SymbologyFlag[Line]) {
for (int i=0; i<numLineStyle; i++) {
xmlNodePtr lineStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "LineStyle", NULL);
sprintf(lineHexColor,"%02x%02x%02x%02x", LineStyle[i].color->alpha, LineStyle[i].color->blue,
LineStyle[i].color->green, LineStyle[i].color->red);
xmlNewChild(lineStyleNode, NULL, BAD_CAST "color", BAD_CAST lineHexColor);
char width[16];
sprintf(width, "%.1f", LineStyle[i].width);
xmlNewChild(lineStyleNode, NULL, BAD_CAST "width", BAD_CAST width);
}
}
if (SymbologyFlag[Symbol]) {
xmlNodePtr iconStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
xmlNodePtr iconNode = xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST SymbolUrl);
/*char scale[16];
sprintf(scale, "%.1f", style->scale);
xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
} else {
const char *value=msLookupHashTable(&currentLayer->metadata, "kml_default_symbol_href");
if (value && strlen(value) > 0) {
xmlNodePtr iconStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "IconStyle", NULL);
xmlNodePtr iconNode = xmlNewChild(iconStyleNode, NULL, BAD_CAST "Icon", NULL);
xmlNewChild(iconNode, NULL, BAD_CAST "href", BAD_CAST value);
}
}
if (SymbologyFlag[Label]) {
xmlNodePtr labelStyleNode = xmlNewChild(styleNode, NULL, BAD_CAST "LabelStyle", NULL);
xmlNewChild(labelStyleNode, NULL, BAD_CAST "color", BAD_CAST labelHexColor);
/*char scale[16];
sprintf(scale, "%.1f", style->scale);
xmlNewChild(iconStyleNode, NULL, BAD_CAST "scale", BAD_CAST scale);*/
}
}
if (styleName)
msFree(styleName);
return styleUrl;
}
void KmlRenderer::flushPlacemark()
{
if (PlacemarkNode) {
char *styleUrl = lookupPlacemarkStyle();
xmlNewChild(PlacemarkNode, NULL, BAD_CAST "styleUrl", BAD_CAST styleUrl);
if (DescriptionNode)
xmlAddChild(PlacemarkNode, DescriptionNode);
if (GeomNode)
xmlAddChild(PlacemarkNode, GeomNode);
}
}
xmlNodePtr KmlRenderer::createDescriptionNode(shapeObj *shape)
{
/*
<description>
<![CDATA[
special characters here
]]>
<description>
*/
/*description nodes for vector layers:
- if kml_description is set, use it
- if not, dump the attributes */
if (pszLayerDescMetadata) {
char *pszTmp=NULL;
char *pszTmpDesc = NULL;
size_t bufferSize = 0;
pszTmpDesc = msStrdup(pszLayerDescMetadata);
for (int i=0; i<currentLayer->numitems; i++) {
bufferSize = strlen(currentLayer->items[i]) + 3;
pszTmp = (char *)msSmallMalloc(bufferSize);
snprintf(pszTmp, bufferSize, "%%%s%%",currentLayer->items[i]);
if (strcasestr(pszTmpDesc, pszTmp))
pszTmpDesc = msCaseReplaceSubstring(pszTmpDesc, pszTmp, shape->values[i]);
msFree(pszTmp);
}
xmlNodePtr descriptionNode = xmlNewNode(NULL, BAD_CAST "description");
xmlNodeAddContent(descriptionNode, BAD_CAST pszTmpDesc);
msFree(pszTmpDesc);
return descriptionNode;
} else if ((papszLayerIncludeItems && nIncludeItems > 0) ||
(papszLayerExcludeItems && nExcludeItems > 0)) {
/* -------------------------------------------------------------------- */
/* preffered way is to use the ExtendedData tag (#3728) */
/* http://code.google.com/apis/kml/documentation/extendeddata.html */
/* -------------------------------------------------------------------- */
xmlNodePtr extendedDataNode = xmlNewNode(NULL, BAD_CAST "ExtendedData");
xmlNodePtr dataNode = NULL;
const char*pszAlias=NULL;
int bIncludeAll = MS_FALSE;
if(papszLayerIncludeItems && nIncludeItems == 1 &&
strcasecmp(papszLayerIncludeItems[0], "all") == 0)
bIncludeAll = MS_TRUE;
for (int i=0; i<currentLayer->numitems; i++) {
int j=0,k=0;
/*TODO optimize to calculate this only once per layer*/
for (j=0; j<nIncludeItems; j++) {
if (strcasecmp(currentLayer->items[i], papszLayerIncludeItems[j]) == 0)
break;
}
if (j<nIncludeItems || bIncludeAll) {
if (papszLayerExcludeItems && nExcludeItems > 0) {
for (k=0; k<nExcludeItems; k++) {
if (strcasecmp(currentLayer->items[i], papszLayerExcludeItems[k]) == 0)
break;
}
}
if (nExcludeItems == 0 || k == nExcludeItems) {
dataNode = xmlNewNode(NULL, BAD_CAST "Data");
xmlNewProp(dataNode, BAD_CAST "name", BAD_CAST currentLayer->items[i]);
pszAlias = getAliasName(currentLayer, currentLayer->items[i], "GO");
if (pszAlias)
xmlNewChild(dataNode, NULL, BAD_CAST "displayName", BAD_CAST pszAlias);
else
xmlNewChild(dataNode, NULL, BAD_CAST "displayName", BAD_CAST currentLayer->items[i]);
if (shape->values[i] && strlen(shape->values[i]))
xmlNewChild(dataNode, NULL, BAD_CAST "value", BAD_CAST shape->values[i]);
else
xmlNewChild(dataNode, NULL, BAD_CAST "value", NULL);
xmlAddChild(extendedDataNode, dataNode);
}
}
}
return extendedDataNode;
}
return NULL;
}
void KmlRenderer::addLineStyleToList(strokeStyleObj *style)
{
/*actually this is not necessary. kml only uses the last LineStyle
so we should not bother keeping them all*/
int i =0;
for (i=0; i<numLineStyle; i++) {
if (style->width == LineStyle[i].width &&
LineStyle[i].color->alpha == style->color->alpha &&
LineStyle[i].color->red == style->color->red &&
LineStyle[i].color->green == style->color->green &&
LineStyle[i].color->blue == style->color->blue)
break;
}
if (i == numLineStyle) {
numLineStyle++;
if (LineStyle == NULL)
LineStyle = (strokeStyleObj *)msSmallMalloc(sizeof(strokeStyleObj));
else
LineStyle = (strokeStyleObj *)msSmallRealloc(LineStyle, sizeof(strokeStyleObj)*numLineStyle);
memcpy(&LineStyle[numLineStyle-1], style, sizeof(strokeStyleObj));
}
}
#endif
Jump to Line
Something went wrong with that request. Please try again.