├── .gitignore
├── Gruntfile.js
├── README.md
├── bower.json
├── build.js
├── build
├── amd-profile-demo.js
├── jsdoctemplate
│ ├── README.md
│ ├── publish.js
│ ├── static
│ │ ├── scripts
│ │ │ ├── linenumber.js
│ │ │ └── prettify
│ │ │ │ ├── Apache-License-2.0.txt
│ │ │ │ ├── lang-css.js
│ │ │ │ └── prettify.js
│ │ └── styles
│ │ │ ├── jsdoc-default.css
│ │ │ ├── prettify-jsdoc.css
│ │ │ └── prettify-tomorrow.css
│ └── tmpl
│ │ ├── container.tmpl
│ │ ├── details.tmpl
│ │ ├── example.tmpl
│ │ ├── examples.tmpl
│ │ ├── exceptions.tmpl
│ │ ├── layout.tmpl
│ │ ├── mainpage.tmpl
│ │ ├── members.tmpl
│ │ ├── method.tmpl
│ │ ├── params.tmpl
│ │ ├── properties.tmpl
│ │ ├── returns.tmpl
│ │ ├── source.tmpl
│ │ ├── tutorial.tmpl
│ │ └── type.tmpl
├── node-package.json
└── parts
│ ├── googleAnalytics.frag
│ └── mitlicense.frag
├── demo
├── default.css
├── index.html
└── main.js
├── package.json
├── release
└── js
│ ├── RTree2d-amd.js
│ ├── RTree2d-common.js
│ └── RTree2d.js
├── setup.js
├── src
├── Branch.js
├── BranchMixin.js
├── PriorityQueue.js
├── RTree.js
├── Rectangle.js
└── type.js
└── test
├── PriorityQueue-test.js
├── RTree-test.js
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | rtree2d.iml
2 | .idea
3 | bower_components
4 | release/rtree2d.zip
5 | release/demo
6 | release/jsdoc
7 | rtree2d.zip
8 | node_modules
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | var async = require("async");
2 | var buildify = require('buildify');
3 | var exec = require('child_process').exec;
4 |
5 | var releaseDir = "./release/";
6 |
7 | var srcDir = "./src/";
8 |
9 | var addGoogleAnalytics = function(content) {
10 | var nb = buildify();
11 | var ga = nb
12 | .load('./build/parts/googleAnalytics.frag')
13 | .content;
14 | return content.replace('', ga + "");
15 | };
16 |
17 | module.exports = function (grunt) {
18 |
19 | require("load-grunt-tasks")(grunt);
20 |
21 | grunt.initConfig({
22 | pkg: grunt.file.readJSON("package.json"),
23 | jshint: {
24 | source: [
25 | srcDir,
26 | "demo"
27 | ]
28 | },
29 | clean: {
30 | release: [
31 | releaseDir
32 | ]
33 | },
34 | requirejs: {
35 |
36 | rtree2d: {
37 | options: {
38 | baseUrl: ".",
39 | mainConfigFile: "src/RTree" + ".js",
40 | name: "bower_components/almond/almond.js",
41 | include: "src/RTree",
42 | out: releaseDir + "js/RTree2d.js",
43 | optimize: "uglify2",
44 | options: {
45 | mangle: true
46 | },
47 | wrap: {
48 | start: "(function(root) {",
49 | end: "root.RTree2d = require('src/RTree');}(this));"
50 | }
51 | }
52 | },
53 |
54 | demo: {
55 | options: {
56 | baseUrl: ".",
57 | mainConfigFile: "demo/main.js",
58 | name: "bower_components/almond/almond.js",
59 | include: "demo/main.js",
60 | out: releaseDir + "demo/main.js",
61 | optimize: "uglify2",
62 | options: {
63 | mangle: true
64 | }
65 | }
66 | }
67 | },
68 |
69 | compress: {
70 | main: {
71 | options: {
72 | archive: function () {
73 | return "release/rtree2d.zip"
74 | }
75 | },
76 | files: [
77 | {cwd: "release/js", src: ["**"], dest: "rtree2d/js", filter: 'isFile', expand: true},
78 | {cwd: "release/demo", src: ["**"], dest: "rtree2d/demo", filter: 'isFile', expand: true}
79 | ]
80 | }
81 | },
82 |
83 | copy: {
84 | demo: {
85 | options: {
86 | process: function (content, name) {
87 | if (name === "demo/index.html") {
88 | content = content.replace(/\<\!\-\-REQUIRE_SOURCE\-\-\>((.|[\r\n])*?)\<\!\-\-REQUIRE_SOURCE\-\-\>/g, ' ');
89 | return content;
90 | } else {
91 | return content;
92 | }
93 | }
94 | },
95 | files: [
96 | {expand: true, cwd: "demo", src: ['**', '!*.js'], dest: "release/demo", filter: 'isFile'}
97 | ]
98 | },
99 | js: {
100 | files: [
101 | {expand: true, cwd: "release/js", src: ['**'], dest: "release/demo/js", filter: 'isFile'}
102 | ]
103 | }
104 | }
105 | });
106 |
107 | function wrapScript(inFile, out, start, end) {
108 | buildify()
109 | .load(inFile)
110 | .perform(function (contents) {
111 | return start + contents + end;
112 | })
113 | .save(out);
114 | }
115 |
116 |
117 | grunt.registerTask("amdify", wrapScript.bind(null, "release/js/RTree2d.js", "release/js/RTree2d-amd.js", "(function(){var scope = this;", "define([],function(){ return scope.RTree2d; });}).call({});"));
118 | grunt.registerTask("commonjsify", wrapScript.bind(null, "release/js/RTree2d.js", "release/js/RTree2d-common.js", "(function(){var scope = this;", "exports.RTree2d=scope.RTree2d;}).call({});"));
119 |
120 | grunt.registerTask('jsdoc', function() {
121 | var done = this.async();
122 | exec('"./node_modules/.bin/jsdoc" ./src/ ./README.md -d ./release/demo/jsdoc -t "./build/jsdoctemplate"', function(err, stdout, sterr) {
123 | if (err || sterr) {
124 | console.error('jsdoc failed.', err, sterr);
125 | done();
126 | }
127 | buildify()
128 | .load('./release/jsdoc/index.html')
129 | .perform(addGoogleAnalytics)
130 | .save('./release/jsdoc/index.html');
131 | done();
132 | });
133 | });
134 |
135 | grunt.registerTask("release", ["jshint", "requirejs", "amdify", "commonjsify", "copy", "jsdoc", "compress"]);
136 |
137 | };
138 |
139 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | RTree2d
2 | -------
3 |
4 | A fast and memory-efficient 2D RTree implementation for Javascript. Supports range searches and nearest neighbour searches.
5 |
6 | ## Installation
7 |
8 | The RTree is available as:
9 | - script: **./release/js/RTree2d.js** (adds a global variable with the name `RTree2d`)
10 | - an AMD module: **./release/js/RTree2d-amd.js**
11 | - a CommonJS module: **./release/js/RTree2d-common.js**
12 |
13 | ## Demo
14 |
15 | Take a test drive [here](http://rtree2d.hepburnave.com).
16 |
17 | ## At a glance
18 |
19 | var rtree = new RTree2d();
20 |
21 | //insert an object at the rectangle (x/y/width/height).
22 | var ob = {store: 'me'};
23 | rtree.insert(ob,2,3,4,5);
24 |
25 | //search a rectangle
26 | var results = rtree.search(0,0,10,10);
27 | //results ~= [{store: "me"}];
28 |
29 | //Instead of using search, you can also use one of the collection comprehension methods.
30 | //(e.g. you might be doing a search 60 frames-per-second. You do not want a new array each time).
31 | rtree.forEachInRectangle(0,0,10,10, function(ob){
32 | console.log("i got a hit, ",ob);
33 | });
34 |
35 | //find the k nearest neighbours (x/y/k)
36 | var kNNArray = rtree.nearestNeighbours(0,0,1);
37 |
38 | //Number of elements in the tree.
39 | var size = rtree.size();
40 |
41 | //Remove an element.
42 | rtree.remove(ob,2,3,4,5);
43 |
44 |
45 | ## Documentation
46 |
47 | [API Documentation](http://rtree2d.hepburnave.com/jsdoc/)
48 |
49 | ## Code repository
50 |
51 | [Git repo on bitbucket](https://github.com/hepburnave/RTree2d)
52 |
53 | ## Dev project setup
54 |
55 | 1) checkout repo
56 |
57 | > git clone https://github.com/hepburnave/RTree2d.git
58 |
59 | 2) run node setup script (might need admin priviliges on windows)
60 |
61 | > node setup.js
62 |
63 | 3) build a release
64 |
65 | > grunt release
66 |
67 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RTree2d",
3 | "version": "0.0.0",
4 | "authors": [
5 | "thomas@neirynck.us"
6 | ],
7 | "description": "Fast and memory-efficient RTree for the browser and node.",
8 | "main": "src/RTree.js",
9 | "keywords": [
10 | "RTree",
11 | "AMD",
12 | "GIS",
13 | "node",
14 | "Visualization",
15 | "spatial",
16 | "data",
17 | "structure",
18 | "index"
19 | ],
20 | "license": "MIT",
21 | "ignore": [
22 | "**/.*",
23 | "node_modules",
24 | "bower_components",
25 | "test",
26 | "tests"
27 | ],
28 | "devDependencies": {
29 | "qunit": "~1.14.0",
30 | "requirejs": "~2.1.11",
31 | "jquery": "~2.1.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | /**
2 | * release script with jake tasks.
3 | *
4 | * run from project root!
5 | */
6 |
7 | var sys = require('sys');
8 | var exec = require('child_process').exec;
9 | var fs = require('fs');
10 | var nodefy = require('nodefy');
11 | var ncp = require('ncp').ncp;
12 | var docco = require('docco');
13 | var rimraf = require('rimraf');
14 | var buildify = require('buildify');
15 |
16 |
17 | //execute from root.
18 | var RELEASE_DIR = './release/';
19 | var NODE_DIR = RELEASE_DIR + 'node/';
20 |
21 | var addGoogleAnalytics = function(content) {
22 | var nb = buildify();
23 | var ga = nb
24 | .load('./build/parts/googleAnalytics.frag')
25 | .content;
26 | return content.replace('', ga + "");
27 | };
28 |
29 | var addLicense = function(content) {
30 | var nb = buildify();
31 | var lic = nb
32 | .load('./build/parts/mitlicense.frag')
33 | .content;
34 |
35 | return lic + content;
36 | };
37 |
38 | function copy(from, to) {
39 | fs
40 | .createReadStream(from)
41 | .pipe(fs.createWriteStream(to));
42 | }
43 |
44 | desc('setup');
45 | task('setup', function() {
46 | fs.mkdir(RELEASE_DIR, function() {
47 | console.log('made release dir');
48 | complete();
49 | });
50 |
51 |
52 | }, true);
53 |
54 | desc('build node package');
55 | task('nodeModule', ['setup'], function() {
56 |
57 | nodefy.batchConvert('./src/*.js', NODE_DIR, function(err, results) {
58 | if (err) {
59 | console.log('node failed.', err);
60 | return;
61 | }
62 | copy('./build/node-package.json', NODE_DIR + 'package.json');
63 | copy('./README.md', NODE_DIR + 'README.md');
64 | complete();
65 | });
66 |
67 | });
68 |
69 |
70 | desc('build jsdoc');
71 | task('jsdoc', ['setup'], function() {
72 |
73 | //build jsdoc
74 | exec('"./node_modules/.bin/jsdoc" ./src/ ./README.md -d ./release/jsdoc -t "./build/jsdoctemplate"', function(err, stdout, sterr) {
75 |
76 | if (err || sterr) {
77 | console.log('jsdoc failed.', err, sterr);
78 | }
79 |
80 | var b = buildify();
81 | b
82 | .load('./release/jsdoc/index.html')
83 | .perform(addGoogleAnalytics)
84 | .save('./release/jsdoc/index.html');
85 |
86 | complete();
87 |
88 | });
89 |
90 | }, true);
91 |
92 | desc('build demo');
93 | task('demo', ['setup'], function() {
94 |
95 |
96 | exec('node ./node_modules/requirejs/bin/r.js -o build/amd-profile-demo.js', function(err, stdout, sterr) {
97 |
98 | if (err || sterr) {
99 | console.error('demo minification failed.', err);
100 | return;
101 | }
102 |
103 | var b = buildify();
104 | b
105 | .load("./release/index.html")
106 | .perform(function(content) {
107 | content = content.replace(/.*([\s\S]*?)\/g, '');
108 | content = content.replace(/.*([\s\S]*?)\/g, '');
109 | return content;
110 | })
111 | .save("./release/index.html");
112 |
113 | //add ga
114 | b = buildify();
115 | b
116 | .load("./release/index.html")
117 | .perform(addGoogleAnalytics)
118 | .save("./release/index.html");
119 |
120 | b = buildify();
121 | b
122 | .load("./release/main.js")
123 | .perform(addLicense)
124 | .save("./release/main.js");
125 |
126 | b = buildify();
127 | b
128 | .load("./bower_components/requirejs/require.js")
129 | .save("./release/require.js");
130 |
131 | complete();
132 |
133 | });
134 |
135 |
136 |
137 | }, true);
138 |
139 |
140 | desc('build release');
141 | task('release', ['demo','jsdoc','nodeModule']);
142 |
143 |
144 |
--------------------------------------------------------------------------------
/build/amd-profile-demo.js:
--------------------------------------------------------------------------------
1 | ({
2 | appDir: '../demo',
3 | baseUrl: '.',
4 | dir: '../release',
5 | modules: [
6 | {
7 | name: 'main'
8 | }
9 | ],
10 | fileExclusionRegExp: /^(r|build|amd|run|amd).*\.js$/,
11 | optimizeCss: 'standard',
12 | removeCombined: true,
13 | paths: {
14 | jquery: '../bower_components/jquery/dist/jquery',
15 | rtree2d: "../src"
16 | }
17 | })
18 |
--------------------------------------------------------------------------------
/build/jsdoctemplate/README.md:
--------------------------------------------------------------------------------
1 | The default template for JSDoc 3 uses: [the Taffy Database library](http://taffydb.com/) and the [Underscore Template library](http://documentcloud.github.com/underscore/#template).
2 |
--------------------------------------------------------------------------------
/build/jsdoctemplate/publish.js:
--------------------------------------------------------------------------------
1 | /*global env: true */
2 | var template = require('jsdoc/template'),
3 | fs = require('jsdoc/fs'),
4 | path = require('jsdoc/path'),
5 | taffy = require('taffydb').taffy,
6 | handle = require('jsdoc/util/error').handle,
7 | helper = require('jsdoc/util/templateHelper'),
8 | htmlsafe = helper.htmlsafe,
9 | linkto = helper.linkto,
10 | resolveAuthorLinks = helper.resolveAuthorLinks,
11 | scopeToPunc = helper.scopeToPunc,
12 | hasOwnProp = Object.prototype.hasOwnProperty,
13 | data,
14 | view,
15 | outdir = env.opts.destination;
16 |
17 |
18 | function find(spec) {
19 | return helper.find(data, spec);
20 | }
21 |
22 | function tutoriallink(tutorial) {
23 | return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' });
24 | }
25 |
26 | function getAncestorLinks(doclet) {
27 | return helper.getAncestorLinks(data, doclet);
28 | }
29 |
30 | function hashToLink(doclet, hash) {
31 | if ( !/^(#.+)/.test(hash) ) { return hash; }
32 |
33 | var url = helper.createLink(doclet);
34 |
35 | url = url.replace(/(#.+|$)/, hash);
36 | return '' + hash + '';
37 | }
38 |
39 | function needsSignature(doclet) {
40 | var needsSig = false;
41 |
42 | // function and class definitions always get a signature
43 | if (doclet.kind === 'function' || doclet.kind === 'class') {
44 | needsSig = true;
45 | }
46 | // typedefs that contain functions get a signature, too
47 | else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names &&
48 | doclet.type.names.length) {
49 | for (var i = 0, l = doclet.type.names.length; i < l; i++) {
50 | if (doclet.type.names[i].toLowerCase() === 'function') {
51 | needsSig = true;
52 | break;
53 | }
54 | }
55 | }
56 |
57 | return needsSig;
58 | }
59 |
60 | function addSignatureParams(f) {
61 | var params = helper.getSignatureParams(f, 'optional');
62 |
63 | f.signature = (f.signature || '') + '('+params.join(', ')+')';
64 | }
65 |
66 | function addSignatureReturns(f) {
67 | var returnTypes = helper.getSignatureReturns(f);
68 |
69 | f.signature = '' + (f.signature || '') + '' +
70 | '' +
71 | (returnTypes && returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') +
72 | '';
73 | }
74 |
75 | function addSignatureTypes(f) {
76 | var types = helper.getSignatureTypes(f);
77 |
78 | f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+'';
79 | }
80 |
81 | function addAttribs(f) {
82 | var attribs = helper.getAttribs(f);
83 |
84 | f.attribs = '' + htmlsafe(attribs.length ?
85 | // we want the template output to say 'abstract', not 'virtual'
86 | '<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + '';
87 | }
88 |
89 | function shortenPaths(files, commonPrefix) {
90 | // always use forward slashes
91 | var regexp = new RegExp('\\\\', 'g');
92 |
93 | Object.keys(files).forEach(function(file) {
94 | files[file].shortened = files[file].resolved.replace(commonPrefix, '')
95 | .replace(regexp, '/');
96 | });
97 |
98 | return files;
99 | }
100 |
101 | function resolveSourcePath(filepath) {
102 | return path.resolve(process.cwd(), filepath);
103 | }
104 |
105 | function getPathFromDoclet(doclet) {
106 | if (!doclet.meta) {
107 | return;
108 | }
109 |
110 | var filepath = doclet.meta.path && doclet.meta.path !== 'null' ?
111 | doclet.meta.path + '/' + doclet.meta.filename :
112 | doclet.meta.filename;
113 |
114 | return filepath;
115 | }
116 |
117 | function generate(title, docs, filename, resolveLinks) {
118 | resolveLinks = resolveLinks === false ? false : true;
119 |
120 | var docData = {
121 | title: title,
122 | docs: docs
123 | };
124 |
125 | var outpath = path.join(outdir, filename),
126 | html = view.render('container.tmpl', docData);
127 |
128 | if (resolveLinks) {
129 | html = helper.resolveLinks(html); // turn {@link foo} into foo
130 | }
131 |
132 | fs.writeFileSync(outpath, html, 'utf8');
133 | }
134 |
135 | function generateSourceFiles(sourceFiles, encoding) {
136 | encoding = encoding || 'utf8';
137 | Object.keys(sourceFiles).forEach(function(file) {
138 | var source;
139 | // links are keyed to the shortened path in each doclet's `meta.filename` property
140 | var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened);
141 | helper.registerLink(sourceFiles[file].shortened, sourceOutfile);
142 |
143 | try {
144 | source = {
145 | kind: 'source',
146 | code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) )
147 | };
148 | }
149 | catch(e) {
150 | handle(e);
151 | }
152 |
153 | generate('Source: ' + sourceFiles[file].shortened, [source], sourceOutfile,
154 | false);
155 | });
156 | }
157 |
158 | /**
159 | * Look for classes or functions with the same name as modules (which indicates that the module
160 | * exports only that class or function), then attach the classes or functions to the `module`
161 | * property of the appropriate module doclets. The name of each class or function is also updated
162 | * for display purposes. This function mutates the original arrays.
163 | *
164 | * @private
165 | * @param {Array.} doclets - The array of classes and functions to
166 | * check.
167 | * @param {Array.} modules - The array of module doclets to search.
168 | */
169 | function attachModuleSymbols(doclets, modules) {
170 | var symbols = {};
171 |
172 | // build a lookup table
173 | doclets.forEach(function(symbol) {
174 | symbols[symbol.longname] = symbol;
175 | });
176 |
177 | return modules.map(function(module) {
178 | if (symbols[module.longname]) {
179 | module.module = symbols[module.longname];
180 | module.module.name = module.module.name.replace('module:', 'require("') + '")';
181 | }
182 | });
183 | }
184 |
185 | /**
186 | * Create the navigation sidebar.
187 | * @param {object} members The members that will be used to create the sidebar.
188 | * @param {array
24 |
25 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |