Introduction
When building a weather app with weather sharing capabilities, it is important we allow our users to easily share brief insightful images of the weather conditions. In this article, we explore how we can dynamically create these weather cards using Google Maps and OpenWeathers APIs`.
Codesandbox
Check the sandbox demo on Codesandbox.
You can also get the project GitHub repo using Github.
Nuxt.Js Setup & Configuration
We will be using Nuxt.Js for this tutorial. It is a versatile and performant Vue.Js framework. To get started ensure you have Yarn or NPM v5.2+/v6.1+ installed. Open the terminal in your preferred working directory and run the following command.
1yarn create nuxt-app nuxtjs-weather-cards2# OR3npx create-nuxt-app nuxtjs-weather-cards4# OR5npm init nuxt-app nuxtjs-weather-cards
This will result in a series of setup questions. Here are our recommended defaults.
Once the setup is complete you may enter and run your project. It will be accessible on localhost:3000
1cd nuxtjs-weather-cards23yarn dev
Google Maps Setup
We will be using Google Maps APIs to retrieve the coordinates of the city as well as the static image map. Before we do this, we need a Google Cloud account to use. Once logged in, create a project to use if you do not have one. Proceed to the Library section and ensure the following APIs are enabled:
- Places API
- Maps Static API
- Maps JavaScript API
Proceed to the credentials section to create an API key to use. Restrict the API key to the above APIs as well as localhost:3000 as the only web referrer.
We are now going to add the API key to our .env
file. This is the file that will host our environmental variables, values we do not want to be stored in our codebase.
1touch .env
Add your Google Maps API Key
1<!-- .env -->2NUXT_ENV_GOOGLE_MAPS_API_KEY=<your-google-maps-api-key>
Additionally, we need to set up the Google Places JS script globally. We do this by adding it to the head
section of the nuxt.config.js
file.
1// nuxt.config.js2export default {3 head: {4 ...5 script: [6 {7 src: `https://maps.googleapis.com/maps/api/js?key=${process.env.NUXT_ENV_GOOGLE_MAPS_API_KEY}&libraries=places`,8 },9 ],10 },11 ...12}
The library will automatically authenticate using your API key.
OpenWeather Setup
We will be using OpenWeather to obtain the weather data. Once registered or loggedin, proceed to the API keys section to create your API key.
Add your API key to the env
file.
1...2NUXT_ENV_OPEN_WEATHER_API_KEY=<your-open-weather-api-key>
Cloudinary setup
Cloudinary is a powerful media management platform with a comprehensive set of APIs and SDKs. If you do not have an account, feel free to create one here.
Proceed to the media library and create a folder called nuxtjs-weather-cards
. Upload this file into the folder. This is the template we will use to create the weather cards.
Once the file is uploaded, proceed to the dashboard to obtain your cloudname
. Add this to your .env
file, it will be used to link your codebase to your account.
1<!-- .env -->2NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>
Let us now install the recommended Nuxt.JS plugin.
1yarn add @nuxtjs/cloudinary2# OR3npm install @nuxtjs/cloudinary
Add the plugin to the modules
section of the nuxt.config.js
file.
1// nuxt.config.js2export default {3 ...4 modules: [5 '@nuxtjs/cloudinary'6 ],7}
We now need to link the module to our account by configuring it. Let us do this in a dedicated cloudinary
section.
1// nuxt.config.js2export default {3 ...4 cloudinary: {5 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,6 useComponent: true,7 }8}
The above configuration additional enables us to use the custom components that come with the package.
Searching cities
Now that setup is complete, we need to enable our users to search for the city they want to generate the weather card for. First, we add a simple HTML input element.
1<!-- pages/index.vue -->2<template>3 ...4 <input id="city-search" />5 ...6</template>
Let us now configure the above input form to autocomplete cities and obtain coordinate data.
1// pages/index.vue23<script>4export default {5 data(){6 return {7 city: null,8 };9 },10 mounted(){11 const input = document.getElementById("city-search");1213 const options = {14 fields: ["geometry","name"],15 strictBounds: false,16 types: ["(cities)"],17 };1819 const autocomplete = new google.maps.places.Autocomplete(input, options);2021 google.maps.event.addListener(22 autocomplete,23 'place_changed',24 () => this.city = autocomplete.getPlace()25 );26 },27}28</script>
The above code specifies that we want to query cities only. For each city we will receive the city name and geometry. Once a city is selected, it will be saved in the page state.
Obtaining weather data
Once the city is changed, we want to reset the weather data in the state as well as get new city's weather data. Let us do this using a watcher
.
1// pages/index.vue2<script>3export default {4 data(){5 return {6 ...7 weather:null8 }9 }10 ...11 watch:{12 city(){13 this.weather = null;14 if(this.city){15 this.getWeatherData();16 }17 },18 },19 ...20}21</script>
Let us now set up the getWeatherData
method.
1// pages/index.vue2export default {3 data(){4 return {5 ...6 status: "Waiting for country selection.."7 };8 }9 ...10 methods:{11 getWeatherData(){12 this.status = "Loading weather data..."13 const requestOptions ={14 method: 'GET',15 redirect: 'follow'16 };1718 const requestURL = "https://api.openweathermap.org/data/2.5/weather?";1920 const queryParams = new URLSearchParams({21 lat: this.city.geometry.location.lat(),22 lon: this.city.geometry.location.lng(),23 appid: process.env.NUXT_ENV_OPEN_WEATHER_API_KEY,24 units: "metric"25 });2627 fetch(requestURL + queryParams, requestOptions)28 .then(response => response.json())29 .then( response => this.weather = response)30 .then(() => this.status = null)31 .catch(error => alert('Error:' + error));32 }33 }34}
Within the getWeatherData
method, we query the OpenWeather APIs for the weather data using metric
units. We store the data received in the state and set the status to null
.
Now we have all the data we need, let us generate the weather card. First, let us create a computed property to get the static map.
1// pages/index.vue2export default {3 ...4 computed:{5 staticMapUrl(){6 let url = "https://maps.googleapis.com/maps/api/staticmap?";7 url += `center=${this.city.geometry.location.lat()},${this.city.geometry.location.lng()}`;8 url += "&zoom=13";9 url += "&size=640x640";10 url += "&maptype=roadmap";11 url += `&key=${process.env.NUXT_ENV_GOOGLE_MAPS_API_KEY}`;1213 return url;14 }15 },16 ...17}
Let us now display the status as well as the weather card.
1<!-- pages/index.vue -->2<template>34 <p v-if="status" class="text-gray-600 text-sm">5 {{status}}6 </p>78 <cld-image width="400" v-if="weather" class="mt-10 mx-auto w-full" public-id="nuxtjs-weather-cards/template">9 <cld-transformation10 :overlay="`fetch:${staticMapUrl}`"11 gravity="north"12 />13 <!-- Weather description -->14 <cld-transformation15 :overlay="`text:Sacramento_100_normal:${weather.weather[0].description},co_rgb:000000`"16 gravity="center"17 y="130"18 />19 <!-- Feels like -->20 <cld-transformation21 :overlay="`text:Roboto_18_normal:Feels like ${weather.main.feels_like} °C,co_rgb:32465c`"22 gravity="center"23 y="220"24 />25 <!-- Main temp -->26 <cld-transformation27 :overlay="`text:Roboto_50_normal:${weather.main.temp} °C,co_rgb:000000`"28 gravity="center"29 y="300"30 />31 <!-- Min temp -->32 <cld-transformation33 :overlay="`text:Roboto_18_normal:MIN,co_rgb:31465d`"34 gravity="center"35 y="400"36 x="-170"37 />38 <cld-transformation39 :overlay="`text:Roboto_18_normal:${weather.main.temp_min} °C,co_rgb:31465d`"40 gravity="center"41 y="430"42 x="-170"43 />44 <!-- Max temp -->45 <cld-transformation46 :overlay="`text:Roboto_18_normal:MAX,co_rgb:31465d`"47 gravity="center"48 y="400"49 x="170"50 />51 <cld-transformation52 :overlay="`text:Roboto_18_normal:${weather.main.temp_max} °C,co_rgb:31465d`"53 gravity="center"54 y="430"55 x="170"56 />57 <!-- Pressure -->58 <cld-transformation59 :overlay="`text:Roboto_18_normal:PRESSURE,co_rgb:31465d`"60 gravity="center"61 y="510"62 x="-170"63 />64 <cld-transformation65 :overlay="`text:Roboto_18_normal:${weather.main.pressure} Pa,co_rgb:31465d`"66 gravity="center"67 y="540"68 x="-170"69 />70 <!-- Humidity -->71 <cld-transformation72 :overlay="`text:Roboto_18_normal:HUMIDITY,co_rgb:31465d`"73 gravity="center"74 y="510"75 x="170"76 />77 <cld-transformation78 :overlay="`text:Roboto_18_normal:${weather.main.humidity} g.kg-1,co_rgb:31465d`"79 gravity="center"80 y="540"81 x="170"82 />83 </cld-image>84</template>
In the above code, we obtain the template from Cloudinary. We additionally overlay the city map, weather description, temperature, pressure and humidity data.
We customize the font, text size, font weight as well as color. This ensures that our output is appealing.
Conclusion
With the above, we have been able to enable our users to search for a city and generated a weather card they can view or share. This is just a scratch of what is possible with these three APIs combined. Feel free to review the below references to learn more about the services.