Professional Documents
Culture Documents
ActiveX Control
By Daniel P. Ames, PhD, PE
-1-
DRAFT
Copyright 2006 Daniel P. Ames
Revision 0.1
-2-
DRAFT
To Mrs. Brassey
-3-
DRAFT
Contents
PREFACE................................................................................................................5
ACKNOWLEDGMENTS.......................................................................................7
1 GIS SOFTWARE DEVELOPMENT PARADIGMS........................................8
2 GETTING STARTED WITH MAPWINGIS..................................................12
GETTING THE LATEST VERSION OF MAPWINGIS................................................12
REGISTERING THE MAPWINGIS ACTIVEX ON YOUR SYSTEM ............................14
CREATING A REFERENCE TO MAPWINGIS IN VISUAL BASIC 2005 EXPRESS
EDITION...............................................................................................................16
ADDING A MAP COMPONENT TO YOUR VISUAL BASIC FORM..............................18
ADDING A SHAPEFILE DATA LAYER TO YOUR MAP ............................................19
CREATING ZOOM AND PAN BUTTONS .................................................................22
3 SHAPEFILE DISPLAY, COLORING SCHEMES, AND LABELS ............25
ADJUSTING SHAPEFILE DISPLAY PROPERTIES .....................................................25
SETTING A COLORING SCHEME ...........................................................................31
USING A POINT IMAGE LIST ................................................................................34
LABELING FEATURES ..........................................................................................37
-4-
DRAFT
Preface
-6-
DRAFT
Acknowledgments
-7-
DRAFT
1
GIS Software Development Paradigms
-9-
DRAFT
training and learning curve associated with commercial desktop GIS
applications may be unnecessary extra burden.
In spite of the noted downfalls, the “extension” or “plug-in” GIS
software development paradigm does meet the need of many projects—in
particular the development of tools that are targeted at other GIS analysts.
For example, a new spatial interpolation method that is likely to be used
only by trained GIS analysts, is a good candidate tool to be built as a plug-in
or extension to an existing GIS software package.
Many software development and location based service provider
companies have addressed this problem of needing to keep their map based
applications simple by moving them to the Internet. Indeed, map-enabled
web sites such as MapQuest (http://www.mapquest.com) and Google Maps
(http://maps.google.com) have rapidly grown in popularity in recent years.
Such sites provide general location information, driving directions and even
satellite imagery when available. Also a variety of government agencies at
all levels have also begun to distribute geospatial data on web-based maps.
Such applications can be used to provide citizens with information as varied
as census data, severe weather warnings, election results, and much more.
Coincident with the current proliferation of map-based web sites, has
come a proliferation of web-based GIS tools. ESRI produces the internet
map server, ArcIMS, AutoDesk, Inc distributes a tool called MapGuide, and
the University of Minnesota has led the open source movement in web-
based GIS with its MapServer product. All of these tools follow a common
development approach based largely on display of and relatively minimal
interaction with static images generated by server based mapping engines.
Many applications are suited to this software development paradigm.
Certainly the best fitting GIS applications are those that require only
minimal map interaction, and are focused primarily on the dissemination of
data to a wide audience.
The third GIS software development paradigm noted above—
development of custom standalone applications using GIS components—is
the primary focus of this book. The governing premise of this paradigm is
the need to place highly customized GIS enabled software tools on the
desktop computers of end users. Many types of applications are well suited
to this approach such as those that require significant user interaction and/or
mobility and hence are less suited to development as web-based tools.
- 10 -
DRAFT
Also, when the intention is the development of a highly customized GIS
application with a unique or branded “look and feel”, then the standalone
development approach is generally better than the plug-in or extension
approach.
The ship tracking example application mentioned previously could be
considered as a good candidate for the standalone software development
paradigm. Imagine a graphical user interface for the software that requires a
single form with three separate maps, five buttons and six menu items.
Ignoring for the moment exactly what the buttons and menu items will do
and what will be displayed on the maps, it should be apparent that such a
simple form does not necessarily require all of the overhead of a full GIS
desktop application, and hence the standalone approach is more suitable
than the plug-in or extension approach. Likewise, by simply adding the
need for deployment of the software on ships that do not have live internet
access, the standalone approach becomes clearly much more suitable than
the web-based mapping paradigm.
In summary, this is an exciting time to be a GIS software developer due
to the many available developer tools, and interesting projects with different
requirements and needs. While no particular GIS software development
paradigm will fit every project and every need, the standalone approach is
well suited to a variety of applications and is the focus of this book. The
remaining chapters are divided into two sections. The first section provides
an introduction to programming standalone GIS software using the
MapWinGIS ActiveX control. This section can be used as part of a course
on custom GIS software development. The second section is a listing of the
objects, functions, properties, and methods that are part of the MapWinGIS
ActiveX control. This section is intended to be used as a reference guide to
the component.
All code examples and screenshots in this book are based on Microsoft
Visual Basic .NET 2005 Express Edition.
- 11 -
DRAFT
2
Getting Started with MapWinGIS
The purpose of this chapter is to quickly get you up and running with your
first MapWinGIS ActiveX control based application. You will learn how to
download and install the latest version of the MapWinGIS ActiveX control;
how to add a reference to the MapWinGIS ActiveX control within
Microsoft Visual Basic 2005 Express Edition; how to add a map component
to a form; how to add a shapefile layer to your map; and how to perform
basic zooming and panning functions on the map. Let’s get started!
- 12 -
DRAFT
1) The CD included with this book contains a simple installer for the
MapWinGIS ActiveX component. You can find the installer by
inserting the CD into your hard drive and following the link on the
auto start index page that should automatically load. This will be
the latest version of the component at the time of preparing this
book. If you want to ensure that the examples you see in this book
are identical to the version of the component that you are using,
then you should use the version that came on the CD with this book.
2) Alternatively, you can download the same installer directly from the
MapWindow GIS project home page:
http://www.MapWindow.org/. Follow the download link and select
the “MapWinGIS ActiveX Control” installer option.
5) Finally, if you are skilled with C++, you can download the current
source code to the MapWinGIS ActiveX control directly from the
Subversion code repository on the MapWindow GIS project web
site. Instructions for doing this are given at
- 13 -
DRAFT
http://www.MapWindow.org/svn.php. Downloading the source
code and recompiling the component are topics beyond the scope of
this book, but you are welcome to take this approach if it fits your
needs. If you are comfortable working with the C++ source code to
the component, then you might want to consider joining the
MapWindow GIS team http://www.MapWindow.org/team.php and
contributing your own enhancements, improvements, and other
great ideas to the project!
This will invoke the Windows Registry Server tool in your System32
folder to register the component with the registry. Successful registration of
the component will return the following confirmation:
- 15 -
DRAFT
Creating a Reference to MapWinGIS in Visual
Basic 2005 Express Edition
1) Start a new Visual Basic 2005 project. In the example that follows,
we will be using the “Windows Application” project template and
will be creating a new project called, “MyFirstMapApp.”
- 16 -
DRAFT
2) Next you need to add the MapWinGIS ActiveX control to your
Visual Basic 2005 toolbox. To do this, right click in the toolbox
area and select the menu item, “Choose Items…” to display the
“Choose Toolbox Items” dialog box. MapWinGIS is a COM object
so you need to click the “COM Components” tab. Note that this
can take a few minutes to load depending on the number of COM
objects on your computer. When the list of COM objects loads,
scroll down the list to the “Map Control” component. Note that
ESRI® also has a component called “Map Control” that may also
appear in your list of components. Make sure you select the
MapWinGIS component.
- 17 -
DRAFT
Adding a Map Component to your Visual Basic
Form
If you want your map to be resized when the user resizes the form,
you can set the “Anchor” property of the map component in the Visual
Basic Properties window. Another nice effect is to use the “Dock”
property to fill the form space with your map.
- 18 -
DRAFT
Adding a Shapefile Data Layer to Your Map
You should now have the MapWinGIS component file loaded on your
computer, registered in the system registry, referenced in Visual Basic 2005,
and added to a new blank form. You are now ready to start viewing data!
We will start with viewing vector shapefile data. The code to load a
single shapefile into your map is very simple. Before we get started, let’s
clean up our project a bit and make sure it is saved. Using the component
names presented here will help ensure that your screen looks the same as the
screen images shown here.
For our first example, you will create a subroutine called “LoadData” to
display a single shapefile. Open the code view for frmMain and view the
code stub for the frmMain_Load function (the easy way to get there is by
double-clicking on an empty area of the form somewhere.) Enter the
following code into the frmMain_Load stub and create the LoadData
function as follows:
- 19 -
DRAFT
to indicate the path to your data file. If you are using the sample data in the
MapWindow application folder, then your full path might be:
“C:\Program Files\MapWindow\Sample
Projects\World\Shapefiles\world_adm0.shp”
Now, start your program by hitting the F5 key, or by using the menu
Debug|Start Debugging. There it is! Your first MapWinGIS application!
For this and other code samples in this book, we will take the approach
of 1) showing the full code snippet; 2) showing the expected results or
output; and 3) walking through the key parts of the code for which further
explanation is useful.
The current example only has three lines of code that need explanation:
¾ sfWorld.Open("C:\... \world_adm0.shp")
- 20 -
DRAFT
A full list of all shapefile objects and functions is given in Part 2 of this
book. Here we are using the “Open” function to open the specified
shapefile. Remember that a shapefile is actually defined by 3 files, *.shp,
*.shx and *.dbf. When opening a shapefile using the shapefile object,
reference the full path of the *.shp file.
¾ mapMain.AddLayer(sfWorld, True)
- 21 -
DRAFT
Creating Zoom and Pan Buttons
Instead of the ToolStrip control, you could also do this example using
four simple command buttons. We will use the ToolStrip control in the
remainder of this example.
Add code to the subroutine stubs for each of the Click event for each of
your buttons. Your code should look like this:
- 22 -
DRAFT
Private Sub btnPan_Click(ByVal sender As System.Object,
… ByVal e As System.EventArgs) Handles
… btnPan.Click
mapMain.CursorMode = MapWinGIS.tkCursorMode.cmPan
End Sub
Private Sub btnFullExtents_Click(ByVal sender As
… System.Object, ByVal e As
… System.EventArgs) Handles
… btnFullExtents.Click
mapMain.ZoomToMaxExtents()
End Sub
- 23 -
DRAFT
Zooming…
- 24 -
DRAFT
3
Shapefile Display, Coloring Schemes,
and Labels
Before we start working with symbology, let’s add another layer to the
project we started in the last chapter. Modify the frmMain_Load event to
include the following code. NOTE: in this code, “…” should be replaced
with the direct path to your shapefiles.
- 25 -
DRAFT
Private Sub LoadData()
Dim sfWorld As New MapWinGIS.Shapefile
Dim sfCities As New MapWinGIS.Shapefile
Dim hndWorld As Integer
Dim hndCities As Integer
sfWorld.Open("C:\...\world_adm0.shp")
sfCities.Open("C:\...\cities_capital_pt.shp")
hndWorld = mapMain.AddLayer(sfWorld, True)
hndCities = mapMain.AddLayer(sfCities, True)
End Sub
Run your application and make sure you see both the country outlines
and the city points. When you add layers to MapWinGIS, the data are
displayed in the opposite order from which you added them to the map. In
other words, if you add the cities layer first and then the countries, you
would not see the cities because the countries data would be on top of them.
Note an important change to the code on the following lines:
Previously we added the data layer to the map without catching the
value returned from the AddLayer function. This is convenient for quickly
dropping a file into a map. However, usually we want to keep track of the
layer that we just added, so we do that with a layer handle returned from the
AddLayer function. In the previous lines, the variables hndWorld and
hndCities will hold an integer value that is a handle to that layer within the
map. We need that handle for applying visualization changes.
Now we will explore global display properties for point and polygon
shapefile layers. Add the following lines of code after your AddLayer
function calls. (Note that when a line of code is too wide for this book, we
are indicating wrapped text using “…”.)
¾ FillColor =
… Convert.ToUInt32(RGB(Color.SpringGreen.R,
… Color.SpringGreen.G, Color.SpringGreen.B))
¾ LineColor = Convert.ToUInt32(RGB(0, 0, 255))
mapMain.ShapeLayerLineColor(hndWorld) = vbRed.
It should now be clear why we needed to capture the return value from
the AddLayer function. The handles, hndWorld and hndCities are used to
indicate which layer we are working on when we set properties in the map.
We will now modify the code to use a bitmap to represent the cities
point shapefile. To do this, we need to first find an appropriate bitmap.
You may want to make your own bitmaps or find suitable bitmaps on the
Internet. For this example, we will use a cute camera I made: .
- 28 -
DRAFT
To use this custom bitmap as the marker for a point shapefile, simply
change the point type parameter to be ptUserDefined. Here is how the
block of code for your cities global display settings should look now:
If you use the camera bitmap provided with this book, your results
should look something like this:
- 29 -
DRAFT
Transparency in Custom Bitmap Images…
MapWinGIS looks at the pixel in the upper left hand corner of your
bitmap and treats it as the transparency color. So if you want to have
transparency somewhere in your bitmap, just choose the color that will
be rendered transparent and place one pixel of that color in the upper
left hand corner of your bitmap.
¾ imgCities.Open("C:\...\camera16.bmp")
Finally, we tell the map to use the imgCities object as the user defined point
type for the cities data layer:
¾ mapMain.set_UDPointType(hndCities, imgCities)
Note that MapWinGIS also allows you to set a unique icon for each
point shape in your map using the ptImageList point type. This function
will be covered later in this chapter.
- 30 -
DRAFT
Setting a Coloring Scheme
- 31 -
DRAFT
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(240, 190, 140)))
Case "Pacific"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(250, 210, 160)))
Case "Sub Saharan Africa"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(255, 230, 180)))
End Select
Next
mapMain.set_LayerVisible(hndCities, False)
When you run your application now, you should see that all of the
major regions of the world have been assigned a unique color. It should
look something like this:
The code that we used to generate this map can be broken down into
three main steps. 1) Identify the field in the attribute table containing the
data that will be used for the divisions or breaks in the coloring scheme. 2)
Cycle through all of the shapes in the shapefile. 3) Identify shapes that have
- 32 -
DRAFT
a particular attribute value. 4) Assign a specific color to the individual
shapes that meet the specified criteria.
In our sample code note that we had to know a priori which field
contained the “region” data:
In the next line, we extract the data contained in the shapefile attribute
table at field index 2 and shape number, ShapeNum:
If you add a debugger “Watch” flag to this variable, Region, then you
will see that as it passes this line, it contains strings such as, “Asia”.
Finally we do a Select Case on the Region variable to take a specific
action depending on the value. In the following line, we set a fill color for
every shape that has the value “Antarctica” set as its region:
¾ Case "Antarctica"
¾ mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(100, 50, 0)))
Notice that we are using the conversion from RGB to UInt32 for all of
these color assignments. Also, notice the distinction between the function
- 33 -
DRAFT
call we used for global coloring, set_ShapeLayerFillColor, versus the
function call used to color an individual shape, set_ShapeFillColor. A final
note on this example: the last line of the example demonstrates how to turn
on or turn off a specific layer in code:
¾ mapMain.set_LayerVisible(hndCities, False)
Here we have turned the cities layer off so that we can have a better
view of the results of our coloring scheme activity.
Using the technique shown here, you should be able to create any kind
of coloring scheme symbology. For example, you can color points or lines
based on a numeric value in the attribute table. You can also size your
points based on a value such as population. Simply follow the same
approach to get the attribute data from the attribute table (using
“CellValue”) and then compare that to an expected value and size your
point accordingly.
In our last symbology example, we will use an image list to specify two
unique bitmaps to represent our cities. One will be used to indicate capital
cities, and the other to indicate regular cities.
Start by identifying or making two bitmaps. You can open Microsoft
Paint, for example, and draw your bitmaps and save them separately. For
our example, I’ve drawn a red star to mark capitol cities and a yellow circle
to represent regular cities.
Now insert the following code at the end of your LoadData function.
Remember to specify the full path to your bitmaps:
When you run your application, you should see a map of the world with
all of the cities flagged as either capitol or regular cities. Here is what it
might look like (with your images of course):
- 35 -
DRAFT
This example is similar to the coloring scheme example in the way that
we search all shapes for attributes that match a specific value. However it is
also different since the map component needs to store all of the images that
you will be referencing for your points. Note the following lines:
¾ mapMain.set_ShapePointImageListID(hndCities,
… ShapeNum, intCapitolIndex)
- 36 -
DRAFT
If the city is not a capitol city (i.e. the value in field 8 is 0) then we
specify to use the image from the image list with the index intRegularIndex:
¾ mapMain.set_ShapePointImageListID(hndCities,
… ShapeNum, intRegularIndex)
Image lists for point shapefile bitmaps can be very useful. For example,
you could specify a series of images that indicate type of weather station,
road, building, environmental monitoring station, etc.
Labeling Features
- 37 -
DRAFT
Dim Y As Double
FieldNum = 2
For ShapeNum = 0 To sfCities.NumShapes - 1
LabelText = sfCities.CellValue(FieldNum, ShapeNum)
X = sfCities.Shape(ShapeNum).Point(0).x
Y = sfCities.Shape(ShapeNum).Point(0).y
mapMain.AddLabel(hndCities, LabelText, LabelColor, X,
… Y, MapWinGIS.tkHJustification.hjCenter)
Next
When you run this code, you should see a map of the world with all of
the major cities labeled by with their names. In the following screenshot,
we have zoomed to Germany and can see several major cities in and around
Germany:
¾ X = sfCities.Shape(ShapeNum).Point(0).x
- 38 -
DRAFT
¾ Y = sfCities.Shape(ShapeNum).Point(0).y
In these two lines of code, we are extracting the X and Y coordinates of
the current point in our For…Next loop. Notice, however, that to get to the
X and Y values, we actually look at the Shape object at index, ShapeNum,
and then for that Shape, we look at its Point object at position 0. The logic
behind this approach might make more sense if you consider a polyline
shapefile.
Assume that you have a polyline shapefile with 20 polylines. In
MapWinGIS, each of these polylines would be considered a single “Shape”
object. Each of these Shape objects are, in turn, comprised of a series of
“Point” objects. Each Point object contains information such as X, Y, and Z
location.
In the case of a point shapefile, each unique Shape object only has one
associated Point object. And that point object is at index 0. The same
object structure is used for both point, line, and polygon shapefiles for the
sake of consistency. Just keep in mind that a shapefile is comprised of
shapes and there is a one-to-one relationship between each shape and a
single record in the shapefile attribute table. Each of these shapes is
comprised of points. And for point shapefiles, there is only one point per
shape.
Looking back at our sample code, once we have obtained the X and Y
coordinates for the city in question, we can use that information to place a
label using this line:
Here we specify the handle index of the layer to which the label
belongs, the text of the label, the color of the label, the position (X and Y)
of the label, and finally the justification of the label. Justification options
include left, right, and centered. You can try the other justification options
and see how they adjust the positioning of the labels.
From this example, it should be clear to you that labels, though
connected to a particular layer, are effectively independent of the data in the
layer. You could just as easily have generated random text and random X
and Y locations and used those as labels on your layer. Of course this isn’t
- 39 -
DRAFT
always useful, but it is meant to give you a high level of flexibility in
applying labels.
You can further customize your map labels by using the function
LayerFont to specify a particular font and font size:
Label scaling is useful if you want to let have your labels grow and shring
with the map when your users zoom in and out:
¾ mapMain.set_LayerLabelsScale(hndCities, False)
A global label offset can be set for a specific layer so that all labels are
adjusted vertically by a constant number of pixels. This is useful if you
want to avoid collision between your label and a custom point bitmap:
¾ LaymapMain.set_LayerLabelsOffset(hndCities, 8)
¾ mapMain.set_LayerLabelsShadow(hndCities, True)
¾ mapMain.set_LayerLabelsShadowColor(hndCities,
ShadowColor)
And finally, once you have applied labels to a layer, you can turn them on
and off using the set_LayerLabelsVisible function:
¾ mapMain.set_LayerLabelsVisible(hndCities, True)
We will now take a look at another function for adding labels to your
map. As its name implies, AddLabelEx is an extended labeling function
with an useful addition – rotation of labels. To see this function work,
replace the AddLabel function call in your code with the following line:
¾ mapMain.AddLabelEx(hndCities, LabelText,
… LabelColor, X, Y, MapWinGIS.tkHJustification.
… hjCenter, 45)
- 40 -
DRAFT
When you run your application, you should see all of the labels rotated at a
45 degree angle (counterclockwise from horizontal) as shown in the
following screenshot:
¾ mapMain.set_UseLabelCollision(hndCities, True)
Notice that you must specify the layer for which you want this function
activated. In our case we specify the handle to the Cities shapefile layer.
The second parameter is a Boolean indicating whether you want the
capability activated or not.
The following screenshot, though not particular visually appealing,
indicates the value of the label collision avoidance function. In the map of
India on the left, you can see that only the city of Dehli is labeled (in the
- 41 -
DRAFT
North part of the map). However, when the user zooms in to the area
around Dehli, all of the other major cities become labeled—as shown in the
figure on the right.
Performance Issues…
- 42 -
DRAFT
(This is a work in progress with several more chapters to go. Check for an
updated and extended version of this document at:
http://www.MapWindow.org/doc/UsingMapWinGIS.pdf
Thanks!
Dan)
- 43 -
DRAFT