Creating a node.js GeometryService : part 3

Its been a while since I posted my first and second post on a GeometryService based on node.js and PostGIS.

Today I want to announce that I open sourced my work in progress. It is in a pre-alpha state and it can be found here. The only thing it can do at the moment is convert ESRI Json geometries from one coordinate system to another. This is done by converting the geometry to WKT, creating a sql statement for the re-projection and then converting the GeoJSON result back to ESRI Json.

A sample input url when you started the geometryservice is : http://127.0.0.1:3000/rest/services/Geometry/GeometryServer/project?inSR=4326&outSR=3819&geometries={%22geometryType%22:%22esriGeometryPoint%22,%22geometries%22:[{%22x%22:-117,%22y%22:34},%20{%22x%22:-115,%22y%22:25}]}. This returns

{"geometries":[{"x":-115.00495449693217,"y":24.99533030379053},{"x":-117.00524224953409,"y":33.99507554185914}]}
as a result. The intermediate sql statement looks like this :
SELECT st_asgeojson(st_transform(st_geomfromtext(geometries.geometry, '4326'), '3819')) g FROM  (SELECT ST_ASTEXT('POINT(-117 34)') geometry UNION SELECT ST_ASTEXT('POINT(-115 25)')) geometries
.

Most of the logic of the code happens in geometryService.js. The conversion from and to ESRI Json happens in datatransformer.js. As stated in my previous post I also created some tests for the functions in the datatransformer.js. There is still a lot to do, especially everything related to error handling and logging and off course the implementation of the other geometry operations.

The main dependency is a runnning instance of PostgreSQL with PostGIS. As the connection string is not configurable yet you'll have to change it in the executeSQL function in geometryService.js.

The code can be found on following url https://bitbucket.org/gissolved/node_geometryservice and it is licensed under the MIT license.

If you like this then consider buying me a book or a license for Sublime Text 2.

Timing functions in Python

Recently I saw a blogpost on tracking time in Python. This is inspired me to share how I track time in my python scripts. First I put the following python decorator function in my code or preferably import it from a common module.

import time                                                

def timeit(method):

    def timed(*args, **kw):
        ts = time.time()
        result = method(*args, **kw)
        te = time.time()

        print('%r (%r, %r) %2.2f sec' % (method.__name__, args, kw, te-ts))
        return result

    return timed

You can use the timeit function like this:

@timeit
def longRunningOperation(dummyArg, optionalDummyArg=None):
    time.sleep(3)
    
longRunningOperation("test", "optional test")

The output of the timeit decorator function looks like this:

'longRunningOperation' (('test', 'optional test'), {}) 3.00 sec

As you can see, the timit function reports the function name, the passed arguments and the elapsed time

If you want to learn more about decorator functions I would suggest to read the python.org wiki article on decorators. On the same site there is also an article with useful decorator functions for things like caching, properties, pre- and post conditions, memoization and many more.

Roundup January 2012

This post is a roundup post for the month january.

Blogging

Just before the end of the year I created a tumblr blog where I shared some inspiring or interesting quotes.

On this blog I wrote 2 posts. The first post of january was my second post on developing a GeometryService in node.js. Some things I talked about where nodemonw for monitoring your files for changes and restarting node.js, node-inspector for debugging and vowsjs for writing tests. The second post of this month elaborated on the Geometry Visualizer that I created this month. The Geometry Visualizer uses the ArcGIS JavaScript API and openlayers to provide a .

GIS/Geography

Programming

Teaching/learning

Other

Entertainment

New blogs to follow

Visualizing Geometries

In this post I'm going to give some more information about the GeometryVisualizer that I created recently.

The goal of the GeometryVisualizer is to be able to quickly visualize a GeoJson, WKT or ESRI JSON geometry. I created this to be able to quickly see how some ESRI JSON and WKT geometries looked like and visually verify some geometry conversion code I'm writing for my GeometryService in node.js. You can also use this to visualize the WKT or GeoJSON output of a PostGIS geometry query.

The ESRI JSON geometries are rendered with the ArcGIS JavaScript API. The WKT and GeoJSON geometries are rendered with OpenLayers.

Some code

In this section I'll show some parts of the code that made this web application possible and compare the ArcGIS JavaScript API with the OpenLayers API. First we'll take a look at the initialization of the map. The most notable facts about the initialization are that we need to add a layer before we are able to add graphics and that disabling the zoomwheel is less straight forward with the OpenLayers API.

ESRI JavaScript API map initialization:

var map = new esri.Map("esriJsonMap");
map.disableScrollWheelZoom();

// add a layer and directly hide it because otherwise we can't create graphics
var basemapURL= "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"

var basemap = new esri.layers.ArcGISDynamicMapServiceLayer(basemapURL);
map.addLayer(basemap);
basemap.visible = false;

OpenLayers map initialization:

var options = {
  maxExtent: new OpenLayers.Bounds(-1000000000, -1000000000, 1000000000, 1000000000),
}
var map = new OpenLayers.Map('openLayersMap', options);

var controls = map.getControlsByClass('OpenLayers.Control.Navigation'); 
for(var i = 0; i<controls.length; ++i){ 
    controls[i].disableZoomWheel(); 
}

var layer = new OpenLayers.Layer.ArcGIS93Rest( "ArcGIS World Street Map", 
                "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
                {layers: '0'} );
map.addLayer(layer);
layer.setVisibility(false);

To be able to render an ESRI JSON geometry string with the ArcGIS JavaScript API you have to do some specific steps :

  • Clear the graphics
  • Parse the geometry string representation
  • Create a graphic and set its symbol according to the geometry type.
  • Update the extent of the map
Not that for updating the extent, I had to access an undocumented property of the graphics object and for point graphics the extent had to be enlarged.

function addGeometry(geometryText) {
    
    if(map.graphics === null) return; // map not loaded yet or something else is wrong
            
    map.graphics.clear();

    var geometryJson = JSON.parse(geometryText);
    var graphic = new esri.Graphic(geometryJson);

    if (!graphic.symbol){
      switch(graphic.geometry.type){
        case "point":
        case "multipoint":
          graphic.setSymbol(new esri.symbol.SimpleMarkerSymbol());
          break;
        case "polyline":
          graphic.setSymbol(new esri.symbol.SimpleLineSymbol());
          break;
        default:
          graphic.setSymbol(new esri.symbol.SimpleFillSymbol());
          break;
      }
    }

    map.graphics.add(graphic);
    // use undocumented object
    var ext = graphic._extent;
    // if point expand the extent
    if(ext.getWidth() < 0.00000001 || ext.getHeight() < 0.00000001){
      var factor = 1;
      ext.update(ext.xmin - factor, ext.ymin - factor, ext.xmax + factor, ext.ymax + factor, ext.spatialReference);
    }

    map.setExtent(ext.expand(2));
}

The process of adding WKT and GeoJSON geometries was very similar.

  • Remove all features
  • Parse the geometry string representation with a predefined formatter (OpenLayers.Format.WKT or OpenLayers.Format.GeoJSON)
  • Update the extent of the map.
Note that I didn't had to specify the symbology and zooming to the extent of the feature was easier. The WKT parsing was very straight forward. For the GeoJSON parsing I had to add two things. First I had to ensure that the parsed JSON had a property "type" with as value "Feature" and the parsed object returned a feature collection with one feature instead of directly returning the feature.

function addGeometry(geometryInput, parser, type){
  vectorLayer.removeAllFeatures();
  var feature = parser.read(geometryInput);
  vectorLayer.addFeatures(feature);
  if(dojo.isArray(feature)){
    feature = feature[0];
  }
  var bounds = feature.geometry.getBounds();
  bounds = bounds.scale(1.1);
  map.zoomToExtent(bounds);  
}

function addWkt(geometryText) {
  var wktFormat = new OpenLayers.Format.WKT();
  addGeometry(geometryText, wktFormat);
}

function addGeoJson(geometryText) {
  var geoJsonFormat = new OpenLayers.Format.GeoJSON();
  var geometry = JSON.parse(geometryText);
  geometry["type"] = "Feature";
  addGeometry(geometry, geoJsonFormat);
}

This are the most interesting parts of the code. The full source code is in the GeometryVisualizer.

Any questions/remarks/improvements ? Let me know !