diff --git a/.gitignore b/.gitignore index f0eb7ff..b4896a2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ example/*.dbf example/*.shp example/*.shx yarn.lock +package-lock.json +yarn.lock diff --git a/README.md b/README.md index 365a358..400fa6f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ -[![Build Status](https://secure.travis-ci.org/mapbox/shp-write.svg?branch=master)](http://travis-ci.org/mapbox/shp-write) - # shp-write +# ANNOUNCEMENT! + +The npm package location (and subsequently unpkg url) for this repo has changed! + +tl;dr: `shp-write` -> `@mapbox/shp-write` + + Writes shapefile in pure javascript. Uses [dbf](https://github.com/tmcw/dbf) for the data component, and [jsZIP](http://stuk.github.io/jszip/) to generate ZIP file downloads in-browser. @@ -77,7 +82,7 @@ const options = { types: { point: "mypoints", polygon: "mypolygons", - line: "mylines", + polyline: "mylines", }, }; @@ -112,6 +117,15 @@ const zipData = shpwrite.zip( ); ``` +## Custom .prj file +To pass a custom [WKT string](http://www.opengeospatial.org/standards/wkt-crs) in the .prj file to define a different projection the prj option can be used: + +```js +var options = { + prj: 'PROJCS["Amersfoort / RD New",GEOGCS["Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Stereographic_North_Pole"],PARAMETER["standard_parallel_1",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]]' +} +``` + ## API ### `write(data, geometrytype, geometries, callback)` diff --git a/dist/index.d.ts b/dist/index.d.ts index 5f2852d..8db94eb 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -18,6 +18,7 @@ declare module "@mapbox/shp-write" { export interface DownloadOptions { folder?: string; filename?: string; + prj?: string; types?: { point?: string; polygon?: string; diff --git a/index.html b/index.html index f856a0a..46db318 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ - + diff --git a/indexTest.js b/indexTest.js new file mode 100644 index 0000000..3a8438e --- /dev/null +++ b/indexTest.js @@ -0,0 +1,87 @@ +require('./src/download')({ + 'type': 'FeatureCollection', + 'features': [{ + 'type': 'Feature', + 'properties': {}, + 'geometry': { + 'type': 'Polygon', + 'coordinates': [ + [ + [ + 24.936046600341797, + 60.175245406790246 + ], + [ + 24.920597076416016, + 60.15577400466598 + ], + [ + 24.953556060791016, + 60.1570553725571 + ], + [ + 24.936046600341797, + 60.175245406790246 + ] + ], + [ + [ + 24.93523120880127, + 60.169247224327165 + ], + [ + 24.945573806762695, + 60.15874243076889 + ], + [ + 24.928064346313477, + 60.15825127085746 + ], + [ + 24.93523120880127, + 60.169247224327165 + ] + ] + ] + } + }, + { + 'type': 'Feature', + 'properties': {}, + 'geometry': { + 'type': 'LineString', + 'coordinates': [ + [ + 24.920940399169922, + 60.17977000114811 + ], + [ + 24.953556060791016, + 60.17486121440947 + ] + ] + } + }, + { + 'type': 'Feature', + 'properties': {}, + 'geometry': { + 'type': 'Point', + 'coordinates': [ + 24.925403594970703, + 60.171830205844614 + ] + } + } + ] +}, { + file: 'my_zip_filename', // if empty, filename will be download.zip + folder: 'my_internal_shapes_folder', // leave empty to put in root + outputType: 'blob', + compression: 'DEFLATE', + types: { + point: 'points', + polygon: 'polygons', + polyline: 'lines' + } +}); \ No newline at end of file diff --git a/package.json b/package.json index a767d3c..d8876c8 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "@mapbox/shp-write", - "version": "0.4.2", + "version": "0.4.3", "description": "write shapefiles from pure javascript", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "test": "mocha -R spec", "prepublish": "npm run make", - "make": "browserify -s shpwrite ./ > shpwrite.js" + "make": "browserify -s shpwrite ./ > shpwrite.js", + "make-test": "browserify indexTest.js > shpwrite.js" }, "repository": { "type": "git", @@ -31,14 +32,14 @@ }, "dependencies": { "dbf": "0.2.0", - "jszip": "3.6.0", - "file-saver": "2.0.5" + "file-saver": "2.0.5", + "jszip": "^3.10.1" }, "devDependencies": { - "browserify": "^13.0.0", + "browserify": "^17.0.0", "cz-conventional-changelog": "^1.2.0", "expect.js": "~0.3.1", - "mocha": "~2.4.5" + "mocha": "^10.2.0" }, "config": { "commitizen": { diff --git a/src/geojson.js b/src/geojson.js index 77eed44..be8f28c 100644 --- a/src/geojson.js +++ b/src/geojson.js @@ -4,32 +4,53 @@ module.exports.multiline = justType("MultiLineString", "POLYLINE"); module.exports.polygon = justType("Polygon", "POLYGON"); module.exports.multipolygon = justType("MultiPolygon", "POLYGON"); -function justType(type, TYPE) { +/** + * Generate a function that returns an object with the geometries, properties, and type of the given GeoJSON type + * @param {string} type the GeoJSON type + * @param {string} TYPE the Shapefile type + * @returns {(gj: { features: Feature[] }) => { geometries: number[] | number[][] | number[][][] | number[][][][], properties: {Object.}, type: string }} + */ +function justType(gjType, shpType) { return function (gj) { - var oftype = gj.features.filter(isType(type)); + var oftype = gj.features.filter(isType(gjType)); return { - geometries: oftype.map(justCoords), + geometries: shpType === 'POLYLINE' ? [oftype.map(justCoords)] : oftype.map(justCoords), properties: oftype.map(justProps), - type: TYPE, + type: shpType, }; }; } -function justCoords(t) { - return t.geometry.coordinates; +/** + * + * @param {Feature} feature The feature to get the coordinates from + * @returns {number[] | number[][] | number[][][] | number[][][][]} + */ +function justCoords(feature) { + return feature.geometry.coordinates; } -function justProps(t) { - return t.properties; +/** + * + * @param {Feature} feature The feature to get the properties from + * @returns {Object.} + */ +function justProps(feature) { + return feature.properties; } -function isType(t) { - if (Array.isArray(t)) +/** + * Generate a function that filters features based on their geometry.type + * @param {string | string[]} type the GeoJSON type to filter with + * @returns {(f: Feature) => boolean} a function that returns true if the feature's type is in {@link type} + */ +function isType(type) { + if (Array.isArray(type)) return function (f) { - return t.includes(f.geometry.type); + return type.includes(f.geometry.type); }; else return function (f) { - return f.geometry.type === t; + return f.geometry.type === type; }; } diff --git a/src/write.js b/src/write.js index 0f0cbbe..42d9ff9 100644 --- a/src/write.js +++ b/src/write.js @@ -1,11 +1,8 @@ -var types = require('./types'), - dbf = require('dbf'), - prj = require('./prj'), - ext = require('./extent'), - getFields = require('./fields'), - assert = require('assert'), - pointWriter = require('./points'), - polyWriter = require('./poly'); +var types = require('./types'); +var dbf = require('dbf'); +var prj = require('./prj'); +var pointWriter = require('./points'); +var polyWriter = require('./poly'); var writers = { 1: pointWriter, @@ -13,23 +10,21 @@ var writers = { 3: polyWriter }; -var recordHeaderLength = 8; - module.exports = write; // Low-level writing interface function write(rows, geometry_type, geometries, callback) { - var TYPE = types.geometries[geometry_type], - writer = writers[TYPE], - parts = writer.parts(geometries, TYPE), - shpLength = 100 + (parts - geometries.length) * 4 + writer.shpLength(geometries), - shxLength = 100 + writer.shxLength(geometries), - shpBuffer = new ArrayBuffer(shpLength), - shpView = new DataView(shpBuffer), - shxBuffer = new ArrayBuffer(shxLength), - shxView = new DataView(shxBuffer), - extent = writer.extent(geometries); + var TYPE = types.geometries[geometry_type]; + var writer = writers[TYPE]; + var parts = writer.parts(geometries, TYPE); + var shpLength = 100 + (parts - geometries.length) * 4 + writer.shpLength(geometries); + var shxLength = 100 + writer.shxLength(geometries); + var shpBuffer = new ArrayBuffer(shpLength); + var shpView = new DataView(shpBuffer); + var shxBuffer = new ArrayBuffer(shxLength); + var shxView = new DataView(shxBuffer); + var extent = writer.extent(geometries); writeHeader(shpView, TYPE); writeHeader(shxView, TYPE); diff --git a/src/zip.js b/src/zip.js index 43daf20..b2b02e1 100644 --- a/src/zip.js +++ b/src/zip.js @@ -1,7 +1,8 @@ -var write = require("./write"), - geojson = require("./geojson"), - prj = require("./prj"), - JSZip = require("jszip"); +var write = require("./write"); +var geojson = require("./geojson"); +var defaultPrj = require('./prj'); +var JSZip = require("jszip"); + module.exports = function ( gj, @@ -14,6 +15,8 @@ module.exports = function ( zipTarget = zip.folder(options.folder); } + var prj = (options && options.prj) ? options.prj : defaultPrj; + [ geojson.point(gj), geojson.line(gj),