by Richard Marsden
This article follows on from my recent articles about using MapPoint’s ImportData and DisplayDataMap methods. It demonstrates how to use these methods from a non-Microsoft language that only supports COM as an extension and not an integral part of the language and libraries.
I will assume that you are familiar with the basics of calling MapPoint from Python using win32com (Using Python to Control MapPoint, Part 1 and Part 2). The samples in this article use MapPoint 2010 North America, Python 2.7, and the IDLE Python IDE. The samples use the same modified sample data used in the previous ImportData article.
Some Initial Notes
win32com is actually pretty smart behind the scenes, making it very simple to pass relatively complex data structures such as two dimensional arrays. Simply pass a two dimensional Python list, and win32com will automatically convert it into the COM equivalent.
An inconvenience is the lack of enumeration constants. It should be possible to write a script that creates a Python module to list these. However, I chose to simply inline the numeric values with suitable comments. Luckily, the MapPoint documentation lists all of the MapPoint enumeration values.
A potentially bigger problem concerns ‘missing’ optional parameters. .NET provides the System.Reflection.Missing.Value constant that can be passed to indicate a ‘missing’ optional parameter. In theory, win32com will do the same when it sees the None constant. Unfortunately, this does not always work. For some parameters, MapPoint/COM will throw an exception, and you are forced to manually insert the required default value or array.
Using ImportData
Here is the C# ImportData example converted to Python with explicit field specifications:
Code:
# Python script to demonstrate MapPoint ImportData
# and DisplayDataMap method calls
import string
import codecs
import gc
import sys
from win32com.client import constants, Dispatch
print("MapPoint ImportData / DisplayDataMap demo")
# start MapPoint
mpApp = Dispatch('MapPoint.Application.NA')
mpApp.Visible = True
mpMap = mpApp.ActiveMap
mpDatasets = mpMap.Datasets
filename = "C:\\Program Files (x86)\\Microsoft MapPoint 2010\\Samples\\Sales.txt"
# Define field specifications using a simple 2d List
# Enumerations are converted to integers
# All fields must be defined, and defined correctly. (eg. names cannot be mis-spelt)
fieldSpecifications = [ ["ID",0], # geoFieldSkipped
["State", 10], # geoFieldRegion1
["Country", 15], # geoFieldCountry
["Our Sales ($)", 23], # geoFieldData
["Competitor A Sales ($)", 23], # geoFieldData
["Competitor B Sales ($)", 23] # geoFieldData
]
myDataset = mpMap.DataSets.ImportData(filename,
fieldSpecifications,
244, # GeoCountry.geoCountryUnitedStates,
9, # GeoDelimiter.geoDelimiterTab,
0 ) # GeoImportFlags.geoImportFirstRowIsHeadings
# List the field information (diagnostics as before
print "Fields read: ",myDataset.Fields.Count
print "Fields (1-ref):"
iCol=1
for fld in myDataset.Fields:
print " ",iCol," ",fld.Name
iCol += 1
#------
# Insert the DisplayDataMap sample code, HERE
#------
# Tidy Mappoint up
str = raw_input("Finished: Press return to exit")
print
fieldSales = 0
myDatamap = 0
myDataset = 0
mpDatasets = 0
mpMap.Saved = True
mpMap = 0
mpApp.Quit()
mpApp = 0
gc.collect()
It is still possible to default the field specifications by passing None instead of the fieldSpecifications array:
Code:
myDataset = mpMap.DataSets.ImportData(filename,
None, # Default the field specifications
244, # GeoCountry.geoCountryUnitedStates,
9, # GeoDelimiter.geoDelimiterTab,
0 ) # GeoImportFlags.geoImportFirstRowIsHeadings
DisplayDataMap
As with the C# examples, DisplayDataMap is a little more complex.
Here is an example that draws shaded same-sized circles with an array of custom ranges:
Code:
# These are the (1-ref) indexes to the columns we want to plot (see above)
ourSalesIDX = 4
# Use these indexes to create an array of data fields to plot
fieldSales = myDataset.Fields.Item(ourSalesIDX)
# 4 ranges, ie. 5 values required
arrayOfCustomRanges = [ 200.0, 1000.0, 2000.0, 3000.0, 5000.0 ]
datamap = myDataset.DisplayDataMap(
3, # GeoDataMapType.geoDataMapTypeShadedCircle
fieldSales,
18, # GeoShowDataBy.geoShowByRegion1
0, # GeoCombineDataBy.geoCombineByNone
3, # GeoDataRangeType.geoRangeTypeDiscreteEqualRanges
-1, # GeoDataRangeOrder.geoRangeOrderDefault
2, # color scheme 2
4, # four data ranges
arrayOfCustomRanges
)
# Note: Last four parameters not given & allowed to default
datamap.LegendTitle = "Sales by State"
This is works very well, but the last four parameters (ArrayofCustomNames, DivideByField, ArrayOfDataFieldLabels, and ArrayOfPushpinSymbols) cannot be defaulted using the None constant. They can either be simply left off the parameter list (as above), or they have to be explicitly listed with valid arrays. For example:
Code:
datamap = myDataset.DisplayDataMap(
3, # GeoDataMapType.geoDataMapTypeShadedCircle
fieldSales,
18, # GeoShowDataBy.geoShowByRegion1
0, # GeoCombineDataBy.geoCombineByNone
3, # GeoDataRangeType.geoRangeTypeDiscreteEqualRanges
-1, # GeoDataRangeOrder.geoRangeOrderDefault
2, # color scheme 2
4, # four data ranges
arrayOfCustomRanges,
["A","B","C","D"]
)
Conclusions
ImportData and DisplayDataMap are more complex than most MapPoint methods, but both can be successfully called from Python. Object array parameters can be easily passed as standard Python 2d lists. However, enumeration constants are not automatically defined, and some optional parameters have to be explicitly provided.