def conductTrial(label, items): samples = 128 n = len(items) rt = RTree(m=2, M=4) for r in items: rt.add(r) box = rt.root.region width = box.x_max - box.x_min height = box.y_max - box.y_min # can compute density for any tree, samples at 128 random spots ct = 0 for _ in range(samples): x = box.x_min + random.random() * width y = box.y_min + random.random() * height for triple in rt.range(Region(x, y, x, y)): assert triple[2] == False ct += 1 print("Density", n, "{:.2f}%".format(100 * ct / (n * samples)), "intersect random point in ", str(box)) trials(n, items, BUILD, "Build") trials(n, items, SEARCH, "Search") trials(n, items, DELETE, "Delete")
def conductTrial(label, items): samples = 128 n = len(items) rt = RTree(m=2, M=4) for r in items: rt.add(r) box = rt.root.region width = box.x_max - box.x_min height = box.y_max - box.y_min # can compute density for any tree, samples at 128 random spots ct = 0 for _ in range(samples): x = box.x_min + random.random() * width y = box.y_min + random.random() * height for triple in rt.range(Region(x, y, x, y)): assert (triple[2] == False) ct += 1 print("Density", n, "{:.2f}%".format(100 * ct / (n * samples)), "intersect random point in ", str(box)) trials(n, items, BUILD, "Build") trials(n, items, SEARCH, "Search") trials(n, items, DELETE, "Delete")
def __init__(self): """App for creating R tree dynamically and executing range queries""" self.tree = RTree(m=2, M=4) self.ready = False # for range query self.selectedRegion = None self.newRegionStart = None self.newRegion = None # for identifiers self.counter = 0 self.master = tkinter.Tk() self.master.title('R Tree Range Query Application') self.w = tkinter.Frame(self.master, width=512, height=512) self.canvas = tkinter.Canvas(self.w, width=512, height=512) self.paint() self.canvas.bind("<Button-1>", self.startAdd) self.canvas.bind("<B1-Motion>", self.extendAdd) # only when right mouse dragged self.canvas.bind("<ButtonRelease-1>", self.endAdd) self.canvas.bind("<Button-3>", self.range) # when right mouse clicked self.canvas.bind("<ButtonRelease-3>", self.clear) self.canvas.bind("<B3-Motion>", self.range) # only when right mouse dragged self.w.pack()
def test_multipleM(self): # check multiple m,M values for m,M in zip([3, 4, 5, 6], [7, 8, 10, 13]): self.tree = RTree(m,M) regions = [] for ctr in range(500): r = Region(2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250)) regions.append((r, ctr)) self.tree.add(r,ctr) # make sure all can be individually found for ctr in range(len(regions)): match = [m for m in self.tree.search(regions[ctr][0])][0] self.assertEquals(match, regions[ctr][0]) # make sure that all are contained within the self.ensure_maxBounding(self.tree.root)
def performanceR(): """Demonstrate RTree stores all rectangles.""" n = 1024 while n <= 131072: rt = RTree() added = [] for _ in range(n): new = randomRectangle() if rt.search(new) is None: rt.add(new) added.append(new) random.shuffle(added) for r in added: if rt.search(r) is None: print ("Cannot find:" + r) assert False print ("pass " + str(n)) n *= 2
def performanceR(): """Demonstrate RTree stores all rectangles.""" n = 1024 while n <= 131072: rt = RTree() added = [] for _ in range(n): new = randomRectangle() if rt.search(new) is None: rt.add(new) added.append(new) random.shuffle(added) for r in added: if rt.search(r) is None: print("Cannot find:" + r) assert False print("pass " + str(n)) n *= 2
class RTreeApp: def __init__(self): """App for creating R tree dynamically and executing range queries""" self.tree = RTree(m=2, M=4) self.ready = False # for range query self.selectedRegion = None self.newRegionStart = None self.newRegion = None # for identifiers self.counter = 0 self.master = tkinter.Tk() self.master.title('R Tree Range Query Application') self.w = tkinter.Frame(self.master, width=512, height=512) self.canvas = tkinter.Canvas(self.w, width=512, height=512) self.paint() self.canvas.bind("<Button-1>", self.startAdd) self.canvas.bind("<B1-Motion>", self.extendAdd) # only when right mouse dragged self.canvas.bind("<ButtonRelease-1>", self.endAdd) self.canvas.bind("<Button-3>", self.range) # when right mouse clicked self.canvas.bind("<ButtonRelease-3>", self.clear) self.canvas.bind("<B3-Motion>", self.range) # only when right mouse dragged self.w.pack() def startAdd(self, event): """End of range search.""" x = event.x y = self.toCartesian(event.y) self.newRegionStart = (x,y) def extendAdd(self, event): """End of range search.""" if self.newRegionStart: x = event.x y = self.toCartesian(event.y) self.newRegion = Region (x,y,x,y).unionPoint(self.newRegionStart) self.paint() def endAdd(self, event): """End of range search.""" if self.newRegionStart: self.newRegionStart = None self.counter += 1 if self.newRegion: self.tree.add(self.newRegion, str(self.counter)) self.newRegion = None self.paint() def toCartesian(self, y): """Convert tkinter point into Cartesian.""" return self.w.winfo_height() - y def toTk(self,y): """Convert Cartesian into tkinter point.""" if y == maxValue: return 0 tk_y = self.w.winfo_height() if y != minValue: tk_y -= y return tk_y def clear(self, event): """End of range search.""" self.selectedRegion = None self.paint() def range(self, event): """Initiate a range search using a selected rectangular region.""" p = (event.x, self.toCartesian(event.y)) if self.selectedRegion is None: self.selectedStart = Region(p[X],p[Y], p[X],p[Y]) self.selectedRegion = self.selectedStart.unionPoint(p) self.paint() # return (node,0,True) where status is True if draining entire tree rooted at node. If False, # then (rect,id,False). Draw these # as shaded red rectangle to identify whole sub-tree is selected. for triple in self.tree.range(self.selectedRegion): if triple[2]: r = triple[0].region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Red', stipple='gray12') else: r = triple[0] self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Red') def click(self, event): """Add point to KDtree.""" p = (event.x, self.toCartesian(event.y)) self.tree.add(p) self.paint() def visit (self, n): """ Visit node to paint properly.""" if n == None: return if n.level == 0: for idx in range(n.count): r = n.children[idx].region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Gray') for idx in range(n.count): self.visit(n.children[idx]) # Do after all children so we can see interior nodes, too. r = n.region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), outline='Black', dash=(2, 4)) color = 'Gray' if n.level == 0: color='Black' self.canvas.create_text(r.x_max - 16*len(n.id), self.toTk(r.y_max) + 16, anchor = tkinter.W, font = "Times 16 bold", fill=color, text=n.id) def prepare(self, event): """prepare to add points.""" if self.label: self.label.destroy() self.label = None self.canvas.pack() def paint(self): """Paint R tree by visiting all nodes, or show introductory message.""" if self.ready: self.canvas.delete(tkinter.ALL) self.visit(self.tree.root) if self.newRegion: self.canvas.create_rectangle(self.newRegion.x_min, self.toTk(self.newRegion.y_min), self.newRegion.x_max, self.toTk(self.newRegion.y_max), outline='Black', dash=(2, 4)) if self.selectedRegion: self.canvas.create_rectangle(self.selectedRegion.x_min, self.toTk(self.selectedRegion.y_min), self.selectedRegion.x_max, self.toTk(self.selectedRegion.y_max), outline='Red', dash=(2, 4)) else: self.label = tkinter.Label(self.w, width=100, height = 40, text="Click To Add Points") self.label.bind("<Button-1>", self.prepare) self.label.pack() self.ready = True
class TestRMethods(unittest.TestCase): def setUp(self): self.tree = RTree() def tearDown(self): self.tree = None def test_basic(self): r = Region(10,20,50,60) self.tree.add(r, 99) for node,_,status in self.tree.range(r): self.assertTrue(status) for rect, ident in node.leafOrder(): self.assertEquals(99, ident) self.assertEquals(r, rect) # Expanded region for node with multiple insertions. def test_expand(self): r = Region(10,20,50,60) r2 = Region(30,60,70,80) self.tree.add(r, 99) self.tree.add(r2, 101) self.assertEquals(Region(10,20,70,80), self.tree.root.region) # Add five regions and watch split. def test_split(self): r1 = Region(10,20,50,60) r2 = Region(30,60,70,80) r3 = Region(-30,60,70,80) r4 = Region(40,70,60,100) self.tree.add(r1, 99) self.tree.add(r2, 101) self.tree.add(r3, 103) self.tree.add(r4, 105) self.assertEquals(Region(-30,20,70,100), self.tree.root.region) r5 = Region(10,60,0,90) self.tree.add(r5, 107) self.assertEquals(Region(-30,20,70,100), self.tree.root.region) # finds all rectangles. r_ids = [] for triple in self.tree.range(Region(-100, -100, 200, 200)): if triple[2]: for d in triple[0].leafOrder(): r_ids.append(d[1]) else: r_ids.append(triple[1]) r_ids.sort() self.assertEquals ([99,101,103,105,107], r_ids) def expand(self, region): """When Full Sub-trees returned as nodes traverse to expand nodes.""" result = [p for p in self.tree.range(region)] expanded = [] for triple in result: if triple[2]: for d in triple[0].leafOrder(): expanded.append(d[0]) else: expanded.append(triple[0]) return expanded def test_iterator(self): # rectangles have all even numbered coordinates for _ in range(500): self.tree.add(Region(2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250))) count = 0 for _ in self.tree: count = count + 1 self.assertEquals(500, count) def test_adding(self): # rectangles have all even numbered coordinates. Doesn't really matter since # range returns regions that intersect target, rather than being wholly contained for _ in range(500): self.tree.add(Region(2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250))) # make sure not to overlap! q_ne = self.expand(Region(125, 125, 501, 501)) q_nw = self.expand(Region(1, 125, 125, 501)) q_sw = self.expand(Region(1, 1, 125, 125)) q_se = self.expand(Region(125, 1, 511, 125)) q_all = self.expand(Region(1,1, 501, 501)) # quick check to see if any were missing combined = [] for r in q_ne + q_nw + q_sw + q_se: if r not in combined: combined.append(r) for q in q_all: if q not in combined: print (q, " missing ") # check duplicates for i in range(len(combined)): for j in range(len(combined)): if j <= i: pass else: if combined[i] == combined[j]: print ("Duplicate:", combined[i]) self.assertEquals(len(q_all), len(combined)) def ensure_maxBounding(self, node): """Ensures R-Node guarantees max bounding rectangle for all interior nodes covers descendants.""" for i in range(node.count): n = node.children[i] self.assertTrue(node.region.containsRegion(n.region)) self.ensure_maxBounding(n) def test_verticalSlices(self): # check case of vertical rectangles all in a row. This forces regular extensions. # Make sure bounding rectangles are computed properly. for x in range(1,50): self.tree.add(Region(x*6,10, x*6+3,100)) # sliding window always grabs eight of them since there is one rect every 6 pixels and query width is 47 for x in range(1, 40): s = Region(x*6, 2, x*6+47, 100) match = self.expand(s) self.assertEquals(8, len(match)) self.ensure_maxBounding(self.tree.root) def test_tiling(self): # create evenly placed rectangles (s=16_ and validate queries for x in range(1,8): for y in range(1,8): self.tree.add(Region(x*64,y*64, x*64+32,y*64+32)) for x in range(1,5): for y in range(1,5): s = Region(x*64, y*64, x*64+255, y*64+127) # includes 2x4 selection or 8 match = self.expand(s) self.assertEquals(8, len(match)) def test_multipleM(self): # check multiple m,M values for m,M in zip([3, 4, 5, 6], [7, 8, 10, 13]): self.tree = RTree(m,M) regions = [] for ctr in range(500): r = Region(2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250)) regions.append((r, ctr)) self.tree.add(r,ctr) # make sure all can be individually found for ctr in range(len(regions)): match = [m for m in self.tree.search(regions[ctr][0])][0] self.assertEquals(match, regions[ctr][0]) # make sure that all are contained within the self.ensure_maxBounding(self.tree.root) # test deletions def test_deleteOnlyOne(self): r = Region(10,20,50,60) self.tree.add(r, 99) self.assertTrue(self.tree.search(r)) self.assertEquals(Region(10,20,50,60), self.tree.root.region) # not in tree. self.assertFalse(self.tree.remove(Region(2,4,22,50))) self.assertTrue(self.tree.remove(r)) # Not present. self.assertTrue(self.tree.search(r) is None) # test deletions def test_delete(self): r = Region(10,20,50,60) r2 = Region(30,60,70,80) self.tree.add(r, 99) self.tree.add(r2, 101) self.assertTrue(self.tree.search(r)) self.assertTrue(self.tree.search(r2)) self.assertEquals(Region(10,20,70,80), self.tree.root.region) # not in tree. self.assertFalse(self.tree.remove(Region(2,4,22,50))) self.assertTrue(self.tree.remove(r2)) # one is in, other is out self.assertTrue(self.tree.search(r)) self.assertTrue(self.tree.search(r2) is None) def test_deleteFive(self): r1 = Region(38,148 , 300,288) r2 = Region(164,384 , 432,428) r3 = Region(316,342 , 456,392) r4 = Region(12,242 , 172,484) r5 = Region(324,200 , 494,276) for r in [r1,r2,r3,r4,r5]: self.tree.add(r) for r in [r1,r2,r3,r4,r5]: self.assertTrue(self.tree.search(r)) self.tree.remove(r) self.assertFalse(self.tree.search(r)) self.assertTrue (self.tree.root == None) def test_deleteThirteen(self): r1 = Region(410,168 , 482,348) r2 = Region(136,236 , 250,266) r3 = Region(290,26 , 496,470) ## FAILS when deleting 10 r4 = Region(398,24 , 444,100) r5 = Region(160,388 , 462,480) r6 = Region(50,124 , 120,486) r7 = Region(22,258 , 144,322) r8 = Region(28,148 , 392,236) r9 = Region(78,190 , 174,370) # problem on this one r10 = Region(38,142 , 232,184) r11 = Region(52,346 , 52,424) r12 = Region(70,120 , 248,400) r13 = Region(274,368 , 444,370) for r in [r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13]: self.tree.add(r) for r in [r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13]: self.assertTrue(self.tree.search(r)) self.tree.remove(r) self.assertFalse(self.tree.search(r)) self.assertTrue (self.tree.root == None) def test_multipleDeletions(self): # rectangles have all even numbered coordinates added = [] for _ in range(4096): r = Region(2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250), 2*random.randint(2,250)) self.tree.add(r) added.append(r) print (self.tree.root.level) random.shuffle(added) for r in added: self.assertTrue(self.tree.search(r)) self.tree.remove(r) self.assertFalse(self.tree.search(r)) # once done, R-Tree is empty once again ct = 0 for _ in self.tree: ct += 1 self.assertEquals (0, ct)
def setUp(self): self.tree = RTree()
class RTreeApp: def __init__(self): """App for creating R tree dynamically and executing range queries""" self.tree = RTree(m=2, M=4) self.ready = False # for range query self.selectedRegion = None self.newRegionStart = None self.newRegion = None # for identifiers self.counter = 0 self.master = tkinter.Tk() self.master.title('R Tree Range Query Application') self.w = tkinter.Frame(self.master, width=512, height=512) self.canvas = tkinter.Canvas(self.w, width=512, height=512) self.paint() self.canvas.bind("<Button-1>", self.startAdd) self.canvas.bind("<B1-Motion>", self.extendAdd) # only when right mouse dragged self.canvas.bind("<ButtonRelease-1>", self.endAdd) self.canvas.bind("<Button-3>", self.range) # when right mouse clicked self.canvas.bind("<ButtonRelease-3>", self.clear) self.canvas.bind("<B3-Motion>", self.range) # only when right mouse dragged self.w.pack() def startAdd(self, event): """End of range search.""" x = event.x y = self.toCartesian(event.y) self.newRegionStart = (x, y) def extendAdd(self, event): """End of range search.""" if self.newRegionStart: x = event.x y = self.toCartesian(event.y) self.newRegion = Region(x, y, x, y).unionPoint(self.newRegionStart) self.paint() def endAdd(self, event): """End of range search.""" if self.newRegionStart: self.newRegionStart = None self.counter += 1 if self.newRegion: self.tree.add(self.newRegion, str(self.counter)) self.newRegion = None self.paint() def toCartesian(self, y): """Convert tkinter point into Cartesian.""" return self.w.winfo_height() - y def toTk(self, y): """Convert Cartesian into tkinter point.""" if y == maxValue: return 0 tk_y = self.w.winfo_height() if y != minValue: tk_y -= y return tk_y def clear(self, event): """End of range search.""" self.selectedRegion = None self.paint() def range(self, event): """Initiate a range search using a selected rectangular region.""" p = (event.x, self.toCartesian(event.y)) if self.selectedRegion is None: self.selectedStart = Region(p[X], p[Y], p[X], p[Y]) self.selectedRegion = self.selectedStart.unionPoint(p) self.paint() # return (node,0,True) where status is True if draining entire tree rooted at node. If False, # then (rect,id,False). Draw these # as shaded red rectangle to identify whole sub-tree is selected. for triple in self.tree.range(self.selectedRegion): if triple[2]: r = triple[0].region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Red', stipple='gray12') else: r = triple[0] self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Red') def click(self, event): """Add point to KDtree.""" p = (event.x, self.toCartesian(event.y)) self.tree.add(p) self.paint() def visit(self, n): """ Visit node to paint properly.""" if n == None: return if n.level == 0: for idx in range(n.count): r = n.children[idx].region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), fill='Gray') for idx in range(n.count): self.visit(n.children[idx]) # Do after all children so we can see interior nodes, too. r = n.region self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max), outline='Black', dash=(2, 4)) color = 'Gray' if n.level == 0: color = 'Black' self.canvas.create_text(r.x_max - 16 * len(n.id), self.toTk(r.y_max) + 16, anchor=tkinter.W, font="Times 16 bold", fill=color, text=n.id) def prepare(self, event): """prepare to add points.""" if self.label: self.label.destroy() self.label = None self.canvas.pack() def paint(self): """Paint R tree by visiting all nodes, or show introductory message.""" if self.ready: self.canvas.delete(tkinter.ALL) self.visit(self.tree.root) if self.newRegion: self.canvas.create_rectangle(self.newRegion.x_min, self.toTk(self.newRegion.y_min), self.newRegion.x_max, self.toTk(self.newRegion.y_max), outline='Black', dash=(2, 4)) if self.selectedRegion: self.canvas.create_rectangle( self.selectedRegion.x_min, self.toTk(self.selectedRegion.y_min), self.selectedRegion.x_max, self.toTk(self.selectedRegion.y_max), outline='Red', dash=(2, 4)) else: self.label = tkinter.Label(self.w, width=100, height=40, text="Click To Add Points") self.label.bind("<Button-1>", self.prepare) self.label.pack() self.ready = True