{"id":17693776,"url":"https://github.com/stefansalewski/rtree","last_synced_at":"2025-05-05T20:25:11.710Z","repository":{"id":109492728,"uuid":"113615384","full_name":"StefanSalewski/RTree","owner":"StefanSalewski","description":"Generic RTree for Nim","archived":false,"fork":false,"pushed_at":"2021-04-03T10:26:25.000Z","size":30,"stargazers_count":26,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-06T04:46:07.994Z","etag":null,"topics":["bulk-load","dynamic","generic","k-nearest-neighbor","nim","nim-lang","nim-language","r-tree","spatial-index"],"latest_commit_sha":null,"homepage":"http://ssalewski.de/tmp/rtree.html","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StefanSalewski.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-12-08T20:40:00.000Z","updated_at":"2024-09-12T13:19:34.000Z","dependencies_parsed_at":"2023-07-14T22:00:30.166Z","dependency_job_id":null,"html_url":"https://github.com/StefanSalewski/RTree","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2FRTree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2FRTree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2FRTree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2FRTree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StefanSalewski","download_url":"https://codeload.github.com/StefanSalewski/RTree/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246392148,"owners_count":20769690,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bulk-load","dynamic","generic","k-nearest-neighbor","nim","nim-lang","nim-language","r-tree","spatial-index"],"created_at":"2024-10-24T13:46:57.426Z","updated_at":"2025-03-30T23:21:29.064Z","avatar_url":"https://github.com/StefanSalewski.png","language":"Nim","readme":"= R-Trees\n(c) Stefan Salewski                                     \n:experimental:\n:imagesdir: http://ssalewski.de/tmp\n:source-highlighter: pygments\n:pygments-style: monokai\n:icons: font\n:toc: left\n\n== Pure Nim generic R-Tree and R*-Tree\n\nR-Trees are dynamic tree data structures for spatial indexing/searching.\n\nimage::NimRTree.png[]\n\nTypical objects to locate are rectangles, polygons, circles or other arbitrary shapes.\n\nWhile used mostly to locate objects in two-dimensional space, this module supports\narbitrary dimensions with coordinates types including int and float.\n\nIncremental insertion and deletion of objects is supported, as well as range search\n(objects overlapping with a query box) and incremental nearest neighbor search. Additional\nbulk loading of an initial set of objects is supported, giving shorter tree construction times,\nlower memory consumption and faster access times. Bulk loaded trees remain fully dynamic. The\nbasic R-Tree as well as the improved R*-Tree is supported.\n\nLocating objects works with bounding boxes, rectangles in 2D. Each element has a bounding box,\nand for searching we specify a query box. The boxes are an array of `Ext[RT] = tuple[a, b: RT]`,\nwith an array element for each dimension, and `Ext` is a tuple of a range type RT like int or float.\nFor the range itself it is important that `a \u003c= b`.\n\nThe search() proc returns all objects overlapping (partly) with a query box. Maybe later we can add\none more proc which returns only objects which are fully contained in the query box. (That is very easy --\nthe difficult part is finding good names for additional procs.)\n\nThe nearest() procs and iterators gets not a box, but a point called BoxCenter as parameter\nand return elements nearest to the query point.\n\nWe can use a plain R-Tree or the advanced R*-Tree to store our objects. Most of the time\nwe will use the R*-Tree, because it's optimized splitting algorithm should give\nbetter performance. First generic parameter of new() proc is the maximal number\nof childs per node. 8 is a common value, 2 is min for plain R-Tree, 4 is min for\nR*-Tree. Maximal number of children is 100, but it is not really limited. Next\nthree generic parameters are dimension \u003e= 1, coordinates type and object type.\nAdditional you can pass the minimal fill factor, which is a percent value between\n30 and 50. All these parameters can have an effect on performance and memory consumption,\nbut general the effects are small. The bulk loading procs gets a seq containing the\ndata objects as a parameter. Generally bulk loading is faster than single inserts and\nshould give an optimized tree -- optimized for performance and memory consumption.\nBulk loaded trees are also fully dynamic, you can later insert() and delete() elements.\nBulk loading should have advantages if elements are available early, and when the\ntree is mostly static. Inserting new elements into a bulk loaded tree may be not\nextremely fast, because nodes of bulk loaded trees are totally filled, so later inserts()\nleads to splits, which takes some time. So for very dynamic data bulk loading may not\nbe really the best solution.\n\nFor nearest neighbor search we have a plain proc which returns a single element nearest\nto a query point, and an iterator which can return multiple elements. Both proc and\niterator are available in a simple version which takes into account only the bounding\nboxes of the objects, and a version which takes a dist() proc as additional parameter.\nThe dist() proc can estimate the distance more accurate, but may be slower.\n\nNOTE: The dist() proc has to return the SQUARED distance from object to query point,\nand this distance value has always to be not negative and not greater than the squared distance from\nthe query point to the bounding box of the object. The later condition is true for\neach meaningful dist() function, the relation allows pruning of objects first by\nplain bounding box checks, resulting in improved performance. The squared distance is\nused because this can avoid sqrt() calculations in many cases -- for the circle dist()\nfunction this is not true.\n\nMost procs for insertion, deletion and location of objects accepts a tuple consisting\nof the bounding box and the object itself as parameter or return this tuple type.\nThis type `L[D; RT; LT] = tuple[b: Box[D, RT], l: LT]` is sometimes called record.\nAnd exception is the proc searchObj() which returns only the object itself, not the box.\nThis is to save costly seq operations when only the objects itself are requested.\n\n=== API\n\nhttp://ssalewski.de/tmp/rtree.html\n\nThe API may change in later versions of this software!\n\n=== Install\n\n----\nnimble install rtree\n----\n\n=== Examples\n\nTo give an not too trivial example, we will use some circles in 2D with float coordinates.\nWe will define a proc box() for our circles, which gives us the bounding\nrectangle, and a proc dist() which gives the distance of a circle from a query point.\n\n[[test.nim]]\n[source,nim]\n.test.nim\n----\n# nim c test.nim\nimport rtree\nfrom math import sqrt, `^`\n\ntype\n  Circ = object\n    x, y, r: float\n\n# squared distance from query point to circle\nproc dist(qo: BoxCenter[2, float]; l: L[2, float, Circ]): float =\n  let c = l.l\n  max(0, math.sqrt((qo[0] - c.x) ^ 2 + (qo[1] - c.y) ^ 2) - c.r) ^ 2\n\n# bounding box of circle\nproc box(c: Circ): Box[2, float] =\n  result[0].a = c.x - c.r\n  result[0].b = c.x + c.r\n  result[1].a = c.y - c.r\n  result[1].b = c.y + c.r\n\nproc test =\n  const P = [(9, 3, 2), (-2, 2, 1), (-5, -3, 2), (4, -2, 1)]\n  var s: seq[L[2, float, Circ]] # buffer for bulk load\n  var t = newRStarTree[8, 2, float, Circ]()\n  var el: L[2, float, Circ]\n  for p in P:\n    let c = Circ(x: p[0].float, y: p[1].float, r: p[2].float)\n    el = (box(c), c)\n    s.add(el)\n    t.insert(el)\n  echo \"circ in first quadrant:\"\n  echo t.search([(0.0, 12.0), (0.0, 12.0)])\n  echo \"circs below x axis\"\n  echo t.search([(-20.0, 20.0), (-12.0, 0.0)])\n  echo \"empty area\"\n  echo t.search([(0.0, 5.0), (0.0, 5.0)])\n\n  echo \"search a single circle located closest to origin using only bounding boxes\"\n  echo t.findNearestBox(BoxCenter[2, float]([0.0, 0.0]))\n  echo \"query circles sorted along x-axis, using only bounding boxes\"\n  for el in t.findNearestBox(BoxCenter[2, float]([-100.0, 0.0])): # sort along x axis\n    echo el.l\n\n  echo \"search a single circle located closest to origin using exact dist() function\"\n  echo t.findNearest(BoxCenter[2, float]([0.0, 0.0]), dist)\n  echo \"query circles sorted along x-axis, using dist() function\"\n  for el in t.findNearest(BoxCenter[2, float]([-100.0, 0.0]), dist):\n    echo el.l\n\n  # and we can delete objects:\n  assert(t.delete(s[0]))\n  assert(not t.delete(s[0])) # that object was already deleted\n\n  var bt = newRStarTree[8, 2, float, Circ](s) # a bulk loaded R-Tree, should work like t\n  echo \"done!\"\n\ntest()\n----\n\n=== Testing\n\nThe rtee.nim module contains testing code, which is executed when you compile and run\nit directly as main module. When you compile with option `-d:cairoGTK` a GUI window\nis opened showing the tree of rectangles. For the later you have to install gintro\nto enable cairo and GTK support. All testing has been done only for 2D yet -- bugs\nfor other dimensions should be easy to fix when they exist.\n\n=== Performance\n\nAll the used algorithm where described in detail in the papers listed below and implemented in\nNim language, so performance is expected to be good and state of the art. The module is currently used\nmost of the time only with a few thousand objects, for example to locate objects on a 2D canvas.\nFor this usecase performance is more than sufficient. When you intent to use the module for\nvery large data sets, then we recommend that you do some benchmarking and profiling yourself. You may compare\nplain R-Tree to R*-Tree, test impact of bulk loading, and test various number of child nodes and\nfill factor. A simple way to improve performance may be to mark some of the smaller procs with\n{.inline.} pragma -- this should be not necessary when you compile your app with -flto option to\nenable link-time-optimization.\n\n=== References\n\n* https://en.wikipedia.org/wiki/R-tree[Wikipedia R-Tree]\n* http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf[original R-Tree by Guttman]\n* http://dbs.mathematik.uni-marburg.de/publications/myPapers/1990/BKSS90.pdf[improved R*-Tree]\n* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.386.8193\u0026rep=rep1\u0026type=pdf[incremental k nearest neighbor search]\n* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.9245\u0026rep=rep1\u0026type=pdf[sketch of TGS bulk loading] \n* http://www.cs.umd.edu/~hjs/pubs/AlborziIPL07.pdf[detailed algorithm of TGS bulk loading]\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefansalewski%2Frtree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstefansalewski%2Frtree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefansalewski%2Frtree/lists"}