Comparing version 1.1.1 to 1.2.0
74
cli.js
#!/usr/bin/env node | ||
var concat = require('concat-stream'), | ||
var geojsonStream = require('geojson-stream'), | ||
reproject = require('./'), | ||
@@ -9,2 +9,3 @@ proj4 = require('proj4'), | ||
useSpatialReference = argv["sr"] || argv["use-spatialreference"], | ||
useEpsgIo = argv["eio"] || argv["use-epsg-io"], | ||
http = require('http'), | ||
@@ -37,20 +38,37 @@ crss, | ||
if ((fromCrs && toCrs) || (!argv.from && !argv.to)) { | ||
((argv._[0] && fs.createReadStream(argv._[0])) || process.stdin).pipe(concat(openData)); | ||
((argv._[0] && fs.createReadStream(argv._[0])) || process.stdin).pipe(geojsonStream.parse()) | ||
.on('header', openData) | ||
.on('footer', openData) | ||
.on('data', openData) | ||
.on('error', function (err) { | ||
console.error(err); | ||
}); | ||
} | ||
} | ||
function openData(body) { | ||
var geojson = JSON.parse(body.toString()); | ||
function openData(geojson) { | ||
// geojson-stream will break a FeaturesCollection's features into chunks that are fed to openData; | ||
// single geometries or GeometryCollection will be fed to as "header", so | ||
// we have to handle them here. | ||
if (geojson) { | ||
var isGeomCol = geojson.type === 'GeometryCollection' && geojson.geometries; | ||
var isFeature = geojson.type === 'Feature' && geojson.geometry; | ||
var isGeometry = geojson.coordinates; | ||
if (argv["reverse"]) { | ||
geojson = reproject.reverse(geojson); | ||
} | ||
if (isGeomCol || isFeature || isGeometry) { | ||
if (argv["reverse"]) { | ||
geojson = reproject.reverse(geojson); | ||
} | ||
if (fromCrs && toCrs) { | ||
geojson = reproject.reproject(geojson, fromCrs, toCrs, crss); | ||
geojson = reproject.reproject(geojson, fromCrs, toCrs, crss); | ||
} | ||
} | ||
console.log(JSON.stringify(geojson, null, 2)); | ||
outputJson(geojson); | ||
} | ||
function outputJson(json) { | ||
console.log(JSON.stringify(json, null, 2)); | ||
} | ||
function loadJson(f) { | ||
@@ -81,18 +99,6 @@ var data; | ||
if (useSpatialReference) { | ||
var crsPath = crsName.toLowerCase().replace(':', '/'), | ||
url = "http://www.spatialreference.org/ref/"+ crsPath + "/proj4/", | ||
crsDef = ''; | ||
http.get(url, function(res) { | ||
if (res.statusCode != 200) { | ||
throw new Error("spatialreference.org responded with HTTP " + res.statusCode + | ||
" when looking up \"" + crsName + "\"."); | ||
} | ||
res.on('data', function(chunk) { | ||
crsDef += chunk; | ||
}).on('end', function() { | ||
crss[crsName] = proj4(crsDef); | ||
cb(crss[crsName]); | ||
}); | ||
}); | ||
var crsPath = crsName.toLowerCase().replace(':', '/'); | ||
getCrs(crsName, "http://www.spatialreference.org/ref/"+ crsPath + "/proj4/", cb); | ||
} else if (useEpsgIo) { | ||
getCrs(crsName, "http://epsg.io/" + crsName.split(":")[1] + ".proj4", cb); | ||
} else { | ||
@@ -105,1 +111,17 @@ throw new Error("Could not find definition for CRS \"" + crsName + "\"."); | ||
} | ||
function getCrs(crsName, url, cb) { | ||
var crsDef = ''; | ||
http.get(url, function(res) { | ||
if (res.statusCode != 200) { | ||
throw new Error("spatialreference.org responded with HTTP " + res.statusCode + | ||
" when looking up \"" + crsName + "\"."); | ||
} | ||
res.on('data', function(chunk) { | ||
crsDef += chunk; | ||
}).on('end', function() { | ||
crss[crsName] = proj4(crsDef); | ||
cb(crss[crsName]); | ||
}); | ||
}); | ||
} |
99
index.js
@@ -26,18 +26,24 @@ 'use strict'; | ||
function traverseGeoJson(geojson, leafCallback, nodeCallback) { | ||
function traverseGeoJson(geometryCb, nodeCb, geojson) { | ||
if (geojson == null) return geojson; | ||
var r = clone(geojson); | ||
var self = traverseGeoJson.bind(this, geometryCb, nodeCb); | ||
if (geojson.type === 'Feature') { | ||
r.geometry = traverseGeoJson(geojson.geometry, leafCallback, nodeCallback); | ||
} else if (geojson.type === 'FeatureCollection') { | ||
r.features = r.features.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); }); | ||
} else if (geojson.type === 'GeometryCollection') { | ||
r.geometries = r.geometries.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); }); | ||
} else { | ||
if (leafCallback) leafCallback(r); | ||
switch (geojson.type) { | ||
case 'Feature': | ||
r.geometry = self(geojson.geometry); | ||
break; | ||
case 'FeatureCollection': | ||
r.features = r.features.map(self); | ||
break; | ||
case 'GeometryCollection': | ||
r.geometries = r.geometries.map(self); | ||
break; | ||
default: | ||
geometryCb(r); | ||
break; | ||
} | ||
if (nodeCallback) nodeCallback(r); | ||
if (nodeCb) nodeCb(r); | ||
@@ -76,2 +82,16 @@ return r; | ||
function calcBbox(geojson) { | ||
var min = [Number.MAX_VALUE, Number.MAX_VALUE], | ||
max = [-Number.MAX_VALUE, -Number.MAX_VALUE]; | ||
traverseGeoJson(function(_gj) { | ||
traverseCoords(_gj.coordinates, function(xy) { | ||
min[0] = Math.min(min[0], xy[0]); | ||
min[1] = Math.min(min[1], xy[1]); | ||
max[0] = Math.max(max[0], xy[0]); | ||
max[1] = Math.max(max[1], xy[1]); | ||
}); | ||
}, null, geojson); | ||
return [min[0], min[1], max[0], max[1]]; | ||
} | ||
function reproject(geojson, from, to, projs) { | ||
@@ -86,5 +106,5 @@ projs = projs || {}; | ||
to = determineCrs(to, projs); | ||
var transform = proj4(from, to); | ||
var transform = proj4(from, to).forward.bind(transform); | ||
return traverseGeoJson(geojson, function(gj) { | ||
var transformGeometryCoords = function(gj) { | ||
// No easy way to put correct CRS info into the GeoJSON, | ||
@@ -95,45 +115,30 @@ // and definitely wrong to keep the old, so delete it. | ||
} | ||
gj.coordinates = traverseCoords(gj.coordinates, function(xy) { | ||
return transform.forward(xy); | ||
}); | ||
}, function(gj) { | ||
gj.coordinates = traverseCoords(gj.coordinates, transform); | ||
} | ||
var transformBbox = function(gj) { | ||
if (gj.bbox) { | ||
// A bbox can't easily be reprojected, just reprojecting | ||
// the min/max coords definitely will not work since | ||
// the transform is not linear (in the general case). | ||
// Workaround is to just re-compute the bbox after the | ||
// transform. | ||
gj.bbox = (function() { | ||
var min = [Number.MAX_VALUE, Number.MAX_VALUE], | ||
max = [-Number.MAX_VALUE, -Number.MAX_VALUE]; | ||
traverseGeoJson(gj, function(_gj) { | ||
traverseCoords(_gj.coordinates, function(xy) { | ||
min[0] = Math.min(min[0], xy[0]); | ||
min[1] = Math.min(min[1], xy[1]); | ||
max[0] = Math.max(max[0], xy[0]); | ||
max[1] = Math.max(max[1], xy[1]); | ||
}); | ||
}); | ||
return [min[0], min[1], max[0], max[1]]; | ||
})(); | ||
gj.bbox = calcBbox(gj); | ||
} | ||
}); | ||
} | ||
return traverseGeoJson(transformGeometryCoords, transformBbox, geojson); | ||
} | ||
module.exports = { | ||
detectCrs: detectCrs, | ||
detectCrs: detectCrs, | ||
reproject: reproject, | ||
reproject: reproject, | ||
reverse: function(geojson) { | ||
return traverseGeoJson(geojson, function(gj) { | ||
gj.coordinates = traverseCoords(gj.coordinates, function(xy) { | ||
return [ xy[1], xy[0] ]; | ||
}); | ||
reverse: function(geojson) { | ||
return traverseGeoJson(function(gj) { | ||
gj.coordinates = traverseCoords(gj.coordinates, function(xy) { | ||
return [ xy[1], xy[0] ]; | ||
}); | ||
}, | ||
}, null, geojson); | ||
}, | ||
toWgs84: function(geojson, from, projs) { | ||
return reproject(geojson, from, proj4.WGS84, projs); | ||
} | ||
}; | ||
toWgs84: function(geojson, from, projs) { | ||
return reproject(geojson, from, proj4.WGS84, projs); | ||
} | ||
}; |
{ | ||
"name": "reproject", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Reproject GeoJSON from one projection/CRS to another", | ||
@@ -22,4 +22,5 @@ "repository": "git@github.com:perliedman/reproject.git", | ||
"dependencies": { | ||
"concat-stream": "^1.5.1", | ||
"geojson-stream": "0.0.1", | ||
"minimist": "^1.2.0", | ||
"concat-stream": "^1.5.1", | ||
"proj4": "^2.3.12" | ||
@@ -29,4 +30,4 @@ }, | ||
"expect.js": "^0.3.1", | ||
"mocha": "^2.4.1" | ||
"mocha": "^4.0.0" | ||
} | ||
} |
reproject [![Build status](https://travis-ci.org/perliedman/reproject.png)](https://travis-ci.org/perliedman/reproject) [![NPM version](https://badge.fury.io/js/reproject.png)](http://badge.fury.io/js/reproject) | ||
========= | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/perliedman/reproject.svg)](https://greenkeeper.io/) | ||
Transforms GeoJSON from one projection / CRS to another. | ||
According to the latest [GeoJSON spec (RFC 7946)](https://tools.ietf.org/html/rfc7946#section-4), GeoJSON coordinates should be assumed to be in WGS84, but sometimes it's useful to use other CRS anyway, and the spec actually leaves some room for this: | ||
> However, where all | ||
> involved parties have a prior arrangement, alternative coordinate | ||
> reference systems can be used without risk of data being | ||
> misinterpreted. | ||
Reproject lets you either explicitly specify a GeoJSON's CRS, or use the conventions from the earlier GeoJSON spec: [GeoJSON 2008](http://geojson.org/geojson-spec.html#coordinate-reference-system-objects). | ||
## cli | ||
@@ -14,3 +25,3 @@ | ||
$ echo '{"type":"Point","coordinates":[319180, 6399862]}' | reproject --use-spatialreference --from=EPSG:3006 --to=EPSG:4326 | ||
$ echo '{"type":"Point","coordinates":[319180, 6399862]}' | reproject --use-epsg-io --from=EPSG:3006 --to=EPSG:4326 | ||
@@ -21,2 +32,4 @@ Options: | ||
* ```--to=crs-name``` is the CRS to convert the GeoJSON to; either a name from `crs-defs`, or a Proj4 CRS definition string | ||
* ```--use-epsg-io``` or ```--eio``` to use [epsg.io](https://epsg.io/) to look up | ||
any CRS definitions that aren't already known | ||
* ```--use-spatialreference``` or ```--sr``` to use [spatialreference.org](http://spatialreference.org/) to look up | ||
@@ -60,2 +73,9 @@ any CRS definitions that aren't already known | ||
For a fully automatic "convert almost any common projection to lat/lon", try this: | ||
```js | ||
var epsg = require('epsg'); | ||
toWgs84(geojson, undefined, epsg); | ||
``` | ||
### detectCrs(geojson, crss) | ||
@@ -62,0 +82,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
28666
11
716
86
4
+ Addedgeojson-stream@0.0.1
+ AddedJSONStream@1.3.5(transitive)
+ Addedgeojson-stream@0.0.1(transitive)
+ Addedjsonparse@1.3.1(transitive)
+ Addedthrough@2.3.8(transitive)