This package provides a function to extract useful information from photos, such as geo location, camera make and model, focal length and angle of view, which could be useful to orient the photo on the map.
Try the demo app with a few geotagged photos. All photos operations are done in the browser. No data is sent to any server.
npm install photo-infoimport { getPhotoInfo } from 'photo-info';
const photoInfo = await getPhotoInfo(file);
console.log(photoInfo);
// {
// make: 'Apple',
// model: 'iPhone 15 Pro',
// angleOfView: 23.5,
// effectiveAngleOfView: 23.5,
// bearing: 45.2,
// gpsPosition: [51.5074, -0.1276, 10.5],
// gpsAccuracy: {
// error: 5,
// grade: 'A',
// description: 'Excellent - High confidence GPS fix'
// },
// gpsSpeed: { value: 5.4, unit: 'km/h' },
// focalLength: 6.86,
// focalLengthIn35mm: 48,
// width: 4032,
// height: 3024,
// orientation: 'landscape',
// frontCamera: false,
// dateTime: '2024-03-15T14:30:00',
// exposureTime: '1/120',
// exposureProgram: 'Normal program',
// fNumber: 'f/1.8',
// lens: 'iPhone 15 Pro back camera 6.86mm f/1.78'
// }const { originalTags, ...photoInfo } = await getPhotoInfo(file, true);Extracts photo information from an image file containing EXIF data.
file- A File object (typically from an input element or drag-and-drop)includeOriginalTags- Optional. Whentrue, includes the raw EXIF data in the response
A PhotoInfo object with the following properties:
| Property | Type | Description |
|---|---|---|
make |
string | null |
Camera manufacturer (e.g., "Canon", "Apple") |
model |
string | null |
Camera model (e.g., "iPhone 15 Pro") |
angleOfView |
number | null |
Horizontal angle of view in degrees |
effectiveAngleOfView |
number | null |
Effective FOV for map display (considers orientation) |
bearing |
number | null |
Compass direction the camera was facing (0-360°) |
gpsPosition |
[lat, lng, alt?] | null |
GPS coordinates: latitude, longitude, altitude (meters) |
gpsAccuracy |
{error, grade, description} | null |
GPS accuracy with error (meters), grade (A-F), and text description |
gpsSpeed |
{value, unit} | null |
Speed at capture time with unit (typically "km/h") |
focalLength |
number | null |
Actual focal length in millimeters |
focalLengthIn35mm |
number | null |
35mm equivalent focal length |
width |
number |
Image width in pixels |
height |
number |
Image height in pixels |
orientation |
'portrait' | 'landscape' | 'square' |
Image orientation |
frontCamera |
boolean |
Whether taken with front-facing camera |
dateTime |
string | null |
ISO 8601 formatted capture time |
exposureTime |
string | null |
Shutter speed (e.g., "1/120") |
exposureProgram |
string | null |
Camera exposure mode |
fNumber |
string | null |
Aperture value (e.g., "f/1.8") |
lens |
string | null |
Lens model/description |
originalTags |
object | undefined |
Raw EXIF data (when requested) |
Get photo information with user-friendly mapped EXIF data.
A mapped object where each EXIF tag has:
value- The raw EXIF valuedisplayName- Human-readable property nameformattedValue- Formatted value for display
const mappedData = await getMappedPhotoInfo(file);
console.log(mappedData.ISO);
// {
// value: 100,
// displayName: 'ISO Speed',
// formattedValue: 'ISO 100'
// }Get photo EXIF data organized by categories.
EXIF data grouped into categories:
Camera- Make, model, lens infoGPS- Location, altitude, speedImage- Dimensions, orientation, compressionCapture- Date, time, settingsOther- Additional metadata
const grouped = await getGroupedPhotoInfo(file);
console.log(grouped.Camera);
// { Make: 'Canon', Model: 'EOS R5', ... }
console.log(grouped.GPS);
// { GPSLatitude: 51.5074, GPSLongitude: -0.1276, ... }Get all photo information formats in a single call.
An object containing:
original- Standard PhotoInfo with all fieldsmapped- User-friendly mapped EXIF datagrouped- EXIF data organized by categories
const comprehensive = await getComprehensivePhotoInfo(file);
console.log(comprehensive.original); // PhotoInfo object
console.log(comprehensive.mapped); // MappedExifData
console.log(comprehensive.grouped); // GroupedExifDataCreates an SVG string for visualizing photo field-of-view on maps.
import { createFovMarkerSvg } from 'photo-info';
const svgString = createFovMarkerSvg({
angleOfView: 65,
bearing: 180,
circleColor: 'red',
fovColor: 'rgba(255, 0, 0, 0.3)',
});
// Use with Leaflet, Mapbox, or other mapping librariesangleOfView- Field of view angle in degreesbearing- Direction in degrees (0-360)viewBoxSize- SVG viewbox size (default: 200)circleSize- Center marker size (default: 5)circleColor- Center marker color (default: 'orange')fovColor- Field of view wedge color (default: 'lightblue')
The library gracefully handles missing or invalid EXIF data:
try {
const info = await getPhotoInfo(file);
// Properties will be null when data is unavailable
if (info.gpsPosition) {
console.log('Photo has GPS coordinates');
}
// Core properties like width/height default to 0 if unavailable
if (info.width === 0) {
console.log('Could not determine image dimensions');
}
} catch (error) {
// File reading errors will throw
console.error('Failed to read file:', error);
}The library also exports various utility functions for advanced use:
import {
calculateAngleOfView,
calculateAnglesOfView,
calculateSensorSize,
calculate35mmEquivalentFocalLength,
calculateCropFactor,
} from 'photo-info';
// Calculate field of view angles
const { horizontal, vertical } = calculateAnglesOfView(
24, // focal length in mm
50, // 35mm equivalent
'3:2', // aspect ratio
);
// Calculate sensor dimensions
const sensorSize = calculateSensorSize(5.6, '4:3'); // crop factor and aspect ratio
// Calculate 35mm equivalent focal length
const equiv = calculate35mmEquivalentFocalLength(24, 1.5); // focal length and crop factorconst photos = await Promise.all(
files.map(async (file) => ({
file,
info: await getPhotoInfo(file),
})),
);
// Filter photos with GPS data
const geotaggedPhotos = photos.filter((p) => p.info.gpsPosition);
// Add markers to your map
geotaggedPhotos.forEach(({ info }) => {
const [lat, lng] = info.gpsPosition;
// Create marker with field-of-view indicator
const marker = L.marker([lat, lng]);
// Show GPS accuracy if available
if (info.gpsAccuracy) {
const { error, grade, description } = info.gpsAccuracy;
marker.bindPopup(`
GPS Accuracy: ${description}
Error: ±${error}m (Grade ${grade})
`);
}
if (info.effectiveAngleOfView && info.bearing) {
// Use effectiveAngleOfView for correct orientation handling
const svg = createFovMarkerSvg({
angleOfView: info.effectiveAngleOfView,
bearing: info.bearing,
});
// Add SVG overlay to map
}
});const info = await getPhotoInfo(file);
if (info.fNumber && info.exposureTime && info.focalLength) {
console.log(
`Shot at ${info.fNumber}, ${info.exposureTime}s, ${info.focalLength}mm`,
);
if (info.focalLengthIn35mm) {
console.log(`35mm equivalent: ${info.focalLengthIn35mm}mm`);
}
}// Get comprehensive data in one call
const { original, mapped, grouped } = await getComprehensivePhotoInfo(file);
// Access camera info from grouped data
console.log('Camera:', grouped.Camera);
// Get user-friendly display values
for (const [tag, data] of Object.entries(mapped)) {
console.log(`${data.displayName}: ${data.formattedValue}`);
}This library requires:
- Modern browser with File API support
- async/await support (or transpilation)
- Works with JPEG, TIFF, PNG, HEIC, and WebP files containing EXIF data