import type { WithAbortSignal } from "#Source/abort/index.ts" import { generalFetch } from "#Source/request/index.ts" const internalSolarElevationAngleDatePattern = /^\d{8}$/ const internalSolarElevationAngleTimePattern = /^\d{4}$/ const internalSolarElevationAngleTimezonePattern = /^[+-]?\d{4}$/ /** * @description 调用和风天气太阳高度角接口时使用的参数。 * * @example * ;``` * const options: SolarElevationAngleOptions = { * baseUrl: "https://abcxyz.qweatherapi.com", * jwt: "eyJhbGciOiJFZERTQSJ9.payload.signature", * location: "120.34,36.08", * date: "20210220", * time: "1230", * tz: "0800", * alt: 43, * timeout: 5_000, * } * ``` */ export interface SolarElevationAngleOptions extends WithAbortSignal { /** * @description 和风天气 API Base URL,例如 `https://abcxyz.qweatherapi.com`。 */ baseUrl: string /** * @description 用于 `Authorization: Bearer` 请求头的 JWT。 */ jwt: string /** * @description 需要查询地区的经度、纬度坐标,例如 `120.34,36.08`。 */ location: string /** * @description 查询日期,格式为 `yyyyMMdd`。 */ date: string /** * @description 查询时间,格式为 `HHmm`,24 小时制。 */ time: string /** * @description 查询地区所在时区,例如 `0800` 或 `-0530`。 */ tz: string /** * @description 海拔高度,单位为米。 */ alt: number | string /** * @description 请求超时时间,单位为毫秒。 */ timeout?: number | undefined } /** * @description 太阳高度角响应中的来源与许可信息。 * * @example * ;``` * const refer: SolarElevationAngleResultRefer = { * sources: ["qweather.com"], * license: ["QWeather Developers License"], * } * ``` */ export interface SolarElevationAngleResultRefer { /** * @description 原始数据来源或数据源说明。 */ sources?: string[] | undefined /** * @description 数据许可或版权声明。 */ license?: string[] | undefined } /** * @description 和风天气太阳高度角接口的响应结果。 * * @example * ;``` * const result: SolarElevationAngleResult = { * code: "200", * solarElevationAngle: "42.88", * solarAzimuthAngle: "185.92", * solarHour: "1217", * hourAngle: "-4.41", * refer: { * sources: ["qweather.com"], * license: ["QWeather Developers License"], * }, * } * ``` */ export interface SolarElevationAngleResult { /** * @description 接口状态码。 */ code: string /** * @description 太阳高度角。 */ solarElevationAngle: string /** * @description 太阳方位角,正北顺时针方向角度。 */ solarAzimuthAngle: string /** * @description 太阳时,格式为 `HHmm`。 */ solarHour: string /** * @description 时角。 */ hourAngle: string /** * @description 响应中的来源与许可信息。 */ refer?: SolarElevationAngleResultRefer | undefined } const internalNormalizeSolarElevationAngleAlt = (value: number | string): string => { if (typeof value === "number") { if (Number.isFinite(value) === false) { throw new TypeError("SolarElevationAngle alt must be a finite number.") } return String(value) } const normalized = value.trim() if (normalized === "") { throw new TypeError("SolarElevationAngle alt must not be empty.") } return normalized } const internalValidateSolarElevationAngleOptions = ( options: SolarElevationAngleOptions, ): { baseUrl: string jwt: string location: string date: string time: string tz: string alt: string } => { const baseUrl = options.baseUrl.trim() const jwt = options.jwt.trim() const location = options.location.trim() const date = options.date.trim() const time = options.time.trim() const tz = options.tz.trim() const alt = internalNormalizeSolarElevationAngleAlt(options.alt) if (baseUrl === "") { throw new TypeError("SolarElevationAngle baseUrl must not be empty.") } if (jwt === "") { throw new TypeError("SolarElevationAngle jwt must not be empty.") } if (location === "") { throw new TypeError("SolarElevationAngle location must not be empty.") } if (date === "") { throw new TypeError("SolarElevationAngle date must not be empty.") } if (time === "") { throw new TypeError("SolarElevationAngle time must not be empty.") } if (tz === "") { throw new TypeError("SolarElevationAngle tz must not be empty.") } if (internalSolarElevationAngleDatePattern.test(date) === false) { throw new RangeError("SolarElevationAngle date must use yyyyMMdd format.") } if (internalSolarElevationAngleTimePattern.test(time) === false) { throw new RangeError("SolarElevationAngle time must use HHmm format.") } if (internalSolarElevationAngleTimezonePattern.test(tz) === false) { throw new RangeError("SolarElevationAngle tz must use +/-HHmm format.") } return { baseUrl, jwt, location, date, time, tz, alt, } } /** * @description 获取指定时间点和地点的太阳高度角及方位角。 * * @see {@link https://dev.qweather.com/docs/api/astronomy/solar-elevation-angle/} */ export const solarElevationAngle = async ( options: SolarElevationAngleOptions, ): Promise => { const { baseUrl, jwt, location, date, time, tz, alt } = internalValidateSolarElevationAngleOptions(options) const { timeout, abortSignal } = options return await generalFetch< { query: { location: string date: string time: string tz: string alt: string } }, { type: "json" data: SolarElevationAngleResult } >({ baseUrl, path: "/v7/astronomy/solar-elevation-angle", method: "get", headers: { Authorization: `Bearer ${jwt}`, }, query: { location, date, time, tz, alt, }, responseType: "json", timeout, abortSignal, }).getJson() }