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 !

3 comments:

Unknown said...

Sorry, but I can't see this WKT geometry:

MULTIPOLYGON(((659186.66 6483521.93,659186.64 6483521.93,659152.26 6483492.96,659166.5 6483486.06,659164.69 6483485.15,659204.137483521 6483463.16140272,659120.72 6483472.51,659125.52 6483527.04,659186.66 6483521.93)),((659044.1 6483478.91,659033.99 6483479.82,658970.23 6483485.17,658955.24 6483486.87,658955.29 6483487.49,659024.34 6483480.78,659044.1 6483478.91)),((658650.47 6483060.78,658655.04 6483113.86,658652.47 6483083.97,658651.19 6483067.76,658896.26 6483043.48,658899.1 6483043.28,658650.47 6483060.78)),((658850.32 6483097.25,658856.62 6483098.11,658860.89 6483098.47,658867.66 6483097.76,658875.85 6483094.91,658875.33 6483095.09,658850.32 6483097.25)),((658655.92 6483113.98,658659.66 6483114.49,658666.43 6483114.85,658678.18 6483113.07,658692.43 6483111.29,658717.01 6483110.22,658744.79 6483108.08,658776.84 6483104.52,658817.44 6483101.32,658836.67 6483097.76,658830.01 6483098.99,658655.92 6483113.98)),((658619.39 6482721.86,658820.73 6482709.3,658822.83 6482715.29,658821.41 6482711.23,658820.33 6482708.08,658670.82 6482718.65,658619.39 6482721.86)),((658749.76 6483115.65,658747.99 6483115.21,658744.43 6483114.85,658743.36 6483113.07,658739.09 6483113.78,658733.39 6483114.85,658730.18 6483115.56,658727.69 6483117.34,658723.77 6483117.34,658717.36 6483118.06,658712.73 6483118.06,658706.68 6483119.84,658700.98 6483119.48,658695.99 6483119.48,658690.29 6483120.55,658683.53 6483120.91,658676.76 6483121.26,658671.42 6483122.33,658672.87 6483122.04,658711.39 6483120.56,658711.25 6483119,658749.76 6483115.65)))

Samuel Bosch said...

Fixed above error by setting a maxExtent when initializing the OpenLayers.Map.

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

Anonymous said...

Very Nice!
Thanks.