Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active April 26, 2019 19:23
Show Gist options
  • Save pbogden/6283017 to your computer and use it in GitHub Desktop.
Save pbogden/6283017 to your computer and use it in GitHub Desktop.
D3 + OpenLayers

This D3 + OpenLayers example mirrors as closely as possible Mike Bostock's D3 + Leaflet example. See his example for an explanation.

Primary differences:

  1. OpenLayers uses Mercator (EPSG:4326) by default, which appears distorted (for example). It's easy to switch to "Spherical Mercator" (EPSG:900913), but then you need to be careful to convert lon/lat to the OpenLayers default x/y coordinates in meters.

  2. As in this example, the code below uses an OpenLayers Vector Layer in place of Leaflet's overlay pane. Whereas Leaflet requires repositioning only on zoom, OpenLayers needs repositioning for both zoom and pan. This makes it easier to size the SVG with OpenLayers -- it need only fit the viewport, as long as you're willing to wait for clipped features to be drawn after pan events. For example, pan this map; notice that Alaska won't be drawn until you raise your mouse.

<!DOCTYPE html>
<html>
<head>
<title>OpenLayers & D3</title>
<script src="http://openlayers.org/api/OpenLayers.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
body {
margin: 0em 0em;
}
#map {
width: 960px;
height: 500px;
}
path {
fill: #000;
fill-opacity: .2;
stroke: #fff;
stroke-width: 1.5px;
}
path:hover {
fill: brown;
fill-opacity: .7;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// Create an OpenLayers map using <div> with id='map' (set width & height with CSS)
// OpenLayers uses lon/lat coordinates for its default projection, EPSG:4326.
// For EPSG:900913, OpenLayers uses x/y coordinates in meters.
var map = new OpenLayers.Map('map', {projection: 'EPSG:900913'});
var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0", {layers:'basic'} );
map.addLayer(wms);
map.setCenter(new OpenLayers.LonLat(-96.9, 37.8).transform("EPSG:4326", "EPSG:900913"), 4);
d3.json("us-states.json", function(err, collection) {
var bounds = d3.geo.bounds(collection),
path = d3.geo.path().projection(project), // create a d3.geo.path that converts GeoJSON to SVG
vector = new OpenLayers.Layer.Vector( "states" ); // create a vector layer
// Use D3 to add the states after the vector layer has been added to the map
vector.afterAdd = function() {
var div = d3.select("#"+vector.div.id);
div.selectAll("svg").remove(); // Prepare OpenLayers vector div to be the container for D3
var svg = div.append("svg"),
g = svg.append("g");
var states = g.selectAll("path")
.data(collection.features)
.enter().append("path");
reset();
// Reposition the SVG to cover the features
function reset() {
var bottomLeft = project(bounds[0]),
topRight = project(bounds[1]);
svg .attr("width", topRight[0] - bottomLeft[0])
.attr("height",bottomLeft[1] - topRight[1])
.style("margin-left", bottomLeft[0] + "px")
.style("margin-top", topRight[1] + "px");
g .attr("transform", "translate(" + -bottomLeft[0] + "," + -topRight[1] + ")")
states.attr("d", path);
};
map.events.register("moveend", map, reset);
};
// Add the vector layer to the map
map.addLayer(vector);
// Use OpenLayers to implement a custom D3 geographic projection.
// Converts lon/lat to viewport coordinates (D3 uses 2-element arrays).
function project(x) {
var point = map.getViewPortPxFromLonLat( new OpenLayers.LonLat(x[0], x[1])
.transform("EPSG:4326", "EPSG:900913")
);
return [ point.x, point.y ];
}
});
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment