{"id":13482271,"url":"https://github.com/geocrystal/kd_tree","last_synced_at":"2025-05-03T16:31:52.816Z","repository":{"id":66224757,"uuid":"144301727","full_name":"geocrystal/kd_tree","owner":"geocrystal","description":"Crystal implementation of \"K-Dimensional Tree\" and \"N-Nearest Neighbors\"","archived":false,"fork":false,"pushed_at":"2024-07-29T10:44:02.000Z","size":66,"stargazers_count":16,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-30T15:58:42.444Z","etag":null,"topics":["crystal","data-structures","k-dimensional","priority-queue","tree-structure"],"latest_commit_sha":null,"homepage":"","language":"Crystal","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/geocrystal.png","metadata":{"files":{"readme":"README.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-10T15:13:37.000Z","updated_at":"2024-07-29T10:42:53.000Z","dependencies_parsed_at":"2024-07-29T13:57:18.538Z","dependency_job_id":"541120d5-9e8a-46d8-aa1f-f29ce64647b1","html_url":"https://github.com/geocrystal/kd_tree","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fkd_tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fkd_tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fkd_tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fkd_tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geocrystal","download_url":"https://codeload.github.com/geocrystal/kd_tree/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224367372,"owners_count":17299501,"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":["crystal","data-structures","k-dimensional","priority-queue","tree-structure"],"created_at":"2024-07-31T17:01:00.456Z","updated_at":"2024-11-13T00:23:41.506Z","avatar_url":"https://github.com/geocrystal.png","language":"Crystal","funding_links":[],"categories":["Algorithms and Data structures"],"sub_categories":[],"readme":"# Kd::Tree\n\n[![Crystal CI](https://github.com/geocrystal/kd_tree/actions/workflows/crystal.yml/badge.svg)](https://github.com/geocrystal/kd_tree/actions/workflows/crystal.yml)\n[![GitHub release](https://img.shields.io/github/release/geocrystal/kd_tree.svg)](https://github.com/geocrystal/kd_tree/releases)\n[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://geocrystal.github.io/kd_tree/)\n[![License](https://img.shields.io/github/license/geocrystal/kd_tree.svg)](https://github.com/geocrystal/kd_tree/blob/master/LICENSE)\n\nCrystal implementation of \"K-Dimensional Tree\" and \"N-Nearest Neighbors\"\nbased on \u003chttp://en.wikipedia.org/wiki/Kd-tree\u003e.\n\n## Installation\n\nAdd this to your application's `shard.yml`:\n\n```yaml\ndependencies:\n  kd_tree:\n    github: geocrystal/kd_tree\n```\n\n## Usage\n\n```crystal\nrequire \"kd_tree\"\n```\n\nFor example, construct a new tree where each point is represented as a two-dimensional array in the form [x, y], where x and y are numbers (such as Int32, Float64, etc).\n\n```crystal\nkd = Kd::Tree(Array(Int32)).new(points)\n```\n\nFind the nearest point to `[x, y]`. Returns an array with one point:\n\n```crystal\nkd.nearest([x, y])\n```\n\nFind the nearest `k` points to `[x, y]`. Returns an array of points:\n\n```crystal\nkd.nearest([x, y], k)\n```\n\n## Example\n\n```crystal\nrequire \"kd_tree\"\n\npoints = [\n  [2.0, 3.0],\n  [5.0, 4.0],\n  [4.0, 7.0],\n  [7.0, 2.0],\n  [8.0, 1.0],\n  [9.0, 6.0],\n]\n\nkd = Kd::Tree(Array(Float64)).new(points)\n\nkd.nearest([1.0, 1.0])\n# =\u003e [[2.0, 3.0]])\n\nkd_tree.nearest([1.0, 1.0], 2)\n# =\u003e [[2.0, 3.0], [5.0, 4.0]])\n```\n\n### Complex objects\n\n`Kd::Tree(T)` can accept any object that responds to `#size` and `#[](i : Int)` methods.\n\n```crystal\nclass GeoLocation\n  property name : String\n  property longitude : Float64\n  property latitude : Float64\n  getter size = 2 # Assuming all GeoLocation objects are 2-dimensional\n\n  def initialize(@name : String, @longitude : Float64, @latitude : Float64)\n  end\n\n  # Define an indexer to allow easy access by index for longitude and latitude\n  def [](index : Int32) : Float64\n    case index\n    when 0 then @longitude\n    when 1 then @latitude\n    else        raise \"Index out of bounds\"\n    end\n  end\nend\n\n# Create an array of GeoLocation points\npoints = [\n  GeoLocation.new(\"New York\", -73.935242, 40.730610),\n  GeoLocation.new(\"Los Angeles\", -118.243683, 34.052235),\n  GeoLocation.new(\"London\", -0.127647, 51.507322),\n  GeoLocation.new(\"Tokyo\", 139.691711, 35.689487),\n]\n\n# Initialize the KD-tree with these points\nkd_tree = Kd::Tree(GeoLocation).new(points)\n\n# Find the nearest point to London\ntarget = GeoLocation.new(\"Near London\", -0.125740, 51.508530)\nnearest_point = kd_tree.nearest(target, 1)\nputs \"Nearest to London: #{nearest_point.first.name} (longitude #{nearest_point.first.longitude}, latitude #{nearest_point.first.latitude})\"\n# Nearest to London: London (longitude -0.127647, latitude 51.507322)\n```\n\n### Distance\n\nFor distance calculations, the squared Euclidean distance is used. However, you can easily monkey-patch the `Kd::Tree#distance` method to implement another algorithm, such as the Haversine formula, to calculate distances between two points given their latitudes and longitudes.\n\n```crystal\nrequire \"haversine\"\n\nmodule Kd\n  class Tree(T)\n    private def distance(m : T, n : T)\n      # Calling `Haversine.distance` with 2 pairs of latitude/longitude coordinates.\n      # Returns a distance in meters.\n      Haversine.distance({m.latitude, m.longitude}, {n.latitude, n.longitude}).to_meters\n    end\n  end\nend\n\npoints = [\n  GeoLocation.new(\"New York\", -73.935242, 40.730610),\n  GeoLocation.new(\"Los Angeles\", -118.243683, 34.052235),\n  GeoLocation.new(\"London\", -0.127647, 51.507322),\n  GeoLocation.new(\"Tokyo\", 139.691711, 35.689487),\n]\n\nkd_tree = Kd::Tree(GeoLocation).new(points)\n\n# Find the nearest point to London\ntarget = GeoLocation.new(\"Near London\", -0.125740, 51.508530)\nnearest_point = kd_tree.nearest(target, 1)\nputs \"Nearest to London: #{nearest_point.first.name} (longitude #{nearest_point.first.longitude}, latitude #{nearest_point.first.latitude})\"\n# Nearest to London: London (longitude -0.127647, latitude 51.507322)\n```\n\n## Performance\n\nUsing a tree with 1 million points `[x, y] of Float64` on my Apple M1 Pro (10) @ 3.23 GHz:\n\n`crystal run benchmark/benchmark.cr --release`\n\n```console\nBenchmarking KD-Tree with 1 million points\n                        user     system      total        real\nbuild(init)         1.840140   0.021103   1.861243 (  1.872732)\nnearest point   1   0.004484   0.000002   0.004486 (  0.004490)\nnearest point   5   0.007391   0.000010   0.007401 (  0.007479)\nnearest point  10   0.011406   0.000090   0.011496 (  0.011679)\nnearest point  50   0.034097   0.000819   0.034916 (  0.035175)\nnearest point 100   0.133828   0.003721   0.137549 (  0.156548)\nnearest point 255   0.220200   0.000631   0.220831 (  0.223081)\nnearest point 999   0.731941   0.000441   0.732382 (  0.737236)\n```\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/geocrystal/kd_tree/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [mamantoha](https://github.com/mamantoha) Anton Maminov - creator, maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fkd_tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeocrystal%2Fkd_tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fkd_tree/lists"}