Tag Archives: Thematic Mapping

What’s New in the Leaflet DVF

OK, OK, it’s been awhile since my last blog post.  I had grand visions of writing posts every month or two, but work and life caught up to me.  It’s been about a year since the Leaflet DVF was officially released on GitHub, so I wanted to share a few updates on the state of the framework.   Also, this post is kind of a catchall for things that I failed to write about over the past year, which was everything new in the DVF, so expect to see lots of screenshots, short overview write ups and examples that probably could have warranted their own blog posts had I been better about writing them.

If you haven’t read my previous posts introducing the DVF and some of its basic features, the DVF started out as my side project at HumanGeo.  I realized that I was duplicating geospatial visualization code, and I wanted to experiment with creating a generic set of tools that make visualizing geospatial data using Leaflet as easy as possible – even when nicely formatted GeoJSON isn’t available.  This framework gave me the opportunity to do just that.  I’m happy to report that the DVF is going strong with ramped up community support, adoption, and participation, plus a whole host of new features.

As an aside, and before I delve into some of the new features in the DVF, we recently updated our website.  Since I helped to design and build the site, unsurprisingly, the DVF is featured prominently – used in the main map tour at the top of the front page combined with an awesome Stamen watercolor background map and some cool parallax effects using parallax.js.  Check it out in a modern browser.  Just click on the HumanGeo logo to start the tour, and expect a blog post about creating this map in the near future (by “near future” I mean a few weeks, not a year).

The new HumanGeo website
The new HumanGeo website

Custom gradients

For a few months now, the DVF has provided support for gradient fills.  To supply a gradient fill, all you had to do was to provide a property called gradient with a value of true, and this would fill your path shape with a gradient that ranged from white to the provided fillColor value along a 45 degree angle (diagonally down and to the right).

var marker = new L.RegularPolygonMarker(new L.LatLng(0, 0), {
     ...
     gradient: true,
     fillColor: '#ff0000'
});

In the latest version, you can now define custom linear gradients with full control over direction and stops.

var marker = new L.RegularPolygonMarker(new L.LatLng(0, 0), {
     ...
     gradient: {
        vector: [['0%', '50%'], ['100%', '50%']],
        stops: [{
            offset: '0%',
            style: {
                color: '#ffffff',
                opacity: 1
            }
        }, {
            offset: '50%',
            style: {
                color: '#ff0000',
                opacity: 1
            }
        }]
     }
});

If you’re lazy and don’t provide a color value as part of the properties of a stop object, your fillColor or color value will be used by default.

Fill Patterns

Like the newly added support for custom gradients, we’ve also added support for custom fill patterns.  This allows you to fill path shapes with a repeating image of your choice.  Properties in the pattern option (e.g. patternUnits) follow the attributes of the SVG pattern element explained in more detail here.  Check out the Sochi Medal Count Map example (discussed later on) for an example.

var polygon = new L.Polygon(..., {
     ...
     fillPattern: {
	url: 'http://images.com/someimage.png',
	pattern: {
	        width: '100px',
	        height: '66px',
		patternUnits: 'userSpaceOnUse',
		patternContentUnits: 'Default'
	},
	image: {
		width: '113px',
		height: '87px'
	}
    }
});

Adding Images to Leaflet Path Shapes

Marker images
Images on Markers

Thanks to a contribution from franck34, the DVF now supports the option of placing images on markers or on any Leaflet path-based layer, which can be specified in one of two ways.  The simplest way is to provide an imageCircleUrl option as part of the normal L.Path options like so:

var marker = new L.RegularPolygonMarker(new L.LatLng(0, 0), {
     ...
     imageCircleUrl: 'images/icon.png'
});

This will place a circle on top of your path shape that is filled with the specified image. The image will be automatically sized to fill the area of the shape.  For finer-grained control over the images you add, you can specify a shapeImage option.  This lets you control the shape of the overlay – you can provide basic SVG shapes such as a circle, a rect, or an ellipse – and also control the size of the image and pattern.  The example below creates a circle with radius of 24 pixels; an image is then specified through the image option with a width and height of 48 pixels

var marker = new L.RegularPolygonMarker(new L.LatLng(0, 0), {
     ...
     shapeImage: {
		shape: {
                        // An SVG shape tag (circle, rect, etc.)
			circle: {
				r: 24, //Radius in pixels
				'fill-opacity': 1.0,
				stroke: 'green',
				'stroke-width': 8.0,
				'stroke-opacity': 0.5
			}
		},
		image: {
			url: 'http://upload.wikimedia.org/wikipedia/commons/a/a7/Emblem-fun.svg',
			width: 48,
			height: 48,
			x: 0,
			y: 0
		},
		pattern: {
			width: 48,
			height: 48,
			x: 0,
			y: 0
		}
     }
});

Adding Text to Leaflet Path Shapes

Markers with text
Text on markers and other paths

You can also now place text on Leaflet path-based objects using the text option.  The only required option within the text option is the text property, which is simply the string you want to display.  Specify attr, style, and path options if you want more control over how that text appears.

var layer = new L.CircleMarker(new L.LatLng(0, 0), {
    // L.Path style options
    ...
    text: {
        text: 'Test',
        // Object of key/value pairs specifying SVG attributes to apply to the text element
        attr: {},
        
        // Object of key/value pairs specifying style attributes to apply to the text element
        style: {},
        
        // Include path options if you want the text to be drawn along the path shape
        path: {
            startOffset: 'Percentage or absolute position along the path where the text should start',
            
            // Objects of key/value pairs specifying SVG attributes/style attributes to apply to the textPath element
            attr: {},
            style: {}
        }
    }
});

L.MarkerGroup

We’ve also added the L.MarkerGroup class.  This class just extends L.FeatureGroup and adds getLatLng and setLatLng methods.  This allows you to create custom markers from a combination of various DVF and Leaflet marker classes.  For instance, the marker displayed in the Earthquake screenshot above is a MarkerGroup composed of multiple CircleMarker instances.

GW country codes

We’ve added support for Gleditsch and Ward (G & W) country codes and provided a few examples that illustrate visualizing data where a G & W country code is the primary method of identifying a country.  This is a newer and perhaps lesser used way of classifying data by country, but, nonetheless, it is still used by some statistical data sources.  Just set the DataLayer locationMode to L.LocationModes.GWCOUNTRY to use it.  Check out the conflict data example, which relies on G & W country codes.

Deaths by conflict by country using G & W country codes
Deaths by conflict by country using G & W country codes

Callouts

Callouts for annotating map data
Callouts for annotating map data

You can now add callouts to your maps using the L.Callout class.  This is useful for annotating map features with additional information.  In this case, callouts consist of an arrow path shape and line with an attached L.Icon or L.DivIcon.  When you create a callout, you can specify the shape of the line (straight, arced, angled) and the shape of the arrow (this is just a regular polygon, so you can specify the number of sides and radius).  Check out the new Markers example for a useful illustration of this feature.  Here’s a quick example that creates a new callout:

var callout = new L.Callout(new L.LatLng(0.0, 0.0), {
    arrow: true,
    numberOfSides: 3,
    radius: 8,
    icon: new L.DivIcon(...),
    direction: L.CalloutLine.DIRECTION.NE,
    lineStyle: L.CalloutLine.LINESTYLE.ARC,
    size: new L.Point(50, 50),
    weight: 2,
    fillOpacity: 1.0,
    color: '#fff',
    fillColor: '#fff'
});

Lines

We’ve added several new features related to visualizing data with lines.

L.FlowLine

Running data visualized with the L.FlowLine class
Running data visualized with the L.FlowLine class
Another re-creation of Minard's famous Napoleon's March visualization using the L.FlowLine class
Another re-creation of Minard’s famous Napoleon’s March visualization using the L.FlowLine class

Recently, we added the L.FlowLine class, which is a special type of DataLayer for visualizing the flow of data spatially.  The goal of this class is to illustrate the change in some measure of data as that measure of data evolves through space/time.  A perfect use case for this class would be GPS measurement data from a GPX file, or perhaps something like stream flow data generated by stream gauges along a river.  As an example, I’ve reproduced Charles Minard’s famous Napoleon’s March visualization (minus the temperature chart) that illustrates Napoleon’s troop loss as he marched on Moscow and subsequently retreated.  Check it out.

L.WeightedPolyline

A run visualized using the L.WeightedPolyline class
A run visualized using the L.WeightedPolyline class

SVG does not currently provide a way to vary the weight of a line between two points.  There’s some discussion in the SVG community about ways of enabling this capability, but in the meantime, we’ve provided a means of drawing variable weight lines as polygons.  Each segment of the entire line is a separate polygon with dynamically calculated points that make it appear as though the entire line has a variable stroke width.  This is analogous to/inspired by MapBox’s Tom MacWright’s running map example implemented using MapBox.  In the DVF case, this functionality is implemented purely in a Leaflet context.  Here’s some example code:


// data is an array of L.LatLng objects, where each L.LatLng object has an additional weight property
// {
//    lat: 0.0,
//    lng: 0.0,
//    weight: 20
// }
var weightedPolyline = new L.WeightedPolyline(data, {
     // L.Path style options
     ...
     // weightToColor specifies how a weight will be translated to a color
     weightToColor: new L.HSLHueFunction([0, 0], [20, 120])
});

The only option you really need to specify in addition to the basic L.Path style options is a weightToColor option.  This controls the fillColor that gets displayed based on the weight at each point.  Note that the weight value directly affects the stroke width – the ultimate stroke width will be two times the weight at each point, so you may have to do some of your own translating to convert raw data values to appropriate weight values for now.  In the future, you’ll be able to control this more easily by specifying your own custom LinearFunction.

One other thing to note is that this feature is currently included in the experimental file in the src folder (leaflet-dvf.experimental.js), meaning that it works, but it’s not necessarily ready for primetime.  You’ll need to include the experimental file in your JavaScript imports in order to use this class.  One of my ultimate goals with this class would be add this as a line style option in the L.FlowLine class.

See the Run Map example.

L.ArcedPolyline

Leaflet provides an L.Polyline class for displaying straight line segments between multiple points.  The L.ArcedPolyline class provides an alternative arced line representation, where the height of the arc is proportional to the distance between two end points.   This gives the connections between points a nice, pseudo-3D effect.  Note that the height calculation can be configured via a distanceToHeight option that takes a LinearFunction specifying how a distance value gets translated to a height value in pixels.

var arcedPolyline = new L.ArcedPolyline([...], {
    distanceToHeight: new L.LinearFunction([0, 0], [4000, 400]),
    color: '#FF00000',
    weight: 4
});

L.Graph

Visualizing flights using the L.Graph class with L.ArcedPolyline edges
Visualizing flights using the L.Graph class with L.ArcedPolyline edges

In a spatial context, it’s sometimes important to visualize the relationships between geospatial locations.  The L.Graph class is a special DataLayer that provides a useful means for illustrating relationships between multiple geospatial locations (vertices) connected by edges.  Locations/vertices can be specified using any of the existing DVF location modes, and edges can be depicted using straight lines or arced lines and can be styled dynamically based on some property of the relationship between two vertices.  There are several use cases for this, such as visualizing network traffic between multiple geo-located nodes or illustrating the movement of people, goods, or other items from place to place or from one administrative area to another.  Check out the Flights example for more details.

Sparklines

Visualizing time series data in Africa using the L.SparklineDataLayer class
Visualizing time series data in Africa using the L.SparklineDataLayer class

The sparkline concept has been around for many years.  Mostly it’s a minimalist way of illustrating the change in data over time or some other measurement in a small space.  I’ve tried to reproduce this in a map context using the L.SparklineDataLayer class.  The goal here is to make it easy for developers to generate geo-located time series plots using any data source that has location (implicit or implied) along with time series data.  As is the case with the L.WeightedPolyline class, this class is included in the experimental file since it hasn’t been fully tested, so you’ll have to include that file separately if you want to use it.  Check out the Sparklines example.

Custom SVG Markers

Custom SVG Markers
Custom SVG Markers

You can now display custom SVG images on the map with full control over manipulating those images.  This is a little bit of a hack at the moment, as it extends the L.Path class, which is not technically appropriate.  This class should really extend a more generic class and implement the Layer interface, but the short answer is that it works until we get around to making it perfect.  The advantage of this approach over using an L.Marker with an L.Icon and an iconUrl that points to an SVG image is that you can programmatically control the style of the SVG in code using the setStyle option.  This is pretty powerful, as it allows you to dynamically restyle the SVG and its sub-elements based on one or more properties in the data.  Use D3, Raphael, Snap SVG, plain old jQuery – whatever you like.

var marker = new L.SVGMarker(new L.LatLng(0, 0), {
     svg: 'url to some SVG file',
     setStyle: function (svg) {
        // Do something with the SVG element here
        $(svg).find('rect').attr('fill', '#f32');
     }
});

L.StackedPieChart

Matthew Sabol has contributed a slick new marker called the L.StackedPieChart, which is more or less really just an advanced Coxcomb chart/polar area chart reminiscent of Florence Nightingale’s Diagram of the Causes of Mortality in the Army in the East.  You can find an example of how to use this marker in the Markers example.

L.StackedPieChart
L.StackedPieChart

New examples

Sochi Medal Count Map

Sochi medal counts by country using the Kimono Labs API
Sochi medal counts by country using the Kimono Labs API

I just squeaked this example in before the end of the Sochi games, but it’s a Sochi Medal Count map driven by data from the Kimono Labs Sochi API.  I saw a Kimono Labs post on Hacker News about their Sochi API and felt like it would be a perfect candidate for a Leaflet DVF map.  The example uses a DataLayer along with the L.StackedRegularPolygonMarker class to draw proportional medal counts by country.  It also illustrates the use of the fillPattern option for filling Leaflet path-based shapes with an image.  In this case, the image is the flag for the given country for which a medal count is displayed, which might be a little garish, but I feel like it goes well with the spirit of the Olympics.

US County Level Statistics

County-level statistics
County-level statistics
County-level statistics comparison
County-level statistics comparison

A question on the DVF GitHub page prompted me to create this example.  I wanted to illustrate using the DVF to display statistics for lower level administrative boundaries, particularly statistics related to US counties.  The example leverages TopoJSON for efficient delivery of county polygons.  I definitely recommend using TopoJSON whenever you have a large number of polygons, as it significantly reduces the size of GeoJSON files being delivered to the browser.

Better support for older browsers

Thanks to Keith Chew, Matthew Sabol, Chris, and several other users, we’ve started to test and support older browsers – particularly IE 8 and below.   There are lots of developers out there still required to support IE 8 and below, particularly those developing internal solutions for large organizations and those supporting governments at all levels, and I feel your pain.   If you were to make the (admittedly weak) analogy between the development cycle for a non-trivial JavaScript framework like the DVF and Gartner’s Hype Cycle, I’m currently in the IE 8 “Trough of Disillusionment”.  Everything was awesome when I initially released the DVF, and I successfully avoided any mention of IE 8, for a little while at least.  Life was good.  Then people started using the framework more, and then came the first IE 8 issue, and then the second one; a few more issues later, and I’m feeling slightly overwhelmed – torn between providing band-aid fixes that help support archaic browsers and developing new capabilities.  Certainly I’d rather be spending time developing new capabilities and fixing bugs than adding patches to support a browser that was released in 2009.  If only Vector Markup Language (VML) expertise was still a marketable skill.  Hopefully IE 8 will be going away soon – many of the more well known Internet players are dropping support for IE 8 altogether (Google, GitHub, etc.).  Now that Microsoft has dropped support for XP, my hope is that larger organizations and governments will move on to newer/better browsers.  In the meantime, you’ll have to include the Core Framework and an associated SVG plugin in order to have certain features work in IE 8.

I’ll end by saying that just as I’ve been bad about blogging, I’ve also been bad about keeping the DVF documentation up to date.  My goal is to devote some time to updating and re-organizing the DVF documentation so that it’s easier to pickup and master.  One of the Program Managers at HumanGeo likes to joke that my development productivity increases any time documentation is due.  He’s pretty much right; in fact, I wrote this blog post while I was in the process of working on documentation for another project.

If you’re using the DVF or want to contribute, we’d like to hear from you.  We’re always interested in hearing how the framework is being used and what you like and dislike about it.  As always, feel free to contribute by submitting pull requests for new capabilities and bug fixes on GitHub.

Leaflet DVF Overview

In my last blog entry, I introduced HumanGeo’s Leaflet Data Visualization Framework (DVF) and provided insight into the motivations driving the development of the framework.  Now let’s take a closer look at some of the new features that the framework provides for simplifying thematic mapping using Leaflet.

New Marker Types

In terms of visualizing point data, Leaflet offers image-based (L.Marker), HTML-based (L.Marker with L.DivIcon), and circle markers (L.CircleMarker).  While these can be useful tools for symbolizing data values (particularly the CircleMarker), it’s always nice to have some variety.  The framework adds several new marker types that are geared towards illustrating dynamic data values.

L.RegularPolygonMarker  Framework RegularPolygonMarker
L.PieChartMarker, L.BarChartMarker, L.CoxcombChartMarker, and L.RadialBarChartMarker  Framework Chart Markers
L.RadialMeterMarker  Framework RadialMeterMarker
L.StackedRegularPolygonMarker  StackedRegularPolygonMarker

Mapping Data Properties to Leaflet Styles

The framework includes classes for dynamically mapping data values to Leaflet style values (e.g. radius, fillColor, etc.).  These classes are similar to D3’s scale concept.  Mapping values from one scale/format to another is a common aspect of creating thematic maps, so this is a critical feature despite its relative simplicity.  The main classes to consider are:

  • L.LinearFunction:  This class maps a data value from one scale to another.  One example use might be to map a numeric data property (e.g. temperature, counts, earthquake magnitude, etc.) to a radius in pixels.  You create a new LinearFunction by passing in two data points composed of x and y values (L.Point instances or any object with x and y properties), where these points represent the bounds of possible values – x values represent the range of input values and y values represent the range of output values.  If you remember back to your algebra days, you’ll recall the concept of linear equations, where given two points in cartesian space, you can calculate the slope (m) and y-intercept (b) values from those points in order to determine the equation for the line that passes through those two points (y = mx + b).  This is really all that the LinearFunction class is doing behind the scenes.  Call the evaluate method of a LinearFunction to get an output value from a provided input value; this method interpolates a y value based on the provided x value using the pre-determined linear equation.  The LinearFunction class also includes options for pre-processing the provided x value (preProcess) and post-processing the returned y value (postProcess) whenever evaluate is called.  This can be useful for translating a non-numeric value into a numeric value or translating a numeric output into some non-numeric value (e.g. a boolean value, category string ,etc.).  It can also be used to chain linear functions together.
  • L.PiecewiseFunction:  It’s not always possible to produce the desired mapping from data property to style property using just a single LinearFunction.  The PiecewiseFunction class allows you to produce more complicated mappings and is literally based on the Piecewise Function concept.  For instance, if you wanted to keep the radius of a marker constant at 5 pixels until your data property reaches a value of 100 and then increase the radius after that from 5 pixels to 20 pixels between the values of 100 and 200, you could by using a PiecewiseFunction composed of two LinearFunctions as illustrated in the example below.
    var radiusFunction = new L.PiecewiseFunction([new L.LinearFunction(new L.Point(0, 5), new L.Point(100, 5)), new L.LinearFunction(new L.Point(100, 5), new L.Point(200, 20))]);
    
  • Color Functions:  Color is an important aspect of data visualization, so the framework provides classes derived from LinearFunction that make it easy to translate data properties into colors.  The framework relies heavily on Hue, Saturation, Luminosity/Lightness (HSL) color space over the more familiar, ubiquitous Red, Green, Blue (RGB) color space.  HSL color space offers some advantages over RGB for data visualizations, particularly with respect to numeric data.  Hue, the main component used to determine a color in HSL space, is an angle on the color wheel that varies from 0 degrees to 360 degrees according to the visible spectrum/colors of the rainbow (red, orange, yellow, green, blue, indigo, violet, back to red).  This makes it easy to map a numeric input value to an output hue using the same LinearFunction concept described previously and gives us nice color scales – green to red, yellow to red, blue to red, etc – that work well for illustrating differences between low and high values.  Achieving the same effect with RGB color requires varying up to three variables at once, leading to more code and complexity.
    • L.HSLHueFunction:  This class produces a color value along a rainbow color scale that varies from one hue to another, while keeping saturation and luminosity constant.
    • L.HSLLuminosityFunction:  This class varies the lightness/darkness of a color value dynamically according to the value of some data property, while keeping hue and saturation constant.
    • L.HSLSaturationFunction:  This class varies the saturation of a color value dynamically according to the value of some data property, while keeping hue and luminosity constant.

Data Layers

As I mentioned in my previous post, one point of the framework is to standardize and simplify the way in which thematic mapping data are loaded and displayed; keeping this in mind, the framework provides classes for loading and displaying data in any JSON format.  The framework introduces the concept of a DataLayer, which serves as a standard foundation for loading/visualizing data from any JavaScript object that has a geospatial component.

  • L.DataLayer:  Visualizes data as dynamically styled points/proportional symbols using regular polygon or circle markers
  • L.ChoroplethDataLayer:  This class allows you to build a choropleth map from US state or country codes in the data.  The framework provides built-in support for creating US state and country choropleth maps without needing server-side components.  Simply import the JavaScript file for state boundaries or the JavaScript file for country boundaries if you’re interested in building a state or country level choropleth.  In addition, states and countries can be referenced using a variety of codes.
  • ChartDataLayers – L.PieChartDataLayer, L.BarChartDataLayer, L.CoxcombChartDataLayer, L.RadialBarChartDataLayer, L.StackedRegularPolygonDataLayer:  These classes visualize multiple data properties at each location using pie charts, bar charts, etc.

Support for custom/non-standard location formats (e.g. addresses)

Data doesn’t always come with nicely formatted latitude and longitude locations.  Often there is work involved in translating those location values into a format that’s useable by Leaflet.  DataLayer classes allow you to pass a function called getLocation as an option.  This function takes a location identified in a record and allows you to provide custom code that turns that location into a format that’s suitable for mapping.  Part of this conversion could involve using an external web service (e.g. geocoding an address).

Support for automatically generating a legend that describes your visualization

Legends are common thematic mapping tools that help users better understand and interpret what a given map is showing.  Simply call getLegend on any DataLayer instance to get chunk of HTML that can be added to your application or add the L.Control.Legend control to your Leaflet map.  This control will automatically display the legend for any DataLayer instance that has been added to the map.

A Quick Example

Here’s a quick example choropleth map of electoral votes by state with states colored from green to red based on the number of electoral votes:

Electoral Votes Choropleth
Electoral Votes by State Colored from Green to Red
//Setup mapping between number of electoral votes and color/fillColor.   In this case, we're going to vary color from green (hue of 120) to red (hue of 0) with a darker border (lightness of 25%) and lighter fill (lightness of 50%)
var colorFunction = new L.HSLHueFunction(new L.Point(1, 120), new L.Point(55, 0), {outputSaturation: '100%', outputLuminosity: '25%'});
var fillColorFunction = new L.HSLHueFunction(new L.Point(1, 120), new L.Point(55, 0), {outputSaturation: '100%', outputLuminosity: '50%'});

var electionData = {…};
var options = {
	recordsField: 'locals',
	locationMode: L.LocationModes.STATE,
	codeField: 'abbr',
	displayOptions: {
		electoral: {
			displayName: 'Electoral Votes',
			color: colorFunction,
			fillColor: fillColorFunction
		}
	},
	layerOptions: {
		fillOpacity: 0.5,
		opacity: 1,
		weight: 1
	},
	tooltipOptions: {
		iconSize: new L.Point(80,55),
		iconAnchor: new L.Point(-5,55)
	}
};

// Create a new choropleth layer from the available data using the specified options
var electoralVotesLayer = new L.ChoroplethDataLayer(electionData, options);

// Create and add a legend
$('#legend').append(electoralVotesLayer.getLegend({
	numSegments: 20,
	width: 80,
	className: 'well'
}));

map.addLayer(electoralVotesLayer);

I want to highlight a few details in the code above. One is that there’s not a lot of code. Most of the code is related to setting up options for the DataLayer. Compare this to the Leaflet Choropleth tutorial example, and you’ll see that there’s less code in the example above (34 lines vs. about 89 lines in the Leaflet tutorial). It’s not a huge reduction in lines of code given that the framework handles some of the functions that the Leaflet tutorial provides (e.g. mouseover interactivity), but the Leaflet tutorial is using GeoJSON, which as I mentioned earlier is well handled by Leaflet, and the example above is not.  I’ve omitted the data for this example, but it comes from Google’s election 2008 data and looks like this:

{
    ...,
    "locals":{
        ...,
        "Mississippi": {
            "name": "Mississippi",
            "electoral": 6,
            ...,
            "abbr": "MS"
        },
        "Oklahoma": {
            "name": "Oklahoma",
            "electoral": 7,
            ...,
            "abbr": "OK"
        },
        ...
    },
        ...
}

When configuring the L.ChoroplethDataLayer, I tell the DataLayer where to look for records in the data (the locals field), what property of each record identifies each boundary polygon (the abbr field), and what property/properties to use for styling (the electoral field).  In this case, the L.ChoroplethDataLayer expects codeField to point to a field in the data that identifies a political/admin boundary by a state code.  In general, DataLayer classes can support any JSON-based data structure, you simply have to point them (using JavaScript style dot notation) to where the records to be mapped reside (recordsField), the property of each record that identifies the location (codeField, latitudeField/longitudeField, etc. – depending on the specific locationMode value), and the set of one or more properties to use for dynamic styling (displayOptions).  Another feature illustrated in the example above is that there’s no picking of a set of colors to use for representing the various possible ranges of numeric data values.  In the example above, color varies continuously with respect to a given data value, based on the range that I’ve specified using an L.HSLHueFunction, which as I mentioned earlier varies the hue of a color along a rainbow color scale.  The last feature I want to highlight is that the framework makes it as easy as one function call to generate a legend that describes your DataLayer to users.  There’s no need to write custom HTML in order to generate a legend.

That’s it for now.  Hopefully this overview has given you a better sense of the key features that the framework provides.  Detailed documentation is still in the works, but check out the examples on GitHub.  In my next post, I’ll walk through the Earthquakes example, which is basically just a recreation of the USGS Real-Time Earthquakes map that I alluded to in my previous post.

Introducing HumanGeo’s Leaflet Data Visualization Framework

leaflet-logoAt HumanGeo, we’re fans of Leaflet, Cloudmade’s JavaScript web mapping framework.  In the past, we’ve used other JavaScript mapping frameworks like OpenLayers and Google Maps, and while these frameworks are great, we like Leaflet for its smooth animation, simple API, and good documentation and examples.  In fact, we like Leaflet so much, we’ve been using it in all of our web projects that require a mapping component.  Since Leaflet is a relative newcomer in the world of JavaScript mapping frameworks (2011), and since the developers have focused on keeping the library lightweight, there are plenty of opportunities to extend the basic functionality that Leaflet offers.

As a side project at HumanGeo, we’ve been working on extending Leaflet’s capabilities to better support visualizing data, and these efforts have produced HumanGeo’s Leaflet Data Visualization Framework.  Before I delve into some of the features of the framework, I’d like to provide a little background on why we created the framework in the first place, in particular, I’d like to focus on the challenges that developers face when creating data-driven geospatial visualizations.

When visualizing data on a 2D map we often wish to illustrate differences in data values geographically by varying point, line, and polygon styles (color, fill color, line weight, opacity, etc.) dynamically based on the values of those data.   The goal is for users to look at our map and quickly understand geographic differences in the data being visualized.  This technique is commonly referred to as thematic mapping, and is a frequently employed technique used in infographics and for illustrating concepts related to human geography.  Within the realm of thematic mapping, proportional symbols and choropleth maps are two widely used approaches for illustrating variations in data.

A subset of Leaflet layer style options
Symbol Styling Options

The proportional symbol approach highlights variations in data values at point locations using symbols sized proportionally to a given data value.  In addition to varying symbol size, we can also vary symbol color or other style properties in order to highlight multiple data properties at each point.  The image on the right shows some of the available style properties that we can vary for circle markers in Leaflet.

A good example of this approach is the USGS Earthquake Map, which by default shows earthquakes of magnitude 2.5 or greater occurring in the past week.  This map denotes earthquakes using circles that are sized by magnitude and colored by age (white – older to yellow – past week to red – past hour).  In an upcoming blog post, I’ll describe how we can use the Leaflet Data Visualization Framework along with USGS’ real-time earthquakes web service to easily reproduce this map.

USGS Real-Time Earthquakes
USGS Real-Time Earthquakes Map

Choropleth mapping involves styling polygons – typically country, state, or other administrative/political boundaries – based on statistical data associated with each polygon.  In US election years, we see tons of maps and infographics showing breakdowns of voter preferences and predicted winners/losers by state, county, or other US political boundary.  Infographics are becoming more and more popular, and there’s no shortage of news outlets like the New York Times, The Washington Post, and others producing maps illustrating various statistics at a range of administrative boundary levels.  On the web, these choropleth maps are often custom developed, static or potentially interactive, single purpose maps that typically make use of a variety of frameworks, including more advanced all-purpose visualization frameworks like D3 rather than web mapping frameworks like Leaflet.  This single purpose approach is not a problem when you’re using the map to show only one thing, but what if you want to show multiple data views or a variety of data on the same map?  Nothing against D3 (which is awesome) or other general purpose visualization frameworks, but why would I want to learn the intricacies of some other framework in order to produce a choropleth map?  If I’m already using Leaflet for all of my web mapping activities, why wouldn’t I use it for creating thematic maps?

Fortunately, Leaflet provides a number of built-in capabilities that enable creating thematic maps, including excellent support for GeoJSON and the ability to draw points using image-based, HTML-based, and circle markers as well as support for drawing lines and polygons and styling those features dynamically.  There are a few tutorials on the Leaflet website that explain how to use Leaflet’s GeoJSON capabilities for displaying point, line, and polygon data and creating choropleth maps.  I recommend checking these tutorials out if you’re interested in using Leaflet or want to better understand what capabilities Leaflet provides out of the box (screenshots of these tutorials plus links to the tutorials appear below).

Loading GeoJSON using Leaflet
Loading GeoJSON using Leaflet
Leaflet Choropleth Map
Creating a Choropleth Map using Leaflet

While Leaflet’s out of the box capabilities simplify thematic map creation, there are still several challenges that developers face when trying to create thematic maps using Leaflet or any web mapping framework for that matter, particularly when GeoJSON data is not available.  The first challenge is a common one for developers – no standard format.  The Internet is full of data that can be used to build thematic maps, but this data comes in a variety of formats (CSV, TSV, XML, JSON, GeoJSON, etc.).  This makes building reusable code that works with and across most datasets challenging.  The underlying issue here, and perhaps the main reason that data is and will continue to be created in a variety of formats, is that the people creating this data aren’t typically creating the data with geospatial visualization in mind, so there will almost always be some aspect of massaging the data so that it can be loaded and displayed using the desired mapping framework.

Mapping data on a political/admin boundary level comes with its own set of challenges.  Often the data driving choropleth and thematic map visualizations related to political boundaries are numbers and categories associated with a code for a given boundary.  These codes can include (among other options): two digit state code, Federal Information Processing Standard (FIPS) code, International Organization for Standardization (ISO) two or three digit country code, state name, country name, etc.  This again comes back to the issue of a lack of standardization; for country level statistics, for instance, you might see country name, two digit, three digit, numeric codes, or other codes being used across data providers.  Very rarely are the geometries associated with each boundary included in the source data, and even more rare is pre-packaged GeoJSON that contains boundary polygons along with statistics as properties of each polygon.  This introduces a challenge for developers in that we must find and associate boundary polygons with those boundary identifiers on the client-side or on the server in order to build a thematic map.  On the client side, this may involve interacting with a web service (e.g. an OGC Web Feature Service (WFS)) that serves up those polygons, particularly in the case where we’re creating choropleth maps for lower level admin/political boundaries.  In general, the two most common types of choropleth maps that people tend to create are country and state level maps.  If I’m building a state or country choropleth, I’m probably going to be using all of the state or country polygons that are available, so making requests to a web service to get each polygon might be a little excessive.  In addition, if we’re trying to display symbols based on state or country codes, we need the centroids of each political boundary in order to position each symbol correctly.  This requires the need to calculate the centroid dynamically or to include it as a property of the boundary polygon.

In addition to challenges with data formats, there are often redundant tasks that developers must perform when creating thematic maps.  These include:

  1. Retrieving data from a local/remote web server and translating those data into JavaScript objects
  2. Translating data locations into web mapping framework appropriate point/layer objects
  3. Dynamically styling point/layer objects based on some combination of data properties
  4. Setting up interactive features – handling mouseover and click events
  5. Displaying a legend to make it easier for the user to understand what’s being shown

In the grand scheme of things, these are not monumental challenges, but they do make the jobs of developers more difficult.  HumanGeo’s Leaflet Data Visualization Framework helps to alleviate some of these challenges by abstracting many of these details from the developer.  In particular, the framework seeks to:

  • Enable cool, interactive, infographic style visualizations
  • Support displaying data in any JSON-based data format (not just GeoJSON)
  • Eliminate redundant code/tasks related to displaying data
  • Provide tools for simplifying mapping one value to another (e.g. temperature to radius, count to color, etc.)
  • Standardize the way in which data are loaded/displayed
  • Minimize dependency on server-side components for typical use cases (e.g. visualizing data by US state or country)
  • Remain consistent with Leaflet’s API/coding style so that it’s easy to pickup and use if you’re already familiar with Leaflet

It wouldn’t be a proper introduction to a framework that’s all about visuals without showing you some sample visualizations, so here are a few example maps created using the framework to give you an idea of the type of maps you can create:

Election Mapping
Election Mapping
Multivariate Date Display
Country Level Data Display
Ethnic Enclaves in NYC
Ethnic Enclaves in New York City

In the next few blog entries, I’ll provide more details and examples illustrating how HumanGeo’s Leaflet Data Visualization Framework simplifies geospatial data visualization and thematic map creation.  In the meantime, check out the code and examples on GitHub, and send me an e-mail if you’re interested, have questions, or want to contribute.