Computing Sunrise and Sunset

Refactoring C++ into Python, using Flask, and getting Javascript to work in Jekyll.

Posted by Scott K. Ralph on December 31, 2020 · 4 mins read
The goal of this small project was to get an output like below, for my house in Oakville:
 

 
Because Google Cloud Platform makes it very easy, and essentially free to host container images, using Cloud Run.

Step 1: Computing the Sunrise and Sunset

For this part I adapted the original code constructed by Dr. Robert Scharein who was my roommate throughout almost all of my graduate school time. The original code was written in C++, and I adapted it to Python. You can see the refactored Python code on GitHub.
I have no real understanding of the code, other than it needs to know:
  • Your position on the earth (lat, lon)
  • The date
  • The offset from UTC
  • The type of event you are interested in.
In actuality, the event "type" all boils down to an effective "altitude" of the sun relative to the point on the earth, and is a constant.

Step 2: Making HTTP Web Service Call Available

This step was remarkably simple with the Python library Flask. This gives you an idea of how simple it is to do:
import flask
from flask import request, jsonify
from flask_cors import CORS
...
app = flask.Flask("sun")
CORS(app)
...
@app.route('/', methods=['GET'])
def home():
 if 'event' not in request.args:
 return error_obj('No event found')
...

The CORS has to do with Cross-Origin Resource Sharing, which is a security thing, and is really a pain. Flask makes it easy to get around this if you don't care.

Step 3: Creating the Docker image

This is straightforward:
FROM python:3.9-slim

ENV APP_HOME /app
WORKDIR $APP_HOME

COPY python/flask_app.py /app
COPY python/julian_date.py /app
COPY python/earth_location.py /app
COPY python/sun.py /app
COPY python/event.py /app
COPY python/trig_util.py /app

RUN pip install flask
RUN pip install flask-cors

CMD python3 /app/flask_app.py

Step 4: Deploying to CloudRun

Using the gcloud tools, the building the command:
gcloud builds submit --config cloudbuild.yaml --substitutions \
    _IMAGE=gcr.io/scottralphorg-container/sun:v1

This creates the docker image and puts it in the Google Cloud Registry.
Once there it is easy to deploy using:
gcloud run deploy sun --platform managed --region us-central1 \
    --image gcr.io/scottralphorg-container/sun:v1 \
    --allow-unauthenticated --concurrency 80

Step 5: Testing the Instance


Once this is deployed, it gives you a URL for the instance, running Kubernetes under the hood: We can try a sample URL in the web browser as:
https://sun-sz6gql4gna-uc.a.run.app/?event=sunset&lat=49.257431&lon=-123.146353&utc_offset=-8&year=2020&month=12&day=30
Each of the arguments are specified by a parameter after the question mark, and separated by the ampersand. If you go to the URL, you should get back the following JSON:
{
   "date":{"day":30,"month":12,"year":2020},
   "event":"Sunset",
   "location":{"lat":49.257431,"lon":-123.146353},
   "status":"success",
   "time_str":"16h 23m 18s",
   "utc_offset":-8.0
}

The important bit being the time in time_str="16h 23m 18s"

Step 6: Getting the Results in JavaScript

Most of this is standard JavaScript. Mostly just forming the URL correctly, and then requesting it. The request is the interesting part:
const sunset_url = `${base_url}?event=sunset&lat=${lat}&lon=${lon}&utc_offset=${offset}&year=${year}&month=${month}&day=${day}`;

const fetch_options = {
   headers: {
   mode: 'cors',
   headers: {
   headers:'Access-Control-Allow-Origin': '*',
   headers:'Content-Type': 'application/json'
   }
}};

fetch(sunrise_url, fetch_options)
   .then(response => {
      return response.json()
})
   .then(jsonData => {
      sunrise_div.innerHTML = `Sunrise occurs at ${jsonData['time_str']}`
})
.catch(error => {
sunrise_div.innerHTML = "Error fetching sunrise."
});

Fetch makes it easy, as it creates a promise that makes asynchronous calling of the HTTP request easier to deal with.