{"id":18768693,"url":"https://github.com/get-convex/geospatial","last_synced_at":"2025-08-15T18:39:36.010Z","repository":{"id":258157014,"uuid":"854792635","full_name":"get-convex/geospatial","owner":"get-convex","description":"Convex component for geospatial indexing","archived":false,"fork":false,"pushed_at":"2025-05-30T23:41:50.000Z","size":1236,"stargazers_count":11,"open_issues_count":7,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-08T14:52:29.136Z","etag":null,"topics":["geospatial","inverted-index","s2","search","spatial-indexing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/get-convex.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,"zenodo":null}},"created_at":"2024-09-09T19:34:35.000Z","updated_at":"2025-07-24T00:51:00.000Z","dependencies_parsed_at":"2024-10-17T21:39:59.644Z","dependency_job_id":"9c3cf63d-1f7f-4a59-835b-d2d83e22628c","html_url":"https://github.com/get-convex/geospatial","commit_stats":null,"previous_names":["get-convex/geospatial"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/get-convex/geospatial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fgeospatial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fgeospatial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fgeospatial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fgeospatial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/get-convex","download_url":"https://codeload.github.com/get-convex/geospatial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fgeospatial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270614530,"owners_count":24616724,"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","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["geospatial","inverted-index","s2","search","spatial-indexing"],"created_at":"2024-11-07T19:13:37.240Z","updated_at":"2025-08-15T18:39:35.994Z","avatar_url":"https://github.com/get-convex.png","language":"TypeScript","readme":"# Convex Geospatial Index (Beta)\n\n[![npm version](https://badge.fury.io/js/@convex-dev%2Fgeospatial.svg)](https://badge.fury.io/js/@convex-dev%2Fgeospatial)\n\n![image](https://frugal-mandrill-176.convex.cloud/api/storage/8fb21c7f-441c-4ce9-9abb-925c31e9faab)\n\n\u003c!-- START: Include on https://convex.dev/components --\u003e\n\nThis component adds a geospatial index to Convex, allowing you to efficiently store and query points on the Earth's surface.\n\n- Insert points into the geospatial key value store along with their geographic coordinates.\n- Efficiently query for all points within a given rectangle on the sphere.\n- Control the sort order for the results with a custom sorting key.\n- Filter query results with equality and `IN` clauses.\n- And since it's built on Convex, everything is automatically consistent, reactive, and cached!\n\nThis component is currently in beta. It's missing some functionality, but what's there should work. We've tested the example\napp up to about 1,000,000 points, so reach out if you're using a much larger dataset.\nIf you find a bug or have a feature request, you can [file it here](https://github.com/get-convex/geospatial/issues).\n\n## Pre-requisite: Convex\n\nYou'll need an existing Convex project to use the component.\nConvex is a hosted backend platform, including a database, serverless functions,\nand a ton more you can learn about [here](https://docs.convex.dev/get-started).\n\nRun `npm create convex` or follow any of the [quickstarts](https://docs.convex.dev/home) to set one up.\n\n## Installation\n\nFirst, add `@convex-dev/geospatial` to your Convex project:\n\n```bash\nnpm install @convex-dev/geospatial\n```\n\nThen, install the component into your Convex project within the `convex/convex.config.ts` file:\n\n```ts\n// convex/convex.config.ts\nimport geospatial from \"@convex-dev/geospatial/convex.config\";\nimport { defineApp } from \"convex/server\";\n\nconst app = defineApp();\napp.use(geospatial);\n\nexport default app;\n```\n\nFinally, create a new `GeospatialIndex` within your `convex/` folder, and point it to the installed component:\n\n```ts\n// convex/index.ts\nimport { GeospatialIndex } from \"@convex-dev/geospatial\";\nimport { components } from \"./_generated/api\";\n\nconst geospatial = new GeospatialIndex(components.geospatial);\n```\n\n## Inserting points\n\nAfter installing the component, you can `insert`, `get`, and `remove` points from the index. You can specify\na `filterKeys` record for filtering at query time and optionally a `sortKey` for the query result order. We\ncurrently only support ascending order on the `sortKey`.\n\n```ts\n// convex/index.ts\n\nconst example = mutation({\n  handler: async (ctx) =\u003e {\n    const cityId = await ctx.db.insert(\"cities\", {...});\n    await geospatial.insert(\n      ctx,\n      \"American Museum of Natural History\",\n      {\n        latitude: 40.7813,\n        longitude: -73.9737,\n      },\n      { category: \"museum\" },\n      28.0, // Price used as the sort key\n    );\n    const result = await geospatial.get(ctx, cityId);\n    await geospatial.remove(ctx, cityId);\n  },\n});\n```\n\nIf you would like some more typesafety, you can specify a type argument for the `GeospatialIndex` class. This\nwill also provide you with auto-complete for the `filterKeys` and `sortKey` parameters.\nAbove the key was \"American Museum of Natural History\" but most commonly the `key` will be an ID in another table of yours.\n\n```ts\n// convex/index.ts\nimport { GeospatialIndex, Point } from \"@convex-dev/geospatial\";\nimport { components } from \"./_generated/api\";\nimport { Id } from \"./_generated/dataModel\";\n\nexport const geospatial = new GeospatialIndex\u003c\n  Id\u003c\"museums\"\u003e,\n  { category: string; anotherFilter?: number }\n\u003e(components.geospatial);\n```\n\n## Querying points within a shape\n\nAfter inserting some points, you can query them with the `query` API.\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const rectangle = {\n      west: -73.9712,\n      south: 40.7831,\n      east: -72.9712,\n      north: 41.7831,\n    };\n    const result = await geospatial.query(ctx, {\n      shape: { type: \"rectangle\", rectangle },\n      limit: 16,\n    });\n    return result;\n  },\n});\n```\n\nThis query will find all points that lie within the query rectangle, sort them in ascending\n`sortKey` order, and return at most 16 results.\n\nYou can optionally add filter conditions to queries.\n\nThe first type of filter condition is an `in()` filter, which requires that a matching\ndocument have a filter field with a value in a specified set.\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const rectangle = {\n      west: -73.9712,\n      south: 40.7831,\n      east: -72.9712,\n      north: 41.7831,\n    };\n    const result = await geospatial.query(ctx, {\n      shape: { type: \"rectangle\", rectangle },\n      filter: (q) =\u003e q.in(\"category\", [\"museum\", \"restaurant\"]),\n    });\n    return result;\n  },\n});\n```\n\nThe second type of filter condition is an `eq()` filter, which requires that a matching\ndocument have a filter field with a value equal to a specified value.\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const result = await geospatial.query(ctx, {\n      shape: { type: \"rectangle\", rectangle },\n      filter: (q) =\u003e q.eq(\"category\", \"museum\"),\n    });\n    return result;\n  },\n});\n```\n\nThe final type of filter condition allows you to specify ranges over the `sortKey`. We currently only support (optional) inclusive lower bounds and exclusive upper bounds.\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const rectangle = {\n      west: -73.9712,\n      south: 40.7831,\n      east: -72.9712,\n      north: 41.7831,\n    };\n    const result = await geospatial.query(ctx, {\n      shape: { type: \"rectangle\", rectangle },\n      filter: (q) =\u003e q.gte(\"sortKey\", 10).lt(\"sortKey\", 30),\n    });\n    return result;\n  },\n});\n```\n\nQueries take in a `limit`, which bounds the maximum number of rows returned. If this limit is hit,\nthe query will return a `nextCursor` for continuation. The query may also return a `nextCursor` with fewer than `limit` results if it runs out of its IO budget while executing.\n\nIn either case, you can continue the stream by passing `nextCursor` to the next call's `cursor` parameter.\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const rectangle = {\n      west: -73.9712,\n      south: 40.7831,\n      east: -72.9712,\n      north: 41.7831,\n    };\n    const startCursor = undefined;\n    const result = await geospatial.query(\n      ctx,\n      {\n        shape: { type: \"rectangle\", rectangle },\n        limit: 16,\n      },\n      startCursor,\n    );\n    if (result.nextCursor) {\n      // Continue the query, starting from the first query's cursor.\n      const nextResult = await geospatial.query(\n        ctx,\n        {\n          shape: { type: \"rectangle\", rectangle },\n          limit: 16,\n        },\n        result.nextCursor,\n      );\n      return [...result.results, ...nextResult.results];\n    }\n    return result.results; // { key, coordinates }[]\n  },\n});\n```\n\n**Note: you typically pass the `nextCursor` in from a client that is paginating through results, to avoid loading too much data in a single query.**\n\n## Querying the points nearest a query point\n\nYou can also query for the points closest to a given point, optionally limiting to a maximum distance (in meters).\n\n```ts\n// convex/index.ts\n\nconst example = query({\n  handler: async (ctx) =\u003e {\n    const maxResults = 16;\n    const maxDistance = 10000;\n    const result = await geospatial.queryNearest(\n      ctx,\n      { latitude: 40.7813, longitude: -73.9737 },\n      maxResults,\n      maxDistance,\n    );\n    return result;\n  },\n});\n```\n\nThe `maxDistance` parameter is optional, but providing it can greatly speed up searching the index.\n\n## Example\n\nSee [`example/`](./example/) for a full example with a [Leaflet](https://leafletjs.com/)-based frontend.\n\n## Development\n\nInstall dependencies and fire up the example app to get started.\n\n```bash\nnpm install\ncd example\nnpm install\nnpm run dev\n```\n\nThe component definition is in `src/` and reflects what users of the component will install. The example app,\nwhich is entirely independent, lives in `example/`.\n\n\u003c!-- END: Include on https://convex.dev/components --\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-convex%2Fgeospatial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fget-convex%2Fgeospatial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-convex%2Fgeospatial/lists"}