Add NWS weather (#23647)

* Add nws weather.

* Hassfest

* Address multiple comments

* Add NWS icon weather code link

* Add metar fallback.

Use metar code from nws observation if normal api is missing data.

* only get 1 observation - we dont use more than 1

* add mocked metar for tests

* lint

* mock metar package for all tests

* add check for metar attributes

* catch errors in setup

* add timeout error

* handle request exceptions

* check and test for missing observations

* refactor to new pynws

* change to simpler api

* Make py3.5 compatible

Remove f string

* bump pynws version

* gen_requirements

* fix wind bearing observation

* Revert "Make py3.5 compatible"

This reverts commit 4946d91779.

* Precommit black missed a file?

* black test

* add exceptional weather condition

* bump pynws version

* update requirements_all

* address comments

* move observation and forecast outside try-except-else

* Revert "move observation and forecast outside try-except-else"

This reverts commit 53b78b3283.

* remove else from update forecast block

* remove unneeded ConfigEntryNotReady import

* add scan_interval, reduce min_time_between_updates

* pytest tests

* lint test docstring

* use async await

* lat and lon inclusive in config
This commit is contained in:
MatthewFlamm 2019-08-23 08:13:06 -04:00 committed by Martin Hjelmare
parent 2b6c5eeb1d
commit 17750a604e
14 changed files with 2150 additions and 3 deletions

View file

@ -190,6 +190,7 @@ homeassistant/components/notify/* @home-assistant/core
homeassistant/components/notion/* @bachya
homeassistant/components/nsw_fuel_station/* @nickw444
homeassistant/components/nuki/* @pschmitt
homeassistant/components/nws/* @MatthewFlamm
homeassistant/components/ohmconnect/* @robbiet480
homeassistant/components/onboarding/* @home-assistant/core
homeassistant/components/opentherm_gw/* @mvn23

View file

@ -0,0 +1 @@
"""NWS Integration."""

View file

@ -0,0 +1,8 @@
{
"domain": "nws",
"name": "National Weather Service",
"documentation": "https://www.home-assistant.io/components/nws",
"dependencies": [],
"codeowners": ["@MatthewFlamm"],
"requirements": ["pynws==0.7.4"]
}

View file

@ -0,0 +1,378 @@
"""Support for NWS weather service."""
from collections import OrderedDict
from datetime import timedelta
from json import JSONDecodeError
import logging
import aiohttp
from pynws import SimpleNWS
import voluptuous as vol
from homeassistant.components.weather import (
WeatherEntity,
PLATFORM_SCHEMA,
ATTR_FORECAST_CONDITION,
ATTR_FORECAST_TEMP,
ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_SPEED,
ATTR_FORECAST_WIND_BEARING,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_NAME,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_MODE,
LENGTH_KILOMETERS,
LENGTH_METERS,
LENGTH_MILES,
PRESSURE_HPA,
PRESSURE_PA,
PRESSURE_INHG,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_validation as cv
from homeassistant.util import Throttle
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.temperature import convert as convert_temperature
_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = "Data from National Weather Service/NOAA"
SCAN_INTERVAL = timedelta(minutes=15)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
CONF_STATION = "station"
ATTR_FORECAST_DETAIL_DESCRIPTION = "detailed_description"
ATTR_FORECAST_PRECIP_PROB = "precipitation_probability"
ATTR_FORECAST_DAYTIME = "daytime"
# Ordered so that a single condition can be chosen from multiple weather codes.
# Catalog of NWS icon weather codes listed at:
# https://api.weather.gov/icons
CONDITION_CLASSES = OrderedDict(
[
(
"exceptional",
[
"Tornado",
"Hurricane conditions",
"Tropical storm conditions",
"Dust",
"Smoke",
"Haze",
"Hot",
"Cold",
],
),
("snowy", ["Snow", "Sleet", "Blizzard"]),
(
"snowy-rainy",
[
"Rain/snow",
"Rain/sleet",
"Freezing rain/snow",
"Freezing rain",
"Rain/freezing rain",
],
),
("hail", []),
(
"lightning-rainy",
[
"Thunderstorm (high cloud cover)",
"Thunderstorm (medium cloud cover)",
"Thunderstorm (low cloud cover)",
],
),
("lightning", []),
("pouring", []),
(
"rainy",
[
"Rain",
"Rain showers (high cloud cover)",
"Rain showers (low cloud cover)",
],
),
("windy-variant", ["Mostly cloudy and windy", "Overcast and windy"]),
(
"windy",
[
"Fair/clear and windy",
"A few clouds and windy",
"Partly cloudy and windy",
],
),
("fog", ["Fog/mist"]),
("clear", ["Fair/clear"]), # sunny and clear-night
("cloudy", ["Mostly cloudy", "Overcast"]),
("partlycloudy", ["A few clouds", "Partly cloudy"]),
]
)
ERRORS = (aiohttp.ClientError, JSONDecodeError)
FORECAST_MODE = ["daynight", "hourly"]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME): cv.string,
vol.Inclusive(
CONF_LATITUDE, "coordinates", "Latitude and longitude must exist together"
): cv.latitude,
vol.Inclusive(
CONF_LONGITUDE, "coordinates", "Latitude and longitude must exist together"
): cv.longitude,
vol.Optional(CONF_MODE, default="daynight"): vol.In(FORECAST_MODE),
vol.Optional(CONF_STATION): cv.string,
vol.Required(CONF_API_KEY): cv.string,
}
)
def convert_condition(time, weather):
"""
Convert NWS codes to HA condition.
Choose first condition in CONDITION_CLASSES that exists in weather code.
If no match is found, return first condition from NWS
"""
conditions = [w[0] for w in weather]
prec_probs = [w[1] or 0 for w in weather]
# Choose condition with highest priority.
cond = next(
(
key
for key, value in CONDITION_CLASSES.items()
if any(condition in value for condition in conditions)
),
conditions[0],
)
if cond == "clear":
if time == "day":
return "sunny", max(prec_probs)
if time == "night":
return "clear-night", max(prec_probs)
return cond, max(prec_probs)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the NWS weather platform."""
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
station = config.get(CONF_STATION)
api_key = config[CONF_API_KEY]
mode = config[CONF_MODE]
websession = async_get_clientsession(hass)
# ID request as being from HA, pynws prepends the api_key in addition
api_key_ha = f"{api_key} homeassistant"
nws = SimpleNWS(latitude, longitude, api_key_ha, mode, websession)
_LOGGER.debug("Setting up station: %s", station)
try:
await nws.set_station(station)
except ERRORS as status:
_LOGGER.error(
"Error getting station list for %s: %s", (latitude, longitude), status
)
raise PlatformNotReady
_LOGGER.debug("Station list: %s", nws.stations)
_LOGGER.debug(
"Initialized for coordinates %s, %s -> station %s",
latitude,
longitude,
nws.station,
)
async_add_entities([NWSWeather(nws, mode, hass.config.units, config)], True)
class NWSWeather(WeatherEntity):
"""Representation of a weather condition."""
def __init__(self, nws, mode, units, config):
"""Initialise the platform with a data instance and station name."""
self.nws = nws
self.station_name = config.get(CONF_NAME, self.nws.station)
self.is_metric = units.is_metric
self.mode = mode
self.observation = None
self._forecast = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Update Condition."""
_LOGGER.debug("Updating station observations %s", self.nws.station)
try:
await self.nws.update_observation()
except ERRORS as status:
_LOGGER.error(
"Error updating observation from station %s: %s",
self.nws.station,
status,
)
else:
self.observation = self.nws.observation
_LOGGER.debug("Updating forecast")
try:
await self.nws.update_forecast()
except ERRORS as status:
_LOGGER.error(
"Error updating forecast from station %s: %s", self.nws.station, status
)
return
self._forecast = self.nws.forecast
@property
def attribution(self):
"""Return the attribution."""
return ATTRIBUTION
@property
def name(self):
"""Return the name of the station."""
return self.station_name
@property
def temperature(self):
"""Return the current temperature."""
temp_c = None
if self.observation:
temp_c = self.observation.get("temperature")
if temp_c:
return convert_temperature(temp_c, TEMP_CELSIUS, TEMP_FAHRENHEIT)
return None
@property
def pressure(self):
"""Return the current pressure."""
pressure_pa = None
if self.observation:
pressure_pa = self.observation.get("seaLevelPressure")
if pressure_pa is None:
return None
if self.is_metric:
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_HPA)
pressure = round(pressure)
else:
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_INHG)
pressure = round(pressure, 2)
return pressure
@property
def humidity(self):
"""Return the name of the sensor."""
humidity = None
if self.observation:
humidity = self.observation.get("relativeHumidity")
return humidity
@property
def wind_speed(self):
"""Return the current windspeed."""
wind_m_s = None
if self.observation:
wind_m_s = self.observation.get("windSpeed")
if wind_m_s is None:
return None
wind_m_hr = wind_m_s * 3600
if self.is_metric:
wind = convert_distance(wind_m_hr, LENGTH_METERS, LENGTH_KILOMETERS)
else:
wind = convert_distance(wind_m_hr, LENGTH_METERS, LENGTH_MILES)
return round(wind)
@property
def wind_bearing(self):
"""Return the current wind bearing (degrees)."""
wind_bearing = None
if self.observation:
wind_bearing = self.observation.get("windDirection")
return wind_bearing
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_FAHRENHEIT
@property
def condition(self):
"""Return current condition."""
weather = None
if self.observation:
weather = self.observation.get("iconWeather")
time = self.observation.get("iconTime")
if weather:
cond, _ = convert_condition(time, weather)
return cond
return None
@property
def visibility(self):
"""Return visibility."""
vis_m = None
if self.observation:
vis_m = self.observation.get("visibility")
if vis_m is None:
return None
if self.is_metric:
vis = convert_distance(vis_m, LENGTH_METERS, LENGTH_KILOMETERS)
else:
vis = convert_distance(vis_m, LENGTH_METERS, LENGTH_MILES)
return round(vis, 0)
@property
def forecast(self):
"""Return forecast."""
if self._forecast is None:
return None
forecast = []
for forecast_entry in self._forecast:
data = {
ATTR_FORECAST_DETAIL_DESCRIPTION: forecast_entry.get(
"detailedForecast"
),
ATTR_FORECAST_TEMP: forecast_entry.get("temperature"),
ATTR_FORECAST_TIME: forecast_entry.get("startTime"),
}
if self.mode == "daynight":
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get("isDaytime")
time = forecast_entry.get("iconTime")
weather = forecast_entry.get("iconWeather")
if time and weather:
cond, precip = convert_condition(time, weather)
else:
cond, precip = None, None
data[ATTR_FORECAST_CONDITION] = cond
data[ATTR_FORECAST_PRECIP_PROB] = precip
data[ATTR_FORECAST_WIND_BEARING] = forecast_entry.get("windBearing")
wind_speed = forecast_entry.get("windSpeedAvg")
if wind_speed:
if self.is_metric:
data[ATTR_FORECAST_WIND_SPEED] = round(
convert_distance(wind_speed, LENGTH_MILES, LENGTH_KILOMETERS)
)
else:
data[ATTR_FORECAST_WIND_SPEED] = round(wind_speed)
else:
data[ATTR_FORECAST_WIND_SPEED] = None
forecast.append(data)
return forecast

View file

@ -125,11 +125,11 @@ class WeatherEntity(Entity):
@property
def state_attributes(self):
"""Return the state attributes."""
data = {
ATTR_WEATHER_TEMPERATURE: show_temp(
data = {}
if self.temperature is not None:
data[ATTR_WEATHER_TEMPERATURE] = show_temp(
self.hass, self.temperature, self.temperature_unit, self.precision
)
}
humidity = self.humidity
if humidity is not None:

View file

@ -1308,6 +1308,9 @@ pynuki==1.3.3
# homeassistant.components.nut
pynut2==2.1.2
# homeassistant.components.nws
pynws==0.7.4
# homeassistant.components.nx584
pynx584==0.4

View file

@ -294,6 +294,9 @@ pymfy==0.5.2
# homeassistant.components.monoprice
pymonoprice==0.3
# homeassistant.components.nws
pynws==0.7.4
# homeassistant.components.nx584
pynx584==0.4

View file

@ -120,6 +120,7 @@ TEST_REQUIREMENTS = (
"pylitejet",
"pymfy",
"pymonoprice",
"pynws",
"pynx584",
"pyopenuv",
"pyotp",

View file

@ -0,0 +1,274 @@
"""Tests for the NWS weather component."""
from homeassistant.components.nws.weather import ATTR_FORECAST_PRECIP_PROB
from homeassistant.components.weather import (
ATTR_WEATHER_HUMIDITY,
ATTR_WEATHER_PRESSURE,
ATTR_WEATHER_TEMPERATURE,
ATTR_WEATHER_VISIBILITY,
ATTR_WEATHER_WIND_BEARING,
ATTR_WEATHER_WIND_SPEED,
)
from homeassistant.components.weather import (
ATTR_FORECAST,
ATTR_FORECAST_CONDITION,
ATTR_FORECAST_TEMP,
ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_BEARING,
ATTR_FORECAST_WIND_SPEED,
)
from homeassistant.const import (
LENGTH_KILOMETERS,
LENGTH_METERS,
LENGTH_MILES,
PRESSURE_INHG,
PRESSURE_PA,
PRESSURE_HPA,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
from homeassistant.util.temperature import convert as convert_temperature
from homeassistant.setup import async_setup_component
from tests.common import load_fixture, assert_setup_component
EXP_OBS_IMP = {
ATTR_WEATHER_TEMPERATURE: round(
convert_temperature(26.7, TEMP_CELSIUS, TEMP_FAHRENHEIT)
),
ATTR_WEATHER_WIND_BEARING: 190,
ATTR_WEATHER_WIND_SPEED: round(
convert_distance(2.6, LENGTH_METERS, LENGTH_MILES) * 3600
),
ATTR_WEATHER_PRESSURE: round(
convert_pressure(101040, PRESSURE_PA, PRESSURE_INHG), 2
),
ATTR_WEATHER_VISIBILITY: round(
convert_distance(16090, LENGTH_METERS, LENGTH_MILES)
),
ATTR_WEATHER_HUMIDITY: 64,
}
EXP_OBS_METR = {
ATTR_WEATHER_TEMPERATURE: round(26.7),
ATTR_WEATHER_WIND_BEARING: 190,
ATTR_WEATHER_WIND_SPEED: round(
convert_distance(2.6, LENGTH_METERS, LENGTH_KILOMETERS) * 3600
),
ATTR_WEATHER_PRESSURE: round(convert_pressure(101040, PRESSURE_PA, PRESSURE_HPA)),
ATTR_WEATHER_VISIBILITY: round(
convert_distance(16090, LENGTH_METERS, LENGTH_KILOMETERS)
),
ATTR_WEATHER_HUMIDITY: 64,
}
EXP_FORE_IMP = {
ATTR_FORECAST_CONDITION: "lightning-rainy",
ATTR_FORECAST_TIME: "2019-08-12T20:00:00-04:00",
ATTR_FORECAST_TEMP: 70,
ATTR_FORECAST_WIND_SPEED: 10,
ATTR_FORECAST_WIND_BEARING: 180,
ATTR_FORECAST_PRECIP_PROB: 90,
}
EXP_FORE_METR = {
ATTR_FORECAST_CONDITION: "lightning-rainy",
ATTR_FORECAST_TIME: "2019-08-12T20:00:00-04:00",
ATTR_FORECAST_TEMP: round(convert_temperature(70, TEMP_FAHRENHEIT, TEMP_CELSIUS)),
ATTR_FORECAST_WIND_SPEED: round(
convert_distance(10, LENGTH_MILES, LENGTH_KILOMETERS)
),
ATTR_FORECAST_WIND_BEARING: 180,
ATTR_FORECAST_PRECIP_PROB: 90,
}
MINIMAL_CONFIG = {
"weather": {
"platform": "nws",
"api_key": "x@example.com",
"latitude": 40.0,
"longitude": -85.0,
}
}
INVALID_CONFIG = {
"weather": {"platform": "nws", "api_key": "x@example.com", "latitude": 40.0}
}
STAURL = "https://api.weather.gov/points/{},{}/stations"
OBSURL = "https://api.weather.gov/stations/{}/observations/"
FORCURL = "https://api.weather.gov/points/{},{}/forecast"
async def test_imperial(hass, aioclient_mock):
"""Test with imperial units."""
aioclient_mock.get(
STAURL.format(40.0, -85.0), text=load_fixture("nws-weather-sta-valid.json")
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-valid.json"),
params={"limit": 1},
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0), text=load_fixture("nws-weather-fore-valid.json")
)
hass.config.units = IMPERIAL_SYSTEM
with assert_setup_component(1, "weather"):
await async_setup_component(hass, "weather", MINIMAL_CONFIG)
state = hass.states.get("weather.kmie")
assert state
assert state.state == "sunny"
data = state.attributes
for key, value in EXP_OBS_IMP.items():
assert data.get(key) == value
assert state.attributes.get("friendly_name") == "KMIE"
forecast = data.get(ATTR_FORECAST)
for key, value in EXP_FORE_IMP.items():
assert forecast[0].get(key) == value
async def test_metric(hass, aioclient_mock):
"""Test with metric units."""
aioclient_mock.get(
STAURL.format(40.0, -85.0), text=load_fixture("nws-weather-sta-valid.json")
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-valid.json"),
params={"limit": 1},
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0), text=load_fixture("nws-weather-fore-valid.json")
)
hass.config.units = METRIC_SYSTEM
with assert_setup_component(1, "weather"):
await async_setup_component(hass, "weather", MINIMAL_CONFIG)
state = hass.states.get("weather.kmie")
assert state
assert state.state == "sunny"
data = state.attributes
for key, value in EXP_OBS_METR.items():
assert data.get(key) == value
assert state.attributes.get("friendly_name") == "KMIE"
forecast = data.get(ATTR_FORECAST)
for key, value in EXP_FORE_METR.items():
assert forecast[0].get(key) == value
async def test_none(hass, aioclient_mock):
"""Test with imperial units."""
aioclient_mock.get(
STAURL.format(40.0, -85.0), text=load_fixture("nws-weather-sta-valid.json")
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-null.json"),
params={"limit": 1},
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0), text=load_fixture("nws-weather-fore-null.json")
)
hass.config.units = IMPERIAL_SYSTEM
with assert_setup_component(1, "weather"):
await async_setup_component(hass, "weather", MINIMAL_CONFIG)
state = hass.states.get("weather.kmie")
assert state
assert state.state == "unknown"
data = state.attributes
for key in EXP_OBS_IMP:
assert data.get(key) is None
assert state.attributes.get("friendly_name") == "KMIE"
forecast = data.get(ATTR_FORECAST)
for key in EXP_FORE_IMP:
assert forecast[0].get(key) is None
async def test_fail_obs(hass, aioclient_mock):
"""Test failing observation/forecast update."""
aioclient_mock.get(
STAURL.format(40.0, -85.0), text=load_fixture("nws-weather-sta-valid.json")
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-valid.json"),
params={"limit": 1},
status=400,
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0),
text=load_fixture("nws-weather-fore-valid.json"),
status=400,
)
hass.config.units = IMPERIAL_SYSTEM
with assert_setup_component(1, "weather"):
await async_setup_component(hass, "weather", MINIMAL_CONFIG)
state = hass.states.get("weather.kmie")
assert state
async def test_fail_stn(hass, aioclient_mock):
"""Test failing station update."""
aioclient_mock.get(
STAURL.format(40.0, -85.0),
text=load_fixture("nws-weather-sta-valid.json"),
status=400,
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-valid.json"),
params={"limit": 1},
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0), text=load_fixture("nws-weather-fore-valid.json")
)
hass.config.units = IMPERIAL_SYSTEM
with assert_setup_component(1, "weather"):
await async_setup_component(hass, "weather", MINIMAL_CONFIG)
state = hass.states.get("weather.kmie")
assert state is None
async def test_invalid_config(hass, aioclient_mock):
"""Test invalid config.."""
aioclient_mock.get(
STAURL.format(40.0, -85.0), text=load_fixture("nws-weather-sta-valid.json")
)
aioclient_mock.get(
OBSURL.format("KMIE"),
text=load_fixture("nws-weather-obs-valid.json"),
params={"limit": 1},
)
aioclient_mock.get(
FORCURL.format(40.0, -85.0), text=load_fixture("nws-weather-fore-valid.json")
)
hass.config.units = IMPERIAL_SYSTEM
with assert_setup_component(0, "weather"):
await async_setup_component(hass, "weather", INVALID_CONFIG)
state = hass.states.get("weather.kmie")
assert state is None

View file

@ -0,0 +1,80 @@
{
"@context": [
"https://raw.githubusercontent.com/geojson/geojson-ld/master/contexts/geojson-base.jsonld",
{
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [
-85.014692800000006,
39.993574700000003
]
},
{
"type": "Polygon",
"coordinates": [
[
[
-85.027968599999994,
40.005368300000001
],
[
-85.0300814,
39.983399599999998
],
[
-85.001420100000004,
39.981779299999999
],
[
-84.999301200000005,
40.0037479
],
[
-85.027968599999994,
40.005368300000001
]
]
]
}
]
},
"properties": {
"updated": "2019-08-12T23:17:40+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2019-08-13T00:33:19+00:00",
"updateTime": "2019-08-12T23:17:40+00:00",
"validTimes": "2019-08-12T17:00:00+00:00/P8DT6H",
"elevation": {
"value": 366.06479999999999,
"unitCode": "unit:m"
},
"periods": [
{
"number": null,
"name": null,
"startTime": null,
"endTime": null,
"isDaytime": null,
"temperature": null,
"temperatureUnit": null,
"temperatureTrend": null,
"windSpeed": null,
"windDirection": null,
"icon": null,
"shortForecast": null,
"detailedForecast": null
}
]
}
}

View file

@ -0,0 +1,80 @@
{
"@context": [
"https://raw.githubusercontent.com/geojson/geojson-ld/master/contexts/geojson-base.jsonld",
{
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [
-85.014692800000006,
39.993574700000003
]
},
{
"type": "Polygon",
"coordinates": [
[
[
-85.027968599999994,
40.005368300000001
],
[
-85.0300814,
39.983399599999998
],
[
-85.001420100000004,
39.981779299999999
],
[
-84.999301200000005,
40.0037479
],
[
-85.027968599999994,
40.005368300000001
]
]
]
}
]
},
"properties": {
"updated": "2019-08-12T23:17:40+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2019-08-13T00:33:19+00:00",
"updateTime": "2019-08-12T23:17:40+00:00",
"validTimes": "2019-08-12T17:00:00+00:00/P8DT6H",
"elevation": {
"value": 366.06479999999999,
"unitCode": "unit:m"
},
"periods": [
{
"number": 1,
"name": "Tonight",
"startTime": "2019-08-12T20:00:00-04:00",
"endTime": "2019-08-13T06:00:00-04:00",
"isDaytime": false,
"temperature": 70,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "7 to 13 mph",
"windDirection": "S",
"icon": "https://api.weather.gov/icons/land/night/tsra,40/tsra,90?size=medium",
"shortForecast": "Showers And Thunderstorms",
"detailedForecast": "A detailed forecast."
}
]
}
}

161
tests/fixtures/nws-weather-obs-null.json vendored Normal file
View file

@ -0,0 +1,161 @@
{
"@context": [
"https://raw.githubusercontent.com/geojson/geojson-ld/master/contexts/geojson-base.jsonld",
{
"wx": "https://api.weather.gov/ontology#",
"s": "https://schema.org/",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#",
"geometry": {
"@id": "s:GeoCoordinates",
"@type": "geo:wktLiteral"
},
"city": "s:addressLocality",
"state": "s:addressRegion",
"distance": {
"@id": "s:Distance",
"@type": "s:QuantitativeValue"
},
"bearing": {
"@type": "s:QuantitativeValue"
},
"value": {
"@id": "s:value"
},
"unitCode": {
"@id": "s:unitCode",
"@type": "@id"
},
"forecastOffice": {
"@type": "@id"
},
"forecastGridData": {
"@type": "@id"
},
"publicZone": {
"@type": "@id"
},
"county": {
"@type": "@id"
}
}
],
"type": "FeatureCollection",
"features": [
{
"id": "https://api.weather.gov/stations/KMIE/observations/2019-08-12T23:53:00+00:00",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.400000000000006,
40.25
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMIE/observations/2019-08-12T23:53:00+00:00",
"@type": "wx:ObservationStation",
"elevation": {
"value": 286,
"unitCode": "unit:m"
},
"station": "https://api.weather.gov/stations/KMIE",
"timestamp": "2019-08-12T23:53:00+00:00",
"rawMessage": null,
"textDescription": "Clear",
"icon": null,
"presentWeather": [],
"temperature": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"dewpoint": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"windDirection": {
"value": null,
"unitCode": "unit:degree_(angle)",
"qualityControl": "qc:V"
},
"windSpeed": {
"value": null,
"unitCode": "unit:m_s-1",
"qualityControl": "qc:V"
},
"windGust": {
"value": null,
"unitCode": "unit:m_s-1",
"qualityControl": "qc:Z"
},
"barometricPressure": {
"value": null,
"unitCode": "unit:Pa",
"qualityControl": "qc:V"
},
"seaLevelPressure": {
"value": null,
"unitCode": "unit:Pa",
"qualityControl": "qc:V"
},
"visibility": {
"value": null,
"unitCode": "unit:m",
"qualityControl": "qc:C"
},
"maxTemperatureLast24Hours": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": null
},
"minTemperatureLast24Hours": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": null
},
"precipitationLastHour": {
"value": null,
"unitCode": "unit:m",
"qualityControl": "qc:Z"
},
"precipitationLast3Hours": {
"value": null,
"unitCode": "unit:m",
"qualityControl": "qc:Z"
},
"precipitationLast6Hours": {
"value": 0,
"unitCode": "unit:m",
"qualityControl": "qc:C"
},
"relativeHumidity": {
"value": null,
"unitCode": "unit:percent",
"qualityControl": "qc:C"
},
"windChill": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"heatIndex": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"cloudLayers": [
{
"base": {
"value": null,
"unitCode": "unit:m"
},
"amount": "CLR"
}
]
}
}
]
}

View file

@ -0,0 +1,161 @@
{
"@context": [
"https://raw.githubusercontent.com/geojson/geojson-ld/master/contexts/geojson-base.jsonld",
{
"wx": "https://api.weather.gov/ontology#",
"s": "https://schema.org/",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#",
"geometry": {
"@id": "s:GeoCoordinates",
"@type": "geo:wktLiteral"
},
"city": "s:addressLocality",
"state": "s:addressRegion",
"distance": {
"@id": "s:Distance",
"@type": "s:QuantitativeValue"
},
"bearing": {
"@type": "s:QuantitativeValue"
},
"value": {
"@id": "s:value"
},
"unitCode": {
"@id": "s:unitCode",
"@type": "@id"
},
"forecastOffice": {
"@type": "@id"
},
"forecastGridData": {
"@type": "@id"
},
"publicZone": {
"@type": "@id"
},
"county": {
"@type": "@id"
}
}
],
"type": "FeatureCollection",
"features": [
{
"id": "https://api.weather.gov/stations/KMIE/observations/2019-08-12T23:53:00+00:00",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.400000000000006,
40.25
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMIE/observations/2019-08-12T23:53:00+00:00",
"@type": "wx:ObservationStation",
"elevation": {
"value": 286,
"unitCode": "unit:m"
},
"station": "https://api.weather.gov/stations/KMIE",
"timestamp": "2019-08-12T23:53:00+00:00",
"rawMessage": "KMIE 122353Z 19005KT 10SM CLR 27/19 A2987 RMK AO2 SLP104 60000 T02670194 10272 20250 58002",
"textDescription": "Clear",
"icon": "https://api.weather.gov/icons/land/day/skc?size=medium",
"presentWeather": [],
"temperature": {
"value": 26.700000000000045,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"dewpoint": {
"value": 19.400000000000034,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"windDirection": {
"value": 190,
"unitCode": "unit:degree_(angle)",
"qualityControl": "qc:V"
},
"windSpeed": {
"value": 2.6000000000000001,
"unitCode": "unit:m_s-1",
"qualityControl": "qc:V"
},
"windGust": {
"value": null,
"unitCode": "unit:m_s-1",
"qualityControl": "qc:Z"
},
"barometricPressure": {
"value": 101150,
"unitCode": "unit:Pa",
"qualityControl": "qc:V"
},
"seaLevelPressure": {
"value": 101040,
"unitCode": "unit:Pa",
"qualityControl": "qc:V"
},
"visibility": {
"value": 16090,
"unitCode": "unit:m",
"qualityControl": "qc:C"
},
"maxTemperatureLast24Hours": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": null
},
"minTemperatureLast24Hours": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": null
},
"precipitationLastHour": {
"value": null,
"unitCode": "unit:m",
"qualityControl": "qc:Z"
},
"precipitationLast3Hours": {
"value": null,
"unitCode": "unit:m",
"qualityControl": "qc:Z"
},
"precipitationLast6Hours": {
"value": 0,
"unitCode": "unit:m",
"qualityControl": "qc:C"
},
"relativeHumidity": {
"value": 64.292485914891955,
"unitCode": "unit:percent",
"qualityControl": "qc:C"
},
"windChill": {
"value": null,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"heatIndex": {
"value": 27.981288713580284,
"unitCode": "unit:degC",
"qualityControl": "qc:V"
},
"cloudLayers": [
{
"base": {
"value": null,
"unitCode": "unit:m"
},
"amount": "CLR"
}
]
}
}
]
}

View file

@ -0,0 +1,996 @@
{
"@context": [
"https://raw.githubusercontent.com/geojson/geojson-ld/master/contexts/geojson-base.jsonld",
{
"wx": "https://api.weather.gov/ontology#",
"s": "https://schema.org/",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#",
"geometry": {
"@id": "s:GeoCoordinates",
"@type": "geo:wktLiteral"
},
"city": "s:addressLocality",
"state": "s:addressRegion",
"distance": {
"@id": "s:Distance",
"@type": "s:QuantitativeValue"
},
"bearing": {
"@type": "s:QuantitativeValue"
},
"value": {
"@id": "s:value"
},
"unitCode": {
"@id": "s:unitCode",
"@type": "@id"
},
"forecastOffice": {
"@type": "@id"
},
"forecastGridData": {
"@type": "@id"
},
"publicZone": {
"@type": "@id"
},
"county": {
"@type": "@id"
},
"observationStations": {
"@container": "@list",
"@type": "@id"
}
}
],
"type": "FeatureCollection",
"features": [
{
"id": "https://api.weather.gov/stations/KMIE",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.393609999999995,
40.234169999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMIE",
"@type": "wx:ObservationStation",
"elevation": {
"value": 284.988,
"unitCode": "unit:m"
},
"stationIdentifier": "KMIE",
"name": "Muncie, Delaware County-Johnson Field",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KVES",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.531899899999999,
40.2044
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KVES",
"@type": "wx:ObservationStation",
"elevation": {
"value": 306.93360000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KVES",
"name": "Versailles Darke County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KAID",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.609769999999997,
40.106119999999997
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KAID",
"@type": "wx:ObservationStation",
"elevation": {
"value": 276.14879999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KAID",
"name": "Anderson Municipal Airport",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KDAY",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.218609999999998,
39.906109999999998
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KDAY",
"@type": "wx:ObservationStation",
"elevation": {
"value": 306.93360000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KDAY",
"name": "Dayton, Cox Dayton International Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KGEZ",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.799819999999997,
39.585459999999998
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KGEZ",
"@type": "wx:ObservationStation",
"elevation": {
"value": 244.1448,
"unitCode": "unit:m"
},
"stationIdentifier": "KGEZ",
"name": "Shelbyville Municipal Airport",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KMGY",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.224720000000005,
39.588889999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMGY",
"@type": "wx:ObservationStation",
"elevation": {
"value": 291.9984,
"unitCode": "unit:m"
},
"stationIdentifier": "KMGY",
"name": "Dayton, Dayton-Wright Brothers Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KHAO",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.520610000000005,
39.36121
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KHAO",
"@type": "wx:ObservationStation",
"elevation": {
"value": 185.0136,
"unitCode": "unit:m"
},
"stationIdentifier": "KHAO",
"name": "Butler County Regional Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KFFO",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.049999999999997,
39.833329900000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KFFO",
"@type": "wx:ObservationStation",
"elevation": {
"value": 250.85040000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KFFO",
"name": "Dayton / Wright-Patterson Air Force Base",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KCVG",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.672290000000004,
39.044559999999997
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KCVG",
"@type": "wx:ObservationStation",
"elevation": {
"value": 262.12799999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KCVG",
"name": "Cincinnati/Northern Kentucky International Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KEDJ",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.819199999999995,
40.372300000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KEDJ",
"@type": "wx:ObservationStation",
"elevation": {
"value": 341.98560000000003,
"unitCode": "unit:m"
},
"stationIdentifier": "KEDJ",
"name": "Bellefontaine Regional Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KFWA",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.206370000000007,
40.97251
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KFWA",
"@type": "wx:ObservationStation",
"elevation": {
"value": 242.9256,
"unitCode": "unit:m"
},
"stationIdentifier": "KFWA",
"name": "Fort Wayne International Airport",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KBAK",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.900000000000006,
39.266669999999998
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KBAK",
"@type": "wx:ObservationStation",
"elevation": {
"value": 199.94880000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KBAK",
"name": "Columbus / Bakalar",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KEYE",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-86.295829999999995,
39.825000000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KEYE",
"@type": "wx:ObservationStation",
"elevation": {
"value": 249.93600000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KEYE",
"name": "Indianapolis, Eagle Creek Airpark",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KLUK",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.41583,
39.105829999999997
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KLUK",
"@type": "wx:ObservationStation",
"elevation": {
"value": 146.9136,
"unitCode": "unit:m"
},
"stationIdentifier": "KLUK",
"name": "Cincinnati, Cincinnati Municipal Airport Lunken Field",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KIND",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-86.281599999999997,
39.725180000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KIND",
"@type": "wx:ObservationStation",
"elevation": {
"value": 240.792,
"unitCode": "unit:m"
},
"stationIdentifier": "KIND",
"name": "Indianapolis International Airport",
"timeZone": "America/Indiana/Indianapolis"
}
},
{
"id": "https://api.weather.gov/stations/KAOH",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.021389999999997,
40.708060000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KAOH",
"@type": "wx:ObservationStation",
"elevation": {
"value": 296.87520000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KAOH",
"name": "Lima, Lima Allen County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KI69",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.2102,
39.078400000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KI69",
"@type": "wx:ObservationStation",
"elevation": {
"value": 256.94640000000004,
"unitCode": "unit:m"
},
"stationIdentifier": "KI69",
"name": "Batavia Clermont County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KILN",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.779169899999999,
39.428330000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KILN",
"@type": "wx:ObservationStation",
"elevation": {
"value": 327.96480000000003,
"unitCode": "unit:m"
},
"stationIdentifier": "KILN",
"name": "Wilmington, Airborne Airpark Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KMRT",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.351600000000005,
40.224699999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMRT",
"@type": "wx:ObservationStation",
"elevation": {
"value": 311.20080000000002,
"unitCode": "unit:m"
},
"stationIdentifier": "KMRT",
"name": "Marysville Union County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KTZR",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.137219999999999,
39.900829999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KTZR",
"@type": "wx:ObservationStation",
"elevation": {
"value": 276.14879999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KTZR",
"name": "Columbus, Bolton Field Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KFDY",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.668610000000001,
41.01361
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KFDY",
"@type": "wx:ObservationStation",
"elevation": {
"value": 248.10720000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KFDY",
"name": "Findlay, Findlay Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KDLZ",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.114800000000002,
40.279699999999998
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KDLZ",
"@type": "wx:ObservationStation",
"elevation": {
"value": 288.036,
"unitCode": "unit:m"
},
"stationIdentifier": "KDLZ",
"name": "Delaware Municipal Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KOSU",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.0780599,
40.078060000000001
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KOSU",
"@type": "wx:ObservationStation",
"elevation": {
"value": 274.92959999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KOSU",
"name": "Columbus, Ohio State University Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KLCK",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.933329999999998,
39.816670000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KLCK",
"@type": "wx:ObservationStation",
"elevation": {
"value": 227.07600000000002,
"unitCode": "unit:m"
},
"stationIdentifier": "KLCK",
"name": "Rickenbacker Air National Guard Base",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KMNN",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.068330000000003,
40.616669999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMNN",
"@type": "wx:ObservationStation",
"elevation": {
"value": 302.97120000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KMNN",
"name": "Marion, Marion Municipal Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KCMH",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.876390000000001,
39.994999999999997
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KCMH",
"@type": "wx:ObservationStation",
"elevation": {
"value": 248.10720000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KCMH",
"name": "Columbus - John Glenn Columbus International Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KFGX",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-83.743399999999994,
38.541800000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KFGX",
"@type": "wx:ObservationStation",
"elevation": {
"value": 277.9776,
"unitCode": "unit:m"
},
"stationIdentifier": "KFGX",
"name": "Flemingsburg Fleming-Mason Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KFFT",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.903329999999997,
38.184719999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KFFT",
"@type": "wx:ObservationStation",
"elevation": {
"value": 245.0592,
"unitCode": "unit:m"
},
"stationIdentifier": "KFFT",
"name": "Frankfort, Capital City Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KLHQ",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.663330000000002,
39.757219900000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KLHQ",
"@type": "wx:ObservationStation",
"elevation": {
"value": 263.95679999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KLHQ",
"name": "Lancaster, Fairfield County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KLOU",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.663610000000006,
38.227780000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KLOU",
"@type": "wx:ObservationStation",
"elevation": {
"value": 166.11600000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KLOU",
"name": "Louisville, Bowman Field Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KSDF",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-85.72972,
38.177219999999998
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KSDF",
"@type": "wx:ObservationStation",
"elevation": {
"value": 150.876,
"unitCode": "unit:m"
},
"stationIdentifier": "KSDF",
"name": "Louisville, Standiford Field",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KVTA",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.462500000000006,
40.022779999999997
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KVTA",
"@type": "wx:ObservationStation",
"elevation": {
"value": 269.13839999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KVTA",
"name": "Newark, Newark Heath Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KLEX",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-84.6114599,
38.033900000000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KLEX",
"@type": "wx:ObservationStation",
"elevation": {
"value": 291.084,
"unitCode": "unit:m"
},
"stationIdentifier": "KLEX",
"name": "Lexington Blue Grass Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KMFD",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.517780000000002,
40.820279900000003
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KMFD",
"@type": "wx:ObservationStation",
"elevation": {
"value": 395.02080000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KMFD",
"name": "Mansfield - Mansfield Lahm Regional Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KZZV",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.892219999999995,
39.94444
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KZZV",
"@type": "wx:ObservationStation",
"elevation": {
"value": 274.01519999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KZZV",
"name": "Zanesville, Zanesville Municipal Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KHTS",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-82.555000000000007,
38.365000000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KHTS",
"@type": "wx:ObservationStation",
"elevation": {
"value": 252.06960000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KHTS",
"name": "Huntington, Tri-State Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KBJJ",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.886669999999995,
40.873060000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KBJJ",
"@type": "wx:ObservationStation",
"elevation": {
"value": 345.94800000000004,
"unitCode": "unit:m"
},
"stationIdentifier": "KBJJ",
"name": "Wooster, Wayne County Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KPHD",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.423609999999996,
40.471939900000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KPHD",
"@type": "wx:ObservationStation",
"elevation": {
"value": 271.88159999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KPHD",
"name": "New Philadelphia, Harry Clever Field",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KPKB",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.439170000000004,
39.344999999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KPKB",
"@type": "wx:ObservationStation",
"elevation": {
"value": 262.12799999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KPKB",
"name": "Parkersburg, Mid-Ohio Valley Regional Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KCAK",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.443430000000006,
40.918109999999999
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KCAK",
"@type": "wx:ObservationStation",
"elevation": {
"value": 369.11279999999999,
"unitCode": "unit:m"
},
"stationIdentifier": "KCAK",
"name": "Akron Canton Regional Airport",
"timeZone": "America/New_York"
}
},
{
"id": "https://api.weather.gov/stations/KCRW",
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-81.591390000000004,
38.379440000000002
]
},
"properties": {
"@id": "https://api.weather.gov/stations/KCRW",
"@type": "wx:ObservationStation",
"elevation": {
"value": 299.00880000000001,
"unitCode": "unit:m"
},
"stationIdentifier": "KCRW",
"name": "Charleston, Yeager Airport",
"timeZone": "America/New_York"
}
}
],
"observationStations": [
"https://api.weather.gov/stations/KMIE",
"https://api.weather.gov/stations/KVES",
"https://api.weather.gov/stations/KAID",
"https://api.weather.gov/stations/KDAY",
"https://api.weather.gov/stations/KGEZ",
"https://api.weather.gov/stations/KMGY",
"https://api.weather.gov/stations/KHAO",
"https://api.weather.gov/stations/KFFO",
"https://api.weather.gov/stations/KCVG",
"https://api.weather.gov/stations/KEDJ",
"https://api.weather.gov/stations/KFWA",
"https://api.weather.gov/stations/KBAK",
"https://api.weather.gov/stations/KEYE",
"https://api.weather.gov/stations/KLUK",
"https://api.weather.gov/stations/KIND",
"https://api.weather.gov/stations/KAOH",
"https://api.weather.gov/stations/KI69",
"https://api.weather.gov/stations/KILN",
"https://api.weather.gov/stations/KMRT",
"https://api.weather.gov/stations/KTZR",
"https://api.weather.gov/stations/KFDY",
"https://api.weather.gov/stations/KDLZ",
"https://api.weather.gov/stations/KOSU",
"https://api.weather.gov/stations/KLCK",
"https://api.weather.gov/stations/KMNN",
"https://api.weather.gov/stations/KCMH",
"https://api.weather.gov/stations/KFGX",
"https://api.weather.gov/stations/KFFT",
"https://api.weather.gov/stations/KLHQ",
"https://api.weather.gov/stations/KLOU",
"https://api.weather.gov/stations/KSDF",
"https://api.weather.gov/stations/KVTA",
"https://api.weather.gov/stations/KLEX",
"https://api.weather.gov/stations/KMFD",
"https://api.weather.gov/stations/KZZV",
"https://api.weather.gov/stations/KHTS",
"https://api.weather.gov/stations/KBJJ",
"https://api.weather.gov/stations/KPHD",
"https://api.weather.gov/stations/KPKB",
"https://api.weather.gov/stations/KCAK",
"https://api.weather.gov/stations/KCRW"
]
}