You are on page 1of 43

Getting Started With the MapWinGIS

ActiveX Control
By Daniel P. Ames, PhD, PE

-1-
DRAFT
Copyright 2006 Daniel P. Ames

All Rights Reserved.

Printed in the United States of America

The names of companies and products herein are trademarks or registered


trademarks of their respective trademark owners.

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

In 1998 I discovered the magical world of object oriented programming and


component architecture software. This was a wonderful discovery for a guy
who began programming with the BASIC language (TRS-80 Model 1) at
age 12 and later was formally trained in the FORTRAN language while at
college. As many have done before me, I immediately recognized the value
of reusing code and objects and began developing simple ActiveX
components in Visual Basic 5.0 (some of which are still available for
download from my legacy web-site, http://www.StonehavenSoftware.com.)
At about the same time, I began work on a fascinating and challenging
doctoral research project that had a large GIS software development aspect.
My fellow students and I originally began to write the needed code using
the ESRI® MapObjects® product. While this product was robust and
effective for most of our needs, it had a few functional deficiencies for our
particular project and, perhaps more significantly, it had an associated
license that would make our product cost-prohibitive to redistribute.
The world of free and open source GIS was still in its infancy at that
time, and there were no free or open source GIS mapping components
-5-
DRAFT
available specifically for use in rapid application development environments
such as Visual Basic. As a result, our research group began developing our
own mapping components. The first version of the MapWindow
MapWinGIS ActiveX control was released in 2002 and was made available
to users of our products free of charge. In early 2004, the Idaho National
Laboratory, a major federal funding source for our research, released much
of the MapWindow project source code to the public domain.
In August 2004, I took a position at Idaho State University and brought
the public domain source code with me for continued development and use.
At that time, I was encouraged by colleagues at AQUA TERRA Consultants
(http://www.aquaterra.com) to release MapWinGIS and its associated
graphical user interface, MapWindow GIS, as open source software. In
January 2005 a new web site, MapWindow.org, was created as a repository,
information site, and distribution mechanism for all of the source code to
the MapWindow GIS application and its underlying components (including
the MapWinGIS ActiveX control).
With the help of some directed advertising using Google AdWords, the
MapWindow GIS project quickly began to gain a large user community of
people who had a similar need for a fast, efficient, and free GIS mapping
component. As of this writing downloads from the MapWindow.org web
site average about 2500 per month and are steadily increasing.
Two major resources for understanding the MapWinGIS ActiveX
control are available on the MapWindow.org web site. These include 1) an
active discussion forum where users post questions and answers on a variety
of ongoing topics; and 2) a live WIKI site that includes documentation and
sample code for most of the functions and objects in the MapWinGIS
Active control. This book is not intended to replace either of these
resources, but rather is meant to serve as a formal step-by-step introduction
to using the component, suitable for use as part of a semester long course on
GIS software development.

-6-
DRAFT
Acknowledgments

Thanks to Chris Michaelis, Lailin (Forest) Chen, Allen Anselmo, Ted


Dunsford, Jey Veluppillai, and all others who have contributed time and
talent to the MapWinGIS project..

-7-
DRAFT
1
GIS Software Development Paradigms

The multibillion dollar geographic information systems (GIS) industry has


largely been built upon a relatively small collection of very successful
commercial GIS software products. In recent years, the commercial GIS
software industry has been supplemented by a thriving free and open source
GIS developer community. From the perspective of GIS tool software
development these products generally fall in one of three categories that
correspond to three unique GIS software development paradigms. These
paradigms are: 1) development of plug-ins and extensions which add
additional functionality to existing desktop GIS systems; 2) development of
web-based mapping and data visualization tools; and 3) development of
custom standalone applications using GIS programming components.
Perhaps the most successful company in all three categories is
Environmental Systems Research Institute (ESRI) of Redlands, California.
The ESRI flagship desktop GIS product, ArcGIS, is known worldwide for
its data analysis capabilities. ArcGIS is used as a teaching and research
platform in most major university geography programs, and the software is
found in government agencies and large engineering and environmental
consulting firms throughout the world. While ArcGIS is primarily a
-8-
DRAFT
desktop data analysis package, it also is extensible through the development
of extensions, scripts, and tools that can be written in several different
languages. This extensibility has made ArcGIS a popular platform for
distributing analytical tools, models, and other custom functionality to third-
party end users.
In some cases, an individual or company may find that redistributing
custom tools developed in a commercial software application is not
particularly desirable. The high cost of commercial GIS software platforms
may create a problem, especially when the custom tool is intended for use
by individuals and entities who may not have the financial resources
necessary to adopt expensive commercial software.
Take, for example, a small engineering company that has produced a
software tool for tracking merchant shipping vessels in the Pacific Ocean—
having based the tool on a commercial software package. If the company
wants to sell their ship tracking tool to a third party, they must ensure that
the purchaser already has a copy of the commercial GIS platform. In such a
scenario, it is possible that the ship tracking software tool would be sold for
much less than the cost of the GIS platform. This being the case, does it
make sense to require the end users to pay the additional overhead for the
commercial GIS package?
Another challenge that can arise is related to the level expertise required
to operate large complex commercial GIS software products. Indeed, it is
very probable that many of the readers of this book have taken semester
long courses dedicated to learning all of the functionality of particular
desktop GIS packages. Certainly a high degree of functionality is not a
negative thing when one is a trained professional GIS analyst. However,
what about the individual who has not taken formal GIS training courses?
Such people can be considered “GIS consumers” rather than “GIS analysts.”
Does it make sense to provide the typical GIS consumer with all of the
functionality of a fully loaded commercial GIS desktop application when
that user may only need access to a small subset of the available
functionality?
Considering the ship tracking application again, it is likely that the end
users of this product will be trained in management, business, and possibly
even ship piloting, but probably not formal GIS analysis. This being the
case, presenting those users with all of the functionality, and required

-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!

Getting the Latest Version of MapWinGIS

As an active open source development effort, the MapWindow GIS project


and its MapWinGIS ActiveX control are regularly updated an improved.
Because of this, you should always consider acquiring the latest version of
the component before beginning any new development project. There are
five ways you can get a copy of the MapWinGIS ActiveX control:

- 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.

3) The MapWinGIS component is also included in the installation


package for the MapWindow GIS desktop application as it is used
heavily by that product. The latest installer for the MapWindow
GIS desktop application can be found on the MapWindow project
web site, and a version of that installer is also included on the CD
with this book.

4) Another option is to download the current pre-release build of the


MapWinGIS ActiveX control directly from the MapWindow GIS
project web site. If you take this approach, you will be on the
bleeding edge, using a version of the component that has not been
released for public consumption, but has any and all of the very
latest features and bug fixes (and maybe new bugs!). The code
repository where this version resides can be browsed at
http://svn.mapwindow.org/svnroot/MapWindow4Dev/Bin/. You
are looking for the file called “MapWinGIS.ocx” in this folder (or
equivalent folder with later version number).

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!

Registering the MapWinGIS ActiveX on Your


System

As an ActiveX control, MapWinGIS can be used within many different


programming languages and environments. MapWinGIS users have built
successful applications using most of the Microsoft development
environments including Visual Basic 6.0, Visual Basic .NET 2003, Visual
Basic 2005, C# .NET 2003, C# .NET 2005 and Visual C++. The
component has also been used in Microsoft Access, Excel, and PowerPoint
using the VBA programming language. Others have used the component in
Borland Delphi. Essentially any software development environment that
supports the ActiveX protocol can be used with MapWinGIS. I even know
one person who uses MapWinGIS on forms developed within ESRI’s
ArcGIS VBA programming environment!
In this chapter and the remainder of this book, Microsoft Visual Basic
.NET 2005 Express Edition will be used for all examples. Some code
samples for the other programming languages can be found on the
MapWindow project web site. Especially useful are the ongoing
discussions on the MapWindow forum
(http://www.MapWindow.org/phorum) and the documentation on the
MapWindow WIKI page (http://www.MapWindow.org/wiki). The WIKI
includes a number of Visual Basic 6.0 sample code snippets that can be
useful for VB 6.0 programmers and VBA programmers.
Once you have installed the MapWinGIS ActiveX control on your
development computer, the most important step is to register the component
with the system registry. This is a “feature” of Microsoft’s COM/ActiveX
- 14 -
DRAFT
paradigm which can be a source of frustration (if you have not registered
your components) or a means of easing your programming effort (by
allowing you to use one actual file (in this case, MapWinGIS.ocx) in several
different software applications.)
If you use any of the MapWindow GIS installers (either for the full
desktop application or for the MapWinGIS ActiveX component itself) then
the installer will register the component on your system for you. However,
if you have simply copied or downloaded the MapWinGIS.ocx file from
another location, then you will need to register the file manually on your
system. The easiest way to do this is to open your “Run” window from the
“Start” menu. Within the “Run” menu dialog enter the following command
and press “OK”.

regsvr32.exe "C:\Program Files\MapWindow\MapWinGIS.ocx"

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:

If the registry server fails to register MapWinGIS on your system then


you are likely missing a required dependency file. MapWinGIS has a
limited list of required dependencies which are required for the component
to work. All of the dependencies are Microsoft runtime libraries and most
will likely already be on your computer—especially if you have already
installed one of the Microsoft development tools. For more information on
the MapWinGIS dependencies, refer to Chapter 12 – Deploying My
Application.

- 15 -
DRAFT
Creating a Reference to MapWinGIS in Visual
Basic 2005 Express Edition

The first step to using MapWinGIS in your application is to create a


reference to the MapWinGIS ActiveX component in your integrated
development environment. Here are the steps in Microsoft 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.

The MapWinGIS ActiveX control will appear in your Visual Basic


2005 tool box with the name, “Map Control.”

- 17 -
DRAFT
Adding a Map Component to your Visual Basic
Form

Adding a MapWinGIS Map component to your Visual Basic form is as


simple as selecting the “Map Control” tool from the toolbox and dropping it
on the form. In the example we have a form called “Form1” and an
instance of MapWinGIS called “AxMap1”on the form.

Auto Sizing Your Map…

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.

1) Rename the form from “Form1” to “frmMain”


2) Rename the map control from “AxMap1” to “mapMain”
3) Give frmMain the title, “My First Map Application”
4) Save your project as “MyFirstMapApp”

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:

Private Sub frmMain_Load(…) Handles MyBase.Load


LoadData()
End Sub

Private Sub LoadData()


Dim sfWorld As New MapWinGIS.Shapefile
sfWorld.Open("C:\...\world_adm0.shp")
mapMain.AddLayer(sfWorld, True)
End Sub

Note that in the frmMain_Load function we have used “…” to indicate


the frmMain_Load default parameters. In your code, you should see these
parameters, not, “…”. Also in the LoadData function we are using “\...\”

- 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:

¾ Dim sfWorld As New MapWinGIS.Shapefile

MapWinGIS has many useful objects—one of the most important is the


MapWinGIS.Shapefile object. Here we are simply creating an instance
of an empty shapefile object with the name, sf1.

¾ 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)

The AddLayer function on the map control takes two parameters, a


generic Object, and a Boolean parameter, “visible,” indicating whether or
not to display the layer when it is added to the map.
The first parameter can be a MapWinGIS.Shapefile or a
MapWinGIS.Image object. The second parameter is used to add the layer
invisibly so that you can change its display properties before “turning it on”.
For example, you may want to change the outline color of the countries
layer before displaying it in the map. In that case, you would add the layer
with the “visible” flag set to false. Then you would change the outline color
on the layer, and finally, change the visible flag to true. We will learn how
to do this in a later chapter.
Now that your first map application is running, you can try using the
zoom functions on the map. When your application starts, the map will
default to the “zoom in” cursor mode. In this mode, you can zoom to any
part of the map with a single click on the map, or by drawing a “zoom box”
on the area you want to zoom in to. To zoom out, simply right click on the
map.

- 21 -
DRAFT
Creating Zoom and Pan Buttons

One of the distinguishing characteristics of a GIS application, versus an


application that shows static, unchanging maps, is the ability to navigate the
map using zoom and pan buttons. In this section you will add a tool strip
with four buttons to expose the following functions: zoom in, zoom out,
pan, and zoom to full extents. You will attach these buttons to the
appropriate functions in MapWinGIS and have fully functioning map
navigation.
First place a ToolStrip component on your form. We will use the
ToolStrip for the navigation buttons. Add four text buttons (or graphic
buttons if you are adventurous) to the ToolStrip. Name your buttons,
“btnZoomIn,” “btnZoomOut,” “btnPan,” and “btnFullExtents” and provide
them with appropriate text, tool tips, and graphics as you see fit.

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:

Private Sub btnZoomIn_Click(ByVal sender As


… System.Object, ByVal e As
… System.EventArgs) Handles btnZoomIn.Click
mapMain.CursorMode = MapWinGIS.tkCursorMode.cmZoomIn
End Sub
Private Sub btnZoomOut_Click(ByVal sender As
… System.Object, ByVal e As
… System.EventArgs) Handles btnZoomOut.Click
mapMain.CursorMode = MapWinGIS.tkCursorMode.cmZoomOut
End Sub

- 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

In this code, you are making extensive use of the “Map.CursorMode”


property. The two remaining other enumerations of Map.CursorMode are
cmSelection and cmNone. The enumeration cmSelection is used to convert
the MapWinGIS cursor into a “pointing finger” that can be used for making
selections on the map. The enumeration cmNone is a special case that is
used when you are developing your own map interaction behavior and you
don’t want one of the other cursor modes to interfere with your custom
actions.
Run your program and see how it works. You should be able to change
to the zoom in, zoom out, or pan mode and you should also be able to zoom
to the full map extents. Here is a screenshot of the application as it should
look so far (zoomed to Italy).
Now might be a good time to save your project as you get ready to
move on to Chapter 3.

- 23 -
DRAFT
Zooming…

MapWinGIS uses several optimization techniques for fast drawing. As


you zoom around your map note that the drawing is slightly faster when
you are zoomed to a small area and slightly slower at the full extents.
This is due to an optimization that only draws those shapes that fall
within your current extent on each zoom or pan function call.

- 24 -
DRAFT
3
Shapefile Display, Coloring Schemes,
and Labels

In most circumstances, GIS software developers need the ability to create


rich and informative maps by presenting data in visually stimulating ways.
Typically, we use such techniques as coloring schemes, markers, labels, and
other types of symbology to improve the visual value of a digital map.
Symbology can be used to convey information about your data, such as the
population of a country, or simply to draw attention to part of your map by
highlighting a certain feature. In this chapter, you will learn how to adjust
the global display properties for a layer, how to apply a coloring scheme to
your data, how to add labels to your data.

Adjusting Shapefile Display Properties

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:

¾ hndWorld = mapMain.AddLayer(sfWorld, True)


¾ hndCities = mapMain.AddLayer(sfCities, True)

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 “…”.)

Dim FillColor As UInt32


Dim LineColor As UInt32
Dim LineWidth As Single
Dim PointColor As UInt32
Dim PointSize As Single
- 26 -
DRAFT
'Global display settings for the countries polygons
mapMain.set_ShapeLayerDrawFill(hndWorld, True)
FillColor = Convert.ToUInt32(RGB(Color.SpringGreen.R,
… Color.SpringGreen.G, Color.SpringGreen.B))
LineColor = Convert.ToUInt32(RGB(0, 0, 255))
LineWidth = 2.0
mapMain.set_ShapeLayerFillColor(hndWorld, FillColor)
mapMain.set_ShapeLayerLineColor(hndWorld, LineColor)
mapMain.set_ShapeLayerLineWidth(hndWorld, LineWidth)
'Global display settings for the cities points
PointColor = Convert.ToUInt32(RGB(255, 255, 0))
PointSize = 8
mapMain.set_ShapeLayerPointColor(hndCities,
… PointColor)
mapMain.set_ShapeLayerPointSize(hndCities, PointSize)
mapMain.set_ShapeLayerPointType(hndCities,
… MapWinGIS.tkPointType.ptCircle)

When you run your application it should look like this:

This sample code demonstrates several global display parameters for


shapefile layers. The first question you might ask is, “why are we using
UInt32 data types for the colors?” This is actually a really good question.
MapWinGIS ActiveX was written to support both .NET and non-.NET
- 27 -
DRAFT
application development environments (e.g. Visual Basic 6.0) that do not
support the .NET color enumerations. Instead these non-.NET languages
expect to pass the control the equivalent of a Visual Basic 6.0 Long Integer.
Hence, when working in .NET we need to cast the .NET color into a UInt32
data type before using it in the function calls. The following lines illustrates
the conversion from a .NET color object and from an RGB defined color:

¾ FillColor =
… Convert.ToUInt32(RGB(Color.SpringGreen.R,
… Color.SpringGreen.G, Color.SpringGreen.B))
¾ LineColor = Convert.ToUInt32(RGB(0, 0, 255))

This code also demonstrates use of the “set_ShapeLayerLineWidth”


function, and the “set_ShapeLayerPointSize” function, both of which
should be relatively self explanatory, requiring only a Single value. The
function, “set_ShapeLayerPointType” provides an enumeration of point
types including square, circle, diamond, and triangles facing up, down, left
or right. The “ptUserDefined” and “ptImageList” enumerations are used
when you want to specify an icon or bitmap to display on each point.

VB 6.0 Function Calls …

In Visual Basic 6.0 and VBA development environments, many of the


function calls described here will appear different, not having the “set_”
prefix. Instead the shape line color, for example, would be specified as:

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:

'Global display settings for the cities points


PointColor = Convert.ToUInt32(RGB(255, 255, 0))
PointSize = 1
mapMain.set_ShapeLayerPointColor(hndCities, PointColor)
mapMain.set_ShapeLayerPointSize(hndCities, PointSize)
mapMain.set_ShapeLayerPointType(hndCities,
… MapWinGIS.tkPointType.ptUserDefined)
Dim imgCities As New MapWinGIS.Image
imgCities.Open("C:\...\camera16.bmp")
mapMain.set_UDPointType(hndCities, imgCities)

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.

We have made three main changes to the code to be able to render


images for our point shapefile. 1) Change the point size property back to
“1”. When using custom bitmaps, the size is multiplied by the value
specified by set_ShapeLayerPointSize. 2) Use set_ShapeLayerPointType
to specify a ptUserDefined point type. 3) Apply your bitmap to the shape
layer. The third step requires the following lines of code:

¾ Dim imgCities As New MapWinGIS.Image

Here we are creating a new MapWinGIS.Image object called imgCities.


Custom bitmaps in MapWinGIS must be defined as a MapWinGIS.Image.
In the next line, we open the selected bitmap file into the
MapWinGIS.Image object:

¾ 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

In MapWinGIS we se the term “coloring scheme” to mean color symbology


that differentiates features on your map using colors based on a value in the
shapefile attribute table. In this section we will build upon the code from
the last section to color the countries data based on an attribute in the data
called “region.”
Enter the following code in your LoadData function after the global
display settings sections:

'Set up a coloring scheme for the countries polygons


Dim ShapeNum As Integer
Dim Region As String
Dim FieldNum As Integer
FieldNum = 2
For ShapeNum = 0 To sfWorld.NumShapes - 1
Region = sfWorld.CellValue(FieldNum, ShapeNum)
Select Case Region
Case "Antarctica"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(100, 50, 0)))
Case "Asia"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(120, 70, 20)))
Case "Australia"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(140, 90, 40)))
Case "Caribbean"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(160, 110, 60)))
Case "Europe"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(180, 130, 80)))
Case "Latin America"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(200, 150, 100)))
Case "North America"
mapMain.set_ShapeFillColor(hndWorld, ShapeNum,
… Convert.ToUInt32(RGB(220, 170, 120)))
Case "NorthAfrica"

- 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:

¾ Dim FieldNum As Integer


¾ FieldNum = 2

In this case, we know that region is contained in field index 2. Fields


are indexed starting at zero, and don’t include the “ShapeNum” field that
you might see if you look at the data in a desktop GIS application such as
MapWindow GIS.
Once you know the field that you need to look in, then you simply cycle
through all of the shapes in your shapefile looking for specific values in that
field. In the following code, we are searching through all of the shapes
based on their indices which range from 0 through n-1 (where n is the total
number of shapes in the shapefile).

¾ For ShapeNum = 0 To sfWorld.NumShapes - 1

In the next line, we extract the data contained in the shapefile attribute
table at field index 2 and shape number, ShapeNum:

¾ Region = sfWorld.CellValue(FieldNum, 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.

Using a Point Image List

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:

'Set up a an image list for a point layer


Dim imgCapitol As New MapWinGIS.Image
Dim imgRegular As New MapWinGIS.Image
Dim intCapitolIndex As Integer
Dim intRegularIndex As Integer
Dim isCapitol As Integer
mapMain.set_ShapeLayerPointType(hndCities,
… MapWinGIS.tkPointType.ptImageList)
- 34 -
DRAFT
imgCapitol.Open("C:\...\capitolcity.bmp")
imgRegular.Open("C:\...\city.bmp")
intCapitolIndex = mapMain.set_UDPointImageListAdd
… (hndCities, imgCapitol)
intRegularIndex = mapMain.set_UDPointImageListAdd
… (hndCities, imgRegular)
FieldNum = 8
For ShapeNum = 0 To sfCities.NumShapes - 1
isCapitol = sfCities.CellValue(FieldNum, ShapeNum)
If isCapitol = 1 Then
mapMain.set_ShapePointImageListID(hndCities,
… ShapeNum, intCapitolIndex)
Else
mapMain.set_ShapePointImageListID(hndCities,
… ShapeNum, intRegularIndex)
End If
Next
mapMain.set_LayerVisible(hndCities, True)

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:

¾ Dim imgCapitol As New MapWinGIS.Image


¾ Dim imgRegular As New MapWinGIS.Image

¾ imgCapitol.Open("C:\...\capitolcity.bmp")
¾ imgRegular.Open("C:\...\city.bmp")

¾ intCapitolIndex = mapMain.set_UDPointImageListAdd
… (hndCities, imgCapitol)
¾ intRegularIndex = mapMain.set_UDPointImageListAdd
… (hndCities, imgRegular)

Here we create two MapWinGIS.Image objects, imgCapitol and


imgRegular. These are then used to open two bitmaps from the disk. Of
course \...\ indicates the full path to your image files on your own
computer. Finally, we add both of these images to a global image list stored
inside the map component using set_UDPointImageListAdd. This makes
the images available to you later when you want to specify a particular
image for a specific point. To reference the images that we added to the
image list, you are given an index as the return value from the function. In
the example, we are storing these indices as intCapitolIndex and
intRegularIndex.
Now look at the loop through the shapes. For each shape we are going
to test the value of the contents of field number 8:
¾ isCapitol = sfCities.CellValue(FieldNum, ShapeNum)

I happen to know that field number 8 contains a flag value of 1 or 0


indicating whether or not the city is a capitol city. For those cities that have
the flag set to 1, we then specify that they should use the image from the
image list with index intCapitolIndex as follows:

¾ 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

Labeling of features on a map is an art that is very difficult to automate.


There are issues of label sizing, fonts, scaling, rotation, following lines,
centering in polygons, placement by points, collision with other labels, and
on and on. Indeed, even the most functional GIS tools often expect the user
to perform final labeling for map production in external, add-on, or third
party applications.
MapWinGIS as primarily a dynamic GIS software development tool (as
opposed to a tool for generating paper map printouts) has a robust and
flexible set of labeling functions, but leaves much of the responsibility for
labeling with the programmer (you!). In this section we’ll learn how to use
the AddLabel and AddLabelEx functions to automatically label a map. We
will use the same two data sets that were used in the previous sections.
To start, we need to decide what features you want to label, and what
text you want to apply. Next you need to obtain positioning information.
Finally you need to determine the look of the labels you are going to apply
as you place them on the map.
For the sake of continuity, use the same code from the previous section
and add the following lines to the end of your LoadData function:

'Add labels to cities


Dim LabelText As String
Dim LabelColor As UInt32 = Convert.ToUInt32(RGB(0,0,0))
Dim X As Double

- 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:

The code for generating these labels is quite simple. We specified a


label color and a field in the shapefile attribute table from which to pull the
label text. Next we cycled through all of the shapes in the shapefile and
extracted the label text as well as the X and Y coordinates of the points.
Finally we used the AddLabel function to apply the labels to the map.
All of the code used here should be fairly straightforward given our
previous examples, however the following lines are worth pointing out:

¾ 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:

¾ mapMain.AddLabel(hndCities, LabelText, LabelColor,


… X, Y, MapWinGIS.tkHJustification.hjCenter)

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:

¾ mapMain.LayerFont(hndCities, "Times New Roman", 9)

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)

Label shadows can help your readability by providing a background around


the label:

¾ 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:

The final labeling function we will explore is automatic label collision


avoidance. Too many labels on a map can make it illegible, especially
when they overlap each other. The purpose of automatic label collision
avoidance is to avoid this situation, and make sure that all of the labels on
your map are separated enough to be legible.
To activate automatic label collision avoidance, use the following
function:

¾ 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.

Label collision avoidance is particularly useful when you are using


fixed size labels. In this case, as you zoom out, the labels remain a constant
size and would begin to block and overlap each other. This is not always as
much of a problem when you use scalable labels that grow and shrink with
your map.
As you become familiar with these labeling functions as well as the
other symbology techniques illustrated in this chapter you should be able to
create dynamic and informative, well-labeled maps for you users.

Performance Issues…

We have shown you lots of interesting and powerful labeling and


symbology capabilities in this chapter. As you use these functions, be
aware that each one carries an additional overhead for speed and
performance of your application. You may want to consider trying a
variety of symbology and labeling techniques with data sets that are
typical for your particular mapping application and make sure that
speed and performance to not begin to suffer.

- 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