Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Convex component for geospatial indexing

License

NotificationsYou must be signed in to change notification settings

get-convex/geospatial

Repository files navigation

npm version

image

This component adds a geospatial index to Convex, allowing you to efficiently store and query points on the Earth's surface.

  • Insert points into the geospatial key value store along with their geographic coordinates.
  • Efficiently query for all points within a given rectangle on the sphere.
  • Control the sort order for the results with a custom sorting key.
  • Filter query results with equality andIN clauses.
  • And since it's built on Convex, everything is automatically consistent, reactive, and cached!

This component is currently in beta. It's missing some functionality, but what's there should work. We've tested the exampleapp up to about 1,000,000 points, so reach out if you're using a much larger dataset.If you find a bug or have a feature request, you canfile it here.

Pre-requisite: Convex

You'll need an existing Convex project to use the component.Convex is a hosted backend platform, including a database, serverless functions,and a ton more you can learn abouthere.

Runnpm create convex or follow any of thequickstarts to set one up.

Installation

First, add@convex-dev/geospatial to your Convex project:

npm install @convex-dev/geospatial

Then, install the component into your Convex project within theconvex/convex.config.ts file:

// convex/convex.config.tsimportgeospatialfrom"@convex-dev/geospatial/convex.config";import{defineApp}from"convex/server";constapp=defineApp();app.use(geospatial);exportdefaultapp;

Finally, create a newGeospatialIndex within yourconvex/ folder, and point it to the installed component:

// convex/index.tsimport{GeospatialIndex}from"@convex-dev/geospatial";import{components}from"./_generated/api";constgeospatial=newGeospatialIndex(components.geospatial);

Inserting points

After installing the component, you caninsert,get, andremove points from the index. You can specifyafilterKeys record for filtering at query time and optionally asortKey for the query result order. Wecurrently only support ascending order on thesortKey.

// convex/index.tsconstexample=mutation({handler:async(ctx)=>{constcityId=awaitctx.db.insert("cities",{...});awaitgeospatial.insert(ctx,"American Museum of Natural History",{latitude:40.7813,longitude:-73.9737,},{category:"museum"},28.0,// Price used as the sort key);constresult=awaitgeospatial.get(ctx,cityId);awaitgeospatial.remove(ctx,cityId);},});

If you would like some more typesafety, you can specify a type argument for theGeospatialIndex class. Thiswill also provide you with auto-complete for thefilterKeys andsortKey parameters.Above the key was "American Museum of Natural History" but most commonly thekey will be an ID in another table of yours.

// convex/index.tsimport{GeospatialIndex,Point}from"@convex-dev/geospatial";import{components}from"./_generated/api";import{Id}from"./_generated/dataModel";exportconstgeospatial=newGeospatialIndex<Id<"museums">,{category:string;anotherFilter?:number}>(components.geospatial);

Querying points within a shape

After inserting some points, you can query them with thequery API.

// convex/index.tsconstexample=query({handler:async(ctx)=>{constrectangle={west:-73.9712,south:40.7831,east:-72.9712,north:41.7831,};constresult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},limit:16,});returnresult;},});

This query will find all points that lie within the query rectangle, sort them in ascendingsortKey order, and return at most 16 results.

You can optionally add filter conditions to queries.

The first type of filter condition is anin() filter, which requires that a matchingdocument have a filter field with a value in a specified set.

// convex/index.tsconstexample=query({handler:async(ctx)=>{constrectangle={west:-73.9712,south:40.7831,east:-72.9712,north:41.7831,};constresult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},filter:(q)=>q.in("category",["museum","restaurant"]),});returnresult;},});

The second type of filter condition is aneq() filter, which requires that a matchingdocument have a filter field with a value equal to a specified value.

// convex/index.tsconstexample=query({handler:async(ctx)=>{constresult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},filter:(q)=>q.eq("category","museum"),});returnresult;},});

The final type of filter condition allows you to specify ranges over thesortKey. We currently only support (optional) inclusive lower bounds and exclusive upper bounds.

// convex/index.tsconstexample=query({handler:async(ctx)=>{constrectangle={west:-73.9712,south:40.7831,east:-72.9712,north:41.7831,};constresult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},filter:(q)=>q.gte("sortKey",10).lt("sortKey",30),});returnresult;},});

Queries take in alimit, which bounds the maximum number of rows returned. If this limit is hit,the query will return anextCursor for continuation. The query may also return anextCursor with fewer thanlimit results if it runs out of its IO budget while executing.

In either case, you can continue the stream by passingnextCursor to the next call'scursor parameter.

// convex/index.tsconstexample=query({handler:async(ctx)=>{constrectangle={west:-73.9712,south:40.7831,east:-72.9712,north:41.7831,};conststartCursor=undefined;constresult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},limit:16,},startCursor,);if(result.nextCursor){// Continue the query, starting from the first query's cursor.constnextResult=awaitgeospatial.query(ctx,{shape:{type:"rectangle", rectangle},limit:16,},result.nextCursor,);return[...result.results, ...nextResult.results];}returnresult.results;// { key, coordinates }[]},});

Note: you typically pass thenextCursor in from a client that is paginating through results, to avoid loading too much data in a single query.

Querying the points nearest a query point

You can also query for the points closest to a given point, optionally limiting to a maximum distance (in meters).

// convex/index.tsconstexample=query({handler:async(ctx)=>{constmaxResults=16;constmaxDistance=10000;constresult=awaitgeospatial.queryNearest(ctx,{latitude:40.7813,longitude:-73.9737},maxResults,maxDistance,);returnresult;},});

ThemaxDistance parameter is optional, but providing it can greatly speed up searching the index.

Example

Seeexample/ for a full example with aLeaflet-based frontend.

Development

Install dependencies and fire up the example app to get started.

npm installcd examplenpm installnpm run dev

The component definition is insrc/ and reflects what users of the component will install. The example app,which is entirely independent, lives inexample/.


[8]ページ先頭

©2009-2025 Movatter.jp