Tiny Weather





5.00/5 (5 votes)
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).
- http://www.newtonsoft.com/json - Newtonsoft.json.dll
- https://www.wunderground.com/weather/api - Weather Underground
Download the Newtonsoft.json.dll and place the file inside your application folder.
Screenshots
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:
- 6 labels
- a picturebox
- 2 buttons
- 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:
- 2 textboxes
- 3 buttons
- 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