Chris Pietschmann

husband, father, hacker, entrepreneur, futurist, innovator, autodidact

NAVIGATION - SEARCH

Bing Maps Shapes (VEShape) to WKT (Well-Known-Text) and Back using JavaScript

One of the standard methods of representing geometric shapes is by using the WKT (Well-Known-Text) standard. This is a human readable standard method of representing geometric shapes that can be used to easily pass spatial data between applications. I know GML or GeoRSS may be a little more applicable since they are based on XML, but WKT can work just fine in some cases.

If you don’t know what WKT is here are a couple links for reference:

http://geoapi.sourceforge.net/2.0/javadoc/org/opengis/referencing/doc-files/WKT.html

http://en.wikipedia.org/wiki/Well-known_text

This is also one of the things that Virtual Earth does NOT have support built in for. So I wrote a little code that simply converts VEShape objects to a WKT string representation.

The code below allows you to represent Pushpins, Polygons and Polylines as strings like the following:

POINT(-99.71000000000001 43.74999999999998)

POLYGON((-99.71000000000001 46.74999999999998,
 -96.71000000000001 46.74999999999998,
 -96.71000000000001 43.74999999999998,
 -99.71000000000001 46.74999999999998))

LINESTRING(-99.71000000000001 40.74999999999998,
 -102.71000000000001 40.74999999999998,
 -102.71000000000001 43.74999999999998)

Here are some examples of using the conversion methods:

// Get Map Center Point
 var centerPoint = map.GetCenter();

 // Create Pushpin VEShape and Get it's WKT representation
 var wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Pushpin, centerPoint));
 // Create a VEShape from the WKT representation
 var shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);


 // Create Polygons' Location Array
 var polygonLocations = [
    new VELatLong(centerPoint.Latitude + 3, centerPoint.Longitude),
    new VELatLong(centerPoint.Latitude + 3, centerPoint.Longitude + 3),
    new VELatLong(centerPoint.Latitude, centerPoint.Longitude + 3)
 ];

 // Create Polygon VEShape and Get it's WKT representation
 wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Polygon, polygonLocations));
 // Create a VEShape from the WKT representation
 shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);


 // Create Polylines' Location Array
 var polylineLocations = [
    new VELatLong(centerPoint.Latitude - 3, centerPoint.Longitude),
    new VELatLong(centerPoint.Latitude - 3, centerPoint.Longitude - 3),
    new VELatLong(centerPoint.Latitude, centerPoint.Longitude - 3)
 ];

 // Create Polyline VEShape and Get it's WKT representation
 wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Polyline, polylineLocations));
 // Create a VEShape from the WKT representation
 shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);

Here’s the full code to the VirtualEarthWKT object that contains the static methods:

// Create the singleton object that contains the WKT (Well-Known-Text) transformation methods.
var VirtualEarthWKT = new (function() {
    // Declare some "private" methods that will only be used internally
    var priv = {
        trimSpaces: function(str) {
            // Trim beginning spaces
            while (priv.startsWith(str, " ")) {
                str = str.substring(1);
            }
            // Trim ending spaces
            while (priv.endsWith(str, " ")) {
                str = str.substring(0, str.length - 1);
            }
            return str;
        },
        startsWith: function(str, startstr) {
            return str.substring(0, startstr.length) == startstr;
        },
        endsWith: function(str, endstr) {
            return str.substring(str.length - endstr.length) == endstr;
        }
    };
    // Declare the "public" methods that will be exposed
    var that = {
        ///<summary>Converts a VEShape object to WKT (Well-Known-Text) string representation.</summary>
        ShapeToWKT: function(shape) {
            if (shape == null) {
                throw "VirtualEarthWKT.ShapeToWKT: 'shape' parameter can not be null.";
            }
            var wktTemplate = "";
            var wktGeomPoints = "";

            // Figure out the WKT Geometry Type
            switch (shape.GetType()) {
                case VEShapeType.Pushpin:
                    wktTemplate = "POINT({points})";
                    break;
                case VEShapeType.Polygon:
                    wktTemplate = "POLYGON(({points}))";
                    break;
                case VEShapeType.Polyline:
                    wktTemplate = "LINESTRING({points})";
                    break;
                default:
                    throw "VirtualEarthWKT.ShapeToWKT: VEShapeType (" + shape.GetType() + ") not supported.";
                    break;
            }

            // Get the List of VELatLong objects represented as WKT compatible list of points
            var shapePoints = shape.GetPoints();
            for (var i = 0; i < shapePoints.length; i++) {
                if (wktGeomPoints.length > 0) {
                    wktGeomPoints += ", ";
                }
                wktGeomPoints += shapePoints[i].Longitude + " " + shapePoints[i].Latitude;
            }

            // return WKT representation of the VEShape
            return wktTemplate.replace("{points}", wktGeomPoints);
        },
        ///<summary>
        ///Converts WKT (Well-Known-Text) string representation of a point/polygon/linestring to a VEShape object.
        ///</summary>
        ShapeFromWKT: function(strWKT) {
            if (strWKT == null) {
                throw "VirtualEarthWKT.ShapeFromWKT: 'strWKT' parameter can not be null.";
            }
            if (strWKT.length == 0) {
                throw "VirtualEarthWKT.ShapeFromWKT: 'strWKT' parameter can not be an empty string.";
            }
            var shapeType = null;
            var wktPoints = null;

            // Get the Shape Type and list of "Longitude Latitude" location points
            switch (strWKT.substring(0, 5).toLowerCase()) {
                case "point":
                    shapeType = VEShapeType.Pushpin;
                    wktPoints = strWKT.substring(6, strWKT.length - 1);
                    break;
                case "polyg":
                    shapeType = VEShapeType.Polygon;
                    wktPoints = strWKT.substring(9, strWKT.length - 2);
                    break;
                case "lines":
                    shapeType = VEShapeType.Polyline;
                    wktPoints = strWKT.substring(11, strWKT.length - 1);
                    break;
                default:
                    throw "VirtualEarthWKT.ShapeFromWKT: Unknown WKT Geometry Type";
                    break;
            }

            // split out the wkt points to be seperate
            wktPoints = wktPoints.split(",");

            // Convert the WKT Points to VELatLong locations
            var shapePoints = new Array();
            for (var i = 0; i < wktPoints.length; i++) {
                // Split the "Longitude Latitude" apart
                var loc = priv.trimSpaces(wktPoints[i]).split(" ");
                // Create VELatLong location
                shapePoints[shapePoints.length] = new VELatLong(parseFloat(loc[1]), parseFloat(loc[0]));
            }
            
            // Return a VEShape that represents this WKT Geometry
            return new VEShape(shapeType, shapePoints);
        }
    };
    // Return the object that contains the "public" and "private" methods
    return that;
})();
blog comments powered by Disqus