Select to view content in your preferred language

Convert Geometry to WKT/WKB in silverlight

11990
10
05-03-2011 11:29 AM
NaveenMaram
Emerging Contributor
Hi All,

I am trying to save the Geometry point collection as WKT string in the database. could some one help me in this. or even help links will be usefull.


Thanks,
Naveen Maram.
0 Kudos
10 Replies
dotMorten_esri
Esri Notable Contributor
The format is fairly straightforward to write (slightly more difficult to read WKT though - WKB is easier to read once you figure out how to get binary values from a stream; -wkb read/write is also a lot more performant).
Here's the WKT/WKB spec (chapter 5+6): http://www.opengeospatial.org/standards/sfa
0 Kudos
JoeShmoe
Deactivated User
0 Kudos
dotMorten_esri
Esri Notable Contributor
Nice work Bill (nice to see my old work put to use :-).
Have you thought about adding WKB? The performance for parsing WKB is apples and oranges compared to WKT. And the parser is much simpler to build.
0 Kudos
TonyBacon
Deactivated User
I also started with the SharpGIS code creating a conversion ESRI.ArcGIS.Client.Geometry.Geometry <-> WKB. It all boils down to two extension methods on the Geometry class: ConvertFromWKB and ConvertToWKB so very easy to use.

I had initially done that for points and recently extended it to polylines and polygons. I have only tested in the simplest cases, i.e. have not tested multi-polygon. The performance seems to be very good.

I am not sure about sharing this work. If someone can give me some pointers on the steps to take and where I could upload it to I'll gladly make it available.
0 Kudos
JoeShmoe
Deactivated User
If someone can give me some pointers on the steps to take and where I could upload it to I'll gladly make it available.


If you put it anywhere I can access it, I'll add it to http://esrislwkt.codeplex.com/
0 Kudos
JustinCornell
Occasional Contributor
Here is what I converted into .NET code it it converts Geometry to WKT and also convert WKT to Geometry.  It's pretty simple class that has worked perfectly for everything that I need. 

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using ESRI.ArcGIS.Client.Geometry;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.ObjectModel;
using System.Text;

namespace Geometry.Model
{
    /// <summary>
    /// Converted from a php class by me to work in .NET c#.  The class was taken from this link: http://trac.mapfish.org/trac/mapfish/browser/sandbox/aabt/MapFish/server/php/GeoJSON/WKT/WKT.class.php?rev=2399
    /// </summary>
    public class WKTModel
    {
        static string _regExpTypeStr = @"^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$";
        static string _regExpSpaces = @"\s+";
        static string _regExpParenComma = @"\)\s*,\s*\(";
        static string _regExpDoubleParenComma = @"\)\s*\)\s*,\s*\(\s*\(";
        static string _regExpTrimParens = @"^\s*\(?(.*?)\)?\s*$";

        public enum GeometryType
        {
            UNKNOWN,
            POINT,
            MULTIPOINT,
            LINESTRING,
            MULTILINESTRING,
            POLYGON,
            MULTIPOLYGON,
            GEOMETRYCOLLECTION
        }

        private WKTModel() { }


        /// <summary>
        /// Converts a geometry object to WKT.
        /// </summary>
        public static string ToWKT(Geometry g)
        {
            StringBuilder sb = new StringBuilder();

            if (g is Envelope)
            {
                var p = g as Envelope;
                var pc = p.ToPolygon();
                sb.AppendFormat("POLYGON ({0})", ConvertPointCollection(pc.Rings[0], true));
            }
            else if (g is MapPoint)
            {
                var p = g as MapPoint;
                sb.AppendFormat("POINT ({0})", ConvertPoint(p).Replace(",", ""));
            }
            else if (g is MultiPoint)
            {
                var mp = g as MultiPoint;
                sb.AppendFormat("MULTIPOINT ({0})", ConvertPointCollection(mp.Points));
            }
            else if (g is Polygon)
            {
                var p = g as Polygon;
                if (p.Rings.Count == 1)
                    sb.AppendFormat("POLYGON ({0})", ConvertPointCollection(p.Rings[0], true));
                else if (p.Rings.Count > 1)
                    sb.AppendFormat("POLYGON ({0})", string.Join(",", p.Rings.Select(x => ConvertPointCollection(x, true))));
            }
            else if (g is Polyline)
            {
                var l = g as Polyline;
                if (l.Paths.Count == 1)
                    sb.AppendFormat("LINESTRING  ({0})", ConvertPointCollection(l.Paths[0]));
                else if (l.Paths.Count > 1)
                    sb.AppendFormat("MULTILINESTRING  ({0})", string.Join(",", l.Paths.Select(x => ConvertPointCollection(x, true))));
            }
            return sb.ToString();
        }

        public static GeometryType GetGeometryType(string WKT)
        {
            if (string.IsNullOrEmpty(WKT))
                return GeometryType.UNKNOWN;

            var matches = Regex.Matches(WKT, _regExpTypeStr);

            try
            {
                var mytype = matches.Count == 0 ? GeometryType.UNKNOWN : (GeometryType)Enum.Parse(typeof(GeometryType), matches[0].Groups[1].Value, true);
                return mytype;
            }
            catch (Exception ex)
            {
                return GeometryType.UNKNOWN;
            }
        }

        /// <summary>
        /// Parses the WKT and truns it into and ESRI Geometry object
        /// </summary>
        public static Geometry ToGeometry(string WKT, int WKID)
        {
            var geometry = new WKTModel().Read(WKT);
            geometry.SpatialReference = new SpatialReference(WKID);
            return geometry;
        }


        /// <summary>
        /// Parses the WKT and truns it into and ESRI geometry object
        /// </summary>
        private Geometry Read(string WKT)
        {
            if (!Regex.IsMatch(WKT, _regExpTypeStr))
                return null;

            var matches = Regex.Matches(WKT, _regExpTypeStr);
            return Parse(ParseToGeometryType(matches[0].Groups[1].Value), matches[0].Groups[2].Value);
        }

        private GeometryType ParseToGeometryType(string type)
        {
            return (GeometryType)Enum.Parse(typeof(GeometryType), type, true);
        }

        /// <summary>
        /// Parses a section of the WKT into a geometry object
        /// </summary>
        private Geometry Parse(GeometryType type, string str)
        {
            var points = new PointCollection();

            switch (type)
            {
                case GeometryType.POINT:
                    var coords = RegExplode(_regExpSpaces, str);
                    double x;
                    double y;
                    var isXValid = double.TryParse(coords[0], out x);
                    var isYValid = double.TryParse(coords[1], out y);
                    if (isXValid && isYValid)
                        return new MapPoint(x, y);
                    else
                        return null;
                case GeometryType.MULTIPOINT:
                    foreach (var p in str.Trim().Split(','))
                        points.Add(Parse(GeometryType.POINT, p) as MapPoint);

                    return new MultiPoint(points);

                case GeometryType.LINESTRING:
                    foreach (var p in str.Trim().Split(','))
                        points.Add(Parse(GeometryType.POINT, p) as MapPoint);

                    var line = new ESRI.ArcGIS.Client.Geometry.Polyline();
                    line.Paths.Add(points);
                    return line;

                case GeometryType.MULTILINESTRING:
                    var lines = RegExplode(_regExpParenComma, str);
                    var nestedPoints = new List<PointCollection>();
                    foreach (var l in lines)
                    {
                        var myline = Regex.Replace(l, _regExpTrimParens, "$1");
                        nestedPoints.Add(((Polyline)Parse(GeometryType.LINESTRING, myline)).Paths[0]);
                    }
                    return new Polyline() { Paths = new ObservableCollection<PointCollection>(nestedPoints) };

                case GeometryType.POLYGON:
                    var rings = RegExplode(_regExpParenComma, str);
                    var ringPoints = new List<PointCollection>();
                    foreach (var r in rings)
                    {
                        var ring = Regex.Replace(r, _regExpTrimParens, "$1");
                        var pline = Parse(GeometryType.LINESTRING, ring) as Polyline;
                        ringPoints.Add(pline.Paths[0]);
                    }
                    return new Polygon() { Rings = new ObservableCollection<PointCollection>(ringPoints) };

                case GeometryType.MULTIPOLYGON:
                    var polygons = RegExplode(_regExpDoubleParenComma, str);
                    var multiPolyRings = new List<PointCollection>();
                    foreach (var p in polygons)
                    {
                        var polygon = Regex.Replace(p, _regExpTrimParens, "$1");
                        var poly = Parse(GeometryType.POLYGON, polygon) as Polygon;
                        multiPolyRings.AddRange(poly.Rings);
                    }
                    return new Polygon() { Rings = new ObservableCollection<PointCollection>(multiPolyRings) };

                case GeometryType.GEOMETRYCOLLECTION:
                    throw new NotImplementedException();

                //  str = preg_replace('/,\s*([A-Za-z])/', '|$1', str); 
                //  $wktArray = explode('|', trim(str)); 
                //  foreach ($wktArray as $wkt) 
                //  { 
                //    components[] = $this->read($wkt); 
                //  } 
                //  return new GeometryCollection(components); 

                default:
                    return null;
            }
        }


        /// <summary>
        /// Split string according to first match of passed regEx index of regExes 
        /// </summary>
        /// <param name="regEx"></param>
        /// <param name="str"></param>
        /// <returns></returns>
        protected List<string> RegExplode(string regEx, string str)
        {
            var matches = Regex.Matches(str, regEx);
            return matches.Count == 0 ? new List<string> { str.Trim() } : str.Trim().Split(new string[] { matches[0].Value }, StringSplitOptions.None).ToList();
        }


        private static string ConvertPointCollection(PointCollection pc, bool wrapInParens = false)
        {
            StringBuilder sb = new StringBuilder();
            if (pc != null)
            {
                foreach (var p in pc)
                    sb.AppendFormat(ConvertPoint(p));
            }

            //trim the trailing comma
            if (sb.Length > 0)
            {
                sb = sb.Remove(sb.Length - 1, 1);
                if (wrapInParens)
                    sb = sb.Insert(0, "(").Append(")");
            }

            return sb.ToString();
        }

        private static string ConvertPoint(MapPoint p)
        {
            return string.Format("{0} {1},", p.X, p.Y);
        }

    }
}


use it like this:
Geometry geom = WKTModel.ToGeometry("POLYGON(...", 4269);
string wkt = WKTModel.ToWKT(geom);
0 Kudos
Werangakaluarachchi
Deactivated User

Hello Justin

I came across your blog post here and I am reaching out to you to ask that how can I use this code. Preferably, if you could confirm that the above sample code can be used under the terms of an official OSI license, such as the Apache License v.2, it would be much appreciated.

Regards,

Weranga

0 Kudos
JustinCornell
Occasional Contributor

You are welcome to use the code however you want.  I wrote that quite a long time ago referencing several different sources online but I couldn't tell you what those sources are now.  If you do publish this code I would like a nod in the article.

0 Kudos
JustinHunter
Regular Contributor
Hope someone still has this topic subscribed!

The problem I'm having is below - I do not have knowledge of Envelope having a ToPolygon method. Could anyone assist me?
/// <summary>
/// Converts a geometry object to WKT.
/// </summary>
public static string ToWKT(Geometry g)
{
    StringBuilder sb = new StringBuilder();

    if (g is Envelope)
    {
        var p = g as Envelope;
        var pc = p.ToPolygon();
        sb.AppendFormat("POLYGON ({0})", ConvertPointCollection(pc.Rings[0], true));
    }
...more code below
0 Kudos