Trying to predict the weather: my slow descent into implementation-related insanity
Tags: webdev, javascript
Published: 2020-06-11 23:01:51

[Do I Need A Jacket](https://doineedajacket.com/) (henceforth referred to as DINAJ), is a simple weather app which, when given a location, says whether you need a jacket or not for the day's activities. For some reason, it's not open source. You can probably use their API to figure out if you need a jacket, but if you were to implement a similar feature without snooping at source code, you'd need to do a lot of stretching in order to figure out what's going on. I did just that. 

![Sample version of my new tab page, weather not shown because of privacy concerns. Believe me, it's there, I just don't want people to know where I live.](https://dev-to-uploads.s3.amazonaws.com/i/1hjyakas3tsuw9lubxce.png)

## Why?

I noticed that a lot of people had custom new tab pages when I was at college. I forget the exact name of the web page - it had the phrase "Good morning / afternoon / evening, person", a giant clock, and the phrase "what do you want to do today?" emblazoned in larger text. I figured I knew nothing about HTML / CSS / JS, so why not try to make it myself? 

I started with simple stuff - date, time, bookmarks, etc. I added some weather (just highs, lows, and current temperature). But I realized: if I was just looking at a homepage for a second, what if it told me what to wear so I could just check it and dress accordingly? That's where I got the idea to copy DINAJ.

## Step 1: simple threshold
If the windchill is less than an arbitrary threshold, then it's jacket weather. Otherwise, it's not jacket weather.

```javascript
if (weather.feels_like > 55) {
  DINAJ = "You should leave your jacket at home."
} else {
  DINAJ = "You should bring a jacket with you."
}
```
This was so simple, I just turned it into a one liner with a ternary operator. I love elegant code like that.

Obviously this has faults. No rain or snow detection, just temperature and wind. It only really works if you have the privilege of living in a place where precipitation doesn't exist (and if you could find that, DM me so I can pack my bags and move there as soon as possible). The next step would attempt to fix that.

## Step 2: bad weather detection
Using the simple threshold, check the weather's icon or label. If it's deemed "bad weather", then flag it so that it's jacket weather. Otherwise, do the weather check.

```javascript
const weatherID = weatherData.current.weather[0].id

// using the OpenWeatherMap return codes
let badWeather = [
200, 221, 230, 201, 202, 231, 232, 
300, 310, 301, 302, 311, 312, 313, 314, 321,
500, 520, 531, 501, 502, 503, 504, 511, 521, 522,
600, 612, 615, 620, 601, 602, 611, 613, 616, 621, 622,
701, 731, 751, 761, 762, 771, 781].includes(weatherID);
```

This improved it slightly, but as computer scientists, we always asks ourselves, "what can we do better"? By and large, this was good for a general guess, but I kept checking the weather reports anyway.

It doesn't help that in between step 2 and step 3, Dark Sky was bought by Apple and subsequently deprecated, so I had to port all existing code to OpenWeatherMap. In that process, step 3 happened.

## Step 3: more complex thresholds + splitting bad weather into sections
Using a baseline guide from the [Washington Post](https://www.washingtonpost.com/weather/2018/10/30/weather-is-what-you-wear-unpacking-clothing-connected-different-climate-conditions-united-states/), use a multi-stepped threshold. Combine that with bad weather detection, separated into light and heavy categories.

```javascript
const weatherID = weatherData.current.weather[0].id

// light means light rain, snow, drizzle, etc.
let lightPrecipitation = 
[200, 221, 230, 
300, 310, 
500, 520, 531,
600, 612, 615, 620,
701, 731].includes(weatherID); 

// heavy means medium + heavy rain, snow drizzle, etc.
let heavyPrecipitation = 
[201, 202, 231, 232, 
301, 302, 311, 312, 313, 314, 321,
501, 502, 503, 504, 511, 521, 522,
601, 602, 611, 613, 616, 621, 622,
751, 761, 762, 771, 781].includes(weatherID);

  ...

let DINAJ = '';
  if ((windchill < 25) || heavyPrecipitation) {
    DINAJ = 'You should bring a heavy jacket with you.';
  } 
  else if ((windchill >= 25 && windchill < 45) || lightPrecipitation) {
    DINAJ = 'You should bring a light jacket with you.';
  }
  else if (windchill >= 45 && windchill < 65) {
    DINAJ = 'You should bring a sweater or fleece with you.';
  }
  else if (windchill >= 65 && windchill < 80) {
    DINAJ = 'You should leave your jacket at home.';
  } 
  else {
    DINAJ = 'You should leave your jacket at home. Consider wearing shorts.';
  } 
```

This was great and seemed to solve most holes in logic, although it starts to look like spaghetti code in comparison to some earlier renditions. However, sometimes I would forget a jacket, and then rain would happen. 

## Future Insanity

Right now, my current goal is to figure out how to do long term predictions using the already existing information, which should solve my "forgetting a jacket and rain happens" problem.

Currently, I'm implementing hourly rain and snow checks. Rain will check the precipitation rates for the next 6 hours, and then check if any of those adhere to a stepped threshold, much like the heavy and light precipitation checks above. Snow will be similar, except with visibility rather than precipitation rates.

I keep telling myself that this could turn into a giant machine learning project. Eventually I could just have some AWS project taking hourly data, churning out a guess, and then spitting it out. If it's right, it gets positive data reinforcement, and otherwise it disregards the data. Then, I breathe in and out, and realize all of this is for a homepage I am only looking at for 10 seconds intently some of the time, and 1-2 seconds without registering the data most of the time. Hell, DINAJ probably doesn't use machine learning for their guesses, just a large nest of fine-tuned data. 

But the fun part of coding is that if you don't like an already existing approach, you can always do it yourself. And then go insane trying to make your code perfectly predict whether you need a jacket or not.