Spatial to the Browser


Fig 1 – example using Bing Maps v8 SpatialMath module

I first noticed migration of spatial functions out to the browser back in 2014 with Morgan Herlocker’s github project turf.js, ref Territorial Turf blog.

Bing Maps version 8 SDK for web applications, released last summer, follows this trend, adding a number of useful modules that previously required custom programming or at least modification of open source projects.

Bing Maps v8
Bing Maps v8 API
Bing Maps v8 Modules

Among the many useful modules published with this version is Microsoft.Maps.SpatialMath. There are 25 geometry functions in this release for such things as intersection, buffer, convex hull, distance, and many others. Leveraging these geometry functions lets us move analytic functions from a SQL backend or C# .Net service layer back out front to the user’s browser.

Some useful side effects of this migration for projects using jsonp services include:

  • No need to host SQL data
  • No need to write a service layer
  • Dispersed computing that relieves compute loads on servers
  • No need to host in a Web Server such as IIS or Apache
  • Ability to publish in simple cloud storage with access to Edge Cache worldwide performance enhancement

As an example of this approach let’s look at the extensive GIS services exposed by the District of Columbia, DCGIS_Apps and DCGIS_Data. In addition to visualizing some layers it would be useful to detect parcels with certain spatial attributes such as distance to street access and area filters. With this ability Alley isolated parcels can be highlighted as potential alley development properties.

First setup a map panel using Bing Maps v8 loading ‘Microsoft.Maps.SpatialMath’

    // Create the v8 map
    getMap: function () {
        Microsoft.Maps.loadModule('Microsoft.Maps.SpatialMath');

        app.initialCenter = new Microsoft.Maps.Location(38.89892, -76.9883);
        app.initialZoom = 18;
        var mapEl = document.getElementById("map"),
            mapOptions = {
                customizeOverlays: true,
                zoom: app.initialZoom,
                credentials: '<Your Bing Key goes here>',
                mapTypeId: Microsoft.Maps.MapTypeId.road,
                showBreadcrumb: false,
                showDashboard: true,
                showMapTypeSelector: true,
                enableClickableLogo: false,
                enableSearchLogo: false,
                center: app.initialCenter
            };

        var dcocto_url = 'https://oblique.sanborn.com/dcocto/?ll='
                     + app.initialCenter.latitude + ',' + app.initialCenter.longitude;
        $('#dcocto_frame').attr('src', dcocto_url);

        try {
            app.map = new Microsoft.Maps.Map(mapEl, mapOptions);
            app.layers = {
                properties: new Microsoft.Maps.Layer({ zIndex: 500 }),
                streets: new Microsoft.Maps.Layer({ zIndex: 600 }),
                alleys: new Microsoft.Maps.Layer({ zIndex: 700 }),
                zones: new Microsoft.Maps.Layer({ zIndex: 800 }),
                interiors: new Microsoft.Maps.Layer({ zIndex: 900 })
            };
            app.showlayers = {
                properties: true,
                streets: true,
                alleys: false,
                zones: false,
                interiors: false
            }
            _.each(app.layers, function (layer) {
                app.map.layers.insert(layer);
            });

            app.getLayers();

            //viewchangeend handler
            Microsoft.Maps.Events.addHandler(app.map, 'viewchangeend', app.getLayers);

            app.infobox = new Microsoft.Maps.Infobox(app.map.getCenter(), {
                title: 'Title',
                description: 'Description',
                visible: false
            });
            app.infobox.setMap(app.map);
		.
		.
		.

District of Columbia DCGIS services provide jsonp callbacks circumventing crossbrowser security constraints and allowing simpler ajax calls without back and forth trips to a proxy server layer.

if (app.showlayers.properties) {
    var bds = app.map.getBounds();
    var urlProperty = 'http://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_APPS/Real_Property_Application/MapServer/3/query?
f=json&returnGeometry=true&spatialRel=esriSpatialRelIntersects&maxAllowableOffset=0&geometry='
+ bds.getWest() + ',' + bds.getSouth() + ',' + bds.getEast() + ',' + bds.getNorth()
+ '&geometryType=esriGeometryEnvelope&inSR=4326&outFields=*&outSR=4326&callback=';
    $.getJSON(urlProperty, function (json) {
        app.renderProperty(json);
     });
}

LocationRect buffer method simplifies extending the street viewport to guarantee all parcels in a city block are checked against surrounding street centerlines. bds.buffer(1.5);

if (app.showlayers.streets) {
	var bds = app.map.getBounds();
	bds.buffer(1.5);
	var urlStreet = 'https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Transportation_WebMercator/MapServer/41/query?
f=json&returnGeometry=true&spatialRel=esriSpatialRelIntersects&maxAllowableOffset=0&geometry=' + bds.getWest()
+ ',' + bds.getSouth() + ',' + bds.getEast() + ',' + bds.getNorth()
+ '&geometryType=esriGeometryEnvelope&inSR=4326&outFields=*&outSR=4326&callback=';
	$.getJSON(urlStreet, function (json) {
		app.renderStreet(json);
	});
}

The algorithm for discovering interior parcels is greatly simplified using lodash and the new Bing Maps v8 spatial geometry functions. Notice that Geometry.distance handles a shortest distance calculation between a property polygon and an array of polylines, “allstreets.”

findInteriors: function () {
        var allproperties = app.layers.properties.getPrimitives();
        var allstreets = app.layers.streets.getPrimitives();
        var interiorProperties = [];
        _.each(allproperties, function (property) {
            var distance = Microsoft.Maps.SpatialMath.Geometry.distance(property, allstreets,
                             Microsoft.Maps.SpatialMath.DistanceUnits.Feet, true);
            var area = Microsoft.Maps.SpatialMath.Geometry.area(property, Microsoft.Maps.SpatialMath.AreaUnits.SquareFeet);
            if (distance > 100 && distance < 1000 && area > 450) {
                interiorProperties.push(property);
            }
        });

        app.layers.interiors.clear();

        _.each(interiorProperties, function (property) {
            property.setOptions({ fillColor: new Microsoft.Maps.Color(200, 255, 0, 0) });
            app.layers.interiors.add(property);
        });
    },

Function findInteriors checks for shortest distance in feet for each parcel against every street centerline. The filter then checks for shortest distances falling between 100 ft and 1000 ft and parcel area sf > 450. Parcels meeting this filter criteria are set to fill red.

The Bing spatial distance function is using the higher accuracy Vincenty’s algorithm, but still performs reasonably well. Experiments using the less accurate Haversine option showed no significant performance difference in this case.

DC GIS Services limit the number of features in a request result to a maximum of 1000. At zoom level 18 the viewport always returns less than this maximum feature limit, but lower zooms can hit this limit and fail to return all parcels in the view. A warning is triggered when feature counts hit 1000 since the interior parcel algorithm will then have an incomplete result.

Summary

The new Bing Maps v8 adds a great number of features simplifying web mapping app development. In addition Bing Maps v8 improves performance by making better use of html5 canvas and immediate mode graphics. This means a larger number of features can be added to a map before map navigation begins to slow. I was able to test with up to 25000 features with no significant problems using map zoom and pan navigation.

Bing Maps SpatialMath module provides many useful spatial algorithms that previously required a server and/or SQL backend. The result is simpler web map applications that can be hosted directly in Azure blob storage.

Sample DCProperties code is available on github:
https://github.com/rkgeorge/DCProperties/

Comments are closed.