Feature: Places & Geotagging
Relevant source files
The following files were used as context for generating this wiki page:The Places & Geotagging feature provides an interactive map view to visualize photos based on their geographical location. The goal is to allow users to browse their photo library by the places where the photos were taken. The feature is built using Leaflet.js to render an OpenStreetMap-powered map, with markers representing photo locations.
Currently, the implementation uses mock data for demonstration purposes. The development roadmap indicates plans to integrate real GPS data extracted from photo EXIF metadata, enabling automatic location tagging and clustering of photos on the map. This will transition the feature from a proof-of-concept to a fully data-driven organizational tool.
Sources: src/pages/Places.tsx, README.md:19, 137-142, src/lib/mockData.ts:2-7
Architecture and Core Technologies
The feature is architecturally centered around the Places.tsx React component, which integrates with the Leaflet.js library to render and manage the map interface.
Sources: src/pages/Places.tsx, package.json:37, 38
Key Technologies
| Library | Purpose |
|---|---|
react | The core UI framework for building the component. |
leaflet | A leading open-source JavaScript library for interactive maps. Used for rendering the map canvas and markers. |
leaflet.markercluster | A Leaflet plugin for clustering markers. It groups a large number of markers into clusters to improve performance and readability. |
OpenStreetMap | Provides the map tile data displayed by Leaflet. |
Sources: package.json:37, 38, 51, src/pages/Places.tsx:6, 9, 41
Component Interaction
The following diagram illustrates the high-level interaction between the main components of the Places feature.
This diagram shows that the App.tsx router directs the user to the Places.tsx component. The component then fetches data (currently from mockData.ts), initializes a Leaflet map, and renders clustered markers for photos that have location information.
Sources: src/App.tsx:28, src/pages/Places.tsx:31, 130
Data Model and Storage
The application is designed to store location information within an IndexedDB database. The schema defines structures for both individual photo metadata and aggregated place data.
Sources: src/lib/db.ts
Database Schema
The PhotoDatabase class defined in db.ts configures two object stores relevant to this feature: photos and places.
This ER diagram shows that the photos store can contain location data, and the places store is designed to group photos by a specific location.
Sources: src/lib/db.ts:5-39
Data Structures
The location data is structured within the PhotoMetadata and Place interfaces.
PhotoMetadata.location
This object is part of the PhotoMetadata interface and stores coordinates for a single photo.
| Field | Type | Description |
|---|---|---|
lat | number | The latitude coordinate. |
lng | number | The longitude coordinate. |
name | string (optional) | The name of the location (e.g., "New York, USA"). |
Sources: src/lib/db.ts:16-20
Place Interface
This interface is intended for the places object store to represent a named location that groups multiple photos.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the place. |
name | string | The common name of the place. |
lat | number | The latitude coordinate for the place's center. |
lng | number | The longitude coordinate for the place's center. |
photoIds | string[] | An array of photo IDs associated with this place. |
Sources: src/lib/db.ts:32-38
User Interface (Places.tsx)
The UI is managed by the Places.tsx component, which handles map initialization, data processing, and rendering.
Sources: src/pages/Places.tsx
Map Initialization and Rendering
The map is initialized within a useEffect hook that runs once when the component mounts.
The flow for initializing the map is as follows:
- Check if the map container
divis available and if a map instance already exists. - Initialize the Leaflet map on the container, setting a default view.
- Add a tile layer from OpenStreetMap.
- Filter the
mockPhotosto get only those with location data. - Group the filtered photos by their location name.
- Initialize a
L.markerClusterGroupfor performance. - Iterate over the location groups, create a
L.markerfor each, and add it to the cluster group. - For each marker, create a popup with location details and photo thumbnails.
- Add the marker cluster group to the map.
Sources: src/pages/Places.tsx:33-122
This flowchart details the logic within the useEffect hook for setting up the interactive map.
Sources: src/pages/Places.tsx:33-122
Marker Popups and Interaction
When a user clicks on a map marker (or a cluster that expands to a marker), a popup appears. This popup is dynamically generated to show the location name, the number of photos, and up to six thumbnails.
// src/pages/Places.tsx:80-95
const popupContent = document.createElement('div');
popupContent.className = 'space-y-2';
popupContent.innerHTML = `
<h3 class="font-semibold text-app-text">${location}</h3>
<p class="text-sm text-app-text-muted">${photos.length} photo${photos.length > 1 ? 's' : ''}</p>
<div class="grid grid-cols-3 gap-1 mt-2">
${photos.slice(0, 6).map(photo => `
<img
src="${photo.url}"
alt="${location}"
class="w-16 h-16 object-cover rounded cursor-pointer hover:ring-2 hover:ring-app-accent"
data-photo-id="${photo.id}"
/>
`).join('')}
</div>
`;
Clicking on a thumbnail within the popup triggers a state update to open a full-screen lightbox view of the selected photo.
Sources: src/pages/Places.tsx:80-106
Fallback UI
If no photos with location data are found, the component renders a fallback view instead of the map. This view informs the user that photos with GPS/EXIF data are required for the feature to work.
Sources: src/pages/Places.tsx:130-143
Future Development
The project's README.md and comments in the source code outline a clear roadmap for this feature.
- EXIF Data Extraction: The system will be enhanced to read GPS coordinates directly from photo EXIF metadata during the scanning process. The comment
// in production, parse from EXIFconfirms this intent. - Database Integration: The feature will transition from using
mockData.tsto querying the IndexedDBphotosandplacesstores. - Reverse Geocoding: A service will be used to convert raw GPS coordinates into human-readable place names (e.g., city, country).
Sources: README.md:137-142, src/pages/Places.tsx:78, src/lib/db.ts:5-39