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
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<filter id="desaturate">
<feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0 0 0 1 0"/>
</filter>
</svg>
<!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