├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bench.js ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json ├── test.js ├── test ├── in │ ├── linestrings.geojson │ ├── points.geojson │ └── polygons.geojson └── out │ ├── all.linestrings.geojson │ ├── all.points.geojson │ ├── all.polygons.geojson │ ├── search.linestrings.geojson │ ├── search.points.geojson │ └── search.polygons.geojson ├── types.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | main.js 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | types.js 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional REPL history 40 | .node_repl_history -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - node -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | ## 3.1.0 - 2018-02-05 5 | 6 | - Improve coverage testing to 100% 7 | - Support Array of Features to `.load()` method 8 | - Allow strict Typing when defining Tree (ex: `const tree = rbush()`) 9 | - Update documentation 10 | - Add `equals` param to `.remove()` method 11 | - Drop BBox from search/remove methods 12 | - Add support for 6 position BBox 13 | 14 | ## 3.0.0 - 2018-02-04 15 | 16 | - Update Typescript definition 17 | - Drop ES Modules in favor of Typescript 18 | 19 | ## 2.2.0 - 2017-11-22 20 | 21 | - Clean up Rollup build 22 | 23 | ## 2.1.0 - 2017-10-16 24 | 25 | - Added Rollup to build CommonJS (`main.js`) 26 | 27 | ## 2.0.4 - 2017-10-10 28 | 29 | - ~Drop Rollup~ 30 | - Update Typescript definition 31 | 32 | ## 2.0.0 - 2017-10-01 33 | 34 | - Support ES modules 35 | 36 | ## 1.1.1 - 2017-07-15 37 | 38 | - Replaced `const` with `var` for pure ES5 compatibility 39 | 40 | ## 1.1.0 - 2017-06-01 41 | 42 | - Add `bbox` support as valid input 43 | - Drop rollup build (ES5 npm package is plenty) 44 | 45 | ## 1.0.0 - 2017-03-20 46 | 47 | - Initialize GeoJSON RBush from https://github.com/Turfjs/turf/pull/609 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Denis Carriere 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoJSON RBush 2 | 3 | [![Build Status](https://travis-ci.org/DenisCarriere/geojson-rbush.svg?branch=master)](https://travis-ci.org/DenisCarriere/geojson-rbush) 4 | [![npm version](https://badge.fury.io/js/geojson-rbush.svg)](https://badge.fury.io/js/geojson-rbush) 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/DenisCarriere/geojson-rbush/master/LICENSE) 6 | 7 | GeoJSON implementation of [RBush](https://github.com/mourner/rbush) — a high-performance JavaScript R-tree-based 2D spatial index for points and rectangles. 8 | 9 | ## Install 10 | 11 | **npm** 12 | 13 | ```bash 14 | $ npm install --save geojson-rbush 15 | ``` 16 | 17 | ## API 18 | 19 | 20 | 21 | #### Table of Contents 22 | 23 | - [rbush](#rbush) 24 | - [insert](#insert) 25 | - [load](#load) 26 | - [remove](#remove) 27 | - [clear](#clear) 28 | - [search](#search) 29 | - [collides](#collides) 30 | - [all](#all) 31 | - [toJSON](#tojson) 32 | - [fromJSON](#fromjson) 33 | 34 | ### rbush 35 | 36 | GeoJSON implementation of [RBush](https://github.com/mourner/rbush#rbush) spatial index. 37 | 38 | **Parameters** 39 | 40 | - `maxEntries` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** defines the maximum number of entries in a tree node. 9 (used by default) is a 41 | reasonable choice for most applications. Higher value means faster insertion and slower search, and vice versa. (optional, default `9`) 42 | 43 | **Examples** 44 | 45 | ```javascript 46 | var geojsonRbush = require('geojson-rbush').default; 47 | var tree = geojsonRbush(); 48 | ``` 49 | 50 | Returns **RBush** GeoJSON RBush 51 | 52 | ### insert 53 | 54 | [insert](https://github.com/mourner/rbush#data-format) 55 | 56 | **Parameters** 57 | 58 | - `feature` **Feature** insert single GeoJSON Feature 59 | 60 | **Examples** 61 | 62 | ```javascript 63 | var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 64 | tree.insert(poly) 65 | ``` 66 | 67 | Returns **RBush** GeoJSON RBush 68 | 69 | ### load 70 | 71 | [load](https://github.com/mourner/rbush#bulk-inserting-data) 72 | 73 | **Parameters** 74 | 75 | - `features` **(FeatureCollection | [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<Feature>)** load entire GeoJSON FeatureCollection 76 | 77 | **Examples** 78 | 79 | ```javascript 80 | var polys = turf.polygons([ 81 | [[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]], 82 | [[[-93, 32], [-83, 32], [-83, 39], [-93, 39], [-93, 32]]] 83 | ]); 84 | tree.load(polys); 85 | ``` 86 | 87 | Returns **RBush** GeoJSON RBush 88 | 89 | ### remove 90 | 91 | [remove](https://github.com/mourner/rbush#removing-data) 92 | 93 | **Parameters** 94 | 95 | - `feature` **Feature** remove single GeoJSON Feature 96 | - `equals` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Pass a custom equals function to compare by value for removal. 97 | 98 | **Examples** 99 | 100 | ```javascript 101 | var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 102 | 103 | tree.remove(poly); 104 | ``` 105 | 106 | Returns **RBush** GeoJSON RBush 107 | 108 | ### clear 109 | 110 | [clear](https://github.com/mourner/rbush#removing-data) 111 | 112 | **Examples** 113 | 114 | ```javascript 115 | tree.clear() 116 | ``` 117 | 118 | Returns **RBush** GeoJSON Rbush 119 | 120 | ### search 121 | 122 | [search](https://github.com/mourner/rbush#search) 123 | 124 | **Parameters** 125 | 126 | - `geojson` **(BBox | FeatureCollection | Feature)** search with GeoJSON 127 | 128 | **Examples** 129 | 130 | ```javascript 131 | var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 132 | 133 | tree.search(poly); 134 | ``` 135 | 136 | Returns **FeatureCollection** all features that intersects with the given GeoJSON. 137 | 138 | ### collides 139 | 140 | [collides](https://github.com/mourner/rbush#collisions) 141 | 142 | **Parameters** 143 | 144 | - `geojson` **(BBox | FeatureCollection | Feature)** collides with GeoJSON 145 | 146 | **Examples** 147 | 148 | ```javascript 149 | var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 150 | 151 | tree.collides(poly); 152 | ``` 153 | 154 | Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** true if there are any items intersecting the given GeoJSON, otherwise false. 155 | 156 | ### all 157 | 158 | [all](https://github.com/mourner/rbush#search) 159 | 160 | **Examples** 161 | 162 | ```javascript 163 | tree.all() 164 | ``` 165 | 166 | Returns **FeatureCollection** all the features in RBush 167 | 168 | ### toJSON 169 | 170 | [toJSON](https://github.com/mourner/rbush#export-and-import) 171 | 172 | **Examples** 173 | 174 | ```javascript 175 | var exported = tree.toJSON() 176 | ``` 177 | 178 | Returns **any** export data as JSON object 179 | 180 | ### fromJSON 181 | 182 | [fromJSON](https://github.com/mourner/rbush#export-and-import) 183 | 184 | **Parameters** 185 | 186 | - `json` **any** import previously exported data 187 | 188 | **Examples** 189 | 190 | ```javascript 191 | var exported = { 192 | "children": [ 193 | { 194 | "type": "Feature", 195 | "geometry": { 196 | "type": "Point", 197 | "coordinates": [110, 50] 198 | }, 199 | "properties": {}, 200 | "bbox": [110, 50, 110, 50] 201 | } 202 | ], 203 | "height": 1, 204 | "leaf": true, 205 | "minX": 110, 206 | "minY": 50, 207 | "maxX": 110, 208 | "maxY": 50 209 | } 210 | tree.fromJSON(exported) 211 | ``` 212 | 213 | Returns **RBush** GeoJSON RBush 214 | -------------------------------------------------------------------------------- /bench.js: -------------------------------------------------------------------------------- 1 | const Benchmark = require('benchmark'); 2 | const {randomPoint, randomPolygon} = require('@turf/random'); 3 | const geojsonRbush = require('./').default; 4 | 5 | // Fixtures 6 | const points = randomPoint(3); 7 | const point = points.features[0]; 8 | const polygons = randomPolygon(3); 9 | const polygon = polygons.features[0]; 10 | 11 | // Load trees before (used to benchmark search) 12 | const pointsTree = geojsonRbush(); 13 | pointsTree.load(points); 14 | const polygonsTree = geojsonRbush(); 15 | polygonsTree.load(polygons); 16 | 17 | /** 18 | * Benchmark Results 19 | * 20 | * rbush.points x 313,979 ops/sec ±10.60% (67 runs sampled) 21 | * rbush.polygons x 428,333 ops/sec ±1.69% (70 runs sampled) 22 | * search.points x 5,986,675 ops/sec ±7.95% (77 runs sampled) 23 | * search.polygons x 6,481,248 ops/sec ±0.93% (90 runs sampled) 24 | */ 25 | new Benchmark.Suite('geojson-rbush') 26 | .add('rbush.points', () => { 27 | const tree = geojsonRbush(); 28 | tree.load(points); 29 | }) 30 | .add('rbush.polygons', () => { 31 | const tree = geojsonRbush(); 32 | tree.load(polygons); 33 | }) 34 | .add('search.points', () => pointsTree.search(point)) 35 | .add('search.polygons', () => polygonsTree.search(polygon)) 36 | .on('cycle', e => console.log(String(e.target))) 37 | .on('complete', () => {}) 38 | .run(); 39 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { BBox, Feature, FeatureCollection, Geometry, GeoJsonProperties } from 'geojson' 2 | 3 | declare class RBush { 4 | insert(feature: Feature): RBush; 5 | load(features: FeatureCollection | Feature[]): RBush; 6 | remove(feature: Feature, equals?: (a: Feature, b: Feature) => boolean): RBush; 7 | clear(): RBush; 8 | search(geojson: Feature | FeatureCollection | BBox): FeatureCollection; 9 | all(): FeatureCollection; 10 | collides(geosjon: Feature | FeatureCollection | BBox): boolean; 11 | toJSON(): any; 12 | fromJSON(data: any): RBush; 13 | } 14 | 15 | /** 16 | * https://github.com/mourner/rbush 17 | */ 18 | export default function rbush(maxEntries?: number): RBush; 19 | 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var rbush = require('rbush'); 2 | var helpers = require('@turf/helpers'); 3 | var meta = require('@turf/meta'); 4 | var turfBBox = require('@turf/bbox').default; 5 | var featureEach = meta.featureEach; 6 | var coordEach = meta.coordEach; 7 | var polygon = helpers.polygon; 8 | var featureCollection = helpers.featureCollection; 9 | 10 | /** 11 | * GeoJSON implementation of [RBush](https://github.com/mourner/rbush#rbush) spatial index. 12 | * 13 | * @name rbush 14 | * @param {number} [maxEntries=9] defines the maximum number of entries in a tree node. 9 (used by default) is a 15 | * reasonable choice for most applications. Higher value means faster insertion and slower search, and vice versa. 16 | * @returns {RBush} GeoJSON RBush 17 | * @example 18 | * var geojsonRbush = require('geojson-rbush').default; 19 | * var tree = geojsonRbush(); 20 | */ 21 | function geojsonRbush(maxEntries) { 22 | var tree = new rbush(maxEntries); 23 | /** 24 | * [insert](https://github.com/mourner/rbush#data-format) 25 | * 26 | * @param {Feature} feature insert single GeoJSON Feature 27 | * @returns {RBush} GeoJSON RBush 28 | * @example 29 | * var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 30 | * tree.insert(poly) 31 | */ 32 | tree.insert = function (feature) { 33 | if (feature.type !== 'Feature') throw new Error('invalid feature'); 34 | feature.bbox = feature.bbox ? feature.bbox : turfBBox(feature); 35 | return rbush.prototype.insert.call(this, feature); 36 | }; 37 | 38 | /** 39 | * [load](https://github.com/mourner/rbush#bulk-inserting-data) 40 | * 41 | * @param {FeatureCollection|Array} features load entire GeoJSON FeatureCollection 42 | * @returns {RBush} GeoJSON RBush 43 | * @example 44 | * var polys = turf.polygons([ 45 | * [[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]], 46 | * [[[-93, 32], [-83, 32], [-83, 39], [-93, 39], [-93, 32]]] 47 | * ]); 48 | * tree.load(polys); 49 | */ 50 | tree.load = function (features) { 51 | var load = []; 52 | // Load an Array of Features 53 | if (Array.isArray(features)) { 54 | features.forEach(function (feature) { 55 | if (feature.type !== 'Feature') throw new Error('invalid features'); 56 | feature.bbox = feature.bbox ? feature.bbox : turfBBox(feature); 57 | load.push(feature); 58 | }); 59 | } else { 60 | // Load a FeatureCollection 61 | featureEach(features, function (feature) { 62 | if (feature.type !== 'Feature') throw new Error('invalid features'); 63 | feature.bbox = feature.bbox ? feature.bbox : turfBBox(feature); 64 | load.push(feature); 65 | }); 66 | } 67 | return rbush.prototype.load.call(this, load); 68 | }; 69 | 70 | /** 71 | * [remove](https://github.com/mourner/rbush#removing-data) 72 | * 73 | * @param {Feature} feature remove single GeoJSON Feature 74 | * @param {Function} equals Pass a custom equals function to compare by value for removal. 75 | * @returns {RBush} GeoJSON RBush 76 | * @example 77 | * var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 78 | * 79 | * tree.remove(poly); 80 | */ 81 | tree.remove = function (feature, equals) { 82 | if (feature.type !== 'Feature') throw new Error('invalid feature'); 83 | feature.bbox = feature.bbox ? feature.bbox : turfBBox(feature); 84 | return rbush.prototype.remove.call(this, feature, equals); 85 | }; 86 | 87 | /** 88 | * [clear](https://github.com/mourner/rbush#removing-data) 89 | * 90 | * @returns {RBush} GeoJSON Rbush 91 | * @example 92 | * tree.clear() 93 | */ 94 | tree.clear = function () { 95 | return rbush.prototype.clear.call(this); 96 | }; 97 | 98 | /** 99 | * [search](https://github.com/mourner/rbush#search) 100 | * 101 | * @param {BBox|FeatureCollection|Feature} geojson search with GeoJSON 102 | * @returns {FeatureCollection} all features that intersects with the given GeoJSON. 103 | * @example 104 | * var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 105 | * 106 | * tree.search(poly); 107 | */ 108 | tree.search = function (geojson) { 109 | var features = rbush.prototype.search.call(this, this.toBBox(geojson)); 110 | return featureCollection(features); 111 | }; 112 | 113 | /** 114 | * [collides](https://github.com/mourner/rbush#collisions) 115 | * 116 | * @param {BBox|FeatureCollection|Feature} geojson collides with GeoJSON 117 | * @returns {boolean} true if there are any items intersecting the given GeoJSON, otherwise false. 118 | * @example 119 | * var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]); 120 | * 121 | * tree.collides(poly); 122 | */ 123 | tree.collides = function (geojson) { 124 | return rbush.prototype.collides.call(this, this.toBBox(geojson)); 125 | }; 126 | 127 | /** 128 | * [all](https://github.com/mourner/rbush#search) 129 | * 130 | * @returns {FeatureCollection} all the features in RBush 131 | * @example 132 | * tree.all() 133 | */ 134 | tree.all = function () { 135 | var features = rbush.prototype.all.call(this); 136 | return featureCollection(features); 137 | }; 138 | 139 | /** 140 | * [toJSON](https://github.com/mourner/rbush#export-and-import) 141 | * 142 | * @returns {any} export data as JSON object 143 | * @example 144 | * var exported = tree.toJSON() 145 | */ 146 | tree.toJSON = function () { 147 | return rbush.prototype.toJSON.call(this); 148 | }; 149 | 150 | /** 151 | * [fromJSON](https://github.com/mourner/rbush#export-and-import) 152 | * 153 | * @param {any} json import previously exported data 154 | * @returns {RBush} GeoJSON RBush 155 | * @example 156 | * var exported = { 157 | * "children": [ 158 | * { 159 | * "type": "Feature", 160 | * "geometry": { 161 | * "type": "Point", 162 | * "coordinates": [110, 50] 163 | * }, 164 | * "properties": {}, 165 | * "bbox": [110, 50, 110, 50] 166 | * } 167 | * ], 168 | * "height": 1, 169 | * "leaf": true, 170 | * "minX": 110, 171 | * "minY": 50, 172 | * "maxX": 110, 173 | * "maxY": 50 174 | * } 175 | * tree.fromJSON(exported) 176 | */ 177 | tree.fromJSON = function (json) { 178 | return rbush.prototype.fromJSON.call(this, json); 179 | }; 180 | 181 | /** 182 | * Converts GeoJSON to {minX, minY, maxX, maxY} schema 183 | * 184 | * @private 185 | * @param {BBox|FeatureCollection|Feature} geojson feature(s) to retrieve BBox from 186 | * @returns {Object} converted to {minX, minY, maxX, maxY} 187 | */ 188 | tree.toBBox = function (geojson) { 189 | var bbox; 190 | if (geojson.bbox) bbox = geojson.bbox; 191 | else if (Array.isArray(geojson) && geojson.length === 4) bbox = geojson; 192 | else if (Array.isArray(geojson) && geojson.length === 6) bbox = [geojson[0], geojson[1], geojson[3], geojson[4]]; 193 | else if (geojson.type === 'Feature') bbox = turfBBox(geojson); 194 | else if (geojson.type === 'FeatureCollection') bbox = turfBBox(geojson); 195 | else throw new Error('invalid geojson') 196 | 197 | return { 198 | minX: bbox[0], 199 | minY: bbox[1], 200 | maxX: bbox[2], 201 | maxY: bbox[3] 202 | }; 203 | }; 204 | return tree; 205 | } 206 | 207 | module.exports = geojsonRbush; 208 | module.exports.default = geojsonRbush; 209 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geojson-rbush", 3 | "version": "3.2.0", 4 | "description": "GeoJSON implementation of RBush", 5 | "main": "index", 6 | "types": "index.d.ts", 7 | "files": [ 8 | "index.js", 9 | "index.d.ts" 10 | ], 11 | "scripts": { 12 | "pretest": "tsc types.ts", 13 | "test": "node test.js --coverage", 14 | "bench": "node bench.js", 15 | "docs": "documentation readme index.js --section=API" 16 | }, 17 | "keywords": [ 18 | "geojson", 19 | "index", 20 | "tree", 21 | "spatial", 22 | "rbush" 23 | ], 24 | "author": "Denis Carriere <@DenisCarriere>", 25 | "contributors": [ 26 | "Vladimir Agafonkin <@mourner>", 27 | "Denis Carriere <@DenisCarriere>", 28 | "Jordan Rousseau <@jvrousseau>" 29 | ], 30 | "license": "MIT", 31 | "devDependencies": { 32 | "@turf/bbox-polygon": "*", 33 | "@turf/random": "*", 34 | "@types/node": "*", 35 | "benchmark": "*", 36 | "documentation": "*", 37 | "load-json-file": "*", 38 | "tap": "*", 39 | "tape": "*", 40 | "typescript": "*", 41 | "write-json-file": "*" 42 | }, 43 | "dependencies": { 44 | "@turf/bbox": "*", 45 | "@turf/helpers": "6.x", 46 | "@turf/meta": "6.x", 47 | "@types/geojson": "7946.0.8", 48 | "rbush": "^3.0.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const test = require('tape'); 3 | const path = require('path'); 4 | const load = require('load-json-file'); 5 | const write = require('write-json-file'); 6 | const bboxPolygon = require('@turf/bbox-polygon').default; 7 | const { polygon, featureCollection, polygons } = require('@turf/helpers'); 8 | const geojsonRbush = require('./').default; 9 | 10 | const directories = { 11 | in: path.join(__dirname, 'test', 'in') + path.sep, 12 | out: path.join(__dirname, 'test', 'out') + path.sep 13 | }; 14 | 15 | const fixtures = fs.readdirSync(directories.in).map(filename => { 16 | return { 17 | filename, 18 | name: path.parse(filename).name, 19 | geojson: load.sync(directories.in + filename) 20 | }; 21 | }); 22 | 23 | test('geojson-rbush', t => { 24 | for (const fixture of fixtures) { 25 | const name = fixture.name; 26 | const filename = fixture.filename; 27 | const geojson = fixture.geojson; 28 | const tree = geojsonRbush(); 29 | tree.load(geojson); 30 | 31 | // Retrive all features inside the RBush index 32 | const all = tree.all(); 33 | 34 | // Search using the first item in the FeatureCollection 35 | const search = tree.search(geojson.features[0]); 36 | 37 | if (process.env.REGEN) { 38 | write.sync(directories.out + 'all.' + filename, all); 39 | write.sync(directories.out + 'search.' + filename, search); 40 | } 41 | 42 | t.deepEqual(all, load.sync(directories.out + 'all.' + filename), 'all.' + name); 43 | t.deepEqual(search, load.sync(directories.out + 'search.' + filename), 'search.' + name); 44 | } 45 | t.end(); 46 | }); 47 | 48 | test('geojson-rbush -- bbox', t => { 49 | const tree = geojsonRbush(); 50 | tree.insert(bboxPolygon([-150, -60, 150, 60])); 51 | 52 | t.equal(tree.collides([-140, -50, 140, 50]), true); 53 | t.equal(tree.search([-140, -50, 140, 50]).features.length, 1); 54 | t.equal(tree.search(bboxPolygon([-150, -60, 150, 60])).features.length, 1); 55 | t.equal(tree.search(featureCollection([bboxPolygon([-150, -60, 150, 60])])).features.length, 1); 56 | t.equal(tree.collides([-180, -80, -170, -60]), false); 57 | 58 | // Errors 59 | t.throws(() => tree.search('foo')); 60 | t.end(); 61 | }); 62 | 63 | test('geojson-rbush -- fromJSON', t => { 64 | const tree = geojsonRbush(); 65 | const poly = bboxPolygon([-150, -60, 150, 60]) 66 | tree.insert(poly); 67 | 68 | const newTree = geojsonRbush() 69 | newTree.fromJSON(tree.toJSON()) 70 | t.equal(newTree.all().features.length, 1) 71 | newTree.remove(poly) 72 | t.equal(newTree.all().features.length, 0) 73 | t.end(); 74 | }); 75 | 76 | 77 | 78 | test('geojson-rbush -- Array of Features -- Issue #5', t => { 79 | // https://github.com/DenisCarriere/geojson-rbush/issues/5 80 | const tree = geojsonRbush(); 81 | const polys = polygons([ 82 | [[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]], 83 | [[[-93, 32], [-83, 32], [-83, 39], [-93, 39], [-93, 32]]] 84 | ]); 85 | // Load Feature Collection 86 | tree.load(polys); 87 | t.equal(tree.all().features.length, 2); 88 | 89 | // Load Array of Features 90 | tree.load(polys.features); 91 | t.equal(tree.all().features.length, 4); 92 | t.end(); 93 | }); 94 | -------------------------------------------------------------------------------- /test/in/linestrings.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -121.9921875, 12 | 41.77131167976407 13 | ], 14 | [ 15 | -112.1484375, 16 | 49.15296965617042 17 | ], 18 | [ 19 | -95.2734375, 20 | 49.15296965617042 21 | ] 22 | ] 23 | } 24 | }, 25 | { 26 | "type": "Feature", 27 | "properties": {}, 28 | "geometry": { 29 | "type": "LineString", 30 | "coordinates": [ 31 | [ 32 | -101.953125, 33 | 34.016241889667015 34 | ], 35 | [ 36 | -88.9453125, 37 | 40.17887331434696 38 | ], 39 | [ 40 | -78.046875, 41 | 50.064191736659104 42 | ] 43 | ] 44 | } 45 | }, 46 | { 47 | "type": "Feature", 48 | "properties": {}, 49 | "geometry": { 50 | "type": "LineString", 51 | "coordinates": [ 52 | [ 53 | -108.6328125, 54 | 42.032974332441405 55 | ], 56 | [ 57 | -102.65625, 58 | 39.36827914916014 59 | ], 60 | [ 61 | -95.97656249999999, 62 | 39.36827914916014 63 | ] 64 | ] 65 | } 66 | }, 67 | { 68 | "type": "Feature", 69 | "properties": {}, 70 | "geometry": { 71 | "type": "LineString", 72 | "coordinates": [ 73 | [ 74 | -71.71875, 75 | 39.842286020743394 76 | ], 77 | [ 78 | -69.169921875, 79 | 35.96022296929667 80 | ], 81 | [ 82 | -66.796875, 83 | 31.952162238024975 84 | ] 85 | ] 86 | } 87 | } 88 | ] 89 | } -------------------------------------------------------------------------------- /test/in/points.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -86.8359375, 11 | 42.8115217450979 12 | ] 13 | } 14 | }, 15 | { 16 | "type": "Feature", 17 | "properties": {}, 18 | "geometry": { 19 | "type": "Point", 20 | "coordinates": [ 21 | -67.5, 22 | 48.69096039092549 23 | ] 24 | } 25 | }, 26 | { 27 | "type": "Feature", 28 | "properties": {}, 29 | "geometry": { 30 | "type": "Point", 31 | "coordinates": [ 32 | -106.171875, 33 | 46.800059446787316 34 | ] 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /test/in/polygons.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Polygon", 9 | "coordinates": [ 10 | [ 11 | [ 12 | -78, 13 | 41 14 | ], 15 | [ 16 | -67, 17 | 41 18 | ], 19 | [ 20 | -67, 21 | 48 22 | ], 23 | [ 24 | -78, 25 | 48 26 | ], 27 | [ 28 | -78, 29 | 41 30 | ] 31 | ] 32 | ] 33 | } 34 | }, 35 | { 36 | "type": "Feature", 37 | "properties": {}, 38 | "geometry": { 39 | "type": "Polygon", 40 | "coordinates": [ 41 | [ 42 | [ 43 | -93, 44 | 32 45 | ], 46 | [ 47 | -83, 48 | 32 49 | ], 50 | [ 51 | -83, 52 | 39 53 | ], 54 | [ 55 | -93, 56 | 39 57 | ], 58 | [ 59 | -93, 60 | 32 61 | ] 62 | ] 63 | ] 64 | } 65 | }, 66 | { 67 | "type": "Feature", 68 | "properties": {}, 69 | "geometry": { 70 | "type": "Polygon", 71 | "coordinates": [ 72 | [ 73 | [ 74 | -97.646484375, 75 | 43.197167282501276 76 | ], 77 | [ 78 | -88.681640625, 79 | 43.197167282501276 80 | ], 81 | [ 82 | -88.681640625, 83 | 48.28319289548349 84 | ], 85 | [ 86 | -97.646484375, 87 | 48.28319289548349 88 | ], 89 | [ 90 | -97.646484375, 91 | 43.197167282501276 92 | ] 93 | ] 94 | ] 95 | } 96 | }, 97 | { 98 | "type": "Feature", 99 | "properties": {}, 100 | "geometry": { 101 | "type": "Polygon", 102 | "coordinates": [ 103 | [ 104 | [ 105 | -93.8671875, 106 | 36.87962060502676 107 | ], 108 | [ 109 | -75.89355468749999, 110 | 36.87962060502676 111 | ], 112 | [ 113 | -75.89355468749999, 114 | 45.1510532655634 115 | ], 116 | [ 117 | -93.8671875, 118 | 45.1510532655634 119 | ], 120 | [ 121 | -93.8671875, 122 | 36.87962060502676 123 | ] 124 | ] 125 | ] 126 | } 127 | } 128 | ] 129 | } -------------------------------------------------------------------------------- /test/out/all.linestrings.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -121.9921875, 12 | 41.77131167976407 13 | ], 14 | [ 15 | -112.1484375, 16 | 49.15296965617042 17 | ], 18 | [ 19 | -95.2734375, 20 | 49.15296965617042 21 | ] 22 | ] 23 | }, 24 | "bbox": [ 25 | -121.9921875, 26 | 41.77131167976407, 27 | -95.2734375, 28 | 49.15296965617042 29 | ] 30 | }, 31 | { 32 | "type": "Feature", 33 | "properties": {}, 34 | "geometry": { 35 | "type": "LineString", 36 | "coordinates": [ 37 | [ 38 | -101.953125, 39 | 34.016241889667015 40 | ], 41 | [ 42 | -88.9453125, 43 | 40.17887331434696 44 | ], 45 | [ 46 | -78.046875, 47 | 50.064191736659104 48 | ] 49 | ] 50 | }, 51 | "bbox": [ 52 | -101.953125, 53 | 34.016241889667015, 54 | -78.046875, 55 | 50.064191736659104 56 | ] 57 | }, 58 | { 59 | "type": "Feature", 60 | "properties": {}, 61 | "geometry": { 62 | "type": "LineString", 63 | "coordinates": [ 64 | [ 65 | -108.6328125, 66 | 42.032974332441405 67 | ], 68 | [ 69 | -102.65625, 70 | 39.36827914916014 71 | ], 72 | [ 73 | -95.97656249999999, 74 | 39.36827914916014 75 | ] 76 | ] 77 | }, 78 | "bbox": [ 79 | -108.6328125, 80 | 39.36827914916014, 81 | -95.97656249999999, 82 | 42.032974332441405 83 | ] 84 | }, 85 | { 86 | "type": "Feature", 87 | "properties": {}, 88 | "geometry": { 89 | "type": "LineString", 90 | "coordinates": [ 91 | [ 92 | -71.71875, 93 | 39.842286020743394 94 | ], 95 | [ 96 | -69.169921875, 97 | 35.96022296929667 98 | ], 99 | [ 100 | -66.796875, 101 | 31.952162238024975 102 | ] 103 | ] 104 | }, 105 | "bbox": [ 106 | -71.71875, 107 | 31.952162238024975, 108 | -66.796875, 109 | 39.842286020743394 110 | ] 111 | } 112 | ] 113 | } 114 | -------------------------------------------------------------------------------- /test/out/all.points.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -86.8359375, 11 | 42.8115217450979 12 | ] 13 | }, 14 | "bbox": [ 15 | -86.8359375, 16 | 42.8115217450979, 17 | -86.8359375, 18 | 42.8115217450979 19 | ] 20 | }, 21 | { 22 | "type": "Feature", 23 | "properties": {}, 24 | "geometry": { 25 | "type": "Point", 26 | "coordinates": [ 27 | -67.5, 28 | 48.69096039092549 29 | ] 30 | }, 31 | "bbox": [ 32 | -67.5, 33 | 48.69096039092549, 34 | -67.5, 35 | 48.69096039092549 36 | ] 37 | }, 38 | { 39 | "type": "Feature", 40 | "properties": {}, 41 | "geometry": { 42 | "type": "Point", 43 | "coordinates": [ 44 | -106.171875, 45 | 46.800059446787316 46 | ] 47 | }, 48 | "bbox": [ 49 | -106.171875, 50 | 46.800059446787316, 51 | -106.171875, 52 | 46.800059446787316 53 | ] 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /test/out/all.polygons.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Polygon", 9 | "coordinates": [ 10 | [ 11 | [ 12 | -78, 13 | 41 14 | ], 15 | [ 16 | -67, 17 | 41 18 | ], 19 | [ 20 | -67, 21 | 48 22 | ], 23 | [ 24 | -78, 25 | 48 26 | ], 27 | [ 28 | -78, 29 | 41 30 | ] 31 | ] 32 | ] 33 | }, 34 | "bbox": [ 35 | -78, 36 | 41, 37 | -67, 38 | 48 39 | ] 40 | }, 41 | { 42 | "type": "Feature", 43 | "properties": {}, 44 | "geometry": { 45 | "type": "Polygon", 46 | "coordinates": [ 47 | [ 48 | [ 49 | -93, 50 | 32 51 | ], 52 | [ 53 | -83, 54 | 32 55 | ], 56 | [ 57 | -83, 58 | 39 59 | ], 60 | [ 61 | -93, 62 | 39 63 | ], 64 | [ 65 | -93, 66 | 32 67 | ] 68 | ] 69 | ] 70 | }, 71 | "bbox": [ 72 | -93, 73 | 32, 74 | -83, 75 | 39 76 | ] 77 | }, 78 | { 79 | "type": "Feature", 80 | "properties": {}, 81 | "geometry": { 82 | "type": "Polygon", 83 | "coordinates": [ 84 | [ 85 | [ 86 | -97.646484375, 87 | 43.197167282501276 88 | ], 89 | [ 90 | -88.681640625, 91 | 43.197167282501276 92 | ], 93 | [ 94 | -88.681640625, 95 | 48.28319289548349 96 | ], 97 | [ 98 | -97.646484375, 99 | 48.28319289548349 100 | ], 101 | [ 102 | -97.646484375, 103 | 43.197167282501276 104 | ] 105 | ] 106 | ] 107 | }, 108 | "bbox": [ 109 | -97.646484375, 110 | 43.197167282501276, 111 | -88.681640625, 112 | 48.28319289548349 113 | ] 114 | }, 115 | { 116 | "type": "Feature", 117 | "properties": {}, 118 | "geometry": { 119 | "type": "Polygon", 120 | "coordinates": [ 121 | [ 122 | [ 123 | -93.8671875, 124 | 36.87962060502676 125 | ], 126 | [ 127 | -75.89355468749999, 128 | 36.87962060502676 129 | ], 130 | [ 131 | -75.89355468749999, 132 | 45.1510532655634 133 | ], 134 | [ 135 | -93.8671875, 136 | 45.1510532655634 137 | ], 138 | [ 139 | -93.8671875, 140 | 36.87962060502676 141 | ] 142 | ] 143 | ] 144 | }, 145 | "bbox": [ 146 | -93.8671875, 147 | 36.87962060502676, 148 | -75.89355468749999, 149 | 45.1510532655634 150 | ] 151 | } 152 | ] 153 | } 154 | -------------------------------------------------------------------------------- /test/out/search.linestrings.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -121.9921875, 12 | 41.77131167976407 13 | ], 14 | [ 15 | -112.1484375, 16 | 49.15296965617042 17 | ], 18 | [ 19 | -95.2734375, 20 | 49.15296965617042 21 | ] 22 | ] 23 | }, 24 | "bbox": [ 25 | -121.9921875, 26 | 41.77131167976407, 27 | -95.2734375, 28 | 49.15296965617042 29 | ] 30 | }, 31 | { 32 | "type": "Feature", 33 | "properties": {}, 34 | "geometry": { 35 | "type": "LineString", 36 | "coordinates": [ 37 | [ 38 | -101.953125, 39 | 34.016241889667015 40 | ], 41 | [ 42 | -88.9453125, 43 | 40.17887331434696 44 | ], 45 | [ 46 | -78.046875, 47 | 50.064191736659104 48 | ] 49 | ] 50 | }, 51 | "bbox": [ 52 | -101.953125, 53 | 34.016241889667015, 54 | -78.046875, 55 | 50.064191736659104 56 | ] 57 | }, 58 | { 59 | "type": "Feature", 60 | "properties": {}, 61 | "geometry": { 62 | "type": "LineString", 63 | "coordinates": [ 64 | [ 65 | -108.6328125, 66 | 42.032974332441405 67 | ], 68 | [ 69 | -102.65625, 70 | 39.36827914916014 71 | ], 72 | [ 73 | -95.97656249999999, 74 | 39.36827914916014 75 | ] 76 | ] 77 | }, 78 | "bbox": [ 79 | -108.6328125, 80 | 39.36827914916014, 81 | -95.97656249999999, 82 | 42.032974332441405 83 | ] 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /test/out/search.points.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -86.8359375, 11 | 42.8115217450979 12 | ] 13 | }, 14 | "bbox": [ 15 | -86.8359375, 16 | 42.8115217450979, 17 | -86.8359375, 18 | 42.8115217450979 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/out/search.polygons.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Polygon", 9 | "coordinates": [ 10 | [ 11 | [ 12 | -78, 13 | 41 14 | ], 15 | [ 16 | -67, 17 | 41 18 | ], 19 | [ 20 | -67, 21 | 48 22 | ], 23 | [ 24 | -78, 25 | 48 26 | ], 27 | [ 28 | -78, 29 | 41 30 | ] 31 | ] 32 | ] 33 | }, 34 | "bbox": [ 35 | -78, 36 | 41, 37 | -67, 38 | 48 39 | ] 40 | }, 41 | { 42 | "type": "Feature", 43 | "properties": {}, 44 | "geometry": { 45 | "type": "Polygon", 46 | "coordinates": [ 47 | [ 48 | [ 49 | -93.8671875, 50 | 36.87962060502676 51 | ], 52 | [ 53 | -75.89355468749999, 54 | 36.87962060502676 55 | ], 56 | [ 57 | -75.89355468749999, 58 | 45.1510532655634 59 | ], 60 | [ 61 | -93.8671875, 62 | 45.1510532655634 63 | ], 64 | [ 65 | -93.8671875, 66 | 36.87962060502676 67 | ] 68 | ] 69 | ] 70 | }, 71 | "bbox": [ 72 | -93.8671875, 73 | 36.87962060502676, 74 | -75.89355468749999, 75 | 45.1510532655634 76 | ] 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | import { point, polygon, featureCollection } from '@turf/helpers' 2 | import { BBox } from 'geojson' 3 | import rbush from './' 4 | 5 | // Fixtures 6 | const bbox: BBox = [-180, -90, 180, 90] 7 | const pt = point([0, 0]) 8 | const points = featureCollection([pt, pt]) 9 | const poly = polygon([[[0, 0], [1, 1], [1, 1], [0, 0]]]) 10 | const polygons = featureCollection([poly, poly]) 11 | 12 | // Initialize GeoJSON RBush Tree 13 | const tree = rbush() 14 | 15 | // Load Tree with a FeatureCollection 16 | tree.load(points); 17 | tree.load(polygons); 18 | 19 | // Insert by Feature 20 | tree.insert(pt) 21 | tree.insert(poly) 22 | 23 | // Find All (returns FeatureCollection) 24 | const all = tree.all() 25 | 26 | // Search by Feature (returns FeatureCollection) 27 | const search = tree.search(poly) 28 | 29 | // Collides by Feature (returns FeatureCollection) 30 | const collides = tree.collides(poly) 31 | 32 | // Remove by Feature 33 | tree.remove(pt) 34 | tree.remove(poly) 35 | 36 | // BBox support 37 | tree.search(bbox) 38 | tree.collides(bbox) 39 | --------------------------------------------------------------------------------