Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix for mismatched .dbf and .shp record count when exporting multiple polylines with data #114

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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
56 changes: 34 additions & 22 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
declare module "@mapbox/shp-write" {
export type OGCGeometry =
'NULL' |
'POINT' |
'POLYLINE' |
'POLYGON' |
'MULTIPOINT' |
'POINTZ' |
'POLYLINEZ' |
'POLYGONZ' |
'MULTIPOINTZ' |
'POINTM' |
'POLYLINEM' |
'POLYGONM' |
'MULTIPOINTM' |
'MULTIPATCH';
| "NULL"
| "POINT"
| "POLYLINE"
| "POLYGON"
| "MULTIPOINT"
| "POINTZ"
| "POLYLINEZ"
| "POLYGONZ"
| "MULTIPOINTZ"
| "POINTM"
| "POLYLINEM"
| "POLYGONM"
| "MULTIPOINTM"
| "MULTIPATCH";

export interface DownloadOptions {
folder?: string;
Expand All @@ -28,7 +28,7 @@ declare module "@mapbox/shp-write" {
};
}

type Compression = 'STORE' | 'DEFLATE';
type Compression = "STORE" | "DEFLATE";
interface OutputByType {
base64: string;
string: string;
Expand All @@ -44,14 +44,25 @@ declare module "@mapbox/shp-write" {
type OutputType = keyof OutputByType;

export interface ZipOptions {
compression: Compression,
outputType: OutputType
compression: Compression;
outputType: OutputType;
}

export function download(
geojson: GeoJSON.FeatureCollection,
options?: DownloadOptions & ZipOptions
): void;
export function download(geojson: GeoJSON.FeatureCollection, options?: DownloadOptions & ZipOptions): void;

type PreparedGeojsonForWriting = {
geometries: number[] | number[][] | number[][][] | number[][][][];
properties: {};
type: string;
};

export var geojson = {
point: (geojson: { features: Feature[] }) => PreparedGeojsonForWriting,
line: (geojson: { features: Feature[] }) => PreparedGeojsonForWriting,
multiline: (geojson: { features: Feature[] }) => PreparedGeojsonForWriting,
polygon: (geojson: { features: Feature[] }) => PreparedGeojsonForWriting,
multipolygon: (geojson: { features: Feature[] }) => PreparedGeojsonForWriting,
};

export function write(
data: Array<object>,
Expand All @@ -70,5 +81,6 @@ declare module "@mapbox/shp-write" {
export function zip<T extends OutputType>(
geojson: GeoJSON.FeatureCollection,
options?: DownloadOptions & ZipOptions,
stream?: boolean): Promise<OutputByType[T]>;
stream?: boolean
): Promise<OutputByType[T]>;
}
1 change: 1 addition & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports.download = require("../src/download");
module.exports.write = require("../src/write");
module.exports.zip = require("../src/zip");
module.exports.geojson = require("../src/geojson");
40 changes: 23 additions & 17 deletions src/extent.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
module.exports.enlarge = function enlargeExtent(extent, pt) {
if (pt[0] < extent.xmin) extent.xmin = pt[0];
if (pt[0] > extent.xmax) extent.xmax = pt[0];
if (pt[1] < extent.ymin) extent.ymin = pt[1];
if (pt[1] > extent.ymax) extent.ymax = pt[1];
return extent;
if (pt[0] < extent.xmin) extent.xmin = pt[0];
if (pt[0] > extent.xmax) extent.xmax = pt[0];
if (pt[1] < extent.ymin) extent.ymin = pt[1];
if (pt[1] > extent.ymax) extent.ymax = pt[1];
if ((pt[2] || 0) < extent.zmin) extent.zmin = pt[2] || 0;
if ((pt[2] || 0) > extent.zmax) extent.zmax = pt[2] || 0;
return extent;
};

module.exports.enlargeExtent = function enlargeExtent(extent, ext) {
if (ext.xmax > extent.xmax) extent.xmax = ext.xmax;
if (ext.xmin < extent.xmin) extent.xmin = ext.xmin;
if (ext.ymax > extent.ymax) extent.ymax = ext.ymax;
if (ext.ymin < extent.ymin) extent.ymin = ext.ymin;
return extent;
if (ext.xmax > extent.xmax) extent.xmax = ext.xmax;
if (ext.xmin < extent.xmin) extent.xmin = ext.xmin;
if (ext.ymax > extent.ymax) extent.ymax = ext.ymax;
if (ext.ymin < extent.ymin) extent.ymin = ext.ymin;
if (ext.zmax && ext.zmax > extent.zmax) extent.zmax = ext.zmax;
if (ext.zmin && ext.zmin < extent.zmin) extent.zmin = ext.zmin;
return extent;
};

module.exports.blank = function() {
return {
xmin: Number.MAX_VALUE,
ymin: Number.MAX_VALUE,
xmax: -Number.MAX_VALUE,
ymax: -Number.MAX_VALUE
};
module.exports.blank = function () {
return {
xmin: Number.MAX_VALUE,
ymin: Number.MAX_VALUE,
zmin: Number.MAX_VALUE,
xmax: -Number.MAX_VALUE,
ymax: -Number.MAX_VALUE,
zmax: -Number.MAX_VALUE,
};
};
8 changes: 4 additions & 4 deletions src/geojson.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ function justType(gjType, shpType) {
return function (gj) {
var oftype = gj.features.filter(isType(gjType));
return {
geometries: shpType === 'POLYLINE' ? [oftype.map(justCoords)] : oftype.map(justCoords),
geometries: shpType === "POLYLINE" ? oftype.map((_) => [justCoords(_)]) : oftype.map(justCoords),
properties: oftype.map(justProps),
type: shpType,
};
};
}

/**
*
*
* @param {Feature} feature The feature to get the coordinates from
* @returns {number[] | number[][] | number[][][] | number[][][][]}
*/
Expand All @@ -31,8 +31,8 @@ function justCoords(feature) {
}

/**
*
* @param {Feature} feature The feature to get the properties from
*
* @param {Feature} feature The feature to get the properties from
* @returns {Object.<string, string>}
*/
function justProps(feature) {
Expand Down
141 changes: 141 additions & 0 deletions src/polyZ.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
var ext = require("./extent"),
types = require("./types");

module.exports.write = function writePoints(geometries, extent, shpView, shxView, TYPE) {
var shpI = 0,
shxI = 0,
shxOffset = 100;

geometries.forEach(writePolyLine);

function writePolyLine(coordinates, i) {
var flattened = justCoords(coordinates),
noParts = parts([coordinates], TYPE),
contentLength = flattened.length * 16 + 48 + (noParts - 1) * 4 + flattened.length * 8 + 16;

var featureExtent = flattened.reduce(function (extent, c) {
return ext.enlarge(extent, c);
}, ext.blank());

// INDEX
shxView.setInt32(shxI, shxOffset / 2); // offset
shxView.setInt32(shxI + 4, contentLength / 2); // offset length

shxI += 8;
shxOffset += contentLength + 8;

shpView.setInt32(shpI, i + 1); // record number
shpView.setInt32(shpI + 4, contentLength / 2); // length
shpView.setInt32(shpI + 8, TYPE, true); // POLYLINEZ=13
shpView.setFloat64(shpI + 12, featureExtent.xmin, true); // EXTENT
shpView.setFloat64(shpI + 20, featureExtent.ymin, true);
shpView.setFloat64(shpI + 28, featureExtent.xmax, true);
shpView.setFloat64(shpI + 36, featureExtent.ymax, true);
shpView.setInt32(shpI + 44, noParts, true);
shpView.setInt32(shpI + 48, flattened.length, true); // POINTS
shpView.setInt32(shpI + 52, 0, true); // The first part - index zero

var onlyParts = coordinates.reduce(function (arr, coords) {
if (Array.isArray(coords[0][0])) {
arr = arr.concat(coords);
} else {
arr.push(coords);
}
return arr;
}, []);
for (var p = 1; p < noParts; p++) {
shpView.setInt32(
// set part index
shpI + 52 + p * 4,
onlyParts.reduce(function (a, b, idx) {
return idx < p ? a + b.length : a;
}, 0),
true
);
}

var zMin = Number.MAX_VALUE;
var zMax = -Number.MAX_VALUE;

shpI += 56 + (noParts - 1) * 4;

flattened.forEach(function writeLine(coords, i) {
if ((coords[2] || 0) < zMin) zMin = coords[2] || 0;
if ((coords[2] || 0) > zMax) zMax = coords[2] || 0;

shpView.setFloat64(shpI, coords[0], true); // X
shpView.setFloat64(shpI + 8, coords[1], true); // Y
shpI += 16;
});

// Write z value range
shpView.setFloat64(shpI, zMin, true);
shpView.setFloat64(shpI + 8, zMax, true);
shpI += 16;

// Write z values.
flattened.forEach(function (p, i) {
shpView.setFloat64(shpI, p[2] || 0, true);
shpI += 8;
});
}
};

module.exports.shpLength = function (geometries, TYPE) {
var flattened = justCoords(geometries);
var length = geometries.length * 56 + flattened.length * 16 + 32 + flattened.length * 16;
return length;
};

module.exports.shxLength = function (geometries) {
return geometries.length * 8;
};

module.exports.extent = function (coordinates) {
return justCoords(coordinates).reduce(function (extent, c) {
return ext.enlarge(extent, c);
}, ext.blank());
};

function parts(geometries, TYPE) {
var no = 1;
if (
TYPE === types.geometries.POLYGON ||
TYPE === types.geometries.POLYLINE ||
TYPE === types.geometries.POLYGONZ ||
TYPE === types.geometries.POLYLINEZ
) {
no = geometries.reduce(function (no, coords) {
no += coords.length;
if (Array.isArray(coords[0][0][0])) {
// multi
no += coords.reduce(function (no, rings) {
return no + rings.length - 1; // minus outer
}, 0);
}
return no;
}, 0);
}
return no;
}

module.exports.parts = parts;

function totalPoints(geometries) {
var sum = 0;
geometries.forEach(function (g) {
sum += g.length;
});
return sum;
}

function justCoords(coords, l) {
if (l === undefined) l = [];
if (typeof coords[0][0] == "object") {
return coords.reduce(function (memo, c) {
return memo.concat(justCoords(c));
}, l);
} else {
return coords;
}
}
Loading