Skip to content

Commit

Permalink
ditch TopoJSON support, close #43
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Mar 27, 2015
1 parent 40fb3a4 commit b4ba6ef
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 371 deletions.
23 changes: 9 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

Geobuf is a compact binary encoding for geographic data.

Geobuf provides _lossless_ compression of GeoJSON and TopoJSON data
Geobuf provides _lossless_ compression of GeoJSON data
into [protocol buffers](https://developers.google.com/protocol-buffers/).
Advantages over using JSON-based formats alone:

- **Very compact**: typically makes GeoJSON 6-8 times smaller and TopoJSON 2-3 times smaller.
- Smaller even when comparing gzipped sizes: 2-2.5x compression for GeoJSON and 20-30% for TopoJSON.
- **Very compact**: typically makes GeoJSON 6-8 times smaller.
- 2-2.5x smaller even when comparing gzipped sizes.
- **Very fast encoding and decoding** — even faster than native JSON parse/stringify.
- Can accommodate any GeoJSON and TopoJSON data, including extensions with arbitrary properties.
- Can accommodate any GeoJSON data, including extensions with arbitrary properties.

The [encoding format](geobuf.proto) also potentially allows:

Expand All @@ -21,7 +21,7 @@ without the need to build in-memory representation of the whole data.
- **Partial reads** — read only the parts you actually need, skipping the rest.

Think of this as an attempt to design a simple, modern Shapefile successor
that works seamlessly with GeoJSON and TopoJSON.
that works seamlessly with GeoJSON.
Unlike [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec/),
it aims for _lossless_ compression of datasets — without tiling, projecting coordinates,
flattening geometries or stripping properties.
Expand All @@ -36,13 +36,8 @@ it may still change as we get community feedback and discover new ways to improv
------------------- | --------- | --------
us-zips.json | 101.85 MB | 26.67 MB
us-zips.pbf | 12.24 MB | 10.48 MB
us-zips.topo.json | 15.02 MB | 3.19 MB
us-zips.topo.pbf | 4.85 MB | 2.72 MB
idaho.json | 10.92 MB | 2.57 MB
idaho.pbf | 1.37 MB | 1.17 MB
idaho.topo.json | 1.9 MB | 612 KB
idaho.topo.pbf | 567 KB | 479 KB


## API

Expand All @@ -52,7 +47,7 @@ idaho.topo.pbf | 567 KB | 479 KB
var buffer = geobuf.encode(geojson, new Pbf());
```

Given a GeoJSON or TopoJSON object and a [Pbf](https://github.com/mapbox/pbf) object to write to,
Given a GeoJSON object and a [Pbf](https://github.com/mapbox/pbf) object to write to,
returns a Geobuf as a `Buffer` object in Node or `UInt8Array` object in browsers.

### decode
Expand All @@ -61,7 +56,7 @@ returns a Geobuf as a `Buffer` object in Node or `UInt8Array` object in browsers
var geojson = geobuf.decode(new Pbf(data));
```

Given a [Pbf](https://github.com/mapbox/pbf) object with Geobuf data, return a GeoJSON or TopoJSON object.
Given a [Pbf](https://github.com/mapbox/pbf) object with Geobuf data, return a GeoJSON object.


## Install
Expand Down Expand Up @@ -89,8 +84,8 @@ npm install -g geobuf

Installs these nifty binaries:

* `geobuf2json`: turn Geobuf from `stdin` or specified file to GeoJSON/TopoJSON on `stdout`
* `json2geobuf`: turn GeoJSON or TopoJSON from `stdin` or specified file to Geobuf on `stdout`
* `geobuf2json`: turn Geobuf from `stdin` or specified file to GeoJSON on `stdout`
* `json2geobuf`: turn GeoJSON from `stdin` or specified file to Geobuf on `stdout`
* `shp2geobuf`: given a Shapefile filename, send Geobuf on `stdout`

```bash
Expand Down
102 changes: 15 additions & 87 deletions decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

module.exports = decode;

var keys, values, lengths, dim, e, isTopo, transformed, names;
var keys, values, lengths, dim, e;

var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString',
'Polygon', 'MultiPolygon', 'GeometryCollection'];

function decode(pbf) {
dim = 2;
e = Math.pow(10, 6);
isTopo = false;
transformed = false;
lengths = null;

keys = [];
Expand All @@ -30,7 +28,6 @@ function readDataField(tag, obj, pbf) {
else if (tag === 4) readFeatureCollection(pbf, obj);
else if (tag === 5) readFeature(pbf, obj);
else if (tag === 6) readGeometry(pbf, obj);
else if (tag === 7) readTopology(pbf, obj);
}

function readFeatureCollection(pbf, obj) {
Expand All @@ -48,31 +45,6 @@ function readGeometry(pbf, geom) {
return pbf.readMessage(readGeometryField, geom);
}

function readTopology(pbf, topology) {
isTopo = true;
topology.type = 'Topology';
topology.objects = {};
names = [];
pbf.readMessage(readTopologyField, topology);
names = null;
return topology;
}

function readTopologyField(tag, topology, pbf) {
if (tag === 1) {
topology.transform = pbf.readMessage(readTransformField, {scale: [], translate: []});
transformed = true;
}
else if (tag === 2) names.push(pbf.readString());
else if (tag === 3) topology.objects[names.shift()] = pbf.readMessage(readGeometryField, {});

else if (tag === 4) lengths = pbf.readPackedVarint();
else if (tag === 5) topology.arcs = readArcs(pbf);

else if (tag === 13) values.push(readValue(pbf));
else if (tag === 15) readProps(pbf, topology);
}

function readFeatureCollectionField(tag, obj, pbf) {
if (tag === 1) obj.features.push(readFeature(pbf, {}));

Expand Down Expand Up @@ -100,22 +72,16 @@ function readGeometryField(tag, geom, pbf) {
geom.geometries = geom.geometries || [];
geom.geometries.push(readGeometry(pbf, {}));
}

else if (tag === 11) geom.id = pbf.readString();
else if (tag === 12) geom.id = pbf.readSVarint();

else if (tag === 13) values.push(readValue(pbf));
else if (tag === 14) geom.properties = readProps(pbf, {});
else if (tag === 15) readProps(pbf, geom);
}

function readCoords(geom, pbf, type) {
var coordsOrArcs = isTopo ? 'arcs' : 'coordinates';
if (type === 'Point') geom.coordinates = readPoint(pbf);
else if (type === 'MultiPoint') geom.coordinates = readLine(pbf, true);
else if (type === 'LineString') geom[coordsOrArcs] = readLine(pbf);
else if (type === 'MultiLineString' || type === 'Polygon') geom[coordsOrArcs] = readMultiLine(pbf);
else if (type === 'MultiPolygon') geom[coordsOrArcs] = readMultiPolygon(pbf);
else if (type === 'LineString') geom.coordinates = readLine(pbf);
else if (type === 'MultiLineString' || type === 'Polygon') geom.coordinates = readMultiLine(pbf);
else if (type === 'MultiPolygon') geom.coordinates = readMultiPolygon(pbf);
}

function readValue(pbf) {
Expand Down Expand Up @@ -143,46 +109,29 @@ function readProps(pbf, props) {
return props;
}

function readTransformField(tag, tr, pbf) {
if (tag === 1) tr.scale[0] = pbf.readDouble();
else if (tag === 2) tr.scale[1] = pbf.readDouble();
else if (tag === 3) tr.translate[0] = pbf.readDouble();
else if (tag === 4) tr.translate[1] = pbf.readDouble();
}

function readPoint(pbf) {
var end = pbf.readVarint() + pbf.pos,
coords = [];
while (pbf.pos < end) coords.push(transformCoord(pbf.readSVarint()));
while (pbf.pos < end) coords.push(pbf.readSVarint() / e);
return coords;
}

function readLinePart(pbf, end, len, isMultiPoint) {
function readLinePart(pbf, end, len) {
var i = 0,
coords = [],
p, d;

if (isTopo && !isMultiPoint) {
p = 0;
while (len ? i < len : pbf.pos < end) {
p += pbf.readSVarint();
coords.push(p);
i++;
}
var prevP = [];
for (d = 0; d < dim; d++) prevP[d] = 0;

} else {
var prevP = [];
for (d = 0; d < dim; d++) prevP[d] = 0;

while (len ? i < len : pbf.pos < end) {
p = [];
for (d = 0; d < dim; d++) {
prevP[d] += pbf.readSVarint();
p[d] = transformCoord(prevP[d]);
}
coords.push(p);
i++;
while (len ? i < len : pbf.pos < end) {
p = [];
for (d = 0; d < dim; d++) {
prevP[d] += pbf.readSVarint();
p[d] = prevP[d] / e;
}
coords.push(p);
i++;
}

return coords;
Expand Down Expand Up @@ -217,24 +166,3 @@ function readMultiPolygon(pbf) {
lengths = null;
return coords;
}

function readArcs(pbf) {
var lines = [],
end = pbf.readVarint() + pbf.pos;

for (var i = 0; i < lengths.length; i++) {
var ring = [];
for (var j = 0; j < lengths[i]; j++) {
var p = [];
for (var d = 0; d < dim && pbf.pos < end; d++) p[d] = transformCoord(pbf.readSVarint());
ring.push(p);
}
lines.push(ring);
}

return lines;
}

function transformCoord(x) {
return transformed ? x : x / e;
}
Loading

0 comments on commit b4ba6ef

Please sign in to comment.