Closes#177. Depends on Apollo#52 (/api/location/at and /api/location/day). Deploy Apollo first. Until then, every Apollo location call 404s, gets logged at warn, and falls back to the local table, so deploying in the wrong order is safe, just noisy.
With APOLLO_API_BASE_URL set, it asks Apollo /api/location/at?ts=<true instant>&max_gap=1800, then:
No sample within 30 min: no location line. Before, it took the nearest local sample within ±3 h whatever the distance, and 17% of photo moments got a position more than 2 km from where the photo was taken.
Inside a Place stay:Location history: You were at Hyland Software (08:07–16:19), with times in the owner's display zone.
Inside a Place, no stay:You were at Home.
Unnamed:You were near <Nominatim>. The geocode only runs when the photo has no EXIF GPS (the EXIF line already has it); otherwise it shows coordinates.
Extras: an informative activity ((IN_VEHICLE)) and, when the fix is more than 5 min from the photo, [location fix 15 min before the photo].
Kept: the existing "skip if within 500 m of EXIF and nothing new" rule.
The rendering is a free function, render_apollo_location, with unit tests.
get_location_history tool (chat and reels)
With Apollo: Apollo's day timeline for the date's local day in the display zone, or date±1 at most. For example:
2017-01-17 (744 location points, 195.2 km):
00:00–07:32 at Cameron's Home - Grafton Road
07:34–08:05 moving, 31.0 km, IN_VEHICLE
08:07–16:19 at Hyland Software
...
Before: the first 20 rows of a ±14-day window sorted ascending, i.e. points from about 14 days before the date, with nothing telling the model so.
Description: reworded to match. days_radius now defaults to 0.
Offered whenever Apollo is configured, even with an empty local table. Photos from 2020 onward finally get location history.
Fallback
When Apollo is unset or a call fails, the local table is used. The local tool path now:
covers the requested local day(s) in the display zone;
samples up to 20 records evenly across them (spread_evenly);
says so in its header, while keeping Found N … parseable for the trajectory summarizer.
Tests
New:
Apollo response parsing (hit, miss, day);
the disabled client;
render_apollo_location: no sample, stay in the display zone, unnamed with Nominatim / coordinates plus activity plus offset, EXIF-skip;
spread_evenly.
cargo test passes: 753 + 846 + the rest. cargo clippy --all-targets is clean on the changed files, and cargo fmt has been run.
Not tested end to end: there's no HTTP mock crate in the repo, so nothing exercises a live Apollo. The wire shapes are pinned by the parsing tests here and by Apollo#52's route tests.
Follow-up (not in this PR)
Once this has run for a while, retire location_history, import_location_history and the parser, or keep them only for Apollo-less deploys.
Closes #177. **Depends on [Apollo#52](https://git.crkc.xyz/Apps/Apollo/pulls/52)** (`/api/location/at` and `/api/location/day`). Deploy Apollo first. Until then, every Apollo location call 404s, gets logged at `warn`, and falls back to the local table, so deploying in the wrong order is safe, just noisy.
## What changes
### Per-photo location context (`gather_location_context`)
With `APOLLO_API_BASE_URL` set, it asks Apollo `/api/location/at?ts=<true instant>&max_gap=1800`, then:
- **No sample within 30 min:** no location line. Before, it took the nearest local sample within ±3 h whatever the distance, and 17% of photo moments got a position more than 2 km from where the photo was taken.
- **Inside a Place stay:** `Location history: You were at Hyland Software (08:07–16:19)`, with times in the owner's display zone.
- **Inside a Place, no stay:** `You were at Home`.
- **Unnamed:** `You were near <Nominatim>`. The geocode only runs when the photo has no EXIF GPS (the EXIF line already has it); otherwise it shows coordinates.
- **Extras:** an informative activity (`(IN_VEHICLE)`) and, when the fix is more than 5 min from the photo, `[location fix 15 min before the photo]`.
- **Kept:** the existing "skip if within 500 m of EXIF and nothing new" rule.
The rendering is a free function, `render_apollo_location`, with unit tests.
### `get_location_history` tool (chat and reels)
- **With Apollo:** Apollo's day timeline for the date's local day in the display zone, or date±1 at most. For example:
```
2017-01-17 (744 location points, 195.2 km):
00:00–07:32 at Cameron's Home - Grafton Road
07:34–08:05 moving, 31.0 km, IN_VEHICLE
08:07–16:19 at Hyland Software
...
```
- **Before:** the first 20 rows of a ±14-day window sorted ascending, i.e. points from about 14 days before the date, with nothing telling the model so.
- **Description:** reworded to match. `days_radius` now defaults to 0.
- **Offered whenever Apollo is configured,** even with an empty local table. Photos from 2020 onward finally get location history.
### Fallback
When Apollo is unset or a call fails, the local table is used. The local tool path now:
- covers the requested local day(s) in the display zone;
- samples up to 20 records evenly across them (`spread_evenly`);
- says so in its header, while keeping `Found N …` parseable for the trajectory summarizer.
## Tests
New:
- Apollo response parsing (hit, miss, day);
- the disabled client;
- `render_apollo_location`: no sample, stay in the display zone, unnamed with Nominatim / coordinates plus activity plus offset, EXIF-skip;
- `spread_evenly`.
`cargo test` passes: 753 + 846 + the rest. `cargo clippy --all-targets` is clean on the changed files, and `cargo fmt` has been run.
**Not tested end to end:** there's no HTTP mock crate in the repo, so nothing exercises a live Apollo. The wire shapes are pinned by the parsing tests here and by Apollo#52's route tests.
## Follow-up (not in this PR)
Once this has run for a while, retire `location_history`, `import_location_history` and the parser, or keep them only for Apollo-less deploys.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The local location_history table is a one-time Takeout import: it ends
in 2019, still has the ghost-device and coarse cell-fix samples Apollo
has since excluded, and was read loosely. The per-photo line took the
nearest sample within +/-3 h at any distance (17% of photo moments got a
position >2 km away), and get_location_history listed the first 20 rows
of a +/-14-day window sorted ascending (always ~14 days before the date).
With APOLLO_API_BASE_URL set:
- per-photo context comes from /api/location/at (30 min max gap; no
sample -> no line). A Place stay renders as "at Home (14:02-18:30)" in
the display zone; an unnamed sample is reverse-geocoded.
- get_location_history returns Apollo's /api/location/day timeline for
the date (+/-1 day at most), and is offered even with an empty local
table.
Both fall back to the local table when Apollo is unset or a call fails;
the local tool path now covers the requested local day(s) and samples
evenly instead of listing a window's first rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #177. Depends on Apollo#52 (
/api/location/atand/api/location/day). Deploy Apollo first. Until then, every Apollo location call 404s, gets logged atwarn, and falls back to the local table, so deploying in the wrong order is safe, just noisy.What changes
Per-photo location context (
gather_location_context)With
APOLLO_API_BASE_URLset, it asks Apollo/api/location/at?ts=<true instant>&max_gap=1800, then:Location history: You were at Hyland Software (08:07–16:19), with times in the owner's display zone.You were at Home.You were near <Nominatim>. The geocode only runs when the photo has no EXIF GPS (the EXIF line already has it); otherwise it shows coordinates.(IN_VEHICLE)) and, when the fix is more than 5 min from the photo,[location fix 15 min before the photo].The rendering is a free function,
render_apollo_location, with unit tests.get_location_historytool (chat and reels)days_radiusnow defaults to 0.Fallback
When Apollo is unset or a call fails, the local table is used. The local tool path now:
spread_evenly);Found N …parseable for the trajectory summarizer.Tests
New:
render_apollo_location: no sample, stay in the display zone, unnamed with Nominatim / coordinates plus activity plus offset, EXIF-skip;spread_evenly.cargo testpasses: 753 + 846 + the rest.cargo clippy --all-targetsis clean on the changed files, andcargo fmthas been run.Not tested end to end: there's no HTTP mock crate in the repo, so nothing exercises a live Apollo. The wire shapes are pinned by the parsing tests here and by Apollo#52's route tests.
Follow-up (not in this PR)
Once this has run for a while, retire
location_history,import_location_historyand the parser, or keep them only for Apollo-less deploys.🤖 Generated with Claude Code
d1f0e4c0c0to7b6eb0c44b7b6eb0c44btoe104f452f3e104f452f3tof62d2ecfb4f62d2ecfb4todc90407afb