You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
15 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// https://github.com/mbostock/shapefile Version 0.6.6. Copyright 2017 Mike Bostock.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.shapefile = {})));
}(this, (function (exports) { 'use strict';
var array_cancel = function() {
this._array = null;
return Promise.resolve();
};
var array_read = function() {
var array = this._array;
this._array = null;
return Promise.resolve(array ? {done: false, value: array} : {done: true, value: undefined});
};
function array(array) {
return new ArraySource(array instanceof Uint8Array ? array : new Uint8Array(array));
}
function ArraySource(array) {
this._array = array;
}
ArraySource.prototype.read = array_read;
ArraySource.prototype.cancel = array_cancel;
var fetchPath = function(url) {
return fetch(url).then(function(response) {
return response.body && response.body.getReader
? response.body.getReader()
: response.arrayBuffer().then(array);
});
};
var requestPath = function(url) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest;
request.responseType = "arraybuffer";
request.onload = function() { resolve(array(request.response)); };
request.onerror = reject;
request.ontimeout = reject;
request.open("GET", url, true);
request.send();
});
};
function path(path) {
return (typeof fetch === "function" ? fetchPath : requestPath)(path);
}
function stream(source) {
return typeof source.read === "function" ? source : source.getReader();
}
var empty = new Uint8Array(0);
var slice_cancel = function() {
return this._source.cancel();
};
function concat(a, b) {
if (!a.length) return b;
if (!b.length) return a;
var c = new Uint8Array(a.length + b.length);
c.set(a);
c.set(b, a.length);
return c;
}
var slice_read = function() {
var that = this, array = that._array.subarray(that._index);
return that._source.read().then(function(result) {
that._array = empty;
that._index = 0;
return result.done ? (array.length > 0
? {done: false, value: array}
: {done: true, value: undefined})
: {done: false, value: concat(array, result.value)};
});
};
var slice_slice = function(length) {
if ((length |= 0) < 0) throw new Error("invalid length");
var that = this, index = this._array.length - this._index;
// If the request fits within the remaining buffer, resolve it immediately.
if (this._index + length <= this._array.length) {
return Promise.resolve(this._array.subarray(this._index, this._index += length));
}
// Otherwise, read chunks repeatedly until the request is fulfilled.
var array = new Uint8Array(length);
array.set(this._array.subarray(this._index));
return (function read() {
return that._source.read().then(function(result) {
// When done, its possible the request wasnt fully fulfilled!
// If so, the pre-allocated array is too big and needs slicing.
if (result.done) {
that._array = empty;
that._index = 0;
return index > 0 ? array.subarray(0, index) : null;
}
// If this chunk fulfills the request, return the resulting array.
if (index + result.value.length >= length) {
that._array = result.value;
that._index = length - index;
array.set(result.value.subarray(0, length - index), index);
return array;
}
// Otherwise copy this chunk into the array, then read the next chunk.
array.set(result.value, index);
index += result.value.length;
return read();
});
})();
};
function slice(source) {
return typeof source.slice === "function" ? source :
new SliceSource(typeof source.read === "function" ? source
: source.getReader());
}
function SliceSource(source) {
this._source = source;
this._array = empty;
this._index = 0;
}
SliceSource.prototype.read = slice_read;
SliceSource.prototype.slice = slice_slice;
SliceSource.prototype.cancel = slice_cancel;
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) {
return value && (value[0] !== 0x1a) ? {done: false, value: 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};
});
};
var view = function(array) {
return new DataView(array.buffer, array.byteOffset, array.byteLength);
};
var dbf = function(source, decoder) {
source = slice(source);
return source.slice(32).then(function(array) {
var head = view(array);
return source.slice(head.getUint16(8, true) - 32).then(function(array) {
return new Dbf(source, decoder, head, view(array));
});
});
};
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) {
var n;
if ((n = ring.length) < 4) return false;
var i = 0, 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$1 = 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) {
if (array == null) return {done: true, value: undefined};
var header = view(array);
// 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 = concat$1(array.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$1(array.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) {
return new Shp(source, view(array));
});
};
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);
} 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").catch(function() {});
shp$$1 = path(shp$$1);
} 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) {
if (typeof source === "string") {
if (!/\.shp$/.test(source)) source += ".shp";
source = path(source);
} 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);
} 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;
Object.defineProperty(exports, '__esModule', { value: true });
})));