Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Tiny Weather

0.00/5 (No votes)
2 Mar 2017 2  
A weather application for the current conditions in your area

Introduction

Tiny Weather uses the services of Weather Underground and Freegeoip.net, this application only grabs the current weather conditions, but with Weather Underground, you can get a lot more.

We get the weather data in JSON format so we need to use the NewtonSoft.json.dll to parse the data (link below). Getting your location can be as easy as typing it in manually or we can geolocate our location by the freegeoip.net service using your IP address, this data is also in the JSON format.

Also, there will be an additional class added to this project, a wind pointer. This class is a Windows Forms control to display the wind direction, because the wind direction data gives a compass direction. It generally means the wind is coming from that direction, not going to. So to address that, we invert the image in the custom control.

In order to get any weather data at all, you need to grab yourself an API key from weather underground, it's free, but has limits. You may not call the weather more than 500 times in 24 hours and there is a limit to how much weather data you can get using the Stratus Plan (the first free plan for a developer's license).

Download the Newtonsoft.json.dll and place the file inside your application folder.

Screenshots

Main form

Settings form

Using the Code

First, let's add a reference to NewtonSoft.json.dll, in the solution explorer, right mouse click on the application name and select "Add Reference".

Browse to your application folder and select the Newtonsoft.json.dll.

Form 1 has the following items from the toolbox:

  1. 6 labels
  2. a picturebox
  3. 2 buttons
  4. wind pointer (the added class below)

Let's Code Form 1 First

Now let's import some things:

Imports System
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class Form1

    Dim deg As String = Chr(176)    'degree symbol
    Dim iconKey As String = ""
    Dim minuteCounter As Integer = 30 ' for updating the weather every 30 minutes
    Dim i As Integer = 60             ' 
    Dim str As String = ""
    Dim tmp_locationstr As String   'temp string to load the location settings to
    Dim _ApiKey As String           'the key you got from weather underground
    'settings are covered later

    ' weather conditions we want to parse from the weather data, 
    ' there's a lot but many are not used
    ' you may wish to pick and choose what you want
#Region "Conditions"
 
    Public LocationFullName, City, State, StateName, Country, CountryISO, _
    Zip, Magic, WMO, Latitude, Longitude, ElevationMeters, ElevationFeet As String
    Public StationID, ObservationTime, ObservationTime_RFC822, ObservationEpoch As String
    Public LocalTime_RFC822, LocalEpoch, LocalTimeZone_Short, _
    LocalTimeZone_Long, LocalTimeZone_Offset As String
    Public WeatherDescription, TempreatureString, Temp_F, Temp_C, RelativeHumidity As String
    Public WindString, WindDir, WindDegrees, _
    WindMPH, WindGustMPH, WindKPH, WindGustKPH As String
    Public PressureMB, PressureIN, PressureTrend As String
    Public DewpointString, Dewpoint_F, Dewpoint_C As String
    Public HeatIndexString, HeatIndex_F, HeatIndex_C As String
    Public WindChillString, WindChill_F, WindChill_C As String
    Public FeelsLikeString, FeelsLike_F, FeelsLike_C As String
    Public Visibiltiy_M, Visibility_K As String
    Public SolarRadiation, UVIndex As String
    Public PrecipitationString_1HR, Precipitation_1HR_In, _
    Precipitation_1HR_Metric, PrecipitationString_Today, _
    Precipitation_Today_In, Precipitation_Today_Metric As String
    Public IconName, IconURL As String ' weather underground has a few icon sets to choose from

#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        
        Timer1.Start()
        lclTempUnit.Text = deg & "c"
        _ApiKey = My.Settings.apikey                   ' load your api key from settings
        tmp_locationstr = My.Settings.locationstring   ' load your location from settings

        GetWeather(_ApiKey)    'call public sub GetWeather()
        UpdateLabels()         ' call the sub to update the necessary labels with weather data

    End Sub

    Public Sub GetWeather(ByVal key As String)    ' GetWeather("api key goes here")

        'create a api web request
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create_
        ("http://api.wunderground.com/api/" & key & _
        "/conditions/q/" & tmp_locationstr & ".json"), HttpWebRequest)
        'create a variable to handle the response from the web request
        Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
       
        Dim reader As New StreamReader(res.GetResponseStream())    'to read the response
        Dim serverResponse As String = reader.ReadToEnd            '
        Dim json As String = serverResponse                        '
        Dim obj As JObject = JObject.Parse(json)                   'create json object

        Try
            ' also here you may wish to keep only what you want
            LocationFullName = obj.SelectToken("current_observation").SelectToken_
            ("display_location").SelectToken("full")
            City = obj.SelectToken("current_observation").SelectToken_
            ("display_location").SelectToken("city")
            State = obj.SelectToken("current_observation").SelectToken_
            ("display_location").SelectToken("state")
   
            WeatherDescription = obj.SelectToken("current_observation").SelectToken("weather")
            TempreatureString = obj.SelectToken("current_observation").SelectToken("temperature_string")
            Temp_F = obj.SelectToken("current_observation").SelectToken("temp_f")
            Temp_C = obj.SelectToken("current_observation").SelectToken("temp_c")
            RelativeHumidity = obj.SelectToken("current_observation").SelectToken("relative_humidity")

            WindString = obj.SelectToken("current_observation").SelectToken("wind_string")
            WindDir = obj.SelectToken("current_observation").SelectToken("wind_dir")
            'wind degrees is what is used for the wind pointer
            WindDegrees = obj.SelectToken("current_observation").SelectToken("wind_degrees")
            WindMPH = obj.SelectToken("current_observation").SelectToken("wind_mph")
            WindGustMPH = obj.SelectToken("current_observation").SelectToken("wind_gust_mph")
            WindKPH = obj.SelectToken("current_observation").SelectToken("wind_kph")
            WindGustKPH = obj.SelectToken("current_observation").SelectToken("wind_gust_kph")

            PressureMB = obj.SelectToken("current_observation").SelectToken("pressure_mb")
            PressureIN = obj.SelectToken("current_observation").SelectToken("pressure_in")
            PressureTrend = obj.SelectToken("current_observation").SelectToken("pressure_trend")

            DewpointString = obj.SelectToken("current_observation").SelectToken("dewpoint_string")
            Dewpoint_F = obj.SelectToken("current_observation").SelectToken("dewpoint_f")
            Dewpoint_C = obj.SelectToken("current_observation").SelectToken("dewpoint_c")

            HeatIndexString = obj.SelectToken("current_observation").SelectToken("heat_index_string")
            HeatIndex_F = obj.SelectToken("current_observation").SelectToken("heat_index_f")
            HeatIndex_C = obj.SelectToken("current_observation").SelectToken("heat_index_c")

            WindChillString = obj.SelectToken("current_observation").SelectToken("windchill_string")
            WindChill_F = obj.SelectToken("current_observation").SelectToken("windchill_f")
            WindChill_C = obj.SelectToken("current_observation").SelectToken("windchill_c")

            FeelsLikeString = obj.SelectToken("current_observation").SelectToken("feelslike_string")
            FeelsLike_F = obj.SelectToken("current_observation").SelectToken("feelslike_f")
            FeelsLike_C = obj.SelectToken("current_observation").SelectToken("feelslike_c")

            Visibiltiy_M = obj.SelectToken("current_observation").SelectToken("visibility_mi")
            Visibility_K = obj.SelectToken("current_observation").SelectToken("visibility_km")

            SolarRadiation = obj.SelectToken("current_observation").SelectToken("solarradiation")
            UVIndex = obj.SelectToken("current_observation").SelectToken("UV")

            PrecipitationString_1HR = _
               obj.SelectToken("current_observation").SelectToken("precip_1hr_string")
            Precipitation_1HR_In = obj.SelectToken("current_observation").SelectToken("precip_1hr_in")
            Precipitation_1HR_Metric = _
               obj.SelectToken("current_observation").SelectToken("precip_1hr_metric")

            PrecipitationString_Today = _
               obj.SelectToken("current_observation").SelectToken("precip_today_string")
            Precipitation_Today_In = _
               obj.SelectToken("current_observation").SelectToken("precip_today_in")
            Precipitation_Today_Metric = _
               obj.SelectToken("current_observation").SelectToken("precip_today_metric")

            IconName = obj.SelectToken("current_observation").SelectToken("icon")
            IconURL = obj.SelectToken("current_observation").SelectToken("icon_url")
            ''''''''''''''''''''''''''''''''''''''''''''
           
        Catch ex As Exception
             ' normally i'd add "msgbox(ex.message)" here but i didn't have any errors
        End Try

    End Sub

    Public Sub UpdateLabels()

        lblTempMain.Text = Temp_C.ToString                       'most relative information
        WindSock_A1.Angle = WindDegrees                          'set the value for the custom control
        lblWindSpeed.Text = "Spd: " & WindMPH & ".mph"           '
        lblWindDir.Text = "Dir: " & WindDir.ToString             '
        lblHumidity.Text = "Hm: " & RelativeHumidity.ToString    '
        lblPressure.Text = "Pr: " & PressureIN.ToString & ".in"  '

        'weather underground has icon sets ranging from a to k find an icon set you like
        'and replace the "g" in the line below to the one you choose
        Dim icnURL As String = "http://icons.wxug.com/i/c/" & "g" & "/" & IconName & ".gif"
        Dim cli As New WebClient    'create a new webclient to download the current weather icon
        Dim tmpBitmap As Bitmap     'create a bitmap to store the icon image
        tmpBitmap = Bitmap.FromStream(New MemoryStream(cli.DownloadData(icnURL)))    'download the image
        pbIcon.Image = tmpBitmap    'display the icon

    End Sub

    ' show the settings window
    Private Sub Button2_Click(ByVal sender As System.Object, _
                   ByVal e As System.EventArgs) Handles Button2.Click
        Form2.Show()
    End Sub

    ' a reminder that the data updates automatically so not to go over the limit while debugging
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
            Handles Button1.Click
        MsgBox("The Weather will update every 30 minutes." & vbCr & _
             "Time till next update is: " & minuteCounter & " minutes , " & i & "seconds.", _
             MsgBoxStyle.Information, "Help:")
    End Sub

    'update timer
    Private Sub Timer1_Tick(ByVal sender As System.Object, _
                   ByVal e As System.EventArgs) Handles Timer1.Tick
       
        i -= 1
        Me.Text = "Tiny Weather: " & " " & WeatherDescription & " - " _
                      & City & "    " & i & "  " & minuteCounter
        If i = 0 AndAlso minuteCounter <= 30 Then
            i = 60
            minuteCounter -= 1
            If minuteCounter <= 0 Then
                minuteCounter = 30
                GetWeather(_ApiKey)
                UpdateLabels()
            End If
        End If

    End Sub

End Class

Form 1 is Done - Let's Move On to form2

As with form 1, we need to import newtonsoft.json to parse the location data from the geolocation service

freegeoip.net.

This form has the following items from the toolbox:

  1. 2 textboxes
  2. 3 buttons
  3. groupbox (optional)

Time to add some settings, go to the project tab and select 'your application name' properties on the left hand panel, select on 'Settings' and add 2 new settings, apikey as type string and locationstring as type string leave scope as it is.

Imports System.Net
Imports System.IO
Imports Newtonsoft.Json.Linq

Public Class Form2

    Dim _city, _country, _key As String


    Private Sub Form2_Load(ByVal sender As System.Object, _
                 ByVal e As System.EventArgs) Handles MyBase.Load

        lbl_apikey.ForeColor = Color.Black             'change the label forecolor back to normal
        txt_apikey.Text = My.Settings.apikey           'load your key from settings
        txt_location.Text = My.Settings.locationstring ' load your location from settings

    End Sub

    'geo location
    'as before we use a web request and 
    'web response to get the data based on your ip address 
    Private Sub btn_geolocate_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btn_geolocate.Click

        ' if used as is the line below will get your location 
        ' from your ip address but you can use another ip
        'address or hostname "http://freegeoip.net/json/google.com" for example
        Dim _request As HttpWebRequest = DirectCast_
        (WebRequest.Create("http://freegeoip.net/json/"), HttpWebRequest)
        Dim _response As HttpWebResponse = DirectCast(_request.GetResponse, HttpWebResponse)
        Dim _reader As New StreamReader(_response.GetResponseStream())
        Dim _ServerResponse As String = _reader.ReadToEnd
        Dim _json As String = _ServerResponse
        Dim _Jobject As JObject = JObject.Parse(_json)

        Try    
            _city = _Jobject.SelectToken("city")
            _country = _Jobject.SelectToken("country_code")
            txt_location.Text = _country & "/" & _city

        Catch ex As Exception
            'MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub btn_save_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btn_save.Click
       ' a warning to make sure the api key is present
        If txt_apikey.Text = Nothing Then
            lbl_apikey.ForeColor = Color.Red
            MsgBox("You need an api key to get the weather.")
        Else

            lbl_apikey.ForeColor = Color.Black
        End If
 
        'set and save settings
        My.Settings.apikey = txt_apikey.Text
        My.Settings.locationstring = txt_location.Text    'you can still type your location manually
        My.Settings.Save()                                'and save settings

    End Sub

    'close the form
    Private Sub btn_close_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btn_close.Click
        Me.Close()
    End Sub

End Class

That's form2 done.

Wind Pointer Control Class

It is better to make a new class project for this control.

I make my controls with .NET Framework 2.0.

You need to add the arrow image supplied to the projects resources by going to "project name" properties in the project tab and selecting resources and simply dragging and dropping the arrow image here.

When receiving the wind direction, the wind meteorologically is coming from that direction.

Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.IO

Public Class WindSock_A : Inherits Control    ' name the control whatever you like

    Dim myimg As Bitmap                      'new bitmap
    Dim _angle As Single                     'angle (as float)
    Dim _p As Pen                            'new pen
    Dim _arcColor As Color = Color.DimGray   'main color for the circle
    Dim _pWidth As Single = 1.0F             'pen width
    Dim _spacing As Single = 20.0F           'label spacing
    Dim _labelPoints As Boolean = True       'draw labels?

    Sub New()
        MyBase.New()
        Me.Width = 100     'initial width and height
        Me.Height = 100    '
       
        SetStyle(ControlStyles.SupportsTransparentBackColor, True)    'supports transparency
    End Sub

    ' properties to change
    Public Property ArcColor() As Color
        Get
            Return _arcColor
        End Get
        Set(ByVal value As Color)
            _arcColor = value
            Me.Invalidate()    'force redraw
        End Set
    End Property

    Public Property Angle() As Single
        Get
            Return _angle
        End Get
        Set(ByVal value As Single)
            _angle = value
            Me.Invalidate()    'force redraw
        End Set
    End Property

    Public Property ArcWidth() As Single
        Get
            Return _pWidth
        End Get
        Set(ByVal value As Single)
            _pWidth = value
            Me.Invalidate()    'force redraw
        End Set
    End Property

    Public Property DrawLabelPoints() As Boolean
        Get
            Return _labelPoints
        End Get
        Set(ByVal value As Boolean)
            _labelPoints = value
            Me.Invalidate()    'force redraw

        End Set
    End Property

    'drawing the control
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Quality
        e.Graphics.CompositingQuality = CompositingQuality.HighQuality
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias

        'image from resources
        myimg = My.Resources.arrow_100    'included insource files

        ' new pen
        _p = New Pen(New SolidBrush(_arcColor), _pWidth)

        '==\/ Label Text
        Dim nSize = e.Graphics.MeasureString("N", MyBase.Font)
        Dim eSize = e.Graphics.MeasureString("E", MyBase.Font)
        Dim sSize = e.Graphics.MeasureString("S", MyBase.Font)
        Dim wSize = e.Graphics.MeasureString("W", MyBase.Font)
        Dim nPoint As Point = New Point(Me.Width / 2 - _
        (CType(nSize.Width, Single) / 2), _spacing - (CType(nSize.Height, Single)))
        Dim ePoint As Point = New Point(Me.Width - _spacing, _
        Me.Height / 2 - (CType(eSize.Height, Single) / 2))
        Dim sPoint As Point = New Point(Me.Width / 2 - _
        (CType(sSize.Width, Single) / 2), Me.Height - _spacing)
        Dim wPoint As Point = New Point(1 + _spacing - _
        (CType(wSize.Width, Single)), Me.Height / 2 - (CType(wSize.Height, Single) / 2))
        '==/\ Label Text

        ' Draw the Circle
        e.Graphics.DrawArc(_p, _pWidth / 2, _pWidth / 2, _
        Me.Width - _pWidth - 1, Me.Height - _pWidth - 1, -90, 360)

        '===== Draw Arrow =====
        myimg.RotateFlip(RotateFlipType.RotateNoneFlipX)           ' flip the arrow to point 
                                                                   ' in the opposite direction
        e.Graphics.TranslateTransform(Me.Width / 2, Me.Height / 2) ' drawposition
        e.Graphics.RotateTransform(-90 + _angle)                   ' angle to rotate
        e.Graphics.DrawImage(myimg, -25, -25, 50, 50)              ' draw image at -half wxh with size
        e.Graphics.ResetTransform()                                ' reset once rotated
        '=========================>

        'Draw Labels
        If _labelPoints = True Then
            e.Graphics.DrawString_
            ("N", MyBase.Font, New SolidBrush(MyBase.ForeColor), nPoint)
            e.Graphics.DrawString_
            ("E", MyBase.Font, New SolidBrush(MyBase.ForeColor), ePoint)
            e.Graphics.DrawString_
            ("S", MyBase.Font, New SolidBrush(MyBase.ForeColor), sPoint)
            e.Graphics.DrawString_
            ("W", MyBase.Font, New SolidBrush(MyBase.ForeColor), wPoint)
        End If

        'graphics disposal after drawing
        e.Graphics.Dispose()
        _p.Dispose()
    End Sub

End Class

After completing the control, build the control, go to the build folder and click and drag the .ddl file into the weather application toolbox.

You can now add the control to your weather application.

Notes

The wind control is named WindSock_A because I was trying different things.

If you run into any errors running the downloaded project, continue when prompted and go to the settings

button. The error is because the application has started before the API key and location are entered,

once entered, the weather will update after 30 minutes, you won't get that same error when you start the application again but you can tweak the code to prevent this.

History

  • 2nd March, 2017: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here