Skip to content

feat(v4): add bbox query param to /v4/places#90

Draft
BenGWeeks wants to merge 2 commits into
teambtcmap:masterfrom
BenGWeeks:feat/v4-places-bbox-filter
Draft

feat(v4): add bbox query param to /v4/places#90
BenGWeeks wants to merge 2 commits into
teambtcmap:masterfrom
BenGWeeks:feat/v4-places-bbox-filter

Conversation

@BenGWeeks
Copy link
Copy Markdown

@BenGWeeks BenGWeeks commented May 13, 2026

Summary

Adds an optional bbox=min_lon,min_lat,max_lon,max_lat parameter to GET /v4/places, following the GeoJSON / Leaflet convention (south-west corner first, longitude first). When present, only places whose coordinates fall inside the bbox are returned.

The parameter composes cleanly with the existing fields, updated_since, include_deleted and limit filters.

Example

curl 'https://api.btcmap.org/v4/places?bbox=-1,50,2,54&fields=id,lat,lon,name'

Validation

Invalid input is rejected with 400 invalid_input:

  • Non-numeric values
  • Wrong arity (not exactly four comma-separated values)
  • Out of geographic range (lat outside [-90, 90], lon outside [-180, 180])
  • Inverted corners (min_lat > max_lat or min_lon > max_lon)

Tests

Five new unit tests under rest::v4::places::test cover the happy path and each validation branch. Full suite passes locally:

test result: ok. 349 passed; 0 failed; 3 ignored

cargo fmt --check and cargo clippy -- -D warnings are both clean.

Why

Today /v4/places returns the entire ~28k worldwide dataset (~5 MB even with slim ?fields=), and the workaround for the web client at btcmap.org is to download cdn.static.btcmap.org/api/v4/places.json once and then delta-sync via ?updated_since=. That works well for browsers that can keep a large localforage cache, but it isn't a great fit for mobile apps — caches get evicted aggressively and users in low-bandwidth areas pay the full cold-sync cost on every reinstall.

A native viewport bbox filter is a much bigger win for those clients. Prompted by an open-source mobile-wallet maintainer (Lightning Piggy) needing viewport queries on low-bandwidth devices.

Files

  • src/rest/v4/places.rs — new bbox query arg, parse_bbox helper, in-memory filter after select_updated_since, five unit tests
  • docs/rest/v4/places.md — parameter table entry + worked example

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Added bounding box filtering capability to the Places API endpoint, enabling users to retrieve places within specified geographic coordinates and boundaries.
  • Documentation

    • Updated API documentation with comprehensive examples and guidance for the new bounding box filtering parameter, including usage instructions and parameter format details.

Review Change Stack

Adds an optional `bbox=min_lon,min_lat,max_lon,max_lat` parameter to
`GET /v4/places`, following the GeoJSON / Leaflet convention. When
present, only places whose coordinates fall inside the bbox are
returned. The parameter composes cleanly with the existing `fields`,
`updated_since`, `include_deleted` and `limit` filters.

Invalid input (non-numeric values, out-of-range coordinates,
inverted corners, wrong arity) is rejected with 400 invalid_input.

Use case: mobile / low-bandwidth clients that want only the places
visible in the current map viewport, instead of downloading the
full worldwide dataset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf0e5179-564a-4efe-b241-9552c10809c6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds bounding-box filtering to the Places API. A new optional bbox query parameter accepts comma-separated min/max coordinates, which are validated and used to filter returned places in-memory. The handler disables SQL-limit pushdown when bbox is active. Comprehensive tests and API documentation accompany the implementation.

Changes

Bbox Query Parameter Feature

Layer / File(s) Summary
Bbox type and validation
src/rest/v4/places.rs
GetListArgs gains a new optional bbox: Option<String> field. A BoundingBox type and parse_bbox helper parse and validate the min_lon,min_lat,max_lon,max_lat format, checking numeric parsing, geographic ranges (longitude ±180, latitude ±90), and corner ordering; invalid input returns RestApiError::invalid_input.
Handler bbox filtering integration
src/rest/v4/places.rs
The get handler parses the optional bbox before database access. When bbox is present, SQL-limit pushdown is disabled. Fetched elements and (when enabled) pending submissions are filtered in-memory by (lat, lon) bounds, then limit is applied via truncation after filtering.
Tests and API documentation
src/rest/v4/places.rs, docs/rest/v4/places.md
Test coverage validates bbox filtering and asserts HTTP 400 for invalid inputs (non-numeric values, inverted corners, out-of-range coordinates, wrong component count). API docs add the bbox parameter to the endpoint description and include a "Fetch Places Inside a Bounding Box" example showing viewport-scoped requests and interaction with incremental syncing.

Sequence Diagram

sequenceDiagram
  participant Client
  participant PlacesHandler
  participant BboxParser
  participant Database
  participant FilterEngine
  
  Client->>PlacesHandler: GET /v4/places?bbox=min_lon,min_lat,max_lon,max_lat
  PlacesHandler->>BboxParser: parse_bbox(bbox_string)
  BboxParser->>BboxParser: validate format, ranges, corner order
  BboxParser-->>PlacesHandler: BoundingBox
  PlacesHandler->>Database: select_updated_since (without LIMIT)
  Database-->>PlacesHandler: all places from offset
  PlacesHandler->>FilterEngine: filter by bbox (lat, lon)
  FilterEngine-->>PlacesHandler: places within bounds
  PlacesHandler->>PlacesHandler: apply limit via truncation
  PlacesHandler-->>Client: filtered and limited places
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • bubelov

Poem

A rabbit hops through coordinates true, 🐰
Drawing boxes to see what's in view.
Min and max bounds, validated with care,
Geographic filters floating through air.
Bounding the places that map the way,
API now filters by region today. 📍

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a bbox query parameter to the /v4/places endpoint, which is the core feature of this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@BenGWeeks BenGWeeks marked this pull request as draft May 13, 2026 21:21
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/rest/v4/places.rs`:
- Around line 151-153: Don't truncate elements or submissions individually
(remove the truncate calls around elements and submissions) because that drops
candidates before final chronological ordering; instead, after assembling
elements and submissions (respecting include_pending), merge them into a single
collection, sort that combined collection by the shared timestamp (e.g.,
created_at) in the desired chronological order, then apply args.limit to
truncate the merged result, and finally map/split the selected items back to the
expected output shape; apply this change for the blocks that reference elements,
submissions and args.limit (including the second truncate at the later block).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06f790d3-4e10-4d05-9b4c-f22e25167e46

📥 Commits

Reviewing files that changed from the base of the PR and between 3716edd and e46e4f5.

📒 Files selected for processing (2)
  • docs/rest/v4/places.md
  • src/rest/v4/places.rs

Comment thread src/rest/v4/places.rs Outdated
CodeRabbit on PR teambtcmap#90 spotted that the per-collection truncate at lines
151-153 (elements) and 176-178 (submissions) could drop legitimate
candidates from one source before they were merged with the other,
returning the wrong final top-N when `include_pending=true` was
combined with a bbox.

Removed both per-source truncates; the existing `.take(limit)` on the
merged + chronologically-sorted result is now the only place the
caller's `limit` is honoured. SQL `LIMIT` is still pushed down to
`select_updated_since` when no bbox is active (no in-memory filter
step that could discard rows), so the dataset-wide hot path stays
unchanged.

All 349 tests still pass; fmt + clippy clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant