feat: implement location selection interface with EXIF, GPS, and map picker

- Created LocationSelector component with three location input methods:
  * Auto-populate from photo EXIF data
  * Device GPS location capture with accuracy indicator
  * Interactive Leaflet map picker with draggable marker
- Added Ireland coordinate validation (lat: 51.4-55.4, lng: -10.5--5.5)
- Created coordinate utility functions for validation and formatting
- Added LocationSelectorDemo page for testing at /demo/location-selector
- Displays selected location with coordinates, source, and accuracy
- Provides clear error messages for validation failures
- Satisfies requirements 1.1, 1.2, 1.3, 8.2
This commit is contained in:
2025-11-16 16:15:42 +00:00
parent ebdfc797ac
commit e18997ed95
9 changed files with 601 additions and 15 deletions

70
src/utils/coordinates.ts Normal file
View File

@@ -0,0 +1,70 @@
/**
* Utility functions for coordinate validation and manipulation
*/
import type { Coordinates } from '@/types';
// Ireland bounding box for coordinate validation
export const IRELAND_BOUNDS = {
minLat: 51.4,
maxLat: 55.4,
minLng: -10.5,
maxLng: -5.5,
};
/**
* Validates if coordinates are within Ireland's boundaries
*/
export function isWithinIreland(lat: number, lng: number): boolean {
return (
lat >= IRELAND_BOUNDS.minLat &&
lat <= IRELAND_BOUNDS.maxLat &&
lng >= IRELAND_BOUNDS.minLng &&
lng <= IRELAND_BOUNDS.maxLng
);
}
/**
* Validates if coordinates object is within Ireland's boundaries
*/
export function validateIrelandCoordinates(coords: Coordinates): boolean {
return isWithinIreland(coords.latitude, coords.longitude);
}
/**
* Calculates the distance between two coordinates in meters using the Haversine formula
*/
export function calculateDistance(
coord1: Coordinates,
coord2: Coordinates
): number {
const R = 6371e3; // Earth's radius in meters
const φ1 = (coord1.latitude * Math.PI) / 180;
const φ2 = (coord2.latitude * Math.PI) / 180;
const Δφ = ((coord2.latitude - coord1.latitude) * Math.PI) / 180;
const Δλ = ((coord2.longitude - coord1.longitude) * Math.PI) / 180;
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
/**
* Formats coordinates for display
*/
export function formatCoordinates(coords: Coordinates, precision: number = 6): string {
return `${coords.latitude.toFixed(precision)}, ${coords.longitude.toFixed(precision)}`;
}
/**
* Gets accuracy label based on accuracy value in meters
*/
export function getAccuracyLabel(accuracy: number): string {
if (accuracy < 10) return 'Excellent';
if (accuracy < 50) return 'Good';
if (accuracy < 100) return 'Fair';
return 'Poor';
}

View File

@@ -1,17 +1,7 @@
// Utility functions for the Beach Litter Mapping Platform
/**
* Validates if coordinates are within Ireland's geographical bounds
*/
export function isValidIrishCoordinates(lat: number, lng: number): boolean {
// Ireland approximate bounds
const MIN_LAT = 51.4;
const MAX_LAT = 55.4;
const MIN_LNG = -10.5;
const MAX_LNG = -5.5;
return lat >= MIN_LAT && lat <= MAX_LAT && lng >= MIN_LNG && lng <= MAX_LNG;
}
// Re-export coordinate utilities
export * from './coordinates';
/**
* Formats a date to ISO 8601 format