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.