spacer1
spacer2 1_1 1_2
2_1
 Subscribe
 The MP2K Update!
 
 
 
 Magazine
Front Cover
What's New
Articles
News
Sample Data
Gallery
Advertise
About
 Features
MapPoint 2013
Press Releases
MapPoint Forums
Companies
Link to MP2Kmag
Wish List
MapPoint Trial
Authors
 Earlier Content
Past News Items
Past What's New Announcements
 Sponsors
 Order

MapPoint 2013

Programming MapPoint in .NET

MapPoint Book

  Spatial Community
SVG Tutorials
MapPoint

Map Visitors

  ARTICLES  


Programming MapPoint via .NET

This article explains how to use VB.NET to create an improved location sensor for MapPoint. DnClipPos can copy the current lat/lon to the clipboard, and supports configurable display formats.

Introduction

Microsoft's .NET initiative has gotten a lot of press lately, and some may have been led to think that it is all only about the Internet and this new thing called web services exclusively, and hence not of interest for developers of desktop apps - e.g. those using MapPoint 2002 in their applications. Not so. While the .NET vision is indeed about revolutionizing the Internet with web services, and the implementation Microsoft delivered has great tools to help doing so, these tools can also be used to make the development of desktop applications simpler, faster, more elegant and more fun. If some of you have heard the words "Internet" and "web service" one time too often, I hereby promise not to mention them for the rest of this article, OK ?

Another common misconception is that Visual Studio.NET is required to create and compile Visual Basic.NET programs. Again - not so. Everything needed to compile and run VB.NET applications can be downloaded from Microsoft for free in an admittedly quite large download. (See "Prerequisites" below)

In the following article, I'd like to illustrate how MapPoint can be programmed using the free commandline version of VB.NET. The small sample program we'll write is called DnClipPos (Dn standing for DotNet) - an improved location sensor with configurable coordinate display format and the ability to copy the current position to the clipboard when the middle mouse button is pressed.

Prerequisites

Before we can start implementing the new location sensor with VB.NET, we need to make sure all the prerequisites are met.

You'll need:

  • a copy of MapPoint 2002, obviously
  • if you just want to run the compiled program: the .NET framework redistributable (20MB download, to be found here )
    (you will not need to download and install this again if you already did so for another .NET application, or have Visual Studio .NET)
  • if you want to be able to compile the source code and write VB.NET programs yourself:
    • the .NET platform SDK (130MB download - this includes the framework redistributable , so you do not need that download. If you have Visual Studio .NET, you do not need to download the platform SDK either)
    • a text editor, like Notepad or UltraEdit

Downloadables

Here's a summary of the downloadable material mentioned in this article:

  • DnClipPos archive: contains the compiled executable as well as the full source code.
  • the .NET framework redistributable - required if you just want to run DnClipPos.exe and this is the first .NET program you use.
  • the .NET platform SDK - required if you want to compile the source code and do not have Visual Studio.NET.

Installation

Well, the good news is that this program does not require any installation beyond the framework redistributable, and that is true for most .NET programs that you will write. Just download the archive that contains both the sourcecode as well as the executable, extract to a local directory on your machine, and run DnClipPos.exe to give it a try. If MapPoint is already running, the program will attach to it. If it isn't, it will launch a new instance.

Using DnClipPos

If all goes well, DnClipPos displays a small window that stays on top, and will show coordinates in its default format (decimal degrees with a single-letter compass point).

To copy the current position to the clipboard, press the middle mouse button or the wheel on your mouse while over the map. (For best precision, zoom in).

If you don't like the coordinate format DnClipPos uses, you can easily change it - either by changing the default format in the source code, or using the following instructions:

  • right-click on DnClipPos.exe and select "create shortcut".
  • select the shortcut and open its properties dialog
  • in the target field, after the name of the EXE, add the pattern string to use for formatting.

Pattern strings are explained in detail in my DmsFormat article (which DnClipPos uses internally), but here's a table that lists the placeholders it understands:

Place- holder Description Result for latitude 1.23456 south Default format
<Di> Integer (whole) degrees, unsigned 1 "##0"
<Mi> Integer (whole) degrees, unsigned 14 "#0"
<Si> Integer (whole) seconds, unsigned 4 "#0"
<Dd> Decimal degrees, unsigned 1.23456 "##0.0####"
<Md> Decimal minutes, unsigned 14.074 "#0.0##"
<Sd> Decimal seconds, unsigned 4.4 "#0.0"
<Cp> Compass point (W, E, N, or S) S "W|E|S|N"

For example, to get the classic "degrees, minutes, decimal seconds, compass point" format, you'd use <Di> <Mi> <Sd> <Cp> as the format string.

Creating the DnClipPos binaries yourself

Before I explain how DnClipPos works, here's how to create your own version using the commandline Visual Basic compiler included in the platform SDK. All you'll need is the two source files: DnClipPos.vb and MpToolbox.vb, and the "response file" DnClipPos.rsp.

So copy them to a new directory, and open a command prompt window. (If you have Visual Studio.NET installed, select "Start / Programs / Microsoft Visual Studio .NET / Visual Studio .NET Command Prompt" to open one that has the correct environment variables set. If you do not have Visual Studio .NET, download and install the .NET Platform SDK as described under "Prerequisites" above.

To check if the prerequisites are met, enter "vbc" (without the quotes) at the command prompt, and press RETURN. The Visual Basic compiler should be launched, and output a few screenfuls of commandline options.

Since programs talking to MapPoint use COM to do so, and .NET does not use components entered in the registry directly, we need a way to inform the VB.NET compiler about the MapPoint objects and their methods, properties and events, .NET style. Fortunately, this is simple since the platform SDK includes a tool to do so. (Visual Studio.NET does this automatically simply set a reference to MapPoint using the COM tab).

The tool in question is called TLBIMP (type library import), and all it needs is the COM type library that contains the above information. For MapPoint 2002 North America, this file is called MPNA81.TLB and is located in your MapPoint installation directory. For MapPoint 2002 Europe, its name is MPEU81.TLB. Assuming MapPoint is installed in e.g. "C:\Program Files\MapPoint 2002 North America", enter the following at the commandline:

TLBIMP "C:\Program Files\MapPoint 2002 North America\MPNA81.TLB"

(The quotes are important this time, especially if the path includes spaces)

The result should be the creation of MapPoint.dll in the current directory (where you also copied the *.vb and the DnClipPos.rsp response file.

To compile the *.vb source file into DnClipPos.exe, use the following command:

VBC @DnClipPos.rsp

If all goes well, DnClipPos.exe should be created in the current directory.

There's nothing special about the response file, it is a simple ASCII text file containing all the VBC commandline parameters that you would've had to enter by hand otherwise.

Porting existing VB code to VB.NET

DnClipPos needs to be able to retrieve the latitude and longitude of the mouse cursor, and to display it in a format specified by the user. Fortunately, I already had existing code available that came in handy here: the CalcPos and DmsFormat routines. This was VB6 code though, so I was a bit worried if I would be able to use it under VB.NET. The good news is that DmsFormat worked without any change, and the changes required to CalcPos were minimal - the mathematical functions Cos and Sin now reside in the System.Math namespace, hence adding an "Imports System.Math" directive at the top was all that was required.

Pleasant surprises ...

The VB6 version of CalcPos required me to write a helper function to compute the arccosine - the Math namespace of the .NET library includes a native Acos function, so I could get rid of the helper function and use the built-in one instead. I could also remove my own constant definition for PI and replace it with the one now built into Math.PI.

... and a gotcha

There's only one occasion where I was bitten by VB.NET's new syntax. The default parameter passing mechanism in VB6 has been "by reference" - which means that by default, any subroutine can inadvertantly change the variables of its caller. This default was changed to "by value" in VB.NET, and it is a welcome change since it usually helps to prevent havoc. In my case, though, it helped create it - in my original code I had not specified the "ByRef", but relied on it to be the default. Moreover, I did not use the conversion wizard to port the code, but simply pasted it into VS.NET via the clipboard. This caused VS.NET to silently add "ByVal" to the parameters not specified with neither "ByVal" nor "ByRef". Since CalcPos returns the computed position by changing two doubles passed by the caller, it now stopped doing so, and appeared broken. Took me some time before I noticed - but you now have been warned.

Dissecting DnClipPos

DnClipPos will be explained "by dissection" in the following paragraphs - explaining its workings a few lines at a time.

Let's have a look at the main source code module, DnClipPos.vb now:

These two statements help save typing - VB.NET functionality is available through classes organized in namespaces - to avoid having to write the entire hierarchy each time you want access to a given class or method, use "Imports". It can be roughly compared to the VB6 "With" construct. As soon as you have e.g. an "Imports System.Windows.Forms", instead of having to type "System.Windows.Forms.Label", you can simply use "Label".

It is important to realize that "Imports" statements will not make additional objects (classes) available to your program. This is done by setting references - quite similar to the way they are used in VB6. So to be able to access objects of a given namespace, find out which assemblies they are part of (e.g. System.Drawing.dll, System.Xml.dll etc.), and add a reference to that assembly - either interactively in VS.NET, or by using the /reference: commandline instruction. Once you have the reference working, you can save typing by using "Imports" statements. To find out which DLL is required for a given type, check out the documentation overview of the type - the name of its assembly (aka DLL, in .NET speak) is usually listed at the very bottom of the page.

You can see a list of the references used by DnClipPos by inspecting the above-mentioned response file, DnClipPos.rsp - each one of the lines starting with "/r:" introduces a reference. But let's continue examining the DnClipPos.vb source code.

A new class "CoordDisplay" (obviously meant to display coordinates) is introduced here, and inherits the methods and properties of methods of the regular label control. We have just created a new type of control, derived from the label control. This is powerful stuff - while you could create custom controls in VB6, it involved some "behind the scenes" magic, and there was no way to inherit from an existing control just like that.

This is where the new "label control that can display coordinates" keeps its internal information - two doubles to represent the coordinates (in MapPoint's internal format), and a DmsFormat compatible format string that represents the way the user wants to see them. Note that the private variables are not only being defined, but initialized at the same time - something not possible in VB until now.

The "New" subroutine (aka 'constructor') is called when an instance of a class is created in VB. Here, we set "our" (the label from which we inherit, in fact) "AutoSize" property to True, which will cause the label to automatically increase its size to fit the text it contains. The initial text the label should display is taken from an argument passed to the constructor - not previously possible in VB either.

UpdateDisplay() is a working horse helper function of the CoordDisplay control - it sets the text property of the label control it is derived from, according to the private latitude and longitude values formatted with the help of DmsFormat, according to the private format string.

This subroutine is called by users of our coordinate control to have it copy the current coordinates, according to the currently set format, to the clipboard.

That's all as far as the functionality of CoordDisplay is concerned - the remaining code for that class are properties that can be read and set from outside.

A user of our CoordDisplay object can control how it shows the latitude and longitude values by setting the "Format" property. The "Get" part simply returns the private variable m_strFmt. (The "Return" statement is new in VB.NET too, btw - an elegant way to return a value from e.g. a function, without requiring the assignment to the name of the function VB6 needs. Nice if you decide on another name for the function later, for example).

The "Set" part is almost as straightforward - the only additional statement is to call "UpdateDisplay" so that the change to the coordinate display format is effective immediately. A slight change to the former property syntax here is that the Get/Set parts are grouped inside one construct - easier to keep track of.

These two properties are - you guessed it the "accessors" for latitude and longitude. Note the call to "UpdateDisplay" when setting the Longitude from outside - it automatically causes the new coordinates to display. Why is this call missing in the corresponding Latitude property ? The answer is: for performance reasons - by convention, Latitude should be set first, then Longitude. This way, we can save one superfluous update.

With the

End Class

we have finally reached the end of the CoordDisplay class - our own, lat/lon aware label control !

LocationSensor is the tiny window that will be used to display the coordinates. We make this class behave like a VB.NET Form and have all its functionality simply by stating "Inherits Form". This form should contain the coordinate display label control detailed above - this is done here by adding a public member variable of this type to the LocationSensor class. Since its constructor wants an initial text to display, we make it display a welcome message.

The constructor of the LocationSensor class accepts a format string, which, if not empty, will be used to set the format of the coordinate label control. It then adds said control to the forms "Controls" collection, so that it gets properly shown and managed. The "TopMost" attribute of the form is set to true, which will make the window stay on top of MapPoint (and indeed of any other window). Finally, the window title is set to "Location Sensor", and it is made just large enough to contain the coordinate control. And that's all of the code for the LocationSensor class.

The DnClipPos module contains the rest of the code we will examine. It holds member variables for an instance of the MapPoint application, the MapPoint map, and our LocationSensor form. The LocationSensor gets initialized with whatever got passed as commandline arguments - this is the way the user can control the format used to display coordinates.

Sub Main is the entry point of the program. An attempt is made to find a running instance of MapPoint, and if that fails, to create a new instance. Note that VB.NET's new structured error handling (the Try/Catch/End Try construct) is used here instead of the dreaded "On Error Goto" device.
After creating an instance of MapPoint or attaching to one, it is made sure it is visible, and won't disappear when the LocationSensor is closed. After retrieving the active map, control is passed to the Run method of the Application class, passing it our frmSensor form. This method will take care of keeping the form running, and will only return when it is closed.

This is the event handler for the maps click event. It only reacts to the middle mouse button and therefore exits if any other button was pressed. If it was indeed the middle mouse button (or the wheel button), the coordinates control is asked to copy its text to the clipboard.

The mouse move events are handled here. oMap.XYToLocation converts screen coordinates (X and Y, the position of the mouse) into a location object - exactly what the CalcPos routine needs to set the values of two doubles passed by reference to the computed latitude and longitude of the spot. Note that the magic that calls the "Set" methods of the lat/lon properties of the coordinate control also works here without the obvious presence of an assignment statement.

In this last event handler, we react to the closing of the application object, release our references to MapPoint objects, and close the form window - wouldn't make much sense to have the location sensor survive the closing of MapPoint.

You may have noticed the difference in names between this last event handler and the two previous ones - the last one conforms to the classic naming convention "<VariableName>_<EventName>" while the previous ones don't - why do they work nevertheless ? The answer is that the name is no longer important to VB.NET - what matters is the additional "Handles <VariableName>.<EventName>". Several handlers can handle events of a single object this way, or a single handler can handle events from multiple sources (by listing them after the "Handles" keyword).

The "End Module" is the last line of the program.

Conclusion

I hope I was able to show that you can indeed use MapPoint via COM interop using the free, commandline based .NET platform SDK tools.

DnClipPos was one of the first programs I wrote using VB.NET, and I liked what I discovered. Not only did I find a few pleasant surprises when porting CalcPos, while writing the rest of the code I found major improvements and changes for the better all over the place: inheritance, flexible event handlers, modern error handling, an amazingly vast and well thought-out class library, variable initialization, New() with parameters ... and so on. I did not plan on writing a ".NET feature demo" - it just happened that I was able to make use of the new features without intending to do so in advance. If you were'nt sure .NET is for you as a VB desktop developer, I'd urge you to give it a try - using the freely available tools if necessary. A similar quantum leap has happened in the IDE, but that's another story. Oh, and did I mention that it is a great tool to develop web services that can be accessed over the Internet ? (oops ...)

Discuss this story in the forum.

Author: Gilles Kohl
Email: gilles(AT)_deletethisincludingunderlines_compuserve.com
URL: http://www.procad.de
Gilles Kohl, a native of Luxembourg living in Germany, is a software development lead with PROCAD GmbH of Karlsruhe, Germany.

Mapping and especially GPS-related topics are a hobby - Gilles enjoys developing solutions for Microsoft MapPoint and his favorite outdoor occupation is confluence hunting.

As always, please direct questions to the newsgroup.



Google
 
MP2Kmag Internet


 Recent Discussion
 Resources
Browse GIS books and periodicals
Find a MapPoint Partner or Consultant
Real Estate Columbia MO Homes for Sale


Want Your Site To Appear Here?

   © 1999-2012 MP2K. Questions and comments to: website@mp2kmag.com
  Microsoft and MapPoint 2002/2004/2006/2009/2010/2011/2013 are either trademarks or registered trademarks of Microsoft.