Dot Tutorials
.Net Core

Create a Weather app using Open Weather Map API in .Net Core

API (Application Programming Interface) is a “must-do” for all the developers. One should be making use of the API’s to become a better developer as the API’s are the communication protocol between different services.

In this article, we will use the API of the Open Weather Map (OWM) to create an application to get the current weather of any city in the world. Our App will display the current temperature of a city along with the weather condition and the description of the weather. On top of that, it will also display an image or you can say icon according to the current weather condition. See the demo of the app below before we start developing it.

We will divide our development process into three parts. In the first part, we will create an API Key for our Weather App then we’ll design the interface of the app, and in the 3rd step, we will write the backend code.

Before writing any code, you must get an API key to request the weather information. So, follow this link and signup for an account first.

Fill up the form and get your account, then follow this link to get your API key. The API key is a string containing letters and numbers:

The Open Weather Map when queried returned a JSON file as response. This response contains all the current information about the weather for a city. They encourage the developers to search by city id rather than the city name, because searching by text may return the redundant response because the string we searched for may not be the name of a city. But the problem is, we cannot ask our user to enter the city id to search for the weather because a user won’t know the id for a city, and also, it is a stupid way to develop apps like that.

So, to tackle this problem, we would ask the user to enter the name of a city, then instead of searching for the city, we will find it’s id and then query with the id of that city, and in case the user entered a city which does not exist, we will show them a friendly message.

Luckily, the OWM provides us with the list of all the cities in the world in the form of a JSON file. This file includes the name of the cities along with their ids. We can simply download the file, parse it, and save all the cities with their respective ids. So, download the list here (compressed .gz file) & uncompress it.

Now, let’s create a new WFP App.

Fire up the Visual Studio and create a new “WPF App .Net Core” project.

Now open the “MainWindow.xaml” file. The interface of the app will consist of the five rows and one column, and we will use the Grid control for that. The following code will create the interface for our app.

<Grid>
   <Grid.RowDefinitions>
      <RowDefinition Height="0.2*" />
      <RowDefinition Height="0.3*" />
      <RowDefinition Height="0.173*" />
      <RowDefinition Height="0.173*" />
      <RowDefinition Height="0.173*" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
      <ColumnDefinition SharedSizeGroup="A" Width="*" />
   </Grid.ColumnDefinitions>
   <TextBox KeyDown="txtCityName_KeyDown" Name="txtCityName" Width="200" Height="20" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
   <TextBlock Grid.Column="0" Grid.Row="0" IsHitTestVisible="False" Text="Enter city and press enter" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="30,0,0,0" Foreground="Gray">
      <TextBlock.Style>
         <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Visibility" Value="Collapsed" />
            <Style.Triggers>
               <DataTrigger Binding="{Binding Text, ElementName=txtCityName}" Value="">
                  <Setter Property="Visibility" Value="Visible" />
               </DataTrigger>
            </Style.Triggers>
         </Style>
      </TextBlock.Style>
   </TextBlock>
   <Image Name="icon" Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" />
   <Label Name="lblCityAndCountry" Width="200" Height="28" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
   <Label Name="lblWeather" Width="200" Height="28" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
   <Label Name="lblWeatherDescription" Width="200" Height="28" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

The above code is self-explanatory except the “Text Block”. You would be wondering why I included that. I am placing the place holder in the search text box with the help of a text block component and bonded its text to the search text box. The text will be collapsed whenever we start typing in the box just like the text place holder in the HTML forms. I haven’t placed the search button either, because I will use the “KeyDown” property of the text box, so pressing the enter key will do the same job as clicking the search button. Below the search box, we have an Image component to display the weather icon, then we have three labels to display the city and the country, current weather and the description of the weather respectively. Now moving on to the backend.

Now It’s time to add the JSON file(we have already downloaded) into our project. move the JSON file to the root directory of your project and add it to your project. To add an existing file to your project, see the screenshot below.

Now, we need to parse this JSON file. for this purpose, we will make use of the Newtonsoft.Json library. To install this, go to Tools -> Nuget Package Manager -> Package manager console and type “Install-Package Newtonsoft.Json -Version 12.0.3”, wait for the installation to be finished. When installation finished, import the library by typing using Newtonsoft.Json;on the very top of the “MainWindow.xaml.cs” file. Now inside the block public partial class MainWindow : Window create a new class as follows:

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Now make a list of type “City” by writing List<City> cityList; under the City class. Now it’s time to parse the JSON file. The following code will parse the JSON file, and save all the cities along with their Ids in the “cityList” object:

using (StreamReader r = new StreamReader("../../../city.list.json"))
{
    string json = r.ReadToEnd();
    cityList = JsonConvert.DeserializeObject<List<City>>(json);
}

Write the above code under the constructor of the “MainWindow” window, i.e inside the public MainWindow() block.

Now, get your API key and save it in a constant string type variable just as follows:

const string APP_ID = "ee146a46d547f----ac01f4ac2be6ffba"; //replace the API key with your own

You may write the above line of code under List<City> cityList; I am using here my own API key just for demonstration and will remove it after writing this article, so you must use your own API key in order to get the weather information.

Now, moving into our KeyDown event listener of the text box. The user will type the city name and press enter key, we should process the request only after he pressed the enter key or return key, so we must first check if the enter or return is pressed by writing an if condition as follows:

if(e.Key == Key.Enter || e.Key == Key.Return)

We will write the code for querying inside this block.

We will make sure that the city name typed by the user is in our list or nor, we can check it by searching our list of the cities, the following method does this job:

private int isCityValid()
{
    return cityList.FindIndex(x => 
    x.Name.ToLower().Equals(txtCityName.Text.ToLower()));
}

We can now, create a string which includes the address of the API, our API key, and the city key, then the string can be passed to the “WebClient” which is used to get a response from the internet, the following two lines will do that for us:

string query = String.Format("http://api.openweathermap.org/data/2.5/weather?id={0}&appid={1}&units=metric", cityList[index].Id, APP_ID);
JObject response = JObject.Parse(new System.Net.WebClient().DownloadString(query));

The variable “response” has the response sent by the Open Weather Map API, this response looks like the following:

{ 
   "coord":{ 
      "lon":-0.13,
      "lat":51.51
   },
   "weather":[ 
      { 
         "id":500,
         "main":"Rain",
         "description":"light rain",
         "icon":"10n"
      }
   ],
   "base":"cmc stations",
   "main":{ 
      "temp":286.164,
      "pressure":1017.58,
      "humidity":96,
      "temp_min":286.164,
      "temp_max":286.164,
      "sea_level":1027.69,
      "grnd_level":1017.58
   },
   "wind":{ 
      "speed":3.61,
      "deg":165.001
   },
   "rain":{ 
      "3h":0.185
   },
   "clouds":{ 
      "all":80
   },
   "dt":1446583128,
   "sys":{ 
      "message":0.003,
      "country":"GB",
      "sunrise":1446533902,
      "sunset":1446568141
   },
   "id":2643743,
   "name":"London",
   "cod":200
}

As you can see that this is a JSON file and we can parse it, we can use “response” variable to get a specific result, for example, to get the weather id, we can write response.SelectToken(“weather[0].id”).ToString(). The complete code of this “KeyDown” method is listed below:

private void txtCityName_KeyDown(object sender, KeyEventArgs e) {
        if (e.Key == Key.Enter || e.Key == Key.Return) {
                try {
                        int index = isCityValid();
                        if (index >= 0) {
                                string query = String.Format("http://api.openweathermap.org/data/2.5/weather?id={0}&appid={1}&units=metric", cityList[index].Id, APP_ID);
                                JObject response = JObject.Parse(new System.Net.WebClient().DownloadString(query));
                                if (response.SelectToken("cod").ToString().Equals("200")) {
                                        displayWeatherImage(Convert.ToInt32(response.SelectToken("weather[0].id").ToString()));
                                        lblCityAndCountry.Content = response.SelectToken("name").ToString() + ", " + response.SelectToken("sys.country").ToString();
                                        lblWeather.Content = response.SelectToken("main.temp").ToString() + "c, " + response.SelectToken("weather[0].main").ToString();
                                        lblWeatherDescription.Content = response.SelectToken("weather[0].description").ToString();
                                }

                                else if (response.SelectToken("cod").ToString().Equals("429")) {
                                        MessageBox.Show("The account is temporary blocked due to exceeding the requests limitition.\nPlease try agian later.");
                                }
                        }
                        else {
                                MessageBox.Show("Enter a valid city name", "Error");
                        }
                }
                catch(Exception ex) {
                        MessageBox.Show(ex.Message, "Error");
                }

        }
}

The “displayWeatherImage” method at line 14 is used to display the icon according to the weather, and its code is listed below:

private void displayWeatherImage(int weatherId) {
        BitmapImage image = new BitmapImage(new Uri("../../../icons/wi-thunderstorm.png", UriKind.Relative));
        if (weatherId >= 200 && weatherId <= 232) {
                image = new BitmapImage(new Uri("../../../icons/wi-thunderstorm.png", UriKind.Relative));
        }

        else if (weatherId >= 300 && weatherId <= 321) {
                image = new BitmapImage(new Uri("../../../icons/wi-raindrops.png", UriKind.Relative));
        }

        else if (weatherId >= 500 && weatherId <= 531) {
                image = new BitmapImage(new Uri("../../../icons/wi-rain.png", UriKind.Relative));
        }

        else if (weatherId >= 600 && weatherId <= 622) {
                image = new BitmapImage(new Uri("../../../icons/wi-snow.png", UriKind.Relative));
        }

        else if (weatherId >= 700 && weatherId <= 781) {
                image = new BitmapImage(new Uri("../../../icons/wi-refresh.png", UriKind.Relative));
        }

        else if (weatherId == 800) {
                image = new BitmapImage(new Uri("../../../icons/wi-night-clear.png", UriKind.Relative));
        }

        else if (weatherId >= 801 && weatherId <= 804) {
                image = new BitmapImage(new Uri("../../../icons/wi-cloud.png", UriKind.Relative));
        }

        icon.Source = image;
}

The full description of the weather Ids can be seen here. I have used the weather icons from open-sourced Weather Icons, these icons can be downloaded and used free of cost. The icons are available to use by using CSS style on the web and by directly using them as SVG images. I took the specific images and convert them to PNG images.

So, that’s all folks, see you in the next article. The entire code for this project can be downloaded here.

Here’re some more related articles:

– WHAT STATS AND SURVEYS ARE SAYING ABOUT .NET CORE IN 2020

– A COMPLETE GUIDE TO SECURE YOUR ASP.NET CORE WEB API

– 12 USEFUL ASP.NET CORE 3 LIBRARIES EVERY DEVELOPER SHOULD KNOW

Author

Write A Comment