New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

geojson-tools

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

geojson-tools - npm Package Compare versions

Comparing version 0.1.5 to 0.1.6

6

HISTORY.md

@@ -0,1 +1,7 @@

0.1.6 / 2014-06-13
==================
* Add support for making a `LineString` or array of coordinates complex by reducing the maximum distance between each points
* [WIP] Add support for some additional GeoJSON objects. This has not been tested, and has thus not yet been documented.
0.1.5 / 2014-03-21

@@ -2,0 +8,0 @@ ==================

6

package.json
{
"name": "geojson-tools",
"version": "0.1.5",
"version": "0.1.6",
"description": "Tools for working with location data, using the GeoJSON specification",

@@ -27,3 +27,7 @@ "main": "index.js",

"underscore": "^1.6.0"
},
"devDependencies": {
"mocha": "1.18.*",
"chai": "~1.9.1"
}
}

@@ -22,2 +22,3 @@ # geojson-tools.js

* [getDistance](#getDistance)
* [complexify](#complexify)

@@ -55,2 +56,185 @@ ### Conversions

<a name="complexify" />
### complexify(linestring, distance)
Convert `LineString` or array of coordinates to a 'complex' line with specified maximum distance between each set of points.
__Argumenta""
* linestring - a valid GeoJSON `LineString`, or an array of locations, in the format `[lat, lng]`.
* distance - the maximum distance between each two locations, in meters.
__Example__
```js
var array = [
[20, 30],
[20.5, 29.5]
];
complexify(array, .5); // 500 meters
/*
[
[
20,
30
],
[
20.01374457881841,
29.98625542118159
],
[
20.02749358953798,
29.97250641046202
],
[
20.04124228409795,
29.95875771590205
],
[
20.054991756377518,
29.945008243622482
],
[
20.068742346786582,
29.931257653213418
],
[
20.082493595207826,
29.917506404792174
],
[
20.09624419012715,
29.90375580987285
],
[
20.109998454446686,
29.890001545553314
],
[
20.1237529737179,
29.8762470262821
],
[
20.13750307275061,
29.86249692724939
],
[
20.151256033129513,
29.848743966870487
],
[
20.165010879444292,
29.834989120555708
],
[
20.17876504224938,
29.82123495775062
],
[
20.192520769690717,
29.807479230309283
],
[
20.206277898630216,
29.793722101369784
],
[
20.22003390658687,
29.77996609341313
],
[
20.233790231590877,
29.766209768409123
],
[
20.247544863923423,
29.752455136076577
],
[
20.261305062529242,
29.738694937470758
],
[
20.275060711787024,
29.724939288212976
],
[
20.288822447172574,
29.711177552827426
],
[
20.3025819531983,
29.6974180468017
],
[
20.3163428239271,
29.6836571760729
],
[
20.330106243909288,
29.669893756090712
],
[
20.34386828218919,
29.65613171781081
],
[
20.357630509669633,
29.642369490330367
],
[
20.37139577415928,
29.62860422584072
],
[
20.385162033041937,
29.614837966958063
],
[
20.398927945160654,
29.601072054839346
],
[
20.412692053486403,
29.587307946513597
],
[
20.426458446138625,
29.573541553861375
],
[
20.4402208587639,
29.5597791412361
],
[
20.45398897904669,
29.54601102095331
],
[
20.467757803444808,
29.532242196555192
],
[
20.481524285475142,
29.518475714524858
],
[
20.49529252216346,
29.50470747783654
],
[
20.5,
29.5
]
];
*/
distance of 'simple' linestring: 76.321 km
distance of 'complex' linesting: 76.321 km
```
<b>Note:</b> The algorithms works relatively well, and will return results that are > 99.9% accurate over short distances. Specifying distances > 500 km might return undesireable results.
We have also not added the option to specify precision, and precision is set at the default rounding of 3 decimals, this is to avoid infinite loops.
## Conversions

@@ -57,0 +241,0 @@

368

src/geojson-tools.js

@@ -19,4 +19,7 @@ /**

var toGeoJSON = function (array, type) {
var georesult = {};
if ( !type ) {
var georesult = {},
arr,
error,
nested;
if (!type) {
type = "point";

@@ -26,9 +29,9 @@ }

switch (type) {
case 'point':
var _array = _.flatten(array);
var _a = JSON.stringify(array) || {};
if (_array.length !== 2) {
return new Error("expected a single set of coordinates in [lat, lng] format. \nReceived " + _a);
}
var arr = [_array[1], _array[0]];
case 'point':
var _array = _.flatten(array);
var _a = JSON.stringify(array) || {};
if (_array.length !== 2) {
error = new Error("expected a single set of coordinates in [lat, lng] format. \nReceived " + _a);
} else {
arr = [parseFloat(_array[1]), parseFloat(_array[0])];
georesult = {

@@ -38,37 +41,105 @@ type: "Point",

};
return georesult;
}
break;
case 'linestring':
var arr = [];
_.each(array, function (a) {
arr.push([a[1], a[0]]);
case 'linestring':
case 'multipoint':
arr = [];
_.each(array, function (a) {
arr.push([parseFloat(a[1]), parseFloat(a[0])]);
});
if (type === 'linestring') {
type = 'LineString';
} else if (type === 'MultiPoint') {
type = 'MultiPoint';
}
georesult = {
type: type,
coordinates: arr
};
break;
case 'polygon':
if (_.isArray(array) && _.isArray(array[0]) && !_.isArray(array[0][0])) {
array = [array];
}
arr = [];
_.find(array, function (_array) {
if (_array.length < 3) {
error = new Error("Expecting 'array' to have at length of at least 3 sets of coordinates.");
return true;
}
if (_array.toString() !== _.last(_array).toString()) {
_array.push(_array[0]);
}
nested = [];
_.each(_array, function (a) {
nested.push([parseFloat(a[1]), parseFloat(a[0])]);
});
arr.push(nested);
return false;
});
if (!error) {
georesult = {
type: "LineString",
coordinates: _.uniq(arr, true, function (x) {
return JSON.stringify(x) ;
})
type: "Polygon",
coordinates: arr
};
return georesult;
}
break;
case 'polygon':
var arr = [[]];
if (array.length < 3) {
return new Error("Expecting 'array' to have at length of at least 3 sets of coordinates.");
case 'multilinestring':
arr = [];
// validate multilinestring
_.find(array, function (a) {
if (a.length < 2 && !error) {
error = new Error("Expecting each LineString in MultiiLineString to have at least 2 points.");
return true;
}
if (array[0].toString() !== _.last(array).toString()) {
array.push(array[0]);
}
_.each(array, function (a) {
arr[0].push([a[1], a[0]]);
nested = [];
_.each(a, function (_a) {
nested.push([parseFloat(_a[1]), parseFloat(_a[0])]);
});
arr.push(nested);
return false;
});
if (!error) {
georesult = {
type: "Polygon",
type: "MultiLineString",
coordinates: arr
};
return georesult;
}
break;
default:
return new Error("type not recognised. Should be 'point', 'linestring', or 'polygon'");
case 'multipolygon':
arr = [];
var outer,
inner;
_.find(array, function (a) {
outer = [];
_.find(a, function (_a) {
inner = [];
if (_a.length < 3) {
error = new Error("Expecting each array in MultiPolygon to have at least 3 points.");
return true;
}
_.find(_a, function (__a) {
inner.push([parseFloat(__a[1]), parseFloat(__a[0])]);
return false;
});
outer.push(inner);
return false;
});
arr.push(outer);
return false;
});
if (!error) {
georesult = {
type: "MultiPolygon",
coordinates: arr
};
}
break;
default:
error = new Error("type not recognised. Should be 'point', 'linestring', or 'polygon'");
}
if (error) {
return error;
}
return georesult;
};

@@ -82,2 +153,4 @@

var toArray = function (geoobj) {
var array,
error;
if (!geoobj.type || !geoobj.coordinates) {

@@ -87,42 +160,57 @@ return new Error("The object specified is not a a valid GeoJSON object");

switch (geoobj.type.toLowerCase()) {
case 'point':
var point = [geoobj.coordinates[1],geoobj.coordinates[0]];
return point;
case 'point':
array = [parseFloat(geoobj.coordinates[1]), parseFloat(geoobj.coordinates[0])];
break;
case 'linestring':
// check if it is a valid line
var array = geoobj.coordinates;
var line = [];
_.each(array, function (ln) {
if (typeof ln === 'object') {
line.push([ln[1], ln[0]]);
}
else {
return new Error("the object specified is not a valid GeoJSON LineString");
}
});
return line;
case 'linestring':
// check if it is a valid line
array = geoobj.coordinates;
var line = [];
_.find(array, function (ln) {
if (typeof ln === 'object') {
line.push([parseFloat(ln[1]), parseFloat(ln[0])]);
return false;
}
error = new Error("the object specified is not a valid GeoJSON LineString");
return true;
});
if (!error) {
array = line;
}
break;
case 'polygon':
var array = geoobj.coordinates[0];
if (!array.length) {
return new Error("the object specified is not a valid GeoJSON Polygon");
case 'polygon':
var poly;
array = [];
// check if valid object
_.find(geoobj.coordinates, function (a) {
if (!a.length) {
error = new Error("the object specified is not a valid GeoJSON Polygon");
return true;
}
var poly = [];
_.each(array, function (a) {
if (a[0].toString() !== _.last(a).toString()) {
return new Error("The first and last coordinates of a Polygon are not the same");
}
if (a.length < 4) {
return new Error("A valid Polygon should have a minimum of 4 coordinates");
}
_.each(_.initial(a), function (pl) {
poly.push([pl[1], pl[0]]);
});
poly = [];
if (a[0].toString() !== _.last(a).toString()) {
error = new Error("The first and last coordinates of the Polygon are not the same");
return true;
}
if (a.length < 4) {
error = new Error("A valid Polygon should have a minimum set of 4 points");
return true;
}
_.each(_.initial(a), function (pl) {
poly.push([parseFloat(pl[1]), parseFloat(pl[0])]);
});
return poly;
array.push(poly);
if (!error) {
array = poly;
}
return false;
});
break;
default:
return new Error("unknown GeoJSON type specified");
default:
error = new Error("unknown GeoJSON type specified");
}
if (error) {
return error;
}
return array;
};

@@ -137,4 +225,4 @@

var getDistance = function (array, decimals) {
if(typeof(Number.prototype.toRad) === "undefined") {
Number.prototype.toRad = function () {
if (Number.prototype.toRad === undefined) {
Number.prototype.toRad = function () {
return this * Math.PI / 180;

@@ -147,21 +235,33 @@ };

distance = 0,
len = array.length;
for (var i = 0; (i + 1) < len; i++) {
var x1 = array[i];
var x2 = array[i + 1];
len = array.length,
i,
x1,
x2,
lat1,
lat2,
lon1,
lon2,
dLat,
dLon,
a,
c,
d;
for (i = 0; (i + 1) < len; i++) {
x1 = array[i];
x2 = array[i + 1];
var lat1 = parseFloat(x1[0]);
var lat2 = parseFloat(x2[0]);
var lon1 = parseFloat(x1[1]);
var lon2 = parseFloat(x2[1]);
lat1 = parseFloat(x1[0]);
lat2 = parseFloat(x2[0]);
lon1 = parseFloat(x1[1]);
lon2 = parseFloat(x2[1]);
var dLat = (lat2 - lat1).toRad();
var dLon = (lon2 - lon1).toRad();
dLat = (lat2 - lat1).toRad();
dLon = (lon2 - lon1).toRad();
lat1 = lat1.toRad();
lat2 = lat2.toRad();
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = earthRadius * c;
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
d = earthRadius * c;
distance += d;

@@ -173,2 +273,105 @@ }

/**
* takes a `LineString` or an array of coordinates, and returns one with more coordinates
+ having the distance parameter as the maximum distance between each set of coordinates.
*
* @param {Object} either a valid LineString, or an array of coordinates
* @param {Number} maximum distance in meters between points
* @returns {Object} either a LineString or array of coordinates
*/
var complexify = function (linestring, distance) {
if (!_.isNumber(distance) || distance < 0.10) {
console.error(new Error('distance should be a number greater than 10 meters'));
return null;
}
var returnGeoJSON;
if (!_.isArray(linestring)) {
// check if it is a GeoJSON LineString and convert it
if (linestring.coordinates && _.isArray(linestring.coordinates) && linestring.type.toLowerCase() === 'linestring') {
returnGeoJSON = true;
linestring = toArray(linestring);
} else {
console.error(new Error('distance should be a number greater than 10 meters'));
return null;
}
}
var t, // threshold
cur,
prev, // temporary points
result = [],
points = [],
d;
cur = linestring.shift();
result.push(cur);
points.push(cur);
// variables used in the loop
var reasonable,
ratio,
_d,
totalDistance,
a,
b,
bearing,
c;
_.each(linestring, function (point) {
prev = cur;
cur = point;
d = getDistance([cur, prev]);
if (d > distance) {
t = 0;
reasonable = false;
// estimate where distance could be, then perform a binary search to find the best-fit coordinates
ratio = distance / d;
_d = distance;
totalDistance = 0;
while (!reasonable || (totalDistance + distance < d)) {
a = _.last(result);
b = cur;
bearing = [];
if (a[0] > b[0]) {
bearing.push(-1);
} else {
bearing.push(1);
}
if (a[1] > b[1]) {
bearing.push(-1);
} else {
bearing.push(1);
}
c = [(b[0] - a[0]) * ratio * bearing[0], (b[1] - a[1]) * ratio * bearing[1]];
// console.log(c);
if (bearing[0] > 0) {
c[0] = c[0] + a[0];
} else {
c[0] = a[0] - c[0];
}
if (bearing[1] > 0) {
c[1] = c[1] + a[1];
} else {
c[1] = a[1] - c[1];
}
bearing = [];
_d = getDistance([a, c]);
if (_d !== distance) {
t = (_d / distance); // goalseek threshold and search for next nearest point
ratio = ratio + (ratio * (1 - t));
} else {
totalDistance += distance;
reasonable = true;
result.push(c);
}
}
totalDistance = 0;
reasonable = false;
result.push(cur);
} else {
result.push(cur);
}
});
if (returnGeoJSON) {
return toGeoJSON(result, 'linestring');
}
return result;
};
/*

@@ -179,2 +382,3 @@ * Export functions

exports.toArray = toArray;
exports.getDistance = getDistance;
exports.getDistance = getDistance;
exports.complexify = complexify;

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc