[GIS] C# / .NET library for converting GeoJson to KML

cgeojsonkmlnet

I'm pulling in data from ArcGIS using ArcGIS.PCL which provides the geometries as GeoJson. I'd like to convert this GeoJson to KML. Does anyone know of any .NET libraries that provides this functionality? I've had a good search but nothing has popped up on my radar.

I guess a couple of libraries may be required if there's no catch all for this problem. One that can convert GeoJson to WKT then another that can convert WKT to KML.

Best Answer

I couldn't find a library, but I coded up a solution that fit my requirements to handle polygons only. The code is provided below for anyone else who might find it useful. ConvertGeoJsonToKml generates complete KML and ConvertGeoJsonToKmlGeometryOnly generates a cut down KML output suitable for use in Google Fusion Tables.

using ArcGIS.ServiceModel.Common;
using ArcGIS.ServiceModel.GeoJson;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Xml.Linq;

namespace MyKickassProject.Utilities
{
    public static class GeometryUtils
    {
        public static string ConvertGeoJsonToKml(string geoJson)
        {
            SharpKml.Dom.Document kmlDocument = new SharpKml.Dom.Document();
            SharpKml.Dom.Placemark kmlPlacemark = new SharpKml.Dom.Placemark();

            kmlPlacemark.Geometry = GeneratePolygon(geoJson);

            kmlDocument.AddFeature(kmlPlacemark);

            SharpKml.Dom.Kml kml = new SharpKml.Dom.Kml();
            kml.Feature = kmlDocument;

            var serializer = new SharpKml.Base.Serializer();
            serializer.Serialize(kml);

            return serializer.Xml;
        }

        public static string ConvertGeoJsonToKmlGeometryOnly(string geoJson)
        {
            SharpKml.Dom.Polygon kmlPolygon = GeneratePolygon(geoJson);

            var serializer = new SharpKml.Base.Serializer();
            serializer.SerializeRaw(kmlPolygon);

            return RemoveAllNamespaces(serializer.Xml);
        }

        private static SharpKml.Dom.Polygon GeneratePolygon(string geoJson)
        {
            var polygon = JsonConvert.DeserializeObject<GeoJsonPolygon>(geoJson);

            if (polygon.Coordinates.Count > 0)
            {
                SharpKml.Dom.Polygon kmlPolygon = new SharpKml.Dom.Polygon();

                SharpKml.Dom.OuterBoundary kmlOuterBoundary = new SharpKml.Dom.OuterBoundary();
                kmlOuterBoundary.LinearRing = PointCollectionToLinearRing(polygon.Coordinates[0]);
                kmlPolygon.OuterBoundary = kmlOuterBoundary;

                if (polygon.Coordinates.Count > 1)
                {
                    for (int i = 1; i < polygon.Coordinates.Count; i++)
                    {
                        SharpKml.Dom.InnerBoundary kmlInnerBoundary = new SharpKml.Dom.InnerBoundary();
                        kmlInnerBoundary.LinearRing = PointCollectionToLinearRing(polygon.Coordinates[i]);
                        kmlPolygon.AddInnerBoundary(kmlInnerBoundary);
                    }
                }

                return kmlPolygon;
            }

            throw new Exception("GeoJSON type is unhandled.");
        }

        private static SharpKml.Dom.LinearRing PointCollectionToLinearRing(PointCollection coordinates)
        {
            var linearRing = new SharpKml.Dom.LinearRing();
            linearRing.Coordinates = new SharpKml.Dom.CoordinateCollection();
            var points = coordinates.AsEnumerable();
            foreach (var point in points)
            {
                linearRing.Coordinates.Add(new SharpKml.Base.Vector(point[1], point[0]));
            }
            return linearRing;
        }

        private static string RemoveAllNamespaces(string xmlDocument)
        {
            XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(xmlDocument));

            return xmlDocumentWithoutNs.ToString();
        }

        private static XElement RemoveAllNamespaces(XElement xmlDocument)
        {
            if (!xmlDocument.HasElements)
            {
                XElement xElement = new XElement(xmlDocument.Name.LocalName);
                xElement.Value = xmlDocument.Value;

                foreach (XAttribute attribute in xmlDocument.Attributes())
                    xElement.Add(attribute);

                return xElement;
            }
            return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));
        }
    }
}
Related Question