Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
*.sublime-workspace
.DS_Store
dist/
node_modules
npm-debug.log
9 changes: 7 additions & 2 deletions dbf/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ var types = {
export default function() {
var that = this, i = 1;
return that._source.slice(that._recordLength).then(function(value) {
return value && (value[0] !== 0x1a) ? {done: false, value: that._fields.reduce(function(p, f) {
if(!value || (value && value[0] === 0x1a)) {
return {done: true, value: undefined};
}
var values = that._fields.reduce(function(p, f) {
p[f.name] = types[f.type](that._decode(value.subarray(i, i += f.length)));
return p;
}, {})} : {done: true, value: undefined};
}, {});
values.markedAsDeleted = value[0] === 0x2A;
return {done: false, value: values};
});
}
376 changes: 376 additions & 0 deletions dist/shapefile.node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,376 @@
'use strict';

var TextDecoder = require("text-encoding").TextDecoder;

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var path = _interopDefault(require('path-source'));
var array = _interopDefault(require('array-source'));
var stream = _interopDefault(require('stream-source'));
var slice = _interopDefault(require('slice-source'));

var dbf_cancel = function() {
return this._source.cancel();
};

var readBoolean = function(value) {
return /^[nf]$/i.test(value) ? false
: /^[yt]$/i.test(value) ? true
: null;
};

var readDate = function(value) {
return new Date(+value.substring(0, 4), value.substring(4, 6) - 1, +value.substring(6, 8));
};

var readNumber = function(value) {
return !(value = value.trim()) || isNaN(value = +value) ? null : value;
};

var readString = function(value) {
return value.trim() || null;
};

var types = {
B: readNumber,
C: readString,
D: readDate,
F: readNumber,
L: readBoolean,
M: readNumber,
N: readNumber
};

var dbf_read = function() {
var that = this, i = 1;
return that._source.slice(that._recordLength).then(function(value) {
if(!value || (value && value[0] === 0x1a)) {
return {done: true, value: undefined};
}
var values = that._fields.reduce(function(p, f) {
p[f.name] = types[f.type](that._decode(value.subarray(i, i += f.length)));
return p;
}, {});
values.markedAsDeleted = value[0] === 0x2A;
return {done: false, value: values};
});
};

var view = function(array$$1) {
return new DataView(array$$1.buffer, array$$1.byteOffset, array$$1.byteLength);
};

var dbf = function(source, decoder) {
source = slice(source);
return source.slice(32).then(function(array$$1) {
var head = view(array$$1);
return source.slice(head.getUint16(8, true) - 32).then(function(array$$1) {
return new Dbf(source, decoder, head, view(array$$1));
});
});
};

function Dbf(source, decoder, head, body) {
this._source = source;
this._decode = decoder.decode.bind(decoder);
this._recordLength = head.getUint16(10, true);
this._fields = [];
for (var n = 0; body.getUint8(n) !== 0x0d; n += 32) {
for (var j = 0; j < 11; ++j) if (body.getUint8(n + j) === 0) break;
this._fields.push({
name: this._decode(new Uint8Array(body.buffer, body.byteOffset + n, j)),
type: String.fromCharCode(body.getUint8(n + 11)),
length: body.getUint8(n + 16)
});
}
}

var prototype = Dbf.prototype;
prototype.read = dbf_read;
prototype.cancel = dbf_cancel;

function cancel() {
return this._source.cancel();
}

var parseMultiPoint = function(record) {
var i = 40, j, n = record.getInt32(36, true), coordinates = new Array(n);
for (j = 0; j < n; ++j, i += 16) coordinates[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];
return {type: "MultiPoint", coordinates: coordinates};
};

var parseNull = function() {
return null;
};

var parsePoint = function(record) {
return {type: "Point", coordinates: [record.getFloat64(4, true), record.getFloat64(12, true)]};
};

var parsePolygon = function(record) {
var i = 44, j, n = record.getInt32(36, true), m = record.getInt32(40, true), parts = new Array(n), points = new Array(m), polygons = [], holes = [];
for (j = 0; j < n; ++j, i += 4) parts[j] = record.getInt32(i, true);
for (j = 0; j < m; ++j, i += 16) points[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];

parts.forEach(function(i, j) {
var ring = points.slice(i, parts[j + 1]);
if (ringClockwise(ring)) polygons.push([ring]);
else holes.push(ring);
});

holes.forEach(function(hole) {
polygons.some(function(polygon) {
if (ringContainsSome(polygon[0], hole)) {
polygon.push(hole);
return true;
}
}) || polygons.push([hole]);
});

return polygons.length === 1
? {type: "Polygon", coordinates: polygons[0]}
: {type: "MultiPolygon", coordinates: polygons};
};

function ringClockwise(ring) {
if ((n = ring.length) < 4) return false;
var i = 0, n, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
return area >= 0;
}

function ringContainsSome(ring, hole) {
var i = -1, n = hole.length, c;
while (++i < n) {
if (c = ringContains(ring, hole[i])) {
return c > 0;
}
}
return false;
}

function ringContains(ring, point) {
var x = point[0], y = point[1], contains = -1;
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
var pi = ring[i], xi = pi[0], yi = pi[1],
pj = ring[j], xj = pj[0], yj = pj[1];
if (segmentContains(pi, pj, point)) {
return 0;
}
if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) {
contains = -contains;
}
}
return contains;
}

function segmentContains(p0, p1, p2) {
var x20 = p2[0] - p0[0], y20 = p2[1] - p0[1];
if (x20 === 0 && y20 === 0) return true;
var x10 = p1[0] - p0[0], y10 = p1[1] - p0[1];
if (x10 === 0 && y10 === 0) return false;
var t = (x20 * x10 + y20 * y10) / (x10 * x10 + y10 * y10);
return t < 0 || t > 1 ? false : t === 0 || t === 1 ? true : t * x10 === x20 && t * y10 === y20;
}

var parsePolyLine = function(record) {
var i = 44, j, n = record.getInt32(36, true), m = record.getInt32(40, true), parts = new Array(n), points = new Array(m);
for (j = 0; j < n; ++j, i += 4) parts[j] = record.getInt32(i, true);
for (j = 0; j < m; ++j, i += 16) points[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];
return n === 1
? {type: "LineString", coordinates: points}
: {type: "MultiLineString", coordinates: parts.map(function(i, j) { return points.slice(i, parts[j + 1]); })};
};

var concat = function(a, b) {
var ab = new Uint8Array(a.length + b.length);
ab.set(a, 0);
ab.set(b, a.length);
return ab;
};

var shp_read = function() {
var that = this;
++that._index;
return that._source.slice(12).then(function(array$$1) {
if (array$$1 == null) return {done: true, value: undefined};
var header = view(array$$1);

// If the record starts with an invalid shape type (see #36), scan ahead in
// four-byte increments to find the next valid record, identified by the
// expected index, a non-empty content length and a valid shape type.
function skip() {
return that._source.slice(4).then(function(chunk) {
if (chunk == null) return {done: true, value: undefined};
header = view(array$$1 = concat(array$$1.slice(4), chunk));
return header.getInt32(0, false) !== that._index ? skip() : read();
});
}

// All records should have at least four bytes (for the record shape type),
// so an invalid content length indicates corruption.
function read() {
var length = header.getInt32(4, false) * 2 - 4, type = header.getInt32(8, true);
return length < 0 || (type && type !== that._type) ? skip() : that._source.slice(length).then(function(chunk) {
return {done: false, value: type ? that._parse(view(concat(array$$1.slice(8), chunk))) : null};
});
}

return read();
});
};

var parsers = {
0: parseNull,
1: parsePoint,
3: parsePolyLine,
5: parsePolygon,
8: parseMultiPoint,
11: parsePoint, // PointZ
13: parsePolyLine, // PolyLineZ
15: parsePolygon, // PolygonZ
18: parseMultiPoint, // MultiPointZ
21: parsePoint, // PointM
23: parsePolyLine, // PolyLineM
25: parsePolygon, // PolygonM
28: parseMultiPoint // MultiPointM
};

var shp = function(source) {
source = slice(source);
return source.slice(100).then(function(array$$1) {
return new Shp(source, view(array$$1));
});
};

function Shp(source, header) {
var type = header.getInt32(32, true);
if (!(type in parsers)) throw new Error("unsupported shape type: " + type);
this._source = source;
this._type = type;
this._index = 0;
this._parse = parsers[type];
this.bbox = [header.getFloat64(36, true), header.getFloat64(44, true), header.getFloat64(52, true), header.getFloat64(60, true)];
}

var prototype$2 = Shp.prototype;
prototype$2.read = shp_read;
prototype$2.cancel = cancel;

function noop() {}

var shapefile_cancel = function() {
return Promise.all([
this._dbf && this._dbf.cancel(),
this._shp.cancel()
]).then(noop);
};

var shapefile_read = function() {
var that = this;
return Promise.all([
that._dbf ? that._dbf.read() : {value: {}},
that._shp.read()
]).then(function(results) {
var dbf = results[0], shp = results[1];
return shp.done ? shp : {
done: false,
value: {
type: "Feature",
properties: dbf.value,
geometry: shp.value
}
};
});
};

var shapefile = function(shpSource, dbfSource, decoder) {
return Promise.all([
shp(shpSource),
dbfSource && dbf(dbfSource, decoder)
]).then(function(sources) {
return new Shapefile(sources[0], sources[1]);
});
};

function Shapefile(shp$$1, dbf$$1) {
this._shp = shp$$1;
this._dbf = dbf$$1;
this.bbox = shp$$1.bbox;
}

var prototype$1 = Shapefile.prototype;
prototype$1.read = shapefile_read;
prototype$1.cancel = shapefile_cancel;

function open(shp$$1, dbf$$1, options) {
if (typeof dbf$$1 === "string") {
if (!/\.dbf$/.test(dbf$$1)) dbf$$1 += ".dbf";
dbf$$1 = path(dbf$$1, options);
} else if (dbf$$1 instanceof ArrayBuffer || dbf$$1 instanceof Uint8Array) {
dbf$$1 = array(dbf$$1);
} else if (dbf$$1 != null) {
dbf$$1 = stream(dbf$$1);
}
if (typeof shp$$1 === "string") {
if (!/\.shp$/.test(shp$$1)) shp$$1 += ".shp";
if (dbf$$1 === undefined) dbf$$1 = path(shp$$1.substring(0, shp$$1.length - 4) + ".dbf", options).catch(function() {});
shp$$1 = path(shp$$1, options);
} else if (shp$$1 instanceof ArrayBuffer || shp$$1 instanceof Uint8Array) {
shp$$1 = array(shp$$1);
} else {
shp$$1 = stream(shp$$1);
}
return Promise.all([shp$$1, dbf$$1]).then(function(sources) {
var shp$$1 = sources[0], dbf$$1 = sources[1], encoding = "windows-1252";
if (options && options.encoding != null) encoding = options.encoding;
return shapefile(shp$$1, dbf$$1, dbf$$1 && new TextDecoder(encoding));
});
}

function openShp(source, options) {
if (typeof source === "string") {
if (!/\.shp$/.test(source)) source += ".shp";
source = path(source, options);
} else if (source instanceof ArrayBuffer || source instanceof Uint8Array) {
source = array(source);
} else {
source = stream(source);
}
return Promise.resolve(source).then(shp);
}

function openDbf(source, options) {
var encoding = "windows-1252";
if (options && options.encoding != null) encoding = options.encoding;
encoding = new TextDecoder(encoding);
if (typeof source === "string") {
if (!/\.dbf$/.test(source)) source += ".dbf";
source = path(source, options);
} else if (source instanceof ArrayBuffer || source instanceof Uint8Array) {
source = array(source);
} else {
source = stream(source);
}
return Promise.resolve(source).then(function(source) {
return dbf(source, encoding);
});
}

function read(shp$$1, dbf$$1, options) {
return open(shp$$1, dbf$$1, options).then(function(source) {
var features = [], collection = {type: "FeatureCollection", features: features, bbox: source.bbox};
return source.read().then(function read(result) {
if (result.done) return collection;
features.push(result.value);
return source.read().then(read);
});
});
}

exports.open = open;
exports.openShp = openShp;
exports.openDbf = openDbf;
exports.read = read;