Getting lost is an unfortunate habit for me. My mind is always somewhere else, and before I know it, I have skipped a Stop sign… or two… My wife always wants to drive, because she always knows where to go and gets just as frustrated as I get when I am lost - but for other reasons… I am not blessed with a natural compass. I have to rely on maps, Google maps. This article will demonstrate how to use Google maps in your VB desktop application.
Google Documentation & Samples
I should say that I am somewhat disappointed with the lack of documentation. I am not saying that there isn't documentation for Google's libraries, I am saying that the way the documentation is structured is not as good as Microsoft's MSDN, or Facebook's documentation. That is a bit disappointing. Perhaps I am too spoilt with Microsoft's huge reference section, that nothing else can compare to it.
The samples I found to be a bit lacking. Whenever I explore new programming
territories, I always like to imagine myself being a beginner programmer. Perhaps it is the teacher in me; or perhaps it is the long road I had to walk, that causes this mindset. Anyway, from a beginner's perspective, I'd say that the samples provided about Google's inner workings by most places are way too advanced. Yes, there are some complicated things that need to be demonstrated, but being a "beginner" I honestly didn't know where to start, or where to add which class. That was tough to figure out. All samples floating around on the net are solely on C#, which I think is quite unfair, hence this VB.NET article :)
Luckily, I came across this codeplex sample, which was really worth my while! It was the most complete example I have found, and it works 100%. In our project, we will use some of this project's output files. I am not saying that we will just make a VB.NET version of this article, we will only utilize what is needed and make our own unique sample, with its help.
The purpose of our project is to quickly find a location on a Google map. In this project we will not go too overboard with unnecessary methods that we might not need.
Open Visual Studio 2010, and start a new Windows Forms VB project. Name it
anything you like. My sample (that I am including), is named FindMeQuick. Add the following objects to the form, with their associated properties :
|Text||Find Me Quick|
|Label||Location||12, 5 ( Inside Panel1 )|
|Text||Address to Search|
|Location||111, 3 ( Inside Panel 1 )|
|Location||515, 3 ( Inside Panel 1 )|
|Location||785, 3 ( Inside Panel 1 )|
|Location||Inside Panel 2|
|Location||Inside Panel 3|
First, download the codeplex project I mentioned earlier. Unzip the downloaded project - note that this project is based on the .NET Framework 3.5. Open the solution, and Run it or Build it. This will produce a library file called Google.Api.Maps.Service.dll inside the src\Google.Api.Maps.Service\bin\Debug folder. We should now set References to this project's output inside our project.
Apart from setting a reference to Google.Api.Maps.Service.dll, we also need to set other References, because we will be working with WPF controls inside our Windows Forms application. Let us set the following references:
We have set the reference to the GoogleMaps project so that we can use its functionality from inside our program. The Windows Presentation Foundation references, we need in order to utilize WPF features and controls inside our Windows Forms application.
Add the following Imports statements:
Imports Google.Api.Maps.Service.Geocoding 'GeoCoding functionalities
Imports Google.Api.Maps.Service.StaticMaps 'Map methods
Imports System.Windows.Media.Imaging 'Added reference to PresentationCore for WPF image capabilities
The first two lines ensure that we are able to make use of the Google Library's methods. The third line imports the Imaging functions present inside System.Windows. These Imaging functions are much more powerful than the Imaging functions inside System.Drawing; and that is probably the biggest reason why I opted to make use of it as well.
Searching for an Address
When searching for an address location, or route on a Google Map, we have to
tap into the Google
Geocoding API. The purpose of this API is to convert a written address into a geographic coordinate. A geographic coordinate is expressed in Latitude and Longitude. Add the
following for your Search button:
Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSearch.Click Try Dim fmqRequest = New GeocodingRequest() 'Request geographical info fmqRequest.Address = txtSearch.Text 'Address that needs to be converted to geographic coordinates fmqRequest.Sensor = "false" 'No built in location sensor Dim fmqResponse = GeocodingService.GetResponse(fmqRequest) 'Get response from request If fmqResponse.Status = ServiceResponse.Ok Then 'Everything OK, add to Listview lvAddressDetails.Items.Add(fmqResponse.Results.Single().FormattedAddress.ToString) 'Main address lvAddressDetails.Items(0).SubItems.Add(fmqResponse.Results.Single().Geometry.Location.Latitude) 'Latitude lvAddressDetails.Items(0).SubItems.Add(fmqResponse.Results.Single().Geometry.Location.Longitude) 'Longitude End If Catch ex As System.InvalidOperationException MessageBox.Show("Please enter Town / Suburb / City with Street Address") 'Improper address End Try End Sub
In this sub, we created a GeocodingRequest object, and set its Address property to the typed in address. We then created a Response object to determine whether or not the supplied address was valid thus making the request valid. If the response was fine, we add the address details as well as the latitude position and longitude position in the ListView. ServiceResponse is an enum that we quickly have to add. This enum contains all possible response values from the GeocodingRequest object. We also needed to catch the InvalidOperation Exception, which usually occurs if an address hasn't been entered completely. We do not need to use:
Catch ex As Exception
Because that will be classified as Pokemon Exception Handling. It is actually common-practice among some programmers I know, and it is so wrong! It is actually quite unprofessional in my opinion. Cater for specific errors and plan for them accordingly without being lazy.
Let us add the enum now:
Private Enum ServiceResponse Unknown 'Unknown response Ok ' Everything is fine InvalidRequest 'Address requested was improperly formatted ZeroResults 'No results to return, possibly a non-existant address OverQueryLimit 'Too many request on a given day RequestDenied 'Reques denied End Enum
Getting the Map
Now that we have found the coordinates to the entered address, we need to display the map according to our coordinates. The best place to add this functionality is inside the ListView's SelectedIndexChanged event. Let us add that now:
Private Sub lvAddressDetails_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lvAddressDetails.SelectedIndexChanged If lvAddressDetails.SelectedIndices Is Nothing Then 'Nothing selected Return End If Dim fmqLocation As New GeoPos 'Latitude and Longitude Values fmqLocation.Latitude = lvAddressDetails.SelectedItems(0).SubItems(1).Text 'Set Latitude property fmqLocation.Longitude = lvAddressDetails.SelectedItems(0).SubItems(2).Text 'Set Longitude property Dim fmqMap = New StaticMap() 'New map object fmqMap.Center = fmqLocation.Latitude.ToString() & "," _ & Convert.ToString(fmqLocation.Longitude) 'Center-focus the map to your desired location fmqMap.Zoom = tbZoom.Value.ToString("0") 'Zoom percentage fmqMap.Size = "250x250" 'Size of map fmqMap.Markers = fmqMap.Center fmqMap.MapType = "Roadmap" 'Can be Roadmap, Satelite, Terrain, Hybrid fmqMap.Sensor = "false" 'No built in location sensor Dim mpMap As New BitmapImage() 'added reference to WindowsBase and System.XAML mpMap.BeginInit() mpMap.CacheOption = BitmapCacheOption.OnDemand 'Cahce only when needed mpMap.UriSource = fmqMap.ToUri() 'Source of map / image mpMap.EndInit() ehImageHost.Child = ehImage 'Set child control in element host ehImage.Source = mpMap 'Set Image source End Sub
A couple of things happen here, so I'll break it down into smaller pieces. First, we determine a selection in the ListView. Based on this selection, we obtain a Latitude position and Longitude position to be used for the map. This happens via the GeoPos class, which only exposes the Latitude and Longitude properties. Feel free to add it to your project now, or later. Here is the code for GeoPos :
Public Class GeoPos Private Lng As Decimal 'Longitude Value Private Lat As Decimal 'Latitude Value Public Property Latitude() As Decimal 'Gets / Sets Latitude Get Return Lat End Get Set(ByVal value As Decimal) Lat = value End Set End Property Public Property Longitude() As Decimal 'Gets / Sets Longitude Get Return Lng End Get Set(ByVal value As Decimal) Lng = value End Set End Property End Class
We then create a StaticMap object and configure its settings, indicating what type of map we want (which can be Roadmap, Satellite, Terrain or Hybrid), what size the map should be and where its focus area should be focused.
Private ehImage As System.Windows.Controls.Image = New System.Windows.Controls.Image() 'Added Element Host from WPF section in toolbox
That's it! If you were to build and run your application now, you will see that it searches for your entered address and displays the map, based on your ListView selection.
Figure 1 - Example
The purpose of this article was just to show how easy it can be to display Google maps in your application. You could use this project as a reference to learn from, or a framework to build upon further. I hope you have enjoyed this article, and that you won't get lost (as much as I do) anymore. Till next time, cheers!
About the Author:
Hannes du Preez is a Microsoft MVP for Visual Basic for the fifth year in a row. He is a trainer at a South African-based company providing IT training in the Vaal Triangle. You could reach him at hannes [at] ncc-cla [dot] com