Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/environment

src/elevation.ts

54 lines · 1.6 KB · TypeScript
import { fetchJsonWithRetry, type RetryOptions } from './open-meteo-client.ts'
import { resolveWeatherLocation } from './weather-geocode.ts'

export type ElevationLookupParams = {
	location?: string
	latitude?: number
	longitude?: number
	label?: string
	live?: RetryOptions
}

const FEET_PER_METER = 3.28084

/** Ground elevation lookup for a location or coordinates via the Open-Meteo Elevation API. */
export async function elevationLookup(params: ElevationLookupParams = {}) {
	const input = typeof params === 'object' && params ? params : {}
	const resolvedLocation = await resolveWeatherLocation(input)
	const url =
		'https://api.open-meteo.com/v1/elevation?latitude=' +
		encodeURIComponent(String(resolvedLocation.latitude)) +
		'&longitude=' +
		encodeURIComponent(String(resolvedLocation.longitude))
	const live = await fetchJsonWithRetry(url, input.live || {})
	const elevations = live.data?.elevation
	const meters = Array.isArray(elevations) ? Number(elevations[0]) : NaN
	if (!Number.isFinite(meters)) {
		throw new Error(
			'Unexpected elevation response shape for ' +
				resolvedLocation.displayName,
		)
	}
	const feet = Math.round(meters * FEET_PER_METER)
	return {
		location: {
			displayName: resolvedLocation.displayName,
			latitude: resolvedLocation.latitude,
			longitude: resolvedLocation.longitude,
			source: resolvedLocation.source,
		},
		meters,
		feet,
		source: 'open-meteo',
		attempts: live.attempts,
		summary:
			resolvedLocation.displayName +
			' elevation: ' +
			Math.round(meters) +
			' m (' +
			feet +
			' ft)',
	}
}

export default elevationLookup