ServicesGeo

Geofences

A geofence is a named shape stored on an index, plus one question you can ask it: is this point inside? Nothing is evaluated automatically - there are no enter/exit events and no subscription. You call check-fence when you have a position worth testing, typically right after writing it.

Fences come in two shapes, and the shape decides which fields are required:

  • circle - center_longitude, center_latitude, and a positive radius_meters.
  • polygon - a vertices array of at least three {longitude, latitude} points. The ring closes itself; do not repeat the first vertex.

Create and manage

snug geo create-fence --id <index-id> --name downtown \
  --shape circle -x -122.4194 -y 37.7749 -r 1500

snug geo create-fence --id <index-id> --name wedge --shape polygon \
  --vertices '[{"longitude":-122.45,"latitude":37.70},{"longitude":-122.35,"latitude":37.70},{"longitude":-122.40,"latitude":37.80}]'

snug geo list-fences --id <index-id> --active true --search downtown
snug geo get-fence --id <index-id> --fence-id <fence-id>
snug geo update-fence --id <index-id> --fence-id <fence-id> -r 3000
snug geo delete-fence --id <index-id> --fence-id <fence-id> --force

Creating a circle returns the whole record:

curl -s -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"docs4-downtown","description":"Downtown pickup zone","shape":"circle","center_longitude":-122.4194,"center_latitude":37.7749,"radius_meters":1500}' \
  "http://localhost:4000/api/v1/geo/$INDEX/fences"
{
  "status": 201,
  "msg": "Created",
  "data": {
    "id": "ChXdmARRMPnHjSHWxQfm",
    "name": "docs4-downtown",
    "description": "Downtown pickup zone",
    "owner_id": "docs-wave",
    "index_id": "XbwKybswsEpjELvHeKxz",
    "shape": "circle",
    "center_longitude": -122.4194,
    "center_latitude": 37.7749,
    "radius_meters": 1500.0,
    "vertices": [],
    "active": true,
    "tags": {},
    "created_at": 1788007778998,
    "updated_at": 1788007778998
  }
}

Missing geometry is rejected at create time with 400 invalid_fence - "Circle fence requires a positive radius" or "Polygon fence requires at least 3 vertices". list-fences accepts the shared search grammar and is scoped to one index; a polygon's unused circle fields come back as 0.0, and a circle's vertices as [].

Checking a point

snug --output json geo check-fence --id <index-id> \
  --fence-id <fence-id> -x -122.4194 -y 37.7749
{ "fence_id": "ChXdmARRMPnHjSHWxQfm", "inside": true, "distance_meters": 0.0 }

For a circle, distance_meters is the great-circle distance from the fence centre to the point, and inside is exactly distance_meters <= radius_meters. Checking a point 3.5 km from the centre of a 3 km circle returns {"inside": false, "distance_meters": 3500.0946204088477}.

A "check every position I just wrote" loop is the normal pattern, since nothing evaluates fences for you:

snug --output json geo query-radius --id "$INDEX" \
  -x -122.4194 -y 37.7749 -r 5000 -d -c \
  | jq -r '.[] | "\(.entity_id) \(.longitude) \(.latitude)"' \
  | while read -r id lon lat; do
      inside=$(snug --output json geo check-fence --id "$INDEX" \
        --fence-id "$FENCE" -x "$lon" -y "$lat" | jq -r '.inside')
      echo "$id inside=$inside"
    done

Gotchas

  • distance_meters is meaningless for a polygon fence. It is always measured from the fence's center_* fields, which a polygon never sets, so it is really the distance from (0, 0). A point genuinely inside a polygon in San Francisco reported {"inside": true, "distance_meters": 12799648.694016874} - about 12,800 km, the distance to the Gulf of Guinea. Trust inside for polygons and ignore the distance.
  • active: false does not disable a fence. A deactivated fence still answers check normally, an inactive circle returned inside: true. The flag is a filter for list-fences and a marker for your own code, nothing more.
  • The index id in the path is ignored on per-fence routes. get, update, delete, and check resolve the fence by fence_id alone; passing a nonexistent index id in the URL still succeeds. Only create and list actually validate the index, which also means those two fail with 400 index_inactive on a deactivated index while check keeps working.
  • Fences are owned by their creator, not by the index. Another user hitting your fence gets 403 unauthorized, "Access denied to this geo fence" - the same wall as the index itself.
  • Deleting the index does not delete its fences. They outlive it, still readable and checkable by id, but invisible to list-fences (which needs a live index). Delete fences before the index that owns them, or you lose the only handle you had on them.
  • Editing geometry is a partial update. update-fence leaves omitted fields alone, so you can resize a circle with -r 3000 without restating its centre. Changing shape is not supported; create a new fence.

Reference

On this page