GeoJSON Format Choice

Why ItemFeature uses GeoJSON structure.


What is GeoJSON

GeoJSON is a standard format for encoding geographic data:

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [-122.4194, 37.7749]
  },
  "properties": {
    "name": "San Francisco"
  }
}

It’s defined by RFC 7946 and widely supported by mapping tools.

Why Use It for Images

Natural Fit for GPS

Photos often contain GPS coordinates in EXIF data. GeoJSON’s geometry field is designed exactly for this:

{
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [-122.4194, 37.7749]  // [longitude, latitude]
  },
  properties: { /* image metadata */ }
}

No need for custom GPS handling or separate fields.

Properties Bag

The properties object is a flexible container for any metadata:

properties: {
  width: 4000,
  height: 3000,
  aspectRatio: 1.333,
  blurhash: 'LEHV6nWB2yk8...',
  averageColor: { hex: '#4a7c59' },
  exif: {
    make: 'Canon',
    model: 'EOS R5',
    lens: 'RF 24-70mm F2.8L'
  }
}

This is standard GeoJSON—no extensions required.

The ItemFeature Structure

Zone5’s output format:

interface ItemFeature {
  type: 'Feature';                    // Required by GeoJSON spec
  id: string;                         // Unique identifier
  geometry: Point | null;             // GPS coordinates or null
  properties: {
    width: number;
    height: number;
    aspectRatio: number;
    blurhash: string;
    averageColor: { hex: string; rgb: number[] };
    exif?: ExifItem;
  };
  assets: Asset[];                    // Image variants (Zone5 extension)
}

The assets Extension

Standard GeoJSON doesn’t include an assets field. Zone5 adds it to store variant URLs:

assets: [
  { href: '/@zone5/photo/640.jpg', width: 640 },
  { href: '/@zone5/photo/1280.jpg', width: 1280 },
  { href: '/@zone5/photo/1920.jpg', width: 1920 }
]

This is inspired by STAC (SpatioTemporal Asset Catalog), another geospatial standard.

Benefits

Existing Tooling

Many libraries work with GeoJSON out of the box:

// Leaflet
L.geoJSON(itemFeature).addTo(map);

// Mapbox GL
map.addSource('photo', { type: 'geojson', data: itemFeature });

// Turf.js
const distance = turf.distance(point1, itemFeature);

Self-Documenting

Developers familiar with GeoJSON immediately understand the structure. The type: 'Feature' field signals “this is standard GeoJSON.”

TypeScript Support

Type definitions exist for GeoJSON:

import type { Feature, Point } from 'geojson';

// ItemFeature extends the standard Feature type
interface ItemFeature extends Feature<Point | null> {
  properties: ImageProperties;
  assets: Asset[];
}

Future Features

The format enables future mapping features:

  • Photo maps with markers
  • Clustering by location

When Geometry is Null

Not all photos have GPS data:

{
  type: 'Feature',
  geometry: null,  // Valid GeoJSON
  properties: { /* still has all metadata */ }
}

This is valid GeoJSON per RFC 7946. Components check for null before using coordinates: