TopoJSON Server API in the Browser
I was working on implementing TopoJSON in dropchop the other day, when I came across an oddity that I previously had never considered — the client library of TopoJSON is not able to convert to TopoJSON from GeoJSON, only from GeoJSON to TopoJSON.
The conversion to TopoJSON is always done on the server side (or as a one-off task), and the conversion to GeoJSON is always done on the client side using the TopoJSON client API. This unidirectional flow of data is so common in web mapping that I had never had a reason to question it until now.
TopoJSON is fairly unique (at least to my knowledge) in that two different APIs exist in the same module under the same name. When I was working on dropchop, I first implemented the addition of a TopoJSON layer, which was very straightforward and something I have done in many applications, and then proceeded to work on saving existing layers as TopoJSON. Given my previous experience with the server API, I assumed it was going to be as simple as
topojson.topology({export: geojson})
which threw an error. Only then did I realize the divergence of the client and server APIs.
Being the lazy developer I am, I started Googling solutions and promptly came across this issue, “Full library in browser”, on the TopoJSON issues page. It was apparent that TopoJSON does not have a built in way to convert from GeoJSON to TopoJSON client side, but others may have had success using browserify, which allows you to bundle up Node.js-style modules with nested dependencies for use in the browser.
This is easy enough, but I wanted to use both the client API and the server API in the browser, and include and use the script in the same way I usually would. By default, browserify does not expose global variables, but it would be nice to use the same global `topojson` object that the client API exposes.
To achieve this result, I first created a script that could reference both the client and server APIs and be run through browserify. It is globally scoped so that it behaves like the existing client API.
window.topojson = {
server: require(‘../node_modules/topojson/index’),
client: require(‘../node_modules/topojson/topojson.js’)
}
I then wrote a small script with Gulp that runs this script through browserify and uglify and outputs a script that can be used in the browser.
var gulp = require(‘gulp’);
var uglify = require(‘gulp-uglify’);
var browserify = require(‘browserify’);
var source = require(‘vinyl-source-stream’);
var buffer = require(‘vinyl-buffer’); gulp.task(‘topojson’, function() {
return browserify([‘./src/topojsonSetup.js’])
.bundle()
.pipe(source(‘topojsonAll.js’))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest(‘./dist/’));}); gulp.task(‘default’, [‘topojson’]);
You can find a repository with all this code here.
At this point you may be asking why on earth someone would want to do this in the first place. Applications like dropchop or geojson.io that allow a user to provide some geographic data in many formats and save it in various ways are fairly obvious use cases, but it’s also easy to imagine an application in which a user creates complex geometries that need to be stored on a server, and bandwidth can be saved by encoding those geometries as TopoJSON before saving them remotely. One could also use the `simplify` method from the server API to dynamically simplify geometries on the client side.
It’s understandable why TopoJSON has different APIs for the client and server — each fits nicely into a standard web mapping workflow without carrying along extra methods that will rarely be used. In those rare instances where you do want access to the full library in the browser, hopefully this process will make it less painful.