This article is an attempt to embed the GoogleMaps collection of web services into a simple .NET control. It should provide
the developer a simple solution for accessing
the most important parts of the GoogleMaps web services. It however does not cover 100% of all its features but concentrates on the general information provided by Google.
I have done a lot of research in order to find a similar control on the Internet, but I have found only one complete solution, present also here on the
CodeProject. Here is the
link. It is called GMap.NET and supports other map service providers.
The control presented in this article is a pure .NET control, written in C#, and embeds
a few of the most important GoogleMaps web services.
It works asynchronously and uses threads to download data from the Internet. You can access all different map types supported
by GoogleMaps: roadmap, terrain, satellite, and hybrid. The control loads parts of the world map
dynamically, similar to the web browser.
However, to avoid memory issues, it does not cache the results.
How is GoogleMaps organized?
link to learn more about this API.
WebBrowser .NET control in order to use GoogleMaps on WinForms
But, can it be solved in a different manner? At this moment, and if Google does not change the URLs and specifications (algorithms) - it can.
The most tricky part of this story is the HTTP request that travels to GoogleMaps servers and back. In most cases these servers will return the images (in the specific format)
and the XML (or JSON) documents. All that is needed to be done, when you finally access the servers, is to put the right images on the right place on the screen.
The Map of the World
GoogleMaps servers will deliver, upon processing the request from the client, the parts of the world map (so called tiles) at the specific zoom level.
These tiles are a part of the simple coordinate system based on the latitude-longitude pair of the point on the world map, and rendered at
a certain zoom level.
The zoom level usually goes from 0 - a single tile representing the whole world, to 17 -
the most detailed representation. The world map
is divided into 2n tiles horizontally and vertically, where n represents the current zoom level. Each tile is of the size of 256x256 pixels.
Depending on the type of the map, the tiles are usually PNG or JPEG images.
So, how do we access these tiles? There is a simple HTTP request that can be sent to GoogleMaps servers to obtain these tiles.
First what is needed to be known is that Google has four main servers dedicated to this task. They are at this moment: mts0, mts1,
mts2, and mts3. This is an example of the HTTP request sent to Google:
This request addresses the mts0 server and requests the tile with coordinates (48, 96) at zoom level 8. The same result is obtained using the following:
So, using the four servers, instead of only one, will speedup the download and will not overload the single server. What are other params of the request?
lyrs specifies the type of map to download. Here is a list
of all of them:
http://mts0.googleapis.com/vt?lyrs=m&x=48&y=96&z=8 - roadmap
http://mts0.googleapis.com/vt?lyrs=t&x=48&y=96&z=8 - terrain
http://mts0.googleapis.com/vt?lyrs=p&x=48&y=96&z=8 - terrain + labels
http://mts0.googleapis.com/vt?lyrs=s&x=48&y=96&z=8 - satellite
http://mts0.googleapis.com/vt?lyrs=y&x=48&y=96&z=8 - satellite + labels
Now, when we have tiles for the map, we need to assemble it in the right way. How
do we do that?
Building the Map
The first thing that needs to be defined is the center of the map. It is the point that is going to be in the center of our screen.
We pick it randomly since it can be any point on the globe. Let's center the map at the city of London (51.5081289, -0.1280050), at
a zoom level of 8. How do we find the correct tiles?
We will start with the latitude-longitude pair of this location. We need to translate it into absolute screen points (x, y). Remember that at
a zoom level of 8 we
have 28 tiles horizontally and vertically, and that means that the size of our map is 28*256 x 28*256 pixels.
It is obvious that we can not download the whole map and neither do we need to. How
do we find the correct tile that holds the center of our map?
We need some transformational equations to translate latitude-longitude pair into absolute screen offset. Please see the following
link for a general information on this topic.
GoogleMapsNet control described in this article uses the following method:
public static Point LatLongToPixel(double latitude, double longitude, double zoom)
Point point = new Point();
double centerPoint = Math.Pow(2, zoom + 7);
double totalPixels = 2 * centerPoint;
double pixelsPerLngDegree = totalPixels / 360;
double pixelsPerLngRadian = totalPixels / (2 * Math.PI);
double siny = Math.Min(Math.Max(Math.Sin(latitude * (Math.PI / 180)), -0.9999), 0.9999);
point = new Point((int)Math.Round(centerPoint + longitude * pixelsPerLngDegree),
(int)Math.Round(centerPoint - 0.5 * Math.Log((1 + siny) / (1 - siny)) * pixelsPerLngRadian));
After calling this method we get the (x, y) pair of values (32745, 21792). This is the absolute world position of the map center.
Next we will calculate the viewport bounds, that is the part of the world map that is visible from our screen. It is quite simple.
The center of the viewport is our center point. We form the viewport rectangle by calculating offset to
the left side by subtracting the half-width of our screen, and the offset
to the top side by subtracting the half-height of our screen. The width and height of our viewport
are set to the width and height of our screen. See below:
m_rcViewport = new Rectangle(m_ptMapCenter.X - this.ClientRectangle.Width / 2,
m_ptMapCenter.Y - this.ClientRectangle.Height / 2,
The last thing that needs to be done is to calculate the left, top, right, and bottom tiles that need to be downloaded. Here is the code:
int startx = m_rcViewport.Left / 256;
int endx = m_rcViewport.Right / 256;
int starty = m_rcViewport.Top / 256;
int endy = m_rcViewport.Bottom / 256;
That's all. After this all we need to do is run the loop and get those tiles, like below:
for (int y = starty; y <= endy; y++)
for (int x = startx; x <= endx; x++)
Finally, our screen looks like the following one:
Adding the Markers
The markers on GoogleMaps are useful since they can keep additional information about places on the map. You can put
the marker anywhere on the map.
The marker is defined by its (latitude, longitude) pair of coordinates. The
GoogleMapsNet control allows you to add markers anywhere.
The links to the original GooogleMaps images are shown below:
http://www.google.com/mapfiles/marker.png - default marker
http://www.google.com/mapfiles/shadow50.png - marker shadow
GoogleMaps Web Service - Goecoding and Reverse Geocoding
The geocoding web service allows you to find locations defined by the (latitude, longitude) pair using the address search criteria.
It is accessible through the following HTTP request:
Here we specify the XML output format, put the keyword in the address param, and set the sensor param to
false. You will receive the XML document from the GoogleMaps servers with
the structure described in this link.
Similarly, Google provides the reverse feature to find the addresses based on the (latitude, longitude) pair search. See below:
The Geocoding and Reverse Geocoding features are supported in the
GoogleMaps Web Service - Directions
The Directions web service will provide you a route between the two points on the map. You access it like this:
Again, you will receive the XML document with the structure described in this link.
The route is a collection of locations, but returned as an encoded string. The encoding algorithm
is described here.
The Directions feature is supported in the
GoogleMaps Web Service - Distance Matrix
The Distance Matrix web service will provide you information about the distance between two points on the map. You access it like this:
The XML document structure is described in this link.
The Distance Matrix feature is supported in the
GoogleMaps Web Service - Elevation
The Elevation web service will provide you information about the elevation (height) of the point on the map. You access it like this:
The XML document structure is described in this link.
The Elevation feature is supported in the
The Windows Forms demo application, written in C#, and using the
GoogleMapsNet control is shown on the screenshot below:
All described features are accessible through the control panel on the right side of the main application window. But, let's now
see how to use it from C#.
Using the Code
First, you should add a reference to the
GoogleMapsNet control to your project, like on the image below:
Next, you add it on the form, or use the creation code, similar to the below one:
googleMapsNet1 = new GoogleMapsNet();
googleMapsNet1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
googleMapsNet1.Latitude = 0;
googleMapsNet1.Location = new System.Drawing.Point(12, 12);
googleMapsNet1.Longitude = 0;
googleMapsNet1.Name = "googleMapsNet1";
googleMapsNet1.ShowGrid = false;
googleMapsNet1.Size = new System.Drawing.Size(1024, 768);
googleMapsNet1.TabIndex = 1;
googleMapsNet1.LongitudeChanged += new System.EventHandler(this.googleMapsNet1_LongitudeChanged);
googleMapsNet1.LatitudeChanged += new System.EventHandler(this.googleMapsNet1_LatitudeChanged);
googleMapsNet1.ZoomChanged += new System.EventHandler(this.googleMapsNet1_ZoomChanged);
googleMapsNet1.Dock = DockStyle.Left;
GoogleMapsNet control exposes three main events:
Add custom handlers for them to track the map params being changed.
To show the map, do the following:
googleMapsNet1.ShowMap(mapType, longitude, latitude, zoom);
To add the marker on the map, do the following:
googleMapsNet1.AddMarker(longitude, latitude, zoom);
To remove all markers on the map, do the following:
To get the Geocoding results, do the following:
DataTable table = googleMapsNet1.AddressToLongitudeLatitude(address);
To get the Reverse Geocoding results, do the following:
DataTable table = googleMapsNet1.LongitudeLatitudeToAddress(longitude, latitude);
To add the route to the map, do the following:
DataTable table = googleMapsNet1.AddRoute(origin, destination);
To remove all routes on the map, do the following:
To get the Distance Matrix results, do the following:
DataTable table = googleMapsNet1.CalculateDistance(origin, destination);
To get the Elevation results, do the following:
DataTable table = googleMapsNet1.CalculateElevation(origin, destination);
Points of Interest
This was one of the most interesting projects I have been working on recently. I had a very good time
working on this and I hope developers will find a good use for this control.
- September, 2012 - GoogleMapsNet control, version 1.0.