浏览代码

Allow specifying state in weather location

Svilen Markov 1 年之前
父节点
当前提交
d8d6625478
共有 4 个文件被更改,包括 79 次插入3 次删除
  1. 23 0
      docs/configuration.md
  2. 1 1
      internal/assets/templates/weather.html
  3. 54 2
      internal/feed/openmeteo.go
  4. 1 0
      internal/widget/weather.go

+ 23 - 0
docs/configuration.md

@@ -579,6 +579,15 @@ Example:
   location: London, United Kingdom
   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:
 Preview:
 
 
 ![](images/weather-widget-preview.png)
 ![](images/weather-widget-preview.png)
@@ -592,6 +601,7 @@ Each bar represents a 2 hour interval. The yellow background represents sunrise
 | location | string | yes |  |
 | location | string | yes |  |
 | units | string | no | metric |
 | units | string | no | metric |
 | hide-location | boolean | no | false |
 | hide-location | boolean | no | false |
+| show-area-name | boolean | no | false |
 
 
 ##### `location`
 ##### `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.
 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`
 ##### `hide-location`
 Optionally don't display the location name on the widget.
 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
 ### 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.
 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.
 
 

+ 1 - 1
internal/assets/templates/weather.html

@@ -23,7 +23,7 @@
 {{ if not .HideLocation }}
 {{ if not .HideLocation }}
 <div class="flex items-center justify-center margin-top-15 gap-7 size-h5">
 <div class="flex items-center justify-center margin-top-15 gap-7 size-h5">
     <div class="location-icon"></div>
     <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>
 </div>
 {{ end }}
 {{ end }}
 {{ end }}
 {{ end }}

+ 54 - 2
internal/feed/openmeteo.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
 	"slices"
 	"slices"
+	"strings"
 	"time"
 	"time"
 
 
 	_ "time/tzdata"
 	_ "time/tzdata"
@@ -17,6 +18,7 @@ type PlacesResponseJson struct {
 
 
 type PlaceJson struct {
 type PlaceJson struct {
 	Name      string
 	Name      string
+	Area      string `json:"admin1"`
 	Latitude  float64
 	Latitude  float64
 	Longitude float64
 	Longitude float64
 	Timezone  string
 	Timezone  string
@@ -48,8 +50,41 @@ type weatherColumn struct {
 	HasPrecipitation bool
 	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) {
 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)
 	request, _ := http.NewRequest("GET", requestUrl, nil)
 	responseJson, err := decodeJsonFromRequest[PlacesResponseJson](defaultClient, request)
 	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)
 		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)
 	loc, err := time.LoadLocation(place.Timezone)
 
 

+ 1 - 0
internal/widget/weather.go

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