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).
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).
In the latest version, you can now define custom linear gradients with full control over direction and stops.
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.
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.
Adding Images to Leaflet Path Shapes
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:
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
Adding Text to Leaflet Path Shapes
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.
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.
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:
We’ve added several new features related to visualizing data with lines.
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.
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:
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.
See the Run Map example.
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.
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.
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
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.
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.
Sochi Medal Count Map
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
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
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.