|
Some checks failed
Build and Publish / build-and-publish (push) Failing after 54s
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| .gitignore | ||
| eslint.config.js | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsup.config.ts | ||
| vitest.config.ts | ||
@lilith/geo-utils
Geolocation utilities for the Lilith Platform: distance calculations, geocoding, and coordinate helpers.
Features
- Distance Calculations: Haversine formula for accurate great-circle distances
- Forward Geocoding: Convert addresses to coordinates (via Nominatim/OSM)
- Reverse Geocoding: Convert coordinates to addresses
- Zero Dependencies: Pure TypeScript, no external runtime dependencies
- Type-Safe: Full TypeScript support with strict typing
Installation
pnpm add @lilith/geo-utils
Quick Start
import {
calculateDistance,
searchLocations,
reverseGeocode,
type Coordinates,
} from '@lilith/geo-utils';
// Calculate distance between two points
const distance = calculateDistance(
{ lat: 40.7128, lng: -74.0060 }, // New York
{ lat: 34.0522, lng: -118.2437 }, // Los Angeles
'miles'
);
// Returns approximately 2451 miles
// Search for a location
const results = await searchLocations('New York City');
// Reverse geocode coordinates
const location = await reverseGeocode({ lat: 40.7128, lng: -74.0060 });
API Reference
Distance Calculations
calculateDistance(from, to, unit?)
Calculate the great-circle distance between two points using the Haversine formula.
calculateDistance(
from: Coordinates,
to: Coordinates,
unit?: 'miles' | 'kilometers' | 'meters'
): number
Example:
const nyToLa = calculateDistance(
{ lat: 40.7128, lng: -74.0060 },
{ lat: 34.0522, lng: -118.2437 },
'miles'
);
// ~2451 miles
isWithinRadius(center, point, radius, unit?)
Check if a point is within a given radius of another point.
isWithinRadius(
center: Coordinates,
point: Coordinates,
radius: number,
unit?: DistanceUnit
): boolean
Example:
const nearby = isWithinRadius(
{ lat: 40.7128, lng: -74.0060 },
{ lat: 40.7580, lng: -73.9855 },
10,
'miles'
);
// true (Central Park is ~5 miles from downtown)
filterByDistance(items, center, maxDistance, getCoordinates, unit?)
Filter an array of items by distance from a center point.
const stores = [
{ name: 'Store A', location: { lat: 40.71, lng: -74.00 } },
{ name: 'Store B', location: { lat: 40.75, lng: -73.99 } },
{ name: 'Store C', location: { lat: 41.00, lng: -74.10 } },
];
const nearby = filterByDistance(
stores,
{ lat: 40.72, lng: -74.00 },
5, // 5 miles
(store) => store.location,
'miles'
);
// Returns Store A and Store B
sortByDistance(items, center, getCoordinates, order?, unit?)
Sort an array of items by distance from a center point.
const sorted = sortByDistance(
stores,
{ lat: 40.72, lng: -74.00 },
(store) => store.location,
'asc', // nearest first
'miles'
);
// Returns stores with distanceFromCenter property added
convertDistance(value, from, to)
Convert distance between units.
const km = convertDistance(10, 'miles', 'kilometers');
// ~16.09 km
formatDistance(distance, unit?, precision?)
Format distance for display.
formatDistance(2.5, 'miles'); // "2.5 mi"
formatDistance(4.0, 'kilometers'); // "4.0 km"
formatDistance(1500, 'meters'); // "1500.0 m"
Geocoding
searchLocations(query, options?)
Search for locations by query string (forward geocoding).
const results = await searchLocations('Empire State Building', {
limit: 5,
});
// Returns:
[{
id: 'way-123456',
name: 'Empire State Building',
type: 'place',
coordinates: { lat: 40.7484, lng: -73.9857 },
formattedAddress: 'Empire State Building, 350 5th Avenue, Midtown...',
boundingBox: [40.747, 40.749, -73.987, -73.984],
importance: 0.85,
}]
reverseGeocode(coordinates, options?)
Convert coordinates to address (reverse geocoding).
const location = await reverseGeocode({ lat: 40.7128, lng: -74.0060 });
// Returns:
{
formattedAddress: 'City Hall, New York, NY 10007, United States',
city: 'New York',
neighborhood: 'Civic Center',
region: 'New York',
country: 'United States',
countryCode: 'US',
postcode: '10007',
shortName: 'Civic Center, New York',
}
createGeocodingClient(config?)
Create a custom geocoding client with specific configuration.
const client = createGeocodingClient({
userAgent: 'MyApp/1.0',
language: 'de', // German results
countryCodes: ['DE', 'AT'], // Restrict to Germany/Austria
timeout: 5000,
});
const results = await client.search('Berlin');
const location = await client.reverse({ lat: 52.52, lng: 13.405 });
Types
Coordinates
interface Coordinates {
lat: number;
lng: number;
}
GeocodedLocation
interface GeocodedLocation {
id: string;
name: string;
type: LocationType;
coordinates: Coordinates;
formattedAddress: string;
boundingBox?: BoundingBox;
importance?: number;
}
ReverseGeocodedLocation
interface ReverseGeocodedLocation {
formattedAddress: string;
city?: string;
neighborhood?: string;
region?: string;
country?: string;
countryCode?: string;
postcode?: string;
shortName: string;
}
DistanceUnit
type DistanceUnit = 'miles' | 'kilometers' | 'meters';
LocationType
type LocationType = 'city' | 'neighborhood' | 'region' | 'country' | 'address' | 'place';
Subpath Exports
Import specific modules for tree-shaking:
// Distance calculations only
import { calculateDistance } from '@lilith/geo-utils/distance';
// Geocoding only
import { searchLocations, reverseGeocode } from '@lilith/geo-utils/geocoding';
// Types only
import type { Coordinates, DistanceUnit } from '@lilith/geo-utils/types';
Nominatim Usage Policy
This package uses Nominatim (OpenStreetMap) for geocoding. Please respect their usage policy:
- Maximum 1 request per second
- Set a valid User-Agent
- Cache results when possible
- Consider running your own Nominatim instance for heavy usage
The default client includes a proper User-Agent string. For production apps with high traffic, consider using a commercial geocoding service or self-hosted Nominatim.
License
MIT