Allow specifying state in weather location

This commit is contained in:
Svilen Markov 2024-05-02 19:54:20 +01:00
parent ad06146784
commit d8d6625478
4 changed files with 79 additions and 3 deletions

View File

@ -579,6 +579,15 @@ Example:
location: London, United Kingdom
```
> [!NOTE]
>
> US cities which have common names can have their state specified as the second parameter like such:
>
> * Greenville, North Carolina, United States
> * Greenville, South Carolina, United States
> * Greenville, Mississippi, United States
Preview:
![](images/weather-widget-preview.png)
@ -592,6 +601,7 @@ Each bar represents a 2 hour interval. The yellow background represents sunrise
| location | string | yes | |
| units | string | no | metric |
| hide-location | boolean | no | false |
| show-area-name | boolean | no | false |
##### `location`
The name of the city and country to fetch weather information for. Attempting to launch the applcation with an invalid location will result in an error. You can use the [gecoding API page](https://open-meteo.com/en/docs/geocoding-api) to search for your specific location. Glance will use the first result from the list if there are multiple.
@ -602,6 +612,19 @@ Whether to show the temperature in celsius or fahrenheit, possible values are `m
##### `hide-location`
Optionally don't display the location name on the widget.
##### `show-area-name`
Whether to display the state/administrative area in the location name. If set to `true` the location will be displayed as:
```
Greenville, North Carolina, United States
```
Otherwise, if set to `false` (which is the default) it'll be displayed as:
```
Greenville, United States
```
### Monitor
Display a list of sites and whether they are reachable (online) or not. This is determined by sending a HEAD request to the specified URL, if the response is 200 then the site is OK. The time it took to receive a response is also shown in milliseconds.

View File

@ -23,7 +23,7 @@
{{ if not .HideLocation }}
<div class="flex items-center justify-center margin-top-15 gap-7 size-h5">
<div class="location-icon"></div>
<div class="text-truncate">{{ .Place.Name }}, {{ .Place.Country }}</div>
<div class="text-truncate">{{ .Place.Name }},{{ if .ShowAreaName }} {{ .Place.Area }},{{ end }} {{ .Place.Country }}</div>
</div>
{{ end }}
{{ end }}

View File

@ -6,6 +6,7 @@ import (
"net/http"
"net/url"
"slices"
"strings"
"time"
_ "time/tzdata"
@ -17,6 +18,7 @@ type PlacesResponseJson struct {
type PlaceJson struct {
Name string
Area string `json:"admin1"`
Latitude float64
Longitude float64
Timezone string
@ -48,8 +50,41 @@ type weatherColumn struct {
HasPrecipitation bool
}
var commonCountryAbbreviations = map[string]string{
"US": "United States",
"USA": "United States",
"UK": "United Kingdom",
}
func expandCountryAbbreviations(name string) string {
if expanded, ok := commonCountryAbbreviations[strings.TrimSpace(name)]; ok {
return expanded
}
return name
}
// Separates the location that Open Meteo accepts from the administrative area
// which can then be used to filter to the correct place after the list of places
// has been retrieved. Also expands abbreviations since Open Meteo does not accept
// country names like "US", "USA" and "UK"
func parsePlaceName(name string) (string, string) {
parts := strings.Split(name, ",")
if len(parts) == 1 {
return name, ""
}
if len(parts) == 2 {
return parts[0] + ", " + expandCountryAbbreviations(parts[1]), ""
}
return parts[0] + ", " + expandCountryAbbreviations(parts[2]), strings.TrimSpace(parts[1])
}
func FetchPlaceFromName(location string) (*PlaceJson, error) {
requestUrl := fmt.Sprintf("https://geocoding-api.open-meteo.com/v1/search?name=%s&count=1&language=en&format=json", url.QueryEscape(location))
location, area := parsePlaceName(location)
requestUrl := fmt.Sprintf("https://geocoding-api.open-meteo.com/v1/search?name=%s&count=10&language=en&format=json", url.QueryEscape(location))
request, _ := http.NewRequest("GET", requestUrl, nil)
responseJson, err := decodeJsonFromRequest[PlacesResponseJson](defaultClient, request)
@ -61,7 +96,24 @@ func FetchPlaceFromName(location string) (*PlaceJson, error) {
return nil, fmt.Errorf("no places found for %s", location)
}
place := &responseJson.Results[0]
var place *PlaceJson
if area != "" {
area = strings.ToLower(area)
for i := range responseJson.Results {
if strings.ToLower(responseJson.Results[i].Area) == area {
place = &responseJson.Results[i]
break
}
}
if place == nil {
return nil, fmt.Errorf("no place found for %s in %s", location, area)
}
} else {
place = &responseJson.Results[0]
}
loc, err := time.LoadLocation(place.Timezone)

View File

@ -12,6 +12,7 @@ import (
type Weather struct {
widgetBase `yaml:",inline"`
Location string `yaml:"location"`
ShowAreaName bool `yaml:"show-area-name"`
HideLocation bool `yaml:"hide-location"`
Units string `yaml:"units"`
Place *feed.PlaceJson `yaml:"-"`