PDA

View Full Version : Using Python to call ImportData and DisplayDataMap



Richard Marsden
12-14-2011, 10:44 AM
by Richard Marsden

This article follows on from my recent articles about using MapPoint’s ImportData (http://www.mapforums.com/using-importdata-displaydatamap-part-1-importdata-method-18537.html) and DisplayDataMap (http://www.mapforums.com/using-importdata-displaydatamap-part-2-introducing-displaydatamap-18538.html) 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 (www.mp2kmag.com/a131--python.pythonwin.mappoint.html) and Part 2 (www.mp2kmag.com/a134--python.winwaed.automation.mappoint.html)). 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:


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


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:


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


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.