Wunder Weather
Posted on 2016-August-16 in programming
Just released this small piece of code a few days back:
https://github.com/nicolas31/wunder
I wanted to be able to bring up the weather forecast for the place I am currently visiting without having to yield my address book to a shady app, or suffer from tons of annoying ads eating through my data plan and phone storage.
The Yahoo weather app is fantastic but has too many ads. Weather web sites are incredibly data heavy, making it nearly impossible to get right to the information I am looking for: is it going to rain today or tomorrow?Expected temperatures? Android has some ad-less widgets but they usually request GPS positioning and I'd rather not activate location services when I don't need them.
So I hacked something. Made a web app that identifies your position by geolocating the requester's IP address, obtains the weather forecast from a reliable source, and displays the only weather information I need on a fast loading page.
First issue: geolocate an IP address.
There are many free services on the net to achieve that. Alternatively, you can download a static list and refresh it at regular intervals, but I wanted to get something a bit more dynamic. I chose:
Their API is dead simple and just works. Provide an IP address, get a country code, city name, latitude and longitude. You do not need to subscribe to their services, just make sure you are not choking them with too many requests.
Second issue: find a reliable weather source.
I fist triedopenweathermap.org. This is a very cool site but has a few shortcomings:
You can get the weather for a given [city, country] or [lat, lon]. The list of supported [city, country] pairs is static and can be downloaded from their web site. While they do support a lot of cities in the world, the problem was figuring out how to match [city, country] between what is returned by ip-api.com and what is understood by openweathermap.org. The matching is not 100% accurate.
Getting the weather by coordinates would work but it is far from user-friendly. You end up with Weather forecast for location Lat=XX Lon=YY. I'd rather look up the weather for San Francisco than for a pair of coordinates that are not obviously recognizable.
I ended up looking up [city, country] by computing the smallest distance on the openweathermap list, but that is just tedious and a lot of work for very little gain.
Other major issue: the weather forecast is only provided GMT, which is utterly useless. What I want is local time, always. What do I care if I am told that it will rain from 2 to 5am GMT if I cannot relate that to local time?
Figuring out a conversion between GMT and local time is a lot trickier than it looks. Thanks to Daylight Saving Time rules that are changed at random intervals in various countries, it is very hard to predict the time offset in some places more than a couple of weeks ahead. Relevant:
A bit of googling around revealed there is an actual API from Google Maps to convert a Unix time stamp + latitude and longitude to a local time. This API takes into account local DST rules at the considered date/time, which is exactly what we want. No need to register with Google, as usual the API is free to use and rate-limited.
Example code can be found here: https://github.com/nicolas314/tz
In summary: getting the weather from openweathermap would require:
- One external API call to associate IP to [lat, lon]
- A search to associate [lat, lon] to [city, country]
- One external API call to obtain actual weather data
- One external API call to convert GMT to local time
I have implemented that and the result is ugly. Ok let's see if we can find something smarter.
Next try:wunderground.com
They also offer an API to obtain weather data for any place in the world and they take care of two things: converting [lat, lon] to [city, country], and converting weather forecast to local time. This is exactly what we want.
Their API can also take care of geolocating an IP address but I found their results to be a lot less reliable than what I get from ip-api.com, so will stick to that for geolocation.
Their terms and conditions are fair. You need to register with them to obtain an API key and that's about it. Results are delivered in metric units and can be localized in several languages. You also get a pointer to icons symbolizing the weather, which is perfect to generate a nice web page effortlessly.
Some comments about my implementation:
Results from wunderground contain a whole bunch of information I am not interested in, like temperatures in Farenheit. Not an issue: the Go JSON API allows defining fewer fields than what is parsed, so you can keep your structs small with only relevant data.
When running behind a reverse proxy, the incoming requesting IP address you see is the one for the proxy. In order to get the real incoming IP address you need to configure the reverse proxy to pass it along, usually in an HTTP header. Since I am running this service behind nginx, I get the address from X-Real-IP. That is probably different for each reverse proxy out there.
Hardcoded handlers are provided to take care of requests for /favicon.ico and /robots.txt. I was tired of seeing 404 requests in my logs for these two.
Results are cached by IP address for one hour to avoid flooding upstream API services with requests. Results are displayed from a template that can easily be tweaked. The one I wrote fits nicely enough on both mobile and desktops, your mileage may vary.
I installed the end result on a tiny VPS instance, for my own use. Hoping that could be useful to somebody else.