Comparing version 0.0.101 to 0.0.104
@@ -7,3 +7,4 @@ //http://stackoverflow.com/questions/839899/how-do-i-calculate-a-point-on-a-circles-circumference | ||
var _ = require('lodash'), | ||
jsts = require('jsts') | ||
jsts = require('jsts'), | ||
fs = require('fs') | ||
var t = {} | ||
@@ -28,2 +29,3 @@ t.featurecollection = require('./featurecollection') | ||
t.combine(feature, function(err, multi){ | ||
multi.properties = {} | ||
bufferOp(multi, radius, done) | ||
@@ -56,3 +58,2 @@ }) | ||
} | ||
} | ||
} |
@@ -1,224 +0,56 @@ | ||
//http://en.wikipedia.org/wiki/Delaunay_triangulation | ||
//https://github.com/ironwallaby/delaunay | ||
// 1. run tin on points | ||
// 2. calculate lenth of all edges and area of all triangles | ||
// 3. remove triangles that fail the max length test | ||
// 4. buffer the results slightly | ||
// 5. merge the results | ||
var fs = require('fs'), | ||
async = require('async') | ||
var t = {} | ||
var _ = require('lodash'), | ||
polygon = require('./polygon'), | ||
nearest = require('./nearest'), | ||
point = require('./point') | ||
t.polygon = polygon | ||
t.nearest = nearest | ||
t.point = point | ||
t.tin = require('./tin') | ||
t.merge = require('./merge') | ||
t.buffer = require('./buffer') | ||
t.distance = require('./distance') | ||
t.point = require('./point') | ||
module.exports = function(points, tolerance, done){ | ||
//break down points | ||
var vertices = [] | ||
_(points.features).each(function(p){ | ||
vertices.push({x:p.geometry.coordinates[0], y:p.geometry.coordinates[1]}) | ||
}) | ||
var triangulated = triangulate(vertices) | ||
var triangles = { | ||
type: 'FeatureCollection', | ||
features: [] | ||
} | ||
_(triangulated).each(function(triangle){ | ||
var coords = [[[triangle.a.x, triangle.a.y], [triangle.b.x, triangle.b.y], [triangle.c.x, triangle.c.y]]] | ||
var poly = t.polygon(coords, {a: null, b: null, c: null}) | ||
triangles.features.push(poly) | ||
}) | ||
// add values from vertices | ||
/*_.each(triangles.features, function(tri){ | ||
var coordinateNumber = 1 | ||
_.each(tri.geometry.coordinates[0], function(c){ | ||
t.nearest(t.point(c[0], c[1]), points, function(err, closest){ | ||
if(coordinateNumber === 1){ | ||
tri.properties.a = closest.properties[z] | ||
} | ||
else if(coordinateNumber === 2){ | ||
tri.properties.b = closest.properties[z] | ||
} | ||
else if(coordinateNumber === 3){ | ||
tri.properties.c = closest.properties[z] | ||
} | ||
coordinateNumber++ | ||
module.exports = function(points, maxEdge, done){ | ||
t.tin(points, null, function(err, tinPolys){ | ||
if(err) done(err) | ||
filterTriangles(tinPolys.features, maxEdge, function(filteredPolys){ | ||
tinPolys.features = filteredPolys | ||
fs.writeFileSync('./testOut/filteredConvcave.geojson', JSON.stringify(tinPolys)) | ||
t.buffer(tinPolys, 1, 'miles', function(err, bufferPolys){ | ||
if(err) done(err) | ||
fs.writeFileSync('./testOut/bufferConvcave.geojson', JSON.stringify(bufferPolys)) | ||
t.merge(bufferPolys, function(err, mergePolys){ | ||
if(err) done(err) | ||
done(null, mergePolys) | ||
}) | ||
}) | ||
}) | ||
})*/ | ||
done(null, triangles) | ||
}) | ||
} | ||
function Triangle(a, b, c) { | ||
this.a = a | ||
this.b = b | ||
this.c = c | ||
var A = b.x - a.x, | ||
B = b.y - a.y, | ||
C = c.x - a.x, | ||
D = c.y - a.y, | ||
E = A * (a.x + b.x) + B * (a.y + b.y), | ||
F = C * (a.x + c.x) + D * (a.y + c.y), | ||
G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)), | ||
minx, miny, dx, dy | ||
/* If the points of the triangle are collinear, then just find the | ||
* extremes and use the midpoint as the center of the circumcircle. */ | ||
if(Math.abs(G) < 0.000001) { | ||
minx = Math.min(a.x, b.x, c.x) | ||
miny = Math.min(a.y, b.y, c.y) | ||
dx = (Math.max(a.x, b.x, c.x) - minx) * 0.5 | ||
dy = (Math.max(a.y, b.y, c.y) - miny) * 0.5 | ||
this.x = minx + dx | ||
this.y = miny + dy | ||
this.r = dx * dx + dy * dy | ||
} | ||
else { | ||
this.x = (D*E - B*F) / G | ||
this.y = (A*F - C*E) / G | ||
dx = this.x - a.x | ||
dy = this.y - a.y | ||
this.r = dx * dx + dy * dy | ||
} | ||
} | ||
Triangle.prototype.draw = function(ctx) { | ||
ctx.beginPath() | ||
ctx.moveTo(this.a.x, this.a.y) | ||
ctx.lineTo(this.b.x, this.b.y) | ||
ctx.lineTo(this.c.x, this.c.y) | ||
ctx.closePath() | ||
ctx.stroke() | ||
} | ||
function byX(a, b) { | ||
return b.x - a.x | ||
} | ||
function dedup(edges) { | ||
var j = edges.length, | ||
a, b, i, m, n | ||
outer: while(j) { | ||
b = edges[--j] | ||
a = edges[--j] | ||
i = j | ||
while(i) { | ||
n = edges[--i] | ||
m = edges[--i] | ||
if((a === m && b === n) || (a === n && b === m)) { | ||
edges.splice(j, 2) | ||
edges.splice(i, 2) | ||
j -= 2 | ||
continue outer | ||
} | ||
var filterTriangles = function(triangles, maxEdge, cb){ | ||
filteredTriangles = [] | ||
async.each(triangles, | ||
function(triangle, asyncCB){ | ||
var pt1 = t.point(triangle.geometry.coordinates[0][0][0], triangle.geometry.coordinates[0][0][1]) | ||
var pt2 = t.point(triangle.geometry.coordinates[0][1][0], triangle.geometry.coordinates[0][1][1]) | ||
var pt3 = t.point(triangle.geometry.coordinates[0][2][0], triangle.geometry.coordinates[0][2][1]) | ||
t.distance(pt1, pt2, 'miles', function(err, dist1){ | ||
t.distance(pt2, pt3, 'miles', function(err, dist2){ | ||
t.distance(pt1, pt3, 'miles', function(err, dist3){ | ||
if(dist1 <= maxEdge && dist2 <= maxEdge && dist3 <= maxEdge){ | ||
filteredTriangles.push(triangle) | ||
} | ||
asyncCB() | ||
}) | ||
}) | ||
}) | ||
}, | ||
function(err){ | ||
cb(filteredTriangles) | ||
} | ||
} | ||
} | ||
function triangulate(vertices) { | ||
/* Bail if there aren't enough vertices to form any triangles. */ | ||
if(vertices.length < 3) | ||
return [] | ||
/* Ensure the vertex array is in order of descending X coordinate | ||
* (which is needed to ensure a subquadratic runtime), and then find | ||
* the bounding box around the points. */ | ||
vertices.sort(byX) | ||
var i = vertices.length - 1, | ||
xmin = vertices[i].x, | ||
xmax = vertices[0].x, | ||
ymin = vertices[i].y, | ||
ymax = ymin | ||
while(i--) { | ||
if(vertices[i].y < ymin) ymin = vertices[i].y | ||
if(vertices[i].y > ymax) ymax = vertices[i].y | ||
} | ||
/* Find a supertriangle, which is a triangle that surrounds all the | ||
* vertices. This is used like something of a sentinel value to remove | ||
* cases in the main algorithm, and is removed before we return any | ||
* results. | ||
* | ||
* Once found, put it in the "open" list. (The "open" list is for | ||
* triangles who may still need to be considered; the "closed" list is | ||
* for triangles which do not.) */ | ||
var dx = xmax - xmin, | ||
dy = ymax - ymin, | ||
dmax = (dx > dy) ? dx : dy, | ||
xmid = (xmax + xmin) * 0.5, | ||
ymid = (ymax + ymin) * 0.5, | ||
open = [ | ||
new Triangle( | ||
{x: xmid - 20 * dmax, y: ymid - dmax, __sentinel: true}, | ||
{x: xmid , y: ymid + 20 * dmax, __sentinel: true}, | ||
{x: xmid + 20 * dmax, y: ymid - dmax, __sentinel: true} | ||
) | ||
], | ||
closed = [], | ||
edges = [], | ||
j, a, b | ||
/* Incrementally add each vertex to the mesh. */ | ||
i = vertices.length | ||
while(i--) { | ||
/* For each open triangle, check to see if the current point is | ||
* inside it's circumcircle. If it is, remove the triangle and add | ||
* it's edges to an edge list. */ | ||
edges.length = 0 | ||
j = open.length | ||
while(j--) { | ||
/* If this point is to the right of this triangle's circumcircle, | ||
* then this triangle should never get checked again. Remove it | ||
* from the open list, add it to the closed list, and skip. */ | ||
dx = vertices[i].x - open[j].x | ||
if(dx > 0 && dx * dx > open[j].r) { | ||
closed.push(open[j]) | ||
open.splice(j, 1) | ||
continue | ||
} | ||
/* If not, skip this triangle. */ | ||
dy = vertices[i].y - open[j].y | ||
if(dx * dx + dy * dy > open[j].r) | ||
continue | ||
/* Remove the triangle and add it's edges to the edge list. */ | ||
edges.push( | ||
open[j].a, open[j].b, | ||
open[j].b, open[j].c, | ||
open[j].c, open[j].a | ||
) | ||
open.splice(j, 1) | ||
} | ||
/* Remove any doubled edges. */ | ||
dedup(edges) | ||
/* Add a new triangle for each edge. */ | ||
j = edges.length | ||
while(j) { | ||
b = edges[--j] | ||
a = edges[--j] | ||
open.push(new Triangle(a, b, vertices[i])) | ||
} | ||
} | ||
/* Copy any remaining open triangles to the closed list, and then | ||
* remove any triangles that share a vertex with the supertriangle. */ | ||
Array.prototype.push.apply(closed, open) | ||
i = closed.length | ||
while(i--) | ||
if(closed[i].a.__sentinel || | ||
closed[i].b.__sentinel || | ||
closed[i].c.__sentinel) | ||
closed.splice(i, 1) | ||
/* Yay, we're done! */ | ||
return closed | ||
} | ||
) | ||
} |
// 1. run tin on points | ||
// 2. merge the tin | ||
//var topojson = require('') | ||
var fs = require('fs') | ||
var t = {} | ||
@@ -12,8 +13,5 @@ t.tin = require('./tin') | ||
if(err) done(err) | ||
//console.log(tinPolys) | ||
t.buffer(tinPolys, .0001, 'miles', function(err, bufferPolys){ | ||
console.log(bufferPolys) | ||
t.buffer(tinPolys, .05, 'miles', function(err, bufferPolys){ | ||
t.merge(bufferPolys, function(err, mergePolys){ | ||
if(err) done(err) | ||
console.log(JSON.stringify(mergePolys, null, 2)) | ||
done(null, mergePolys) | ||
@@ -20,0 +18,0 @@ }) |
@@ -50,5 +50,19 @@ //http://en.wikipedia.org/wiki/Delaunay_triangulation | ||
} | ||
_.each(triangles.features, function(tri){ | ||
tri = correctRings(tri) | ||
}) | ||
done(null, triangles) | ||
} | ||
function correctRings(poly){ | ||
_.each(poly.geometry.coordinates, function(ring){ | ||
var isWrapped =_.isEqual(ring[0], ring.slice(-1)[0]) | ||
if(!isWrapped){ | ||
ring.push(ring[0]) | ||
} | ||
}) | ||
return poly | ||
} | ||
function Triangle(a, b, c) { | ||
@@ -55,0 +69,0 @@ this.a = a |
{ | ||
"name": "turf", | ||
"version": "0.0.101", | ||
"version": "0.0.104", | ||
"description": "a node.js library for performing geospatial operations with geojson", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -5,6 +5,4 @@ turf | ||
***a node.js library for performing geospatial operations with geojson*** | ||
***a fast and fully featured open gis engine written in javascript*** | ||
All features are written in a functional manner with no side effects. In nearly all cases, they accept objects created by the point, linestring, polygon, and featurecollection functions, but these are simply for convenience. Any valid geojson Feature of FeatureCollection will do. | ||
- - - | ||
@@ -95,2 +93,4 @@ | ||
- [donuts](#donuts) | ||
- [convex](#convex) | ||
- [concave](#concave) | ||
@@ -106,3 +106,2 @@ ####misc | ||
- concave | ||
- clockwise | ||
@@ -113,3 +112,2 @@ - krige | ||
- cluster | ||
- interpolate | ||
- area | ||
@@ -1093,5 +1091,39 @@ - smooth | ||
###convex | ||
Takes a set of points and returns a convex hull polygon. | ||
```javascript | ||
var t = require('turf') | ||
t.load('../test/testIn/convexIn.geojson', function(err, points){ | ||
t.convex(points, function(err, hull){ | ||
if(err) throw err | ||
fs.writeFileSync('./testOut/convex.geojson', JSON.stringify(hull)) | ||
console.log(hull) | ||
}) | ||
}) | ||
``` | ||
###concave | ||
Takes a set of points and returns a concave hull polygon. | ||
```javascript | ||
var t = require('turf') | ||
var maxEdge = 2.5 | ||
t.load('../test/testIn/concaveIn.geojson', function(err, points){ | ||
t.concave(points, maxEdge, function(err, hull){ | ||
if(err) throw err | ||
console.log(hull) | ||
}) | ||
}) | ||
``` | ||
- - - | ||
This library is built and maintained by [@morganherlocker](https://twitter.com/morganherlocker) :) | ||
This library is built and maintained by [@morganherlocker](https://twitter.com/morganherlocker) :) |
@@ -6,22 +6,10 @@ var t = require('../index'), | ||
describe('concave', function(){ | ||
it('should create a concave polygon', function(done){ | ||
t.load('../test/testIn/concaveIn.geojson', function(err, points){ | ||
t.concave(points, 1, function(err, concaved){ | ||
it('should take a set of points and return a concave hull polygon', function(done){ | ||
var maxEdge = 2.5 | ||
t.load('../test/testIn/concaveIn2.geojson', function(err, points){ | ||
t.concave(points, maxEdge, function(err, hull){ | ||
if(err) throw err | ||
concaved.should.be.ok | ||
concaved.features[0].geometry.type.should.equal('Polygon') | ||
concaved.features[0].geometry.coordinates.should.be.ok | ||
fs.writeFileSync(__dirname + '/testOut/concave1.geojson',JSON.stringify(concaved)) | ||
done() | ||
}) | ||
}) | ||
}) | ||
it('should create a concave polygon with a hole', function(done){ | ||
t.load('../test/testIn/concaveInHole.geojson', function(err, points){ | ||
t.concave(points, 1, function(err, concaved){ | ||
if(err) throw err | ||
concaved.should.be.ok | ||
concaved.features[0].geometry.type.should.equal('Polygon') | ||
concaved.features[0].geometry.coordinates.should.be.ok | ||
fs.writeFileSync(__dirname + '/testOut/concave2.geojson',JSON.stringify(concaved)) | ||
fs.writeFileSync('./testOut/concave.geojson', JSON.stringify(hull)) | ||
hull.should.be.ok | ||
done() | ||
@@ -28,0 +16,0 @@ }) |
@@ -10,6 +10,4 @@ var t = require('../index'), | ||
if(err) throw err | ||
fs.writeFileSync('./testOut/convex.geojson', JSON.stringify(hull)) | ||
//fs.writeFileSync('./testOut/convex.geojson', JSON.stringify(hull)) | ||
hull.should.be.ok | ||
console.log(hull) | ||
//hull.features.should.be.ok | ||
done() | ||
@@ -19,2 +17,12 @@ }) | ||
}) | ||
it('should take a set of points and return a convex hull polygon', function(done){ | ||
t.load('../test/testIn/convexIn2.geojson', function(err, points){ | ||
t.convex(points, function(err, hull){ | ||
if(err) throw err | ||
//fs.writeFileSync('./testOut/convex2.geojson', JSON.stringify(hull)) | ||
hull.should.be.ok | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
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
3087408
223
1125
22795
42