/** * Helper: Converts [Degrees, Minutes, Seconds] to Decimal Degrees */ const convertDMSToDD = (dms: number[] | undefined, ref: string | undefined): number | null => { if (!dms || dms.length < 3) return null; const [degrees, minutes, seconds] = dms; let dd = degrees + (minutes / 60) + (seconds / 3600); if (ref === 'S' || ref === 'W') { dd = dd * -1; } return parseFloat(dd.toFixed(6)); }; /** * MAPPER: Transforms the raw database JSON into a structured GUI object. * Designed to handle the lowercase keys generated by the sanitizer. */ export const mapImageMetadata = (metadata: any) => { if (!metadata || !metadata.details) { return { title: metadata?.title || "Unknown File" }; } const { details, dimensions, title } = metadata; const { image = {}, photo = {}, gpsInfo = {} } = details; return { fileName: title, device: `${image.make || ''} ${image.model || ''}`.trim() || 'Unknown Device', // Dates from EXIF are strings after sanitization timestamp: photo.dateTimeOriginal ? new Date(photo.dateTimeOriginal) : null, resolution: { width: photo.pixelXDimension || dimensions?.split('x')[0], height: photo.pixelYDimension || dimensions?.split('x')[1], }, settings: { aperture: photo.fNumber ? `f/${photo.fNumber}` : 'N/A', shutterSpeed: photo.exposureTime ? (photo.exposureTime < 1 ? `1/${Math.round(1 / photo.exposureTime)}s` : `${photo.exposureTime}s`) : 'N/A', iso: photo.iSOSpeedRatings || 'N/A', focalLength: photo.focalLength ? `${photo.focalLength}mm` : 'N/A', }, location: { latitude: convertDMSToDD(gpsInfo.gpsLatitude, gpsInfo.gpsLatitudeRef), longitude: convertDMSToDD(gpsInfo.gpsLongitude, gpsInfo.gpsLongitudeRef), altitude: gpsInfo.gpsAltitude ? Math.round(gpsInfo.gpsAltitude) : null, mapUrl: (gpsInfo.gpsLatitude && gpsInfo.gpsLongitude) ? `https://www.google.com/maps?q=${convertDMSToDD(gpsInfo.gpsLatitude, gpsInfo.gpsLatitudeRef)},${convertDMSToDD(gpsInfo.gpsLongitude, gpsInfo.gpsLongitudeRef)}` : null } }; };