UNPKG

leaflet.layergroup.collision

Version:

Leaflet plugin for uncluttering L.Markers using basic collision detection.

379 lines (291 loc) 11.5 kB
'use strict'; /*eslint key-spacing: 0, comma-spacing: 0, quotes: 0*/ var rbush = require('../rbush.js'), t = require('tape'); function sortedEqual(t, a, b, compare) { t.same(a.slice().sort(compare), b.slice().sort(compare)); } function someData(n) { var data = []; for (var i = 0; i < n; i++) { data.push([i, i, i, i]); } return data; } var data = [[0,0,0,0],[10,10,10,10],[20,20,20,20],[25,0,25,0],[35,10,35,10],[45,20,45,20],[0,25,0,25],[10,35,10,35], [20,45,20,45],[25,25,25,25],[35,35,35,35],[45,45,45,45],[50,0,50,0],[60,10,60,10],[70,20,70,20],[75,0,75,0], [85,10,85,10],[95,20,95,20],[50,25,50,25],[60,35,60,35],[70,45,70,45],[75,25,75,25],[85,35,85,35],[95,45,95,45], [0,50,0,50],[10,60,10,60],[20,70,20,70],[25,50,25,50],[35,60,35,60],[45,70,45,70],[0,75,0,75],[10,85,10,85], [20,95,20,95],[25,75,25,75],[35,85,35,85],[45,95,45,95],[50,50,50,50],[60,60,60,60],[70,70,70,70],[75,50,75,50], [85,60,85,60],[95,70,95,70],[50,75,50,75],[60,85,60,85],[70,95,70,95],[75,75,75,75],[85,85,85,85],[95,95,95,95]]; var emptyData = [[-Infinity, -Infinity, Infinity, Infinity],[-Infinity, -Infinity, Infinity, Infinity], [-Infinity, -Infinity, Infinity, Infinity],[-Infinity, -Infinity, Infinity, Infinity], [-Infinity, -Infinity, Infinity, Infinity],[-Infinity, -Infinity, Infinity, Infinity]]; var testTree = {"children":[{ "children":[ {"children":[[0,0,0,0],[10,10,10,10],[20,20,20,20],[25,0,25,0]],"height":1,"bbox":[0,0,25,20],"leaf":true}, {"children":[[0,25,0,25],[10,35,10,35],[25,25,25,25],[20,45,20,45]],"height":1,"bbox":[0,25,25,45],"leaf":true}, {"children":[[50,0,50,0],[35,10,35,10],[60,10,60,10],[45,20,45,20]],"height":1,"bbox":[35,0,60,20],"leaf":true}, {"children":[[50,25,50,25],[35,35,35,35],[60,35,60,35],[45,45,45,45]],"height":1,"bbox":[35,25,60,45],"leaf":true} ],"height":2,"bbox":[0,0,60,45] },{ "children":[ {"children":[[0,50,0,50],[25,50,25,50],[10,60,10,60],[20,70,20,70]],"height":1,"bbox":[0,50,25,70],"leaf":true}, {"children":[[0,75,0,75],[25,75,25,75],[10,85,10,85],[20,95,20,95]],"height":1,"bbox":[0,75,25,95],"leaf":true}, {"children":[[35,60,35,60],[50,50,50,50],[60,60,60,60],[45,70,45,70]],"height":1,"bbox":[35,50,60,70],"leaf":true}, {"children":[[50,75,50,75],[60,85,60,85],[45,95,45,95],[35,85,35,85]],"height":1,"bbox":[35,75,60,95],"leaf":true} ],"height":2,"bbox":[0,50,60,95] },{ "children":[ {"children":[[70,20,70,20],[75,0,75,0],[75,25,75,25],[70,45,70,45]],"height":1,"bbox":[70,0,75,45],"leaf":true}, {"children":[[75,50,75,50],[70,70,70,70],[75,75,75,75],[70,95,70,95]],"height":1,"bbox":[70,50,75,95],"leaf":true}, {"children":[[85,10,85,10],[95,20,95,20],[85,35,85,35],[95,45,95,45]],"height":1,"bbox":[85,10,95,45],"leaf":true}, {"children":[[85,60,85,60],[95,70,95,70],[85,85,85,85],[95,95,95,95]],"height":1,"bbox":[85,60,95,95],"leaf":true} ],"height":2,"bbox":[70,0,95,95] }],"height":3,"bbox":[0,0,95,95]}; t('constructor accepts a format argument to customize the data format', function (t) { var tree = rbush(4, ['.minLng', '.minLat', '.maxLng', '.maxLat']); t.same(tree.toBBox({minLng: 1, minLat: 2, maxLng: 3, maxLat: 4}), [1, 2, 3, 4]); t.end(); }); t('constructor uses 9 max entries by default', function (t) { var tree = rbush().load(someData(9)); t.equal(tree.toJSON().height, 1); var tree2 = rbush().load(someData(10)); t.equal(tree2.toJSON().height, 2); t.end(); }); t('#toBBox, #compareMinX, #compareMinY can be overriden to allow custom data structures', function (t) { var tree = rbush(4); tree.toBBox = function (item) { return [item.minLng, item.minLat, item.maxLng, item.maxLat]; }; tree.compareMinX = function (a, b) { return a.minLng - b.minLng; }; tree.compareMinY = function (a, b) { return a.minLat - b.minLat; }; var data = [ {minLng: -115, minLat: 45, maxLng: -105, maxLat: 55}, {minLng: 105, minLat: 45, maxLng: 115, maxLat: 55}, {minLng: 105, minLat: -55, maxLng: 115, maxLat: -45}, {minLng: -115, minLat: -55, maxLng: -105, maxLat: -45} ]; tree.load(data); function byLngLat(a, b) { return a.minLng - b.minLng || a.minLat - b.minLat; } sortedEqual(t, tree.search([-180, -90, 180, 90]), [ {minLng: -115, minLat: 45, maxLng: -105, maxLat: 55}, {minLng: 105, minLat: 45, maxLng: 115, maxLat: 55}, {minLng: 105, minLat: -55, maxLng: 115, maxLat: -45}, {minLng: -115, minLat: -55, maxLng: -105, maxLat: -45} ], byLngLat); sortedEqual(t, tree.search([-180, -90, 0, 90]), [ {minLng: -115, minLat: 45, maxLng: -105, maxLat: 55}, {minLng: -115, minLat: -55, maxLng: -105, maxLat: -45} ], byLngLat); sortedEqual(t, tree.search([0, -90, 180, 90]), [ {minLng: 105, minLat: 45, maxLng: 115, maxLat: 55}, {minLng: 105, minLat: -55, maxLng: 115, maxLat: -45} ], byLngLat); sortedEqual(t, tree.search([-180, 0, 180, 90]), [ {minLng: -115, minLat: 45, maxLng: -105, maxLat: 55}, {minLng: 105, minLat: 45, maxLng: 115, maxLat: 55} ], byLngLat); sortedEqual(t, tree.search([-180, -90, 180, 0]), [ {minLng: 105, minLat: -55, maxLng: 115, maxLat: -45}, {minLng: -115, minLat: -55, maxLng: -105, maxLat: -45} ], byLngLat); t.end(); }); t('#load bulk-loads the given data given max node entries and forms a proper search tree', function (t) { var tree = rbush(4).load(data); sortedEqual(t, tree.all(), rbush(4).fromJSON(testTree).all()); t.end(); }); t('#load uses standard insertion when given a low number of items', function (t) { var tree = rbush(8) .load(data) .load(data.slice(0, 3)); var tree2 = rbush(8) .load(data) .insert(data[0]) .insert(data[1]) .insert(data[2]); t.same(tree.toJSON(), tree2.toJSON()); t.end(); }); t('#load does nothing if loading empty data', function (t) { var tree = rbush().load([]); t.same(tree.toJSON(), rbush().toJSON()); t.end(); }); t('#load handles the insertion of maxEntries + 2 empty bboxes', function (t) { var tree = rbush(4) .load(emptyData); t.equal(tree.toJSON().height, 2); sortedEqual(t, tree.all(), emptyData); t.end(); }); t('#insert handles the insertion of maxEntries + 2 empty bboxes', function (t) { var tree = rbush(4); emptyData.forEach(function (datum) { tree.insert(datum); }); t.equal(tree.toJSON().height, 2); sortedEqual(t, tree.all(), emptyData); t.end(); }); t('#load properly splits tree root when merging trees of the same height', function (t) { var tree = rbush(4) .load(data) .load(data); t.equal(tree.toJSON().height, 4); sortedEqual(t, tree.all(), data.concat(data)); t.end(); }); t('#load properly merges data of smaller or bigger tree heights', function (t) { var smaller = someData(10); var tree1 = rbush(4) .load(data) .load(smaller); var tree2 = rbush(4) .load(smaller) .load(data); t.equal(tree1.toJSON().height, tree2.toJSON().height); sortedEqual(t, tree1.all(), data.concat(smaller)); sortedEqual(t, tree2.all(), data.concat(smaller)); t.end(); }); t('#search finds matching points in the tree given a bbox', function (t) { var tree = rbush(4).load(data); var result = tree.search([40, 20, 80, 70]); sortedEqual(t, result, [ [70,20,70,20],[75,25,75,25],[45,45,45,45],[50,50,50,50],[60,60,60,60],[70,70,70,70], [45,20,45,20],[45,70,45,70],[75,50,75,50],[50,25,50,25],[60,35,60,35],[70,45,70,45] ]); t.end(); }); t('#collides returns true when search finds matching points', function (t) { var tree = rbush(4).load(data); var result = tree.collides([40, 20, 80, 70]); t.same(result, true); t.end(); }); t('#search returns an empty array if nothing found', function (t) { var result = rbush(4).load(data).search([200, 200, 210, 210]); t.same(result, []); t.end(); }); t('#collides returns false if nothing found', function (t) { var result = rbush(4).load(data).collides([200, 200, 210, 210]); t.same(result, false); t.end(); }); t('#all returns all points in the tree', function (t) { var tree = rbush(4).load(data); var result = tree.all(); sortedEqual(t, result, data); sortedEqual(t, tree.search([0, 0, 100, 100]), data); t.end(); }); t('#toJSON & #fromJSON exports and imports search tree in JSON format', function (t) { var tree = rbush(4); tree.fromJSON(testTree); var tree2 = rbush(4).load(data); sortedEqual(t, tree.all(), tree2.all()); t.end(); }); t('#insert adds an item to an existing tree correctly', function (t) { var tree = rbush(4).load([ [0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2] ]); tree.insert([3, 3, 3, 3]); t.same(tree.toJSON(), { 'children':[[0,0,0,0],[1,1,1,1],[2,2,2,2],[3,3,3,3]], 'leaf':true, 'height':1, 'bbox':[0,0,3,3] }); tree.insert([1, 1, 2, 2]); t.same(tree.toJSON(), { 'children':[ {'children':[[0,0,0,0],[1,1,1,1]],'leaf':true,'height':1,'bbox':[0,0,1,1]}, {'children':[[1,1,2,2],[2,2,2,2],[3,3,3,3]],'height':1,'leaf':true,'bbox':[1,1,3,3]} ], 'height':2, 'bbox':[0,0,3,3], 'leaf':false }); t.end(); }); t('#insert does nothing if given undefined', function (t) { t.same( rbush().load(data), rbush().load(data).insert()); t.end(); }); t('#insert forms a valid tree if items are inserted one by one', function (t) { var tree = rbush(4); for (var i = 0; i < data.length; i++) { tree.insert(data[i]); } var tree2 = rbush(4).load(data); t.ok(tree.toJSON().height - tree2.toJSON().height <= 1); sortedEqual(t, tree.all(), tree2.all()); t.end(); }); t('#remove removes items correctly', function (t) { var tree = rbush(4).load(data); var len = data.length; tree.remove(data[0]); tree.remove(data[1]); tree.remove(data[2]); tree.remove(data[len - 1]); tree.remove(data[len - 2]); tree.remove(data[len - 3]); sortedEqual(t, data.slice(3, len - 3), tree.all()); t.end(); }); t('#remove does nothing if nothing found', function (t) { t.same( rbush().load(data), rbush().load(data).remove([13, 13, 13, 13])); t.end(); }); t('#remove does nothing if given undefined', function (t) { t.same( rbush().load(data), rbush().load(data).remove()); t.end(); }); t('#remove brings the tree to a clear state when removing everything one by one', function (t) { var tree = rbush(4).load(data); for (var i = 0; i < data.length; i++) { tree.remove(data[i]); } t.same(tree.toJSON(), rbush(4).toJSON()); t.end(); }); t('#clear should clear all the data in the tree', function (t) { t.same( rbush(4).load(data).clear().toJSON(), rbush(4).toJSON()); t.end(); }); t('should have chainable API', function (t) { t.doesNotThrow(function () { rbush() .load(data) .insert(data[0]) .remove(data[0]) .fromJSON(testTree); }); t.end(); });