turf
a node.js library for performing geospatial operations with geojson
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.
npm install turf
Turf can also be run in a browser. To use it, download the minified file, and include it in a script tag.
<script src="turf.min.js"></script>
It can also be installed using bower:
bower install turf
note: This module is under active development and is in a pre-release form. The first official release is planned mid November 2013. Most features are pretty stable, but expect some changes periodically up until then.
Features
- load
- save
- point
- linestring
- polygon
- featurecollection
- extent
- square
- size
- center
- bboxPolygon
- envelope
- centroid
- explode
- combine
- remove
- filter
- distance
- buffer
- nearest
- tin
- grid
- planepoint
- inside
- midpoint
- quantile
- jenks
- reclass
- contour
- sample
- tag
- bezier
Planned Features
Additional feature requests welcomed and encouraged. To request a feature, please add a github issue with a description.
- sum
- average
- aggregate
- krige
- interval
- cluster
- interpolate
- area
- intersect
- union
- erase
- smooth
- simplify
Examples:
load
Loads a Feature or FeaturCollection from a file.
var t = require('turf')
geojsonFile = '/path/to/file/example.geojson'
t.load(geoJsonFile, function(trees, err){
if(err) throw err
console.log(trees)
})
save
Saves out a feature or feature collection. 'geojson' and 'topojson' are currently supported.
var path = './testOut/poly.geojson'
var poly = t.polygon([[[0,0], [1,0], [1,1],[0,1]]])
var type = 'geojson'
t.save(path, poly, type, function(err, res){
if(err) throw err
console.log(res)
done()
})
point
Creates a geojson point Feature based on an x and a y coordinate. Properties can be added optionally.
var t = require('turf')
var point1 = t.point(-75.343, 39.984)
var point2 = t.point(-75.343, 39.984, {name: 'point 1', population: 5000})
console.log(point1)
console.log(point2)
linestring
Creates a geojson linestring Feature based on a coordinate array. Properties can be added optionally.
var t = require('turf')
var linestring1 = t.linestring([[102.0, -10.0], [103.0, 1.0], [104.0, 0.0], [130.0, 4.0]])
var linestring2 = t.linestring([[102.0, -10.0], [103.0, 1.0], [104.0, 0.0], [130.0, 4.0]],
{name: 'line 1', distance: 145})
console.log(linestring1)
console.log(linestring2)
polygon
Creates a geojson polygon Feature based on a coordinate array. Properties can be added optionally.
var t = require('turf')
var polygon1 = t.point([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]])
var polygon2 = t.point([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]],
{name: 'line 1', distance: 145})
console.log(polygon1)
console.log(polygon2)
featurecollection
Creates a geojson FeatureCollection based on an array of features.
var t = require('turf')
var pt1 = t.point(-75.343, 39.984, {name: 'Location A'})
var pt2 = t.point(-75.833, 39.284, {name: 'Location B'})
var pt3 = t.point(-75.534, 39.123, {name: 'Location C'})
var fc = t.featurecollection([pt1, pt2, pt3])
console.log(fc)
extent
Calculates the extent of all features and returns a bounding box.
var t = require('turf')
t.load('path/to/file/example.geojson', function(err, features){
if(err) throw err
t.extent(features, function(extent){
console.log(extent)
})
})
square
Calculates the minimum square bounding box for another bounding box.
var t = require('turf')
var bbox = [0,0,5,10]]
t.square(bbox, function(err, square){
if(err) throw err
console.log(square)
})
size
Takes a bbox and returns a new bbox with a size expanded or contracted by a factor of X.
var bbox = [0, 0, 10, 10]
t.size(bbox, 2, function(err, doubled){
if(err) throw err
console.log(doubled)
})
center
Calculates the absolute center point of all features.
var t = require('turf')
t.load('path/to/file/example.geojson', function(layer, err){
if(err) throw err
t.center(layer, function(center){
console.log(center)
})
})
bboxPolygon
Takes a bbox and returns the equivalent polygon feature.
var t = require('turf')
var bbox = [0,0,10,10]
t.bboxPolygon(bbox, function(err, poly){
if(err) throw err
console.log(poly)
})
envelope
Takes a Feature or FeatureCollection and returns a rectangular polygon feature that encompasses all vertices.
var t = require('turf')
var pt1 = t.point(-75.343, 39.984, {name: 'Location A'})
var pt2 = t.point(-75.833, 39.284, {name: 'Location B'})
var pt3 = t.point(-75.534, 39.123, {name: 'Location C'})
var fc = t.featurecollection([pt1, pt2, pt3])
t.envelope(fc, function(err, envelopePoly){
if(err) throw err
console.log(envelopePoly)
})
centroid
Calculates the centroid of a polygon Feature or FeatureCollection using the geometric mean of all vertices. This lessons the effect of small islands and artifacts when calculating the centroid of a set of polygons.
var t = require('turf')
var poly = t.polygon([[[0,0], [0,10], [10,10] , [10,0]]])
t.centroid(poly, function(err, centroid){
if(err) throw err
console.log(centroid)
})
explode
Takes a Feature or FeatureCollection and return all vertices as a collection of points.
var t = require('turf')
var poly = t.polygon([[[0,0], [0,10], [10,10] , [10,0]]])
t.explode(poly, function(err, vertices){
if(err) throw err
console.log(vertices)
})
combine
Combines feature collection of point, linestring, or polygon features into multipoint, multilinestring, or multipolygon features.
var t = require('turf')
var pt1 = t.point(50, 1)
var pt2 = t.point(100, 101)
var fc = t.featurecollection([pt1, pt2])
t.combine(fc, function(err, combined){
if(err) throw err
console.log(combined)
})
remove
Removes any features from a feature collection that match a property value.
var t = require('turf')
var trees = t.featurecollection([t.point(1,2, {species: 'oak'}),
t.point(2,1, {species: 'dogwood'}),
t.point(3,1, {species: 'maple'})])
t.remove(points, 'species', 'dogwood', function(err, result) {
if(err) throw err
console.log(result)
})
filter
Keeps any features from a feature collection that match a property value.
var t = require('turf')
var trees = t.featurecollection([
t.point(1,2, {species: 'oak'}),
t.point(2,1, {species: 'birch'}),
t.point(3,1, {species: 'oak'}),
t.point(2,2, {species: 'redwood'}),
t.point(2,3, {species: 'maple'}),
t.point(4,2, {species: 'oak'})
])
t.filter(trees, 'species', 'oak', function(err, oaks){
if(err) throw err
console.log(oaks)
})
inside
Checks to see if a point is inside of a polygon. The polygon can be convex or concave.
var t = require('turf')
var poly = t.polygon([[[0,0], [50, 50], [0,100], [100,100], [100,0]]])
var pt = t.point(75, 75)
t.inside(pt, poly, function(err, isInside){
if(err) throw err
console.log(isInside)
})
buffer
Buffers a point feature to a given radius. Lines and Polygons support coming soon. Unit selection coming soon too (degrees, miles, km).
var t = require('turf')
var pt = t.point(0, 0.5)
var unit = 'miles'
t.buffer(pt, 10, unit, function(err, buffered){
if(err) throw err
console.log(buffered)
})
distance
Calculates the distance between two point features in degrees, radians, miles, or kilometers. This uses the haversine formula to account for global curvature.
var t = require('turf')
var point1 = t.point(-75.343, 39.984)
var point2 = t.point(-75.534, 39.123)
var unit = 'miles'
t.distance(point1, point2, unit, function(err, distance){
if(err) throw err
console.log(distance)
})
nearest
Returns the nearest point feature.
var t = require('turf')
var inPoint = t.point(-75.4, 39.4, {name: 'Location A'})
var pt1 = t.point(-75.343, 39.984, {name: 'Location B'})
var pt2 = t.point(-75.833, 39.284, {name: 'Location C'})
var pt3 = t.point(-75.534, 39.123, {name: 'Location D'})
var inFeatures = t.featurecollection([pt1, pt2, pt3])
t.nearest(inPoint, inFeatures, function(err, closestPoint){
if(err) throw err
console.log(closestPoint)
})
tin
Takes a set of points and the name of a z-value property and creates a tin (Triangulated Irregular Network). These are often used for developing elevation contour maps or stepped heat visualizations.
var t = require('turf')
var z = 'elevation'
t.load('/path/to/pointsfeatures/elevationPoints.geojson', function(err, points){
t.tin(points, z, function(err, tin){
if(err) throw err
console.log(tin)
})
})
grid
Takes a bounding box and a cell depth and outputs a feature collection of points in a grid.
var t = require('turf')
var depth = 15
t.grid([0,0,10,10], depth, function(err, grid){
console.log(grid)
})
planepoint
Takes a trianglular plane and calculates the z value for a point on the plane.
var t = require('turf')
var point = t.point(-75.3221, 39.529)
var triangle = t.polygon(
[[[-75.1221,39.57],[-75.58,39.18],[-75.97,39.86]]],
"properties": {"a": 11, "b": 122, "c": 44}
)
t.planepoint(point, triangle, function(err, zValue){
if(err) throw err
console.log(zValue)
})
midpoint
Takes two point features and returns the mid point.
var t = require('turf')
var pt1 = t.point(0,0)
var pt2 = t.point(10, 0)
t.midpoint(pt1, pt2, function(err, midpoint){
if(err) throw err
console.log(midpoint)
})
quantile
Takes a set of features, a property name, and a set of percentiles and outputs a quantile array. This can be passed as a break array to the contour function.
var t = require('turf')
var propertyName = 'elevation'
var percentiles = [10,30,40,60,80,90,99]
t.load('./testIn/Points3.geojson', function(err, pts){
if(err) throw err
t.quantile(pts, propertyName, percentiles, function(err, quantiles){
if(err) throw err
console.log(quantiles)
})
})
jenks
Takes a set of features, a property name, and the desired number of breaks and outputs an array of natural breaks. This classification can be used in the contour function or for theming.
var t = require('turf')
var propertyName = 'elevation'
var num = 10
t.load('./testIn/Points3.geojson', function(err, pts){
if(err) throw err
t.jenks(pts, 'elevation', num, function(err, breaks){
if(err) throw err
done()
})
})
reclass
Takes a feature collection, a in field, an out field, and an array of translations and outputs an identical feature collection with the out field property populated.
var t = require('turf')
var inField = 'elevation',
outField = 'heightIndex',
translations = [[0, 20, 1], [20, 40, 2], [40, 60 , 3], [60, Infinity, 4]]
t.load('./testIn/Points3.geojson', function(err, pts){
if(err) throw err
t.reclass(pts, inField, outField, translations, function(err, outPts){
if(err) throw err
console.log(outPts)
})
})
contour
Takes a FeatureCollection of points with z values and an array of value breaks and generates contour polygons. This is a great way to visualize interpolated density on a map. It is often used for elevation maps, weather maps, and isocrones. The main advantage over a heat map is that contours allow you to see definitive value boundaries, and the polygons can be used to aggregate data. For example, you could get the 5000 ft elevation contour of a mountain and the 10000 ft elevation contour, then aggregate the number of trees in each to see how elevation affects tree survival.
var t = require('turf')
var z = 'elevation'
var resolution = 15
var breaks = [.1, 22, 45, 55, 65, 85, 95, 105, 120, 180]
t.load('../path/to/points.geojson', function(err, points){
t.contour(points, z, resolution, breaks, function(err, contours){
if(err) throw err
console.log(contours)
})
})
sample
Takes a feature collection and returns N random features as a feature collection.
var t = require('turf')
var num = 10
t.load('./testIn/Points3.geojson', function(err, pts){
if(err) throw err
t.sample(pts, num, function(err, outPts){
if(err) throw err
console.log(outPts)
})
})
tag
Performs a spatial join on a set of points from a set of polygons.
var t = require('turf')
t.load('./testIn/tagPoints.geojson', function(err, points){
t.load('./testIn/tagPolygons.geojson', function(err, polygons){
t.tag(points, polygons, 'polyID', function(err, taggedPoints){
console.log(taggedPoints)
})
})
})
bezier
Takes a linestring and outputs a curved version of the line.
var t = require('turf')
var resolution = 5000
var intensity = .85
var lineIn = t.linestring([
[
-80.08724212646484,
32.77428536643231
],
[
-80.03746032714844,
32.84007757059952
],
[
-80.01548767089844,
32.74512501406368
],
[
-79.95368957519531,
32.850461360442424
]
])
t.bezier(lineIn, 5000, .85, function(err, lineOut){
if(err) throw err
console.log(lineOut)
})
Development
Run Tests
cd test
mocha .
Build
sh build
Want to Contribute?
Pull requests, feature requests, comments on issues, testing, documentation, or any other type of support is welcome and encouraged. This is a big project, and I appreciate any help I can get. Let's go build a better geospatial engine for the web! Not sure where to start? Shoot me an email at morgan.herlocker [at] gmail.com or @morganherlocker.
A few notes before diving in:
- The focus of the project is on building a core geospatial engine. Vendor specific stuff belongs in a seperate module.
- Geojson is the primary format. Topojson can be used as intermediate format.
- No pull requests will be accepted that provide only style changes.
- Never add an external dependency unless you absolutely have to. Even then, please ask first, because there may be a work around.
- Do not make calls to web services.
- Simplicity is the name of the game. Every feature should be a simple file in /lib with a corresponding file in /test. Reference the new module in index.js and you are done.
- Testing is absolutely required 100% of the time. Look at the existing tests for examples.
- This is a functional library. Ensure that your functions never have side effects, and avoid an OO style whenever possible.
- Always create an issue before starting a new feature. This will allow us to discuss how something is being implemented and integrated. Turned down pull requests are no fun for anyone.
This library is built and maintained by @morganherlocker :)