I built a dew point calculator utilizing AWS Lambda and AWS API Gateway. Why? Because.


Hot, humid, and dew-pointy

I am passionate about cloud stuff and also immensely curious about weather (I promise there's a point... Stick with me). I also don't like being uncomfortable... especially in the office. Ask anyone I work with if I complain it's too hot or too humid and you'll probably just get laughs. I do it. A lot.

In weather, meteorologists really wish we would use dew points to instead of relative humidity to determine how humid it will feel outside. I don't want to explain, but the Washington Post already has, so that's convenient. https://www.washingtonpost.com/news/capital-weather-gang/wp/2013/07/08/weather-weenies-prefer-dew-point-over-relative-humidity-and-you-should-too/?utm_term=.b42643790204


That's a real picture I really took from a real thermometer that was really in my office over the weekend the heat went crazy. It's not usually that hot...

See my dew point problem? No, you probably don't, because you're not crazy... But I often look at this thermometer in my office and wonder "am I really uncomfortable or am I just imagining it's hot and humid?"

Right. So we know its hot because the thermometer says so. However I need the dew point to know if it's actually humid. There are equations to get the dew point from temperature and relative humidity, which my thermometer gives me. But forget busting out a calculator, this is a cloud blog right? Right?!

Right.

Problem

I wanted a way to get dew points that didn't rely on third-party websites.

I wanted to use AWS. Because I can.

I was looking at Lambda and wanted to use it.

Solution - Lambda Part

First things first... If you want to know more about Lambda, look here _____ (link in the future). In that post I'll try to describe Lambda simply and show you how to set up a function. I may just do the AWS tutorial. No idea yet. Watch that space. Moving on...

I wrote a Lambda function to do the math. I used Python 3.6. I found an equation from somewhere since forgotten which you'll see below. Here's the code for my dew  point calculator.

import json
import math

DP = 0
RH = 0
TF = 0

#print('Loading function')

def lambda_handler(event, context):
    #print("Received event: " + json.dumps(event, indent=2))
    
    TF = event["queryStringParameters"]["TempIn"]
    RH = event["queryStringParameters"]["RHIn"]
    
    TF = float(TF)
    RH = float(RH)
    
    print("Recieved temperature = " + str(TF) + " F")
    print("Received relative humidity = " + str(RH) + "%")
    
    T = (TF - 32) * 5/9
    DP=243.04*(math.log(RH/100)+((17.625*T)/(243.04+T)))/(17.625-math.log(RH/100)-((17.625*T)/(243.04+T)))
    
    DPF = (DP * 9/5) + 32
    DPF = round(DPF,1)
    
    print("Output Dew Point = " + str(DPF) + " F")
    return {
        "isBase64Encoded": "false",
        "statusCode": 200,
        "body": str(DPF)
    }
    #return DPF  # Echo back the first key value
    #raise Exception('Something went wrong')

We can break this down a little bit...

Breakdown

This Lambda function is in Python, so we have the lambda_handler function. This is the function Lambda looks to execute when triggered. It takes in two things: called event, and context. The event is important and has all the info we need to move forward. Don't worry about where it comes from yet...

The event type is a dict. It can be accessed with the square brackets and the element name. As you can see in the line defining TF, event["queryStringParameters"] has more

event is a Python dict. It has a bunch of data stored in key/value pairs, each element can be accessed with the square brackets and the element name. As you can see in the line TF = event["queryStringParameters"]["TempIn"], that event["queryStringParameters"] returns another dict, because we are accessing the "TempIn" element. This gives us the temperature in Fahrenheit. Got it? Good. Same stuff in the next line. Next up we turn these values to floats so that I can do math on them. Strings don't do math. Or... don't do the math I want to do the way I want to do it. Dont @ me.

Next up we have the print() lines. These print to an error console that AWS provides when testing and also to the CloudWatch logs. You can see that log entry below...

CloudWatch log entry for a Dew Point Calculator API hit

Next up I convert to Celsius to do the math because that's the equation I found and I was lazy. I print the output to the console/logs and next up is the funky return line. It looks like a JSON, but it's a Python dict. I'm returning a dict? Why not just the value? Why is there extra stuff? Stay tuned...


Solution - API Gateway Part

The key to delivering this Rube-Goldberg-esque solution is that I wanted something that I could easily get to. Sure, I could have just returned the dew point from the calculation and be done with it. I could have logged into AWS each time, pulled up the Lambda console and created a test event for my specific temperature and relative humidity values.

The Lambda test console and setting up a test to get my answer. Relevant pert is at line 26-29. Other stuff is AWS sample event info.

That's cumbersome. What if I could just, you know, get my answer after giving my parameters...Say, from a web browser. Well AWS has a solution for that, and it's the API Gateway. If you want more info on how to set up an API Gateway, look here ____ (link coming in the future. Watch this space too).

Remember way up there when I said not to worry about why we're taking in a dict called "event?" Well. this is why. API Gateway sends a dict called event and a context to your "lambda_handler" function. API Gateway can get this information from multiple ways. I chose to use the Lambda proxy integration, which will pass all the particulars directly to my function.

Look under "Integration Request" to see I'm using LAMBDA_PROXY
Look at those query string parameters...

I go to the URL API Gateway gives me to access my API and give it two parameters. These are my event["queryStringParameters"] from earlier in the Lambda section. They're passed directly to the function, so they have to be named properly.

Two images up also shows why I have the extra stuff in the function return in my raw code. Just as a reminder, see below, collapsed into one line. The API Gateway configuration is to proxy whatever the Lambda returns, without the data below, a web browser doesn't understand what to display. Without a status code, it's not a valid HTTP response and the "isBase64Encoded" part is needed to tell the browser it's just text. "body" tells the browser where to put the dew point (in the form of a string).

return { "isBase64Encoded": "false", "statusCode": 200, "body": str(DPF) }

Conclusion & Improvements

This post is longer than I expected it would be. Even after pushing tutorials to other posts. That's that though... I have hopefully shown a practical example of something tangentially useful you can do with Lambda and API Gateway. No real reason other than to learn. And this wasn't a single-shot success. You can see my debug code commented out and lazily left there. And you can be sure that I needed to fight with with API Gateway and Lambda syntax, which is why I spent a couple paragraphs in the breakdown section above discussing dicts and the event.

Obvious improvements are a front end. as it stands, the output is literally just a number. It's ugly. I know it's ugly. The URL is long and impossible to remember. If this were more useful than answering the occasional "what is the dew point anyway?" curiosity I'd probably give it a URL, or a whole page which calls the API in the background. Learning how to do that is a future step. Watch this space.

Oh! Thanks for reading all this. It's long winded and I'll work on making these shorter in the future. As my first project post I think I can get a pass for now though... Later, taters.