[GIS] How to create Geodesic Polygons in OpenLayers

openlayers-2

I would like to create geodesic polygons in openlayers. This is not the same as this question although VERY similar.

Geographika was able to do it but his solution is based on a circle. I'm interested in creating polygons that aren't necessarily circles, such as squares. Or maybe even a line.

Anyone know of any javascript snippet that might convert my geometric openlayers polygons to geodesic ones?

Google Maps has the Geodesic:true property for its shapes. OpenLayers should definitely have it.

Best Answer

If you only have two points on a line you may have to create more vertices and then reproject these to EPSG:4326 and back again to create something similar to the Google example.

There is an OpenLayers example of Great Circle lines here. The source code for the algorithms is also online.

There is a getGeodesicArea function on OpenLayer polygons, and geometry collections. There is also a getGeodesicLength function. These algorithms are implemented based on the following (according to the docs):

Robert. G. Chamberlain and William H. Duquette, “Some Algorithms for Polygons on a Sphere”, JPL Publication 07-03, Jet Propulsion Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409

Looking at the sourcecode it reprojects points to 4326, and then uses the OpenLayers.Util.distVincenty function to calculate the distance between points on the surface of an ellipsoid (the earth).

getGeodesicLength: function(projection) {
    var geom = this; // so we can work with a clone if needed
    if(projection) {
        var gg = new OpenLayers.Projection("EPSG:4326");
        if(!gg.equals(projection)) {
            geom = this.clone().transform(projection, gg);
        }
    }
    var length = 0.0;
    if(geom.components && (geom.components.length > 1)) {
        var p1, p2;
        for(var i=1, len=geom.components.length; i<len; i++) {
            p1 = geom.components[i-1];
            p2 = geom.components[i];
            // this returns km and requires lon/lat properties
            length += OpenLayers.Util.distVincenty(
                {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
            );
        }
    }
    // convert to m
    return length * 1000;
}

For polygons areas, the algorithm is in the LinearRing source, and uses the earth's radius of 6378137 and geometry to calculate the geodesic area.

getGeodesicArea: function(projection) {
    var ring = this; // so we can work with a clone if needed
    if(projection) {
        var gg = new OpenLayers.Projection("EPSG:4326");
        if(!gg.equals(projection)) {
            ring = this.clone().transform(projection, gg);
        }
    }
    var area = 0.0;
    var len = ring.components && ring.components.length;
    if(len > 2) {
        var p1, p2;
        for(var i=0; i<len-1; i++) {
            p1 = ring.components[i];
            p2 = ring.components[i+1];
            area += OpenLayers.Util.rad(p2.x - p1.x) *
                    (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
                    Math.sin(OpenLayers.Util.rad(p2.y)));
        }
        area = area * 6378137.0 * 6378137.0 / 2.0;
    }
    return area;
},

The main problem I had with the circle was that I only had one reference coordinate, and there was no inbuilt way in OpenLayers to create a circle around this point apart from on a Cartesian plane. However with the OpenLayers.Util.distVincenty function it was relatively easy to calculate these.