Skip to content

Instantly share code, notes, and snippets.

@webmappergists
Last active September 6, 2016 11:48
Show Gist options
  • Save webmappergists/69d6d58b0f86b2834606 to your computer and use it in GitHub Desktop.
Save webmappergists/69d6d58b0f86b2834606 to your computer and use it in GitHub Desktop.
Optimise an interactive choropleth map: TopoJSON and OpenLayers 2

This example shows various ways to optimise an interactive choropleth map for fast delivery:

  • Create a custom OpenLayers build instead of using the default one
  • Use TopoJSON to encode the geometry
  • Reduce the number of decimals in geographic coordinates
  • Serve map tiles from multiple sub-domains

This map is created for a blog post on Webmapper.

Cheers,

Edward @emacgillavry

bevolkingsdichtheid = [{"id":3,"value":507},{"id":5,"value":236},{"id":7,"value":82},{"id":9,"value":165},{"id":10,"value":195},{"id":14,"value":2503},{"id":15,"value":140},{"id":17,"value":408},{"id":18,"value":517},{"id":22,"value":309},{"id":24,"value":92},{"id":25,"value":160},{"id":34,"value":1506},{"id":37,"value":279},{"id":40,"value":103},{"id":47,"value":366},{"id":48,"value":96},{"id":50,"value":85},{"id":51,"value":148},{"id":53,"value":137},{"id":55,"value":128},{"id":56,"value":148},{"id":58,"value":144},{"id":59,"value":274},{"id":60,"value":59},{"id":63,"value":115},{"id":70,"value":199},{"id":72,"value":633},{"id":74,"value":320},{"id":79,"value":116},{"id":80,"value":1216},{"id":81,"value":252},{"id":82,"value":178},{"id":85,"value":115},{"id":86,"value":133},{"id":88,"value":21},{"id":90,"value":468},{"id":93,"value":55},{"id":96,"value":30},{"id":98,"value":115},{"id":106,"value":820},{"id":109,"value":120},{"id":114,"value":322},{"id":118,"value":429},{"id":119,"value":587},{"id":140,"value":83},{"id":141,"value":1078},{"id":147,"value":837},{"id":148,"value":166},{"id":150,"value":751},{"id":153,"value":1125},{"id":158,"value":231},{"id":160,"value":190},{"id":163,"value":258},{"id":164,"value":1329},{"id":166,"value":357},{"id":168,"value":228},{"id":171,"value":100},{"id":173,"value":1493},{"id":175,"value":96},{"id":177,"value":213},{"id":180,"value":121},{"id":183,"value":143},{"id":184,"value":1667},{"id":189,"value":251},{"id":193,"value":1100},{"id":196,"value":275},{"id":197,"value":280},{"id":200,"value":462},{"id":202,"value":1529},{"id":203,"value":305},{"id":209,"value":579},{"id":213,"value":253},{"id":214,"value":193},{"id":216,"value":940},{"id":221,"value":996},{"id":222,"value":713},{"id":225,"value":482},{"id":226,"value":753},{"id":228,"value":345},{"id":230,"value":352},{"id":232,"value":207},{"id":233,"value":305},{"id":236,"value":262},{"id":241,"value":430},{"id":243,"value":1182},{"id":244,"value":509},{"id":246,"value":233},{"id":252,"value":412},{"id":262,"value":156},{"id":263,"value":364},{"id":265,"value":684},{"id":267,"value":581},{"id":268,"value":3102},{"id":269,"value":233},{"id":273,"value":281},{"id":274,"value":686},{"id":275,"value":534},{"id":277,"value":53},{"id":279,"value":682},{"id":281,"value":1293},{"id":282,"value":281},{"id":285,"value":192},{"id":289,"value":1226},{"id":293,"value":2154},{"id":294,"value":209},{"id":296,"value":616},{"id":297,"value":338},{"id":299,"value":608},{"id":301,"value":1152},{"id":302,"value":206},{"id":303,"value":121},{"id":304,"value":182},{"id":307,"value":2380},{"id":308,"value":746},{"id":310,"value":633},{"id":312,"value":394},{"id":313,"value":667},{"id":317,"value":283},{"id":321,"value":873},{"id":327,"value":494},{"id":331,"value":184},{"id":335,"value":361},{"id":339,"value":265},{"id":340,"value":452},{"id":342,"value":984},{"id":344,"value":3412},{"id":345,"value":3230},{"id":351,"value":337},{"id":352,"value":484},{"id":353,"value":1619},{"id":355,"value":1266},{"id":356,"value":2573},{"id":358,"value":1498},{"id":361,"value":3229},{"id":362,"value":2035},{"id":363,"value":4821},{"id":365,"value":321},{"id":370,"value":124},{"id":373,"value":312},{"id":375,"value":2184},{"id":376,"value":819},{"id":377,"value":558},{"id":381,"value":4034},{"id":383,"value":692},{"id":384,"value":2103},{"id":385,"value":1763},{"id":388,"value":1474},{"id":392,"value":5238},{"id":393,"value":287},{"id":394,"value":806},{"id":396,"value":1435},{"id":397,"value":2856},{"id":398,"value":1377},{"id":399,"value":1207},{"id":400,"value":1258},{"id":402,"value":1885},{"id":405,"value":3524},{"id":406,"value":2620},{"id":415,"value":463},{"id":416,"value":1119},{"id":417,"value":877},{"id":420,"value":355},{"id":424,"value":442},{"id":425,"value":806},{"id":431,"value":792},{"id":432,"value":273},{"id":437,"value":549},{"id":439,"value":3398},{"id":441,"value":275},{"id":448,"value":84},{"id":450,"value":680},{"id":451,"value":1556},{"id":453,"value":1499},{"id":457,"value":884},{"id":458,"value":89},{"id":473,"value":517},{"id":478,"value":165},{"id":479,"value":2025},{"id":482,"value":2235},{"id":484,"value":1325},{"id":489,"value":2388},{"id":491,"value":283},{"id":498,"value":326},{"id":499,"value":1021},{"id":501,"value":592},{"id":502,"value":4632},{"id":503,"value":4342},{"id":505,"value":1499},{"id":512,"value":1856},{"id":513,"value":4205},{"id":518,"value":6178},{"id":523,"value":1047},{"id":530,"value":1238},{"id":531,"value":2684},{"id":532,"value":1464},{"id":534,"value":1617},{"id":537,"value":2554},{"id":542,"value":3701},{"id":545,"value":613},{"id":546,"value":5457},{"id":547,"value":2315},{"id":553,"value":1427},{"id":556,"value":3766},{"id":568,"value":216},{"id":569,"value":343},{"id":575,"value":723},{"id":576,"value":696},{"id":579,"value":3172},{"id":584,"value":1250},{"id":585,"value":418},{"id":588,"value":139},{"id":589,"value":252},{"id":590,"value":3385},{"id":597,"value":1909},{"id":599,"value":2956},{"id":603,"value":3370},{"id":606,"value":4228},{"id":608,"value":1891},{"id":610,"value":1900},{"id":611,"value":234},{"id":612,"value":2770},{"id":613,"value":1147},{"id":614,"value":260},{"id":617,"value":172},{"id":620,"value":498},{"id":622,"value":2999},{"id":623,"value":181},{"id":626,"value":2210},{"id":627,"value":903},{"id":629,"value":503},{"id":632,"value":563},{"id":637,"value":3562},{"id":638,"value":381},{"id":642,"value":2192},{"id":643,"value":507},{"id":644,"value":300},{"id":653,"value":107},{"id":654,"value":160},{"id":664,"value":398},{"id":668,"value":237},{"id":677,"value":136},{"id":678,"value":336},{"id":687,"value":979},{"id":689,"value":227},{"id":703,"value":214},{"id":707,"value":183},{"id":715,"value":218},{"id":716,"value":173},{"id":717,"value":164},{"id":718,"value":1300},{"id":733,"value":218},{"id":736,"value":425},{"id":737,"value":213},{"id":738,"value":253},{"id":743,"value":233},{"id":744,"value":87},{"id":748,"value":828},{"id":753,"value":834},{"id":755,"value":291},{"id":756,"value":253},{"id":757,"value":477},{"id":758,"value":1413},{"id":762,"value":271},{"id":765,"value":260},{"id":766,"value":865},{"id":770,"value":220},{"id":772,"value":2490},{"id":777,"value":763},{"id":779,"value":807},{"id":784,"value":394},{"id":785,"value":545},{"id":786,"value":468},{"id":788,"value":235},{"id":794,"value":1672},{"id":796,"value":1691},{"id":797,"value":548},{"id":798,"value":158},{"id":809,"value":460},{"id":815,"value":208},{"id":820,"value":671},{"id":823,"value":176},{"id":824,"value":403},{"id":826,"value":751},{"id":828,"value":556},{"id":840,"value":345},{"id":844,"value":559},{"id":845,"value":479},{"id":846,"value":278},{"id":847,"value":231},{"id":848,"value":621},{"id":851,"value":159},{"id":852,"value":328},{"id":855,"value":1778},{"id":856,"value":610},{"id":858,"value":555},{"id":860,"value":478},{"id":861,"value":1389},{"id":865,"value":763},{"id":866,"value":746},{"id":867,"value":718},{"id":870,"value":252},{"id":873,"value":236},{"id":874,"value":292},{"id":879,"value":175},{"id":880,"value":406},{"id":881,"value":373},{"id":882,"value":1542},{"id":888,"value":778},{"id":889,"value":486},{"id":893,"value":127},{"id":899,"value":1690},{"id":907,"value":363},{"id":917,"value":1971},{"id":928,"value":2154},{"id":935,"value":2144},{"id":938,"value":713},{"id":944,"value":449},{"id":946,"value":167},{"id":951,"value":468},{"id":957,"value":931},{"id":962,"value":538},{"id":965,"value":681},{"id":971,"value":1202},{"id":981,"value":409},{"id":983,"value":801},{"id":984,"value":263},{"id":986,"value":400},{"id":988,"value":464},{"id":994,"value":457},{"id":995,"value":327},{"id":1507,"value":221},{"id":1509,"value":291},{"id":1525,"value":1253},{"id":1581,"value":363},{"id":1586,"value":272},{"id":1598,"value":277},{"id":1621,"value":1041},{"id":1640,"value":223},{"id":1641,"value":523},{"id":1651,"value":84},{"id":1652,"value":237},{"id":1655,"value":392},{"id":1658,"value":148},{"id":1659,"value":393},{"id":1663,"value":61},{"id":1667,"value":162},{"id":1669,"value":238},{"id":1671,"value":301},{"id":1672,"value":327},{"id":1674,"value":724},{"id":1676,"value":147},{"id":1680,"value":92},{"id":1681,"value":93},{"id":1684,"value":482},{"id":1685,"value":216},{"id":1690,"value":105},{"id":1695,"value":87},{"id":1696,"value":485},{"id":1699,"value":154},{"id":1700,"value":319},{"id":1701,"value":68},{"id":1702,"value":118},{"id":1705,"value":736},{"id":1706,"value":265},{"id":1708,"value":149},{"id":1709,"value":229},{"id":1711,"value":311},{"id":1714,"value":85},{"id":1719,"value":278},{"id":1721,"value":331},{"id":1722,"value":90},{"id":1723,"value":103},{"id":1724,"value":179},{"id":1728,"value":260},{"id":1729,"value":197},{"id":1730,"value":226},{"id":1731,"value":98},{"id":1734,"value":425},{"id":1735,"value":165},{"id":1740,"value":372},{"id":1742,"value":399},{"id":1771,"value":1249},{"id":1773,"value":155},{"id":1774,"value":148},{"id":1783,"value":1291},{"id":1842,"value":385},{"id":1859,"value":173},{"id":1876,"value":131},{"id":1883,"value":1189},{"id":1884,"value":405},{"id":1891,"value":223},{"id":1892,"value":683},{"id":1894,"value":271},{"id":1895,"value":170},{"id":1896,"value":267},{"id":1900,"value":191},{"id":1901,"value":433},{"id":1903,"value":323},{"id":1904,"value":657},{"id":1908,"value":199},{"id":1911,"value":133},{"id":1916,"value":2219},{"id":1924,"value":184},{"id":1926,"value":1350},{"id":1927,"value":246},{"id":1955,"value":329},{"id":1987,"value":153}]
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.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<title>TopoJSON in OpenLayers</title>
<meta name="author" content="Edward Mac Gillavry">
<link rel="stylesheet" href="main.css">
</head>
<body>
<div id="map-canvas"></div>
<div id="info"></div>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdn.jsdelivr.net/openlayers/2.13.1/OpenLayers.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.12/proj4.js"></script>
<script src="bevolkingsdichtheid2013.json"></script>
<script src="main.js"></script>
</body>
</html>
body {
font: 14px/22px "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
-webkit-font-smoothing: antialiased;
color: #57574D;
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
}
#map-canvas {
height: 400px;
width: 100%;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAACVBMVEUAAADl5eX////EwbxGAAAAAXRSTlMAQObYZgAAABFJREFUeAFjYESCKACdT38ZAAWhAAxcPQc7AAAAAElFTkSuQmCC) repeat scroll 0 0 #f9f9f9;
cursor: move;
-webkit-tap-highlight-color: transparent;
}
.olTileImage {
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-o-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-perspective: 1000;
-moz-perspective: 1000;
-ms-perspective: 1000;
perspective: 1000;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
filter: grayscale(100%);
filter: url(greyscale.svg#desaturate);
filter: gray;
}
.olImageLoadError {
background-image: url("http://webmapper.net/theme/img/missing-tile.png") !important;
background-repeat: no-repeat !important;
background-color: transparent !important;
}
div.olControlZoom {
position: absolute;
z-index: 1003;
background: none repeat scroll 0 0 rgba(255, 255, 255, 0.4);
border-radius: 4px;
left: 8px;
padding: 2px;
position: absolute;
top: 8px;
}
.olControlNoSelect {
-moz-user-select: none;
}
div.olControlZoom a {
background: none repeat scroll 0 0 rgba(160, 195, 63, 1.0);
color: #FFFFFF;
display: block;
font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif;
font-size: 18px;
font-weight: bold;
height: 22px;
line-height: 19px;
margin: 1px;
padding: 0;
text-align: center;
text-decoration: none;
width: 22px;
}
div.olControlZoom a:hover {
background: none repeat scroll 0 0 rgba(145, 177, 55, 1.0);
}
a.olControlZoomIn {
border-radius: 4px 4px 0 0;
}
a.olControlZoomOut {
border-radius: 0 0 4px 4px;
}
.olControlAttribution {
background-color: rgba(255,255,255,0.6);
bottom: 0px;
display: block;
font-size: smaller;
position: absolute;
padding: 0 2px;
right: 0px;
color: #666;
}
var map;
var wm = {};
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 1;
window.Proj4js = {
Proj: function(code) {
return proj4(Proj4js.defs[code]);
},
defs: proj4.defs,
transform: proj4
};
proj4.defs['EPSG:28992'] = '+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.040,49.910,465.840,-0.40939,0.35971,-1.86849,4.0772 +units=m +no_defs';
function onFeaturesAdded(event) {
map.zoomToExtent(this.getDataExtent());
}
function onFeatureOver(feature) {
document.getElementById('info').innerHTML = feature.attributes.name + ': ' + feature.attributes.value + ' inwoners per km2';
}
function onFeatureOut(event) {
document.getElementById('info').innerHTML = '';
}
function createStyles() {
var template = {
fillOpacity: 0.9,
strokeOpacity: 0.9,
strokeWidth: 1,
strokeColor: '#fee',
graphicZIndex: 10,
cursor: 'pointer',
title: '${name}'
};
var theme = new OpenLayers.Style(template);
var range5 = new OpenLayers.Rule({
filter:new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
property:'value',
value: 2000
}),
symbolizer:{fillColor:'#7f0000'}
});
var range4 = new OpenLayers.Rule({
filter: new OpenLayers.Filter.Logical({
type: OpenLayers.Filter.Logical.AND, filters:[
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.LESS_THAN,
property:'value',
value: 2000
}),
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
property:'value',
value: 1000
})
]
}),
symbolizer: {fillColor:'#b30000'}
});
var range3 = new OpenLayers.Rule({
filter: new OpenLayers.Filter.Logical({
type: OpenLayers.Filter.Logical.AND, filters:[
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.LESS_THAN,
property: 'value',
value: 1000
}),
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
property:'value',
value: 500
})
]
}),
symbolizer: {fillColor:'#d7301f'}
});
var range2 = new OpenLayers.Rule({
filter: new OpenLayers.Filter.Logical({
type: OpenLayers.Filter.Logical.AND, filters:[
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.LESS_THAN,
property: 'value',
value: 500
}),
new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
property: 'value',
value: 250
})
]
}),
symbolizer: {fillColor:'#ef6548'}
});
var range1 = new OpenLayers.Rule({
filter: new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.LESS_THAN,
property: 'value',
value: 250
}),
symbolizer:{fillColor:'#fc8d59'}
});
var range0 = new OpenLayers.Rule({
filter: new OpenLayers.Filter.Comparison({
type:OpenLayers.Filter.Comparison.EQUAL_TO,
property: 'value',
value: 'No data'
}),
symbolizer:{fillColor:'#ffcccc'}
});
theme.addRules([range1, range2, range3, range4, range5, range0]);
return new OpenLayers.StyleMap({
'default': theme,
'onselect': new OpenLayers.Style({
strokeColor: '#333',
strokeOpacity: .8,
strokeWidth: 2,
fillOpacity: 1.0,
cursor: 'pointer',
graphicZIndex: 100
})
});
}
window.onload = function() {
var controls = [
new OpenLayers.Control.Navigation(
{dragPanOptions: {enableKinetic: true}}
),
new OpenLayers.Control.Attribution(),
new OpenLayers.Control.Zoom()
];
map = new OpenLayers.Map ('map-canvas',{
tileManager: new OpenLayers.TileManager(),
controls: controls,
theme: null,
maxExtent: new OpenLayers.Bounds(-285401.92, 22598.08, 595401.92, 903401.92),
serverResolutions: [3440.64, 1720.32, 860.16, 430.08, 215.04, 107.52, 53.76, 26.88, 13.44, 6.72, 3.36, 1.68, 0.84, 0.42, 0.21],
resolutions: [3440.64, 1720.32, 860.16, 430.08, 215.04, 107.52, 53.76, 26.88, 13.44, 6.72, 3.36, 1.68],
units: 'm',
projection: new OpenLayers.Projection('EPSG:28992')
});
wm.wm_ref =new OpenLayers.Layer.TMS(
"Webmapper Referentiekaart",
[
'http://t1.maps.geocoders.nl/webmapper',
'http://t2.maps.geocoders.nl/webmapper',
'http://t3.maps.geocoders.nl/webmapper',
'http://t4.maps.geocoders.nl/webmapper'],
{layername: "aK8T1SRt",
serviceVersion: "",
attribution: 'Kaartgegevens: &copy; <a href="http://www.cbs.nl">CBS</a>, <a href="http://www.kadaster.nl">Kadaster</a>, <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> <span class="printhide">contributors</span>',
type:"png"}
);
wm.gemeenten = new OpenLayers.Layer.Vector(
'Gemeenten', {
styleMap: createStyles(),
rendererOptions: {zIndexing: true}
}
);
wm.gemeenten.events.on({
'featuresadded': onFeaturesAdded,
'loadend': function() {
if(this.features.length === 0) {
alert('No features loaded');
}
}
});
var selectControl = new OpenLayers.Control.SelectFeature(
wm.gemeenten, {
hover: false,
toggle: true,
onSelect: onFeatureOver,
onUnselect: onFeatureOut,
renderIntent: 'onselect'
}
);
selectControl.handlers.feature.stopDown = false;
map.addLayers([wm.wm_ref,wm.gemeenten]);
var geojson_format = new OpenLayers.Format.GeoJSON({
'internalProjection': new OpenLayers.Projection('EPSG:28992'),
'externalProjection': new OpenLayers.Projection('EPSG:4326')
});
OpenLayers.Request.GET({
url: 'http://places.geocoders.nl/webmapper/dd4eqye9/2013.topo.json',
async: true,
success: function(e) {
var json = new OpenLayers.Format.JSON().read(e.responseText);
var features = geojson_format.read(topojson.feature(json,json.objects.gemeenten));
for (var i = 0; i < features.length; i++) {
var value;
for (var k = 0; k < bevolkingsdichtheid.length; k++) {
if (features[i].fid == bevolkingsdichtheid[k].id ) {
value = bevolkingsdichtheid[k].value;
if (OpenLayers.String.isNumeric(value)) {
value = parseInt(value);
}
}
}
features[i].attributes.value = value;
}
wm.gemeenten.addFeatures(features);
}
});
map.addControl(selectControl);
selectControl.activate();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment