Skip to content

Instantly share code, notes, and snippets.

@tmcw
Created September 21, 2012 03:45
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tmcw/3759614 to your computer and use it in GitHub Desktop.
Save tmcw/3759614 to your computer and use it in GitHub Desktop.
OSM Growth

What is this? It's about 7 years of OpenStreetMap growth. Each step in the animation is 72 days, and the colors alternate between red and blue. The data is from Latest Weekly Changesets from planet.openstreetmap.org, processed into an SQLite database with sometimemachine. The points being drawn are the centers of the bounding boxes of changesets. There are 13,098,655 changesets in the database. The script to generate the visualization is below, and the rest of the make steps are

gm mogrify -format gif *.png
gifsicle --loop -d20 *.gif > ../animation.gif

Requiring gifsicle and graphicsmagick.

Fast growth in rectangular areas (like the DRC) is typically because of imports to the OSM data from other open datasources. The dots in the middle of the ocean are because of bounding boxes that either cross the meridian or span the entire globe.

var Canvas = require('canvas'),
moment = require('moment'),
fs = require('fs');
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('changesets_precise.sqlite');
var block = 1;
var w = 360 * 4, h = 180 * 4;
var c = new Canvas(w, h);
var ctx = c.getContext('2d');
ctx.fillStyle = '#e9e9e9';
ctx.fillRect(0, 0, w, h);
function x(lon) {
return (lon + 180) / 360 * w;
}
function y(lat) {
return (90 - lat) / 180 * h;
}
var drawn = {};
var gap = (60 * 60 * 24 * 73);
var n = 0;
var colors = ['#FF00C3', '#0091FF'];
var col = 0;
ctx.globalCompositeOperation = 'multiply';
ctx.fillStyle = colors[col];
ctx.fillText('OpenStreetMap', w - 100, h - 20);
db.get('SELECT min(closed_at) as m FROM osm_changeset LIMIT 1;', function(err, min) {
db.get('SELECT max(closed_at) as m FROM osm_changeset LIMIT 1;', function(err, max) {
var start = min.m;
var end = min.m + gap;
ctx.fillText(moment(new Date(min.m * 1000)).format('L'), 10, h - 20);
ctx.fillText(moment(new Date(max.m * 1000)).format('L'), 400, h - 20);
function run() {
drawn = {};
ctx.globalAlpha = 0.2;
ctx.fillRect(
70,
h - 26,
~~((((start - min.m) / (max.m - min.m)) * 290)),
5);
ctx.globalAlpha = 1;
db.each("SELECT lon, lat FROM osm_changeset WHERE closed_at BETWEEN " + start + " AND " + end + ";", function(err, row) {
var xp = Math.floor(x(row.lon));
var yp = Math.floor(y(row.lat));
var k = xp + ' ' + yp;
if (drawn[k]) return;
ctx.fillRect(
xp,
yp,
block, block);
drawn[k] = true;
}, function() {
var k = '' + n;
if (k.length == 1) k = '0' + k;
fs.writeFileSync('changesets_' + k + '.png', c.toBuffer());
if (start > max.m) return;
start += gap;
end += gap;
n++;
col++;
if (col > 1) col = 0;
ctx.fillStyle = colors[col];
run();
});
}
run();
});
});
Copy link

ghost commented Jul 16, 2014

Create a lon and lat column in osm_changeset table type REAL.

Then,

UPDATE osm_changeset SET lat=(max_lon - ((max_lon - min_lon) / 2));
UPDATE osm_changeset SET lat=(max_lat - ((max_lat - min_lat) / 2));

Above creates arbitrary center points between the maximum and minimum values for lon and lat for the script to use.

Copy link

ghost commented Jul 16, 2014

**** UPDATE osm_changeset SET lon=(max_lon - ((max_lon - min_lon) / 2));

typo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment