feat: map view with GPS extraction fix

Adds a new Map sidebar entry that plots photos by their EXIF GPS
coordinates on a clustered Leaflet map. While wiring this up, the
metadata extractor was reading unprefixed GPS keys that never exist
in `exiftool -G -j` output AND assumed coordinates were already
floats — every photo silently lost its GPS. The new extract_gps
helper handles Composite/EXIF group prefixes and parses DMS strings,
and lat/lon are stored as first-class indexed columns so the map
can query them without parsing exif_json on every request.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 23:44:29 +02:00
parent 9c9f5bd899
commit 7cf546af7a
17 changed files with 529 additions and 46 deletions

View File

@@ -1,6 +1,7 @@
import { useState } from 'react'
import { Timeline } from './components/timeline/Timeline'
import { DuplicatesView } from './components/duplicates/DuplicatesView'
import { MapView } from './components/map/MapView'
import { LeftSidebar } from './components/layout/LeftSidebar'
import { RightSidebar } from './components/layout/RightSidebar'
import { TopBar } from './components/layout/TopBar'
@@ -73,11 +74,17 @@ function App() {
<FilterBar />
<DiscardActionBar />
<div className="flex-1 overflow-auto">
{/* The Duplicates section gets its own grouped grid view —
* the regular timeline can't represent groups, and a flat
* filtered list of "is_duplicate=true" photos was the old
* half-broken UX. */}
{currentSection === 'duplicates' ? <DuplicatesView /> : <Timeline />}
{/* Section-level routing. The Map view replaces the timeline
* with a Leaflet map of GPS-tagged photos; Duplicates gets its
* own grouped grid; everything else falls through to the
* filter-driven Timeline. */}
{currentSection === 'map' ? (
<MapView />
) : currentSection === 'duplicates' ? (
<DuplicatesView />
) : (
<Timeline />
)}
</div>
{/* Floating keyboard hints — bottom-center of the main column,
* glassy. Mounted here so it's centered against the timeline,