{"id":16444807,"url":"https://github.com/plokhotnyuk/rtree2d","last_synced_at":"2025-03-15T11:33:09.735Z","repository":{"id":34018137,"uuid":"132483497","full_name":"plokhotnyuk/rtree2d","owner":"plokhotnyuk","description":"RTree2D is a 2D immutable R-tree for ultra-fast nearest and intersection queries in plane and spherical coordinates","archived":false,"fork":false,"pushed_at":"2024-05-08T05:11:01.000Z","size":16734,"stargazers_count":127,"open_issues_count":9,"forks_count":11,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-05-21T01:04:34.472Z","etag":null,"topics":["2d","geo-index","high-performance","rtree","scala","scala-js","scala-native","sort-tile-recursive","spatial-index","str","str-packing"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plokhotnyuk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":"FUNDING.yml","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,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"plokhotnyuk","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2018-05-07T15:52:10.000Z","updated_at":"2024-05-08T05:11:05.000Z","dependencies_parsed_at":"2023-09-26T22:26:25.394Z","dependency_job_id":"1eea0391-b76e-493f-baf4-414aaa29948f","html_url":"https://github.com/plokhotnyuk/rtree2d","commit_stats":null,"previous_names":["sizmek/rtree2d"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plokhotnyuk%2Frtree2d","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plokhotnyuk%2Frtree2d/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plokhotnyuk%2Frtree2d/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plokhotnyuk%2Frtree2d/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plokhotnyuk","download_url":"https://codeload.github.com/plokhotnyuk/rtree2d/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243725030,"owners_count":20337660,"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":["2d","geo-index","high-performance","rtree","scala","scala-js","scala-native","sort-tile-recursive","spatial-index","str","str-packing"],"created_at":"2024-10-11T09:26:51.769Z","updated_at":"2025-03-15T11:33:09.185Z","avatar_url":"https://github.com/plokhotnyuk.png","language":"Scala","readme":"# RTree2D\n\n[![Actions Build](https://github.com/plokhotnyuk/rtree2d/workflows/build/badge.svg)](https://github.com/plokhotnyuk/rtree2d/actions)\n[![Scala Steward](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)\n[![Scala.js](https://www.scala-js.org/assets/badges/scalajs-1.0.0.svg)](https://www.scala-js.org)\n[![Maven Central](https://img.shields.io/badge/maven--central-0.11.13-blue.svg)](https://search.maven.org/search?q=com.github.plokhotnyuk.rtree2d)\n\nRTree2D is a 2D immutable [R-tree](https://en.wikipedia.org/wiki/R-tree) with \n[STR (Sort-Tile-Recursive)](https://archive.org/details/DTIC_ADA324493) packing for ultra-fast nearest and intersection \nqueries.\n\n## Goals\n\nMain our requirements was:\n- *efficiency* - we wanted the R-Tree to be able to search through millions of entries efficiently even in case of highly \noverlapped entries, also, we needed to be able to quickly rebuild R-tries with a per minute rate producing minimum \npressure on GC\n- *immutability* - different threads needed to be able to work with the same R-tree without problems,\nat the same time some thread can build a new version of the R-tree reusing immutable entries from the previous version\n\nTo archive these goals we have used:\n- STR packing that is a one of the most efficient packing method which produces balanced R-tree\n- a memory representation and access patterns to it which are aware of a cache hierarchy of contemporary CPUs\n- an efficient TimSort version of merge sorting from Java which minimize access to memory during packing \n- efficient implementations of nearest and range search functions with minimum of virtual calls and allocations\n\n## How to use\n\nAdd the library to a dependency list:\n\n```sbt\nlibraryDependencies += \"com.github.plokhotnyuk.rtree2d\" %% \"rtree2d-core\" % \"0.11.13\"\n ```\n\nEntries of R-tree are represented by `RTreeEntry` instances which contains payload and 4 coordinates of the minimum \nbounding rectangle (MBR) for it.\n\nAdd import, create entries, build an R-tree from them, and use it for search a nearest entry or search intersections \nby point or rectangle requests:\n\n```scala\nimport com.github.plokhotnyuk.rtree2d.core._                                                         \nimport EuclideanPlane._                                                                  \n                                                                                         \nval box1 = entry(1.0f, 1.0f, 2.0f, 2.0f, \"Box 1\")                                        \nval box2 = entry(2.0f, 2.0f, 3.0f, 3.0f, \"Box 2\")                                        \nval entries = Seq(box1, box2)                                                            \n                                                                                         \nval rtree = RTree(entries)                                                               \n                                                                                         \nassert(rtree.entries == entries)                                                         \nassert(rtree.nearestOption(0.0f, 0.0f) == Some(box1))                      \nassert(rtree.nearestOption(0.0f, 0.0f, maxDist = 1.0f) == None)                          \nassert(rtree.nearestK(0.0f, 0.0f, k = 1) == Seq(box1))                     \nassert(rtree.nearestK(0.0f, 0.0f, k = 2, maxDist = 10f) == Seq(box2, box1))  \nassert(rtree.searchAll(0.0f, 0.0f) == Nil)                                               \nassert(rtree.searchAll(1.5f, 1.5f) == Seq(box1))                                         \nassert(rtree.searchAll(2.5f, 2.5f) == Seq(box2))                                         \nassert(rtree.searchAll(2.0f, 2.0f) == Seq(box1, box2))                                   \nassert(rtree.searchAll(2.5f, 2.5f, 3.5f, 3.5f) == Seq(box2))                             \nassert(rtree.searchAll(1.5f, 1.5f, 2.5f, 2.5f).forall(entries.contains))                 \n```\n\nRTree2D can be used for indexing spherical coordinates, where X-axis is used for latitudes, and Y-axis for longitudes\nin degrees. Result distances are in kilometers:\n \n```scala\nimport com.github.plokhotnyuk.rtree2d.core._\nimport SphericalEarth._\n\nval city1 = entry(50.0614f, 19.9383f, \"Kraków\")\nval city2 = entry(50.4500f, 30.5233f, \"Kyiv\")\nval entries = Seq(city1, city2)\n\nval rtree = RTree(entries, nodeCapacity = 4/* the best capacity for nearest queries for spherical geometry */)\n\nassert(rtree.entries == entries)\nassert(rtree.nearestOption(0.0f, 0.0f) == Some(city1))\nassert(rtree.nearestOption(50f, 20f, maxDist = 1.0f) == None)\nassert(rtree.nearestK(50f, 20f, k = 1) == Seq(city1))\nassert(rtree.nearestK(50f, 20f, k = 2, maxDist = 1000f) == Seq(city2, city1))\nassert(rtree.searchAll(50f, 30f, 51f, 31f) == Seq(city2))\nassert(rtree.searchAll(0f, -180f, 90f, 180f).forall(entries.contains))\n```\nPrecision of 32-bit float number allows to locate points with a maximum error ±1 meter at anti-meridian.\n\nUsed spherical model of the Earth with [the Mean radius](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius) and\n[Haversine formula](https://en.wikipedia.org/wiki/Haversine_formula) allow to get ±0.3% accuracy in calculation of\ndistances comparing with [Vincenty’s formulae](http://www.cqsrg.org/tools/GCDistance/) on an oblate spheroid model. \n\nPlease, check out\n[Scala docs in sources](https://github.com/plokhotnyuk/rtree2d/blob/master/core/src/main/scala/com/github/plokhotnyuk/rtree2d/core/RTree.scala) \nand [tests](https://github.com/plokhotnyuk/rtree2d/blob/master/core/src/test/scala/com/github/plokhotnyuk/rtree2d/core/RTreeTest.scala)\nfor other functions which allows filtering or accumulating found entries without allocations. \n\n## How it works\n\nCharts below are latest results of benchmarks which compare RTree2D with [Archery](https://github.com/non/archery),\n[David Monten's rtree](https://github.com/davidmoten/rtree), and [JTS](https://github.com/locationtech/jts) libraries \non the following environment: Intel® Core™ i9-11900H CPU @ 2.5GHz (max 4.9GHz), RAM 32Gb DDR4-3200, Ubuntu 22.04, \nOracle JDK 17.\n\nMain metric tested by benchmarks is an execution time in nanoseconds. So lesser values are better. Please, check out \nthe Run benchmarks section bellow how to test other metrics like allocations in bytes or number of some CPU events.     \n\nBenchmarks have the following parameters:\n- `geometry` to switch geometry between `plane` and `spherical` (currently available only for the RTree2D library)\n- `nearestMax` a maximum number of entries to return for nearest query \n- `nodeCapacity` a maximum number of children nodes (BEWARE: Archery use hard coded 50 for limiting a number of children nodes)\n- `overlap` is a size of entries relative to interval between them \n- `partToUpdate` a part of RTree to update\n- `rectSize` is a size of rectangle request relative to interval between points\n- `shuffle` is a flag to turn on/off shuffling of entries before R-tree building\n- `size` is a number of entries in the R-tree\n\nThe `apply` benchmark tests building of R-tries from a sequence of entires.\n\n[![apply](docs/overlap-1/apply[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/apply[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `nearest` benchmark tests search an entry of the R-tree that is nearest to the specified point.\n\n[![nearest](docs/overlap-1/nearest[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/nearest[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `nearestK` benchmark tests search up to 10 entries in the R-tree that are nearest to the specified point.\n\n[![nearest](docs/overlap-1/nearestK[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/nearestK[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `searchByPoint` benchmark tests requests that search entries with intersects with the specified point.\n\n[![searchByPoint](docs/overlap-1/searchByPoint[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/searchByPoint[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `searchByRectangle` benchmark tests requests that search entries with intersects with the specified rectangle that\ncan intersect with up to 100 entries.\n\n[![searchByRectangle](docs/overlap-1/searchByRectangle[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/searchByRectangle[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `entries` benchmark tests returning of all entries that are indexed in the R-tree.\n\n[![entries](docs/overlap-1/entries[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/entries[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\nThe `update` benchmark tests rebuild of R-tree with removing of +10% entries and adding of +10% another entries to it.\n\n[![update](docs/overlap-1/update[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)](docs/overlap-1/update[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png)\n\n\nCharts with their results are available in subdirectories (each for different value of overlap parameter) of the\n [docs](docs/) directory.  \n\n## How to contribute\n\n### Build\n\nTo compile, run tests, check coverage for different Scala versions use a command:\n\n```sh\nsbt clean +test\nsbt clean coverage test coverageReport mimaReportBinaryIssues\n```\n\n### Run benchmarks\n\nBenchmarks are developed in the separated module using [Sbt plugin](https://github.com/ktoso/sbt-jmh)\nfor [JMH tool](http://openjdk.java.net/projects/code-tools/jmh/). \n\nFeel free to modify benchmarks and check how it works with your data, JDK, and Scala versions.\n\nTo see throughput with allocation rate run benchmarks with GC profiler using the following command:\n\n```sh\nsbt -java-home /usr/lib/jvm/jdk-17 clean 'rtree2d-benchmark/jmh:run -prof gc -rf json -rff rtries.json .*'\n```\n\nIt will save benchmark report in `rtree2d-benchmark/rtries.json` file.\n\nResults that are stored in JSON can be easy plotted in [JMH Visualizer](http://jmh.morethan.io/) by drugging \u0026 dropping\nof your file(s) to the drop zone or using the `source` or `sources` parameters with an HTTP link to your file(s) in the \nURLs: `http://jmh.morethan.io/?source=\u003clink to json file\u003e` or \n`http://jmh.morethan.io/?sources=\u003clink to json file1\u003e,\u003clink to json file2\u003e`.\n\nAlso, there is an ability to run benchmarks and visualize results with a `charts` command. It adds `-rf` and `-rff` \noptions to all passes options and supply them to `jmh:run` task, then group results per benchmark and plot main score \nseries to separated images. Here is an example how it can be called for specified version of JVM, value of the `overlap`\nparameter, and patterns of benchmark names:\n\n```sh\nsbt -java-home /usr/lib/jvm/zulu-17 clean 'charts -p overlap=1 -p rectSize=10 -p nearestMax=10 -p nodeCapacity=16 -p partToUpdate=0.1 -p geometry=plane .*'\n```\n\nResults will be places in a cross-build suffixed subdirectory of the `benchmark/target` directory in `*.png` files\n(one file with a chart for each benchmark):\n```sh\n$ ls rtree2d-benchmark/target/scala-2.12/*.png\n\nrtree2d-benchmark/target/scala-2.12/apply[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png\n...\nrtree2d-benchmark/target/scala-2.12/searchByRectangle[geometry=plane,nearestMax=10,nodeCapacity=16,overlap=1,partToUpdate=0.1,rectSize=10].png\n``` \n\nFor testing of RTree2D with `spherical` geometry and different node capacities use the following command (chart files \nwill be placed in the same directory as above):\n\n```sh\nsbt -java-home /usr/lib/jvm/zulu-17 clean 'charts -p overlap=1 -p rectSize=10 -p nearestMax=10 -p nodeCapacity=4,8,16 -p partToUpdate=0.1 -p geometry=spherical RTree2D.*'\n```\n\n### Publish locally\n\nPublish to local Ivy repo:\n\n```sh\nsbt +publishLocal\n```\n\nPublish to local Maven repo:\n\n```sh\nsbt +publishM2\n```\n\n### Release\n\nFor version numbering use [Recommended Versioning Scheme](http://docs.scala-lang.org/overviews/core/binary-compatibility-for-library-authors.html#recommended-versioning-scheme)\nthat is used in the Scala ecosystem.\n\nDouble check binary and source compatibility, including behavior, and release using the following command (credentials \nare required):\n\n```sh\nsbt -java-home /usr/lib/jvm/jdk-8 -J-Xmx8g clean release\n```\n\nDo not push changes to github until promoted artifacts for the new version are not available for download on \n[Maven Central Repository](http://repo1.maven.org/maven2/com/github/plokhotnyuk/rtree2d) \nto avoid binary compatibility check failures in triggered Travis CI builds. \n","funding_links":["https://github.com/sponsors/plokhotnyuk"],"categories":["Scala","Table of Contents"],"sub_categories":["Geospatial"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplokhotnyuk%2Frtree2d","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplokhotnyuk%2Frtree2d","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplokhotnyuk%2Frtree2d/lists"}