mirror of
https://github.com/Luzifer/go-metar.git
synced 2024-10-18 05:04:21 +00:00
130 lines
6.1 KiB
Go
130 lines
6.1 KiB
Go
|
package metar
|
|||
|
|
|||
|
import (
|
|||
|
"encoding/xml"
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
"net/http"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
apiSource = "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&stationString=%s&hoursBeforeNow=2&mostRecent=true"
|
|||
|
)
|
|||
|
|
|||
|
var (
|
|||
|
// HTTPClient is used to make requests, you can insert your own
|
|||
|
HTTPClient = http.DefaultClient
|
|||
|
)
|
|||
|
|
|||
|
// Result holds all the data from the METAR request
|
|||
|
type Result struct {
|
|||
|
XMLName xml.Name `xml:"METAR"`
|
|||
|
RawText string `xml:"raw_text"` // The raw METAR
|
|||
|
StationID string `xml:"station_id"` // Station identifier; Always a four character alphanumeric( A-Z, 0-9)
|
|||
|
ObservationTime time.Time `xml:"observation_time"` // Time this METAR was observed
|
|||
|
Latitude float64 `xml:"latitude"` // The latitude (in decimal degrees) of the station that reported this METAR
|
|||
|
Longitude float64 `xml:"longitude"` // The longitude (in decimal degrees) of the station that reported this METAR
|
|||
|
Temperature float64 `xml:"temp_c"` // Air temperature (celsius)
|
|||
|
Dewpoint float64 `xml:"dewpoint_c"` // Dewpoint temperature (celsius)
|
|||
|
WindDirDegrees int64 `xml:"wind_dir_degrees"` // Direction from which the wind is blowing. 0 degrees=variable wind direction.
|
|||
|
WindSpeed int64 `xml:"wind_speed_kt"` // Wind speed; 0 degree wdir and 0 wspd = calm winds (kts)
|
|||
|
WindGust int64 `xml:"wind_gust_kt"` // Wind gust
|
|||
|
VisibilityStatute float64 `xml:"visibility_statute_mi"` // Horizontal visibility (statute miles)
|
|||
|
Altimeter float64 `xml:"altim_in_hg"` // Altimeter (inches of Hg)
|
|||
|
SeaLevelPressure float64 `xml:"sea_level_pressure_mb"` // Sea-level pressure (mb)
|
|||
|
QualityControlFlags QualityControlFlags `xml:"quality_control_flags"` // Quality control flags provide useful information about the METAR station(s) that provide the data.
|
|||
|
WXString string `xml:"wx_string"` // WX string descriptions (https://www.aviationweather.gov/static/adds/docs/metars/wxSymbols_anno2.pdf)
|
|||
|
SkyCondition struct {
|
|||
|
SkyCover SkyCover `xml:"sky_cover,attr"` // Sky cover, up to four levels of sky cover can be reported ; OVX present when vert_vis_ft is reported
|
|||
|
} `xml:"sky_condition"`
|
|||
|
FlightCategory FlightCategory `xml:"flight_category"` // Flight category of this METAR
|
|||
|
// Fields 19 to 29 currently not implemented
|
|||
|
MetarType string `xml:"metar_type"` // METAR or SPECI
|
|||
|
Elevation float64 `xml:"elevation_m"` // The elevation of the station that reported this METAR (meters)
|
|||
|
}
|
|||
|
|
|||
|
// QualityControlFlags provide useful information about the METAR station(s) that provide the data.
|
|||
|
type QualityControlFlags struct {
|
|||
|
XMLName xml.Name `xml:"quality_control_flags"`
|
|||
|
NoSignal bool `xml:"no_signal"`
|
|||
|
}
|
|||
|
|
|||
|
// SkyCover defines and explains possible sky coverage situations
|
|||
|
type SkyCover string
|
|||
|
|
|||
|
// Common SkyCover situations
|
|||
|
const (
|
|||
|
SkyCoverSKC SkyCover = "SKC" // "No cloud/Sky clear" used worldwide but in North America is used to indicate a human generated report
|
|||
|
SkyCoverCLR SkyCover = "CLR" // "No clouds below 12,000 ft (3,700 m) (U.S.) or 25,000 ft (7,600 m) (Canada)", used mainly within North America and indicates a station that is at least partly automated
|
|||
|
SkyCoverNSC SkyCover = "NSC" // "No (nil) significant cloud", i.e., none below 5,000 ft (1,500 m) and no TCU or CB. Not used in North America.
|
|||
|
SkyCoverFEW SkyCover = "FEW" // "Few" = 1–2 oktas
|
|||
|
SkyCoverSCT SkyCover = "SCT" // "Scattered" = 3–4 oktas
|
|||
|
SkyCoverBKN SkyCover = "BKN" // "Broken" = 5–7 oktas
|
|||
|
SkyCoverOVC SkyCover = "OVC" // "Overcast" = 8 oktas, i.e., full cloud coverage
|
|||
|
SkyCoverCAVOK SkyCover = "CAVOK" // Ceiling And Visibility OKay, indicating no cloud below 5,000 ft (1,500 m) or the highest minimum sector altitude and no cumulonimbus or towering cumulus at any level, a visibility of 10 km (6 mi) or more and no significant weather change
|
|||
|
)
|
|||
|
|
|||
|
// FlightCategory defines and explains possible flight category situations
|
|||
|
type FlightCategory string
|
|||
|
|
|||
|
// Common FlightCategory situations
|
|||
|
const (
|
|||
|
FlightCategoryVFR FlightCategory = "VFR" // Visual Flight Rules (Ceiling greater than 3,000 feet AGL and visibility greater than 5 miles)
|
|||
|
FlightCategoryMVFR FlightCategory = "MVFR" // Marginal Visual Flight Rules (Ceiling 1,000 to 3,000 feet AGL and/or visibility 3 to 5 miles)
|
|||
|
FlightCategoryIFR FlightCategory = "IFR" // Instrument Flight Rules (Ceiling 500 to below 1,000 feet AGL and/or visibility 1 mile to less than 3 miles)
|
|||
|
FlightCategoryLIFR FlightCategory = "LIFR" // Low Instrument Flight Rules (Ceiling below 500 feet AGL and/or visibility less than 1 mile)
|
|||
|
)
|
|||
|
|
|||
|
type response struct {
|
|||
|
XMLName xml.Name `xml:"response"`
|
|||
|
Data struct {
|
|||
|
NumResults int `xml:"num_results,attr"`
|
|||
|
Results []Result `xml:"METAR"`
|
|||
|
} `xml:"data"`
|
|||
|
}
|
|||
|
|
|||
|
// FetchCurrentStationWeather fetches the last result from the specified station if it was reported during last 2 hours
|
|||
|
func FetchCurrentStationWeather(station string) (*Result, error) {
|
|||
|
req, _ := http.NewRequest("GET", fmt.Sprintf(apiSource, station), nil)
|
|||
|
res, err := HTTPClient.Do(req)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
r := &response{}
|
|||
|
if err = xml.NewDecoder(res.Body).Decode(r); err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
if r.Data.NumResults != len(r.Data.Results) {
|
|||
|
return nil, errors.New("Got inconsistent number of results")
|
|||
|
}
|
|||
|
|
|||
|
if r.Data.NumResults == 0 {
|
|||
|
return nil, errors.New("Did not find any data for your station")
|
|||
|
}
|
|||
|
|
|||
|
return &r.Data.Results[0], nil
|
|||
|
}
|
|||
|
|
|||
|
// InHgTohPa converts "inch of mercury" to "hectopascal"
|
|||
|
func InHgTohPa(inHg float64) float64 {
|
|||
|
return inHg * 33.8638866667
|
|||
|
}
|
|||
|
|
|||
|
// KtsToMs converts "knots" to "meters per second"
|
|||
|
func KtsToMs(kts float64) float64 {
|
|||
|
return kts * 0.514444
|
|||
|
}
|
|||
|
|
|||
|
// StatMileToKm converts "statute miles" to "kilometers"
|
|||
|
func StatMileToKm(sm float64) float64 {
|
|||
|
return sm * 1.60934
|
|||
|
}
|
|||
|
|
|||
|
// MbTohPa converts "millibar" to "hectopascal"
|
|||
|
func MbTohPa(mb float64) float64 {
|
|||
|
return mb * 0.1
|
|||
|
}
|