decode.js 5.6 KB
 (function (root, factory){
    if (typeof define === 'function' && define.amd){ // AMD
        define(factory);
    } else if (typeof module !== 'undefined' && module.exports) { // Node.js
        module.exports = factory();
    } else { // Browser
        root.decode = factory();
    }
})(this, function () {   
    
    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);
        lengths = null;

        keys = [];
        values = [];
        var obj = pbf.readFields(readDataField, {});
        keys = null;

        return obj;
    }

    function readDataField(tag, obj, pbf) {
        if (tag === 1) keys.push(pbf.readString());
        else if (tag === 2) dim = pbf.readVarint();
        else if (tag === 3) e = Math.pow(10, pbf.readVarint());

        else if (tag === 4) readFeatureCollection(pbf, obj);
        else if (tag === 5) readFeature(pbf, obj);
        else if (tag === 6) readGeometry(pbf, obj);
    }

    function readFeatureCollection(pbf, obj) {
        obj.type = 'FeatureCollection';
        obj.features = [];
        return pbf.readMessage(readFeatureCollectionField, obj);
    }

    function readFeature(pbf, feature) {
        feature.type = 'Feature';
        return pbf.readMessage(readFeatureField, feature);
    }

    function readGeometry(pbf, geom) {
        return pbf.readMessage(readGeometryField, geom);
    }

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

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

    function readFeatureField(tag, feature, pbf) {
        if (tag === 1) feature.geometry = readGeometry(pbf, {});

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

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

    function readGeometryField(tag, geom, pbf) {
        if (tag === 1) geom.type = geometryTypes[pbf.readVarint()];

        else if (tag === 2) lengths = pbf.readPackedVarint();
        else if (tag === 3) readCoords(geom, pbf, geom.type);
        else if (tag === 4) {
            geom.geometries = geom.geometries || [];
            geom.geometries.push(readGeometry(pbf, {}));
        }
        else if (tag === 13) values.push(readValue(pbf));
        else if (tag === 15) readProps(pbf, geom);
    }

    function readCoords(geom, pbf, type) {
        if (type === 'Point') geom.coordinates = readPoint(pbf);
        else if (type === 'MultiPoint') geom.coordinates = readLine(pbf, true);
        else if (type === 'LineString') geom.coordinates = readLine(pbf);
        else if (type === 'MultiLineString') geom.coordinates = readMultiLine(pbf);
        else if (type === 'Polygon') geom.coordinates = readMultiLine(pbf, true);
        else if (type === 'MultiPolygon') geom.coordinates = readMultiPolygon(pbf);
    }

    function readValue(pbf) {
        var end = pbf.readVarint() + pbf.pos,
            value = null;

        while (pbf.pos < end) {
            var val = pbf.readVarint(),
                tag = val >> 3;

            if (tag === 1) value = pbf.readString();
            else if (tag === 2) value = pbf.readDouble();
            else if (tag === 3) value = pbf.readVarint();
            else if (tag === 4) value = -pbf.readVarint();
            else if (tag === 5) value = pbf.readBoolean();
            else if (tag === 6) value = JSON.parse(pbf.readString());
        }
        return value;
    }

    function readProps(pbf, props) {
        var end = pbf.readVarint() + pbf.pos;
        while (pbf.pos < end) props[keys[pbf.readVarint()]] = values[pbf.readVarint()];
        values = [];
        return props;
    }

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

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

        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] = prevP[d] / e;
            }
            coords.push(p);
            i++;
        }
        if (closed) coords.push(coords[0]);

        return coords;
    }

    function readLine(pbf) {
        return readLinePart(pbf, pbf.readVarint() + pbf.pos);
    }

    function readMultiLine(pbf, closed) {
        var end = pbf.readVarint() + pbf.pos;
        if (!lengths) return [readLinePart(pbf, end, null, closed)];

        var coords = [];
        for (var i = 0; i < lengths.length; i++) coords.push(readLinePart(pbf, end, lengths[i], closed));
        lengths = null;
        return coords;
    }

    function readMultiPolygon(pbf) {
        var end = pbf.readVarint() + pbf.pos;
        if (!lengths) return [[readLinePart(pbf, end, null, true)]];

        var coords = [];
        var j = 1;
        for (var i = 0; i < lengths[0]; i++) {
            var rings = [];
            for (var k = 0; k < lengths[j]; k++) rings.push(readLinePart(pbf, end, lengths[j + 1 + k], true));
            j += lengths[j] + 1;
            coords.push(rings);
        }
        lengths = null;
        return coords;
    }

    return decode;
});