Skip to content

Commit d4c9b6a

Browse files
Merge pull request #1 from hurricanemark/Phase1-Extended-Weather-Forecasts
Phase1 extended weather forecasts
2 parents 239483f + 08cd264 commit d4c9b6a

File tree

5 files changed

+321
-2
lines changed

5 files changed

+321
-2
lines changed

README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Weather Service
2+
3+
Ever wonder why your phone shows weather data of your immediate vicinity on your travel? This project demonstrates the use of Navigator.geolocation API that is part of NodeJS to pinpoint your immediate location. The geolocation method gets the current position in longitude and latitude. The weather app uses these values to call a weather service API for weather data. This non-interactive process happens automatically for your convenience.
4+
5+
## The Value of Weather Information
6+
7+
Weather information is powerful knowledge used in forecasting the production of certain operations to help determine the economic bottom line. There are countless human endeavors affected by weather factors. Rain is good for growers, and forewarning is valuable in extreme weather, while the sunny sky and windy days could be good for energy production. For example, solar power production needs to know the number of hours of sunlight specific to geographical location. Wind farm needs to know wind speed, direction, etc. The travel and hospitality industry is most affected by dynamic weather patterns. From Agriculture to space-faring, weather plays a key role in the decision-making process.
8+
9+
There are weather detection stations situated in and around you. The inner working of making data available is not of interest to the populous but it is vital to a functioning society. Although, the National Weather Service is a government agency that disseminates data free of charge. Weather data has been monetized by repackaging in ways the population can consume. e.g. Local TV news reserve a segment devoted to weather ubiquitously. Weather data is available at your finger tips. It is all factored into a transferable cost by some service providers you're currently paying.
10+
11+
<br />
12+
13+
> Below is the template for rudimentary completion of an example on interfacing with a weather data provider. For indepth logics and real life data implementation, continue to [here](public/README.md).
14+
15+
<br />
16+
17+
## NodeJS and Express
18+
19+
I recently wrote this weather reporting app using the cloud editor `replit` where the bootstraping of node JS is hidden. All I had to do was to focus on the core logic in app.js, style.css, and index.html. To create a fully functional development environment locally, I needed to bootstrap NodeJS, Express to my replit code.
20+
21+
Here is how I add scalffolding my Nodejs project.
22+
23+
**Configure Project Development Environment for ES6**
24+
25+
>Let's assume you are using NodeJS with a version later than 13. Then, it is required to configure the development environment to work for ExpressJS with dotenv and ES6 modules.
26+
27+
First, you will need to change your `package.json` to include:
28+
29+
```c
30+
...
31+
"type": "module",
32+
...
33+
```
34+
35+
Then, use imports in your `app.js`. Here is an express start with dotenv:
36+
37+
```c
38+
import dotenv from "dotenv"
39+
import express from "express"
40+
41+
dotenv.config()
42+
43+
const app = express()
44+
const port = process.env.PORT || 5000
45+
46+
app.get('/', (req, res) => {
47+
res.send("Local Weather. Don't leave home without reading it")
48+
})
49+
50+
app.listen(port, () => {
51+
console.log(`Local weather server is serving you from http://0.0.0.0:${port}`)
52+
})
53+
```
54+
55+
<br />
56+
57+
Step 1: Migrate replit code to local VSCode.
58+
Create a folder for your project. Create sub folders `views` and `public` to place client-side programming.
59+
60+
`mkdir views`
61+
62+
`mkdir public`
63+
64+
Step 2: Initialize the folder as a node project
65+
66+
` npm init -y`
67+
68+
Step 3: Install *express* to confiure a lightweight Node server
69+
70+
`npm install express`
71+
72+
Step 4: Install *helmet* to configure runtime security
73+
74+
`nmp install helmet`
75+
76+
Now, you are good to start developing and runing from VSCode.
77+
78+
### Project Layout
79+
80+
It is a typical NodeJS project layout.
81+
82+
'app.js' - server file
83+
84+
'./public/script.js' - interface to https://weather-proxy.freecodecamp.rocks/api/
85+
86+
'./views/index.html' - client-side presentation.
87+
88+
## Runtime
89+
90+
`npm start`
91+
92+
<strong>Output</strong>
93+
94+
![codepen.io output](./public/Runtime.PNG)
95+
96+
## Build and Run Docker Image
97+
98+
Build a docker image base on the given Dockerfile and .dockerignore is this folder. After successful docker build, run the image to verify correctness. Then it can be pushed to dockerhub or your favorite cloud provider.
99+
100+
### Build
101+
Docker container is built and saved to current working directory. Replace tag name 'hurricanemark' with your own username.
102+
103+
`docker build -t hurricanemark/localweather:1.0 .`
104+
105+
```c
106+
[+] Building 139.3s (11/11) FINISHED
107+
=> [internal] load build definition from Dockerfile 0.1s
108+
=> => transferring dockerfile: 210B 0.1s
109+
=> [internal] load .dockerignore
110+
...
111+
=> [internal] load build context 0.8s
112+
=> => transferring context: 143.24kB 0.6s
113+
=> [2/5] WORKDIR /app 1.2s
114+
=> [3/5] COPY package.json ./ 0.1s
115+
=> [4/5] RUN npm install 6.6s
116+
=> [5/5] COPY . . 0.2s
117+
=> exporting to image 0.4s
118+
=> => exporting layers 0.3s
119+
=> => writing image sha256:bb89b0646be41055287fdac18ea1e405a2a19ef4b2919a0a02c213b9dc947b34 0.0s
120+
=> => naming to docker.io/hurricanemark/localweather:1.0
121+
```
122+
123+
** List the image **
124+
125+
```c
126+
PS D:\DEVEL\NODEJS\BrainUnscramblers\LocalWeather> docker image ls
127+
REPOSITORY TAG IMAGE ID CREATED SIZE
128+
hurricanemark/localweather 1.0 bb89b0646be4 6 minutes ago 949MB
129+
```
130+
131+
### Run docker
132+
133+
Note that Dockerfile exposes port 8080. This needs to be forwarded to a port on your local machine. i.e. `LOCAL_PORT:CONTAINER_PORT` for example *4321:8080*
134+
135+
`docker run -p 4321:8080 bb89b0646be4`
136+
137+
```c
138+
PS D:\DEVEL\NODEJS\BrainUnscramblers\LocalWeather> docker run -p 4321:8080 bb89b0646be4
139+
140+
> LocalWeather@1.0.0 start /app
141+
> node app.js
142+
143+
Local Weather is listening on port 8080
144+
```
145+
146+
<br />
147+
148+
To access the Local Weather app running in docker container, point your browser to the forwarding port 4321.
149+
150+
`http:/localhost:4321`

index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ app.post('/', (req, res) => {
2727
let city = req.body.locale;
2828
let url = process.env.WEATHER_VISUALCROSSING_API_BASE_URI;
2929

30-
/* https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Fremont%2C%20CA?unitGroup=us&contentType=json&key=E5PZ48U2C8EB6692GBD39U9LC */
30+
/* https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Fremont%2C%20CA?unitGroup=us&contentType=json&key=apiKey */
3131
let uriStr = `${url}${city}?unitGroup=us&contentType=json&key=${apiKey}`;
3232
console.log(uriStr);
3333
request(uriStr, async function (err, response, body) {
@@ -62,6 +62,6 @@ let port = process.env.PORT || 3210;
6262

6363
// creating a server that is listening on ${port} for connections.
6464
app.listen(port, () => {
65-
console.log(`Staging app is listening on port ${port}`);
65+
console.log(`StagingWeather report app is listening on port ${port}`);
6666
});
6767

public/README.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Core Logic
2+
3+
To obtain the pinpoint weather data, earth location is a required parameter. For this, the longitude and latitude would suffice programmatically. For the interactive option, a physical postal address (partial address of City, State, or Country) is required. Optionally, you could also register with the google map service (*additional charge will incur*). Then, integrate google-map API where the user can point to a location on the map to trigger a localized weather report.
4+
5+
* The geolocation of a known address can be obtained by using the client-side javascript [Windows Navigator](https://www.w3schools.com/jsref/obj_navigator.asp). Your browser uses different types and sources of information to identify your location. These include your IP address, geolocation via HTML5 in your browser, and your PC's language and time settings. For more detail on how Navigator.geolocation is determined, [read more here](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition).
6+
7+
Sample code for client-side geolocation:
8+
9+
```c
10+
<script type=javascript>
11+
if (navigator.geolocation) {
12+
navigator.geolocation.getCurrentPosition(showPosition);
13+
} else {
14+
document.getElementById("geo-coord").innerHTML =
15+
"Geolocation is not supported by this browser.";
16+
}
17+
18+
function showPosition(position) {
19+
document.getElementById("geo-coord").innerHTML =
20+
"Latitude: " + position.coords.latitude +
21+
"Longitude: " + position.coords.longitude;
22+
}
23+
</script>
24+
```
25+
26+
* The API query for weather data can be describe as follow.
27+
28+
> <https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/LOCATION?unitGroup=metric&key=API_ACCESS_KEY&contentType=json>
29+
30+
Where, **LOCATION** can be City, State
31+
32+
and **API_ACCESS_KEY** can be obtained from registering with an API provider.
33+
34+
* With the return JSON object representing the weather data, you wrap the individual data objects in a grid to make it user-friendly.
35+
36+
<br />
37+
38+
# Getting Real Data from A Paid API Provider
39+
40+
Replace freecodecamp proxy URI (file: public/scripts.js) for weather data provider via a paid subscription. In this project, you can use the API service from the [VisualCrossing.com](https://www.visualcrossing.com). The good news is if you registered to use the free tier, a good fit for demonstration purposes, there is no charge as long as you stay within the allowed call limit. Read [here](https://www.visualcrossing.com/resources/blog/five-easy-weather-api-calls-to-get-the-weather-data-you-need/) to learn ways to make weather API calls.
41+
42+
<br />
43+
44+
# Steps to Integrate A New API Provider
45+
46+
1. Register with a weather reporting service. Consult with their API documentation on how to call their APIs.
47+
2. Obtain the API access key from the same provider above.
48+
3. Build an API https query and manually test it out.
49+
4. Write an async function to incorporate the API call and retrieve the weather data.
50+
51+
52+
> On the server, the `API_KEY` is hidden in the `.env` file. By design, environment variables are not accessible to the client. For the client to directly query weather data, the API_KEY has to be available on the browser. This will break the security of our application!
53+
54+
**Options**:
55+
56+
* Make the API calls on the server-side and then send response containing the weather data to the client;
57+
* or, require the client to use their own API keys;
58+
* or, using the serverless option with locked down to a service. *This bad option multiplies your cost as your user base grows*.
59+
60+
**Decision:**
61+
62+
You would want to live free and die harder, let's make the client calls the server which in turn requests weather data and sends responses back. The client then can parse the response into a grid for display. This means the server will need to provide GET requests!
63+
64+
---
65+
66+
**Options**:
67+
68+
You need to implement a middleware to retrieve weather data and serve it to the client using either
69+
- `EJS` embedded templates
70+
- or `ReactJS`.
71+
72+
Obviously, many developers have accountered this issue. Lucky for us, `EJS`, `ReactJS` typically solves this problem. EJS is an easier choice having less of a learning curve.
73+
74+
**Decision:**
75+
Let's introduce the Embedded Javascript ([EJS](https://www.npmjs.com/package/ejs)) middleware template rendering weather data upon client requestt.
76+
77+
e.g.
78+
```c
79+
app.post("/geoLngLatWeather", (req, res) => {...})
80+
81+
app.get("/geoLngLatWeather", (req, res) => {...})
82+
83+
app.get("/physicalAddrWeather", (req, res, next) => {...})
84+
```
85+
<br />
86+
87+
<strong><span style="color:gray; font-size:18px"> Staying within the Free Tier Limit</span> </strong>
88+
89+
A typical free daily allowance cycle consists of up to 1000 free API queries. In this case, the user has to wait until the beginning of the next free tier cycle. To avoid being charged, this source code has a *a server-side accounting logic* to pause querying the weathercrossing API once its periodic allowance has been reached. Unpause is automatic once the clock rolls to the beginning of a new daily free tier cycle.
90+
91+
>*Potential development needed to notify the server admin if demands grew and if there are ways to monetize it.*
92+
93+
<strong><span style="color:gray; font-size:18px">Caching the Weather Data</span></strong>
94+
95+
Weather forecast data from `visualcrossing.com` typically contains information spanning 15 days, each day contains 24 hourly objects. Additional queries within the same period would usually result in the same data. We need to cache this information so as not to incur needless and sometimes costly API calls.
96+
97+
We could elect to use the DOM Storage API, also called the [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API) methods: `Window.sessionStorage` where data persists as long as the session is running, or `Window.localStorage` which has no expiration. Note: when the last private tab is closed, data stored in the localStorage object of a site opened in a private tab or incognito mode is cleared.
98+
99+
<br />
100+
101+
<span style="color:green">**IMPORTANT:**</span> Web Storage API needs to be implemented on the browser side (client with the DOM). In this case, we make sure to place the javascript script at the bottom of the HTML body section.
102+
103+
>Another option is to use no-sql storage such as `redis`. A topic not in the scope of this exercise.
104+
105+
A sample of Web Storage API test:
106+
107+
```c
108+
/* client-side script */
109+
<script type=javascript>
110+
function storageAvailable(type) {
111+
let storage;
112+
try {
113+
storage = window[type];
114+
const x = '__storage_test__';
115+
storage.setItem(x, x);
116+
storage.removeItem(x);
117+
return true;
118+
}
119+
catch (e) {
120+
return e instanceof DOMException && (
121+
// everything except Firefox
122+
e.code === 22 ||
123+
// Firefox
124+
e.code === 1014 ||
125+
// test name field too, because code might not be present
126+
// everything except Firefox
127+
e.name === 'QuotaExceededError' ||
128+
// Firefox
129+
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
130+
// acknowledge QuotaExceededError only if there's something already stored
131+
(storage && storage.length !== 0);
132+
}
133+
}
134+
135+
if (storageAvailable(methodString)) {
136+
// Yippee! We can use localStorage awesomeness
137+
// Save data to sessionStorage
138+
sessionStorage.setItem("lw_name", "marcus");
139+
140+
// Get saved data from sessionStorage
141+
let data = sessionStorage.getItem("lw_name");
142+
143+
alert(methodString +' says my name is ' + data);
144+
// Remove saved data from sessionStorage
145+
sessionStorage.removeItem("lw_name");
146+
}
147+
else {
148+
// Too bad, no localStorage for us
149+
alert(methodString + ' says Web Storage API is disabled.')
150+
}
151+
</script>
152+
```
153+
154+
155+
## Next Phases
156+
157+
The followings are potential developments in subsequent iterations of this weather report project:
158+
159+
* Integrate the server with auth0 and/or passport authentication
160+
161+
* Registered members have access to a full-blown dashboard with maritime alerts, historical weather data, extended forecasts, etc.
162+
163+
* Build and make mobile apps available to subscribed members.

public/Runtime.PNG

24.2 KB
Loading

views/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# [EJS](https://www.npmjs.com/package/ejs) - Embedded Javascript Templates
2+
3+
The `views` is where you keep all *.ejs files.
4+
5+
The `views/patials` folder is where you keep all ejs templates that meant for reusable code.
6+

0 commit comments

Comments
 (0)