Follow us on Twitter

MapPoint & F#: A Practical Example


The F# Introduction page demonstrates how F# can be used to call MapPoint using both early and late binding. This page presents a more practical example that classifies pushpins according to their data fields. Functional languages are defined in terms of functions, and it seemed that a classification application with non-trivial logic would be a good way of doing this. In fact some functional languages such as Prolog are designed for this kind of logic-based classification.

The specific application uses wetland data produced by ecology students of the University of Dallas (see for a Bing Maps / PHP implementation). The underlying database consists of many data points with field measurements. This data has been imported into MapPoint using the Data Import Wizard. The application will determine if each pushpin represents a wetland based on the field measurements (stored as pushpin data fields), and change the pushpin symbol accordingly. Note that some data might be incomplete, but this is marked in the database using flags.

The determination is based on the hydrology, vegetation, and soil. The data point is a wetland if two or more of these indicate a wetland. For example, the presence of standing water does not necessarily make it a wetland — it might be a temporary flood.

The Code

This example uses early binding. This allows us to take advantage of F# type checking and Visual Studio’s Intellisense. It is also simpler to code. It is assumed that you have created the MapPoint COM Interop as per the F# Introduction.

First we add references to the MapPoint COM Interop and to the .NET namespaces that we require:

Next we define a record type called DataRecord. Due to the way the data fields are stored in MapPoint, we have to look over them all, extracting the values. By putting them in a record, we can then access them in an arbitrary manner as/when we want them. All of the data fields are boolean flags (typically marking the presence of a characteristic) or integers (typically soil percentages or soil color measurements). The flags havehydro, haveveg, and havesoil all mark if the data has been collected for these respective groups of measurements. Here is the record definition:

Next we create a function called extract_fields which takes a MapPoint Recordset that is positioned at a pushpin, and creates a DataRecord from it:

This is actually a two stage process. The function loops over the fields in the Recordset. These are then inserted into a .NET Dictionary object. This stores the values for the data fields in an easy to access manner. However, all the data values have to use the same type. Also, a native F# record is much better suited to F#’s pattern matching and processing. Therefore we convert the completed dictionary into the DataRecord object which is returned. This is probably more complicated than it needs to be, but it does demonstrate the way F# can easily use the standard .NET libraries. Note that we are assume all the data types are correct and that missing fields will be interpreted as 0 or false. This would handled better in a production system. This is simply an example that has been hard-coded to a particular dataset.

Next we define a function to determine if we have sufficient information to say whether a data point is a wetland or not. Our final classifications are: wetland, not a wetland, and not enough information. Here is the function:

This function takes a DataRecord record and matches three fields. It returns true (we have sufficient information) if two (or more) of these three fields are true, ie. if two of vegetation, hydrology, and soil have field measurements. match is a very powerful statement in F# and we will use it a few times. The ‘_‘ character indicates a wildcard.

Next we define three similar functions. These determine if vegetation, hydrology, and soil indicate a wetland:

Note that we continue to use match, although conventional logic is used for wetland_veg. In the two match statements above, we also have a logic clause (a series of ‘||’ or clauses) in the match parameters.

The above three functions can now be combined, to determine if a data point is a wetland:

At least two of the determinations have to be true, for the point to be a wetland. Note that this function will return a possible false negative if there’s insufficient data. This is handled separately.

First we must define one more function. This chooses a symbol index according to whether there’s sufficient information for a determination, and whether the it is determined to be a wetland or not:

That is the last function. Now we can start on the global code. First, start MapPoint, load the map file, and create a Recordset for the first dataset:

Again, we are assuming that the first dataset is the one that is required – you would not do this in a production environment.

Now we (finally) come to the ‘guts’. Here we loop over each pushpin, extract the fields, determine if we have enough information and whether it is a wetland, and choose a symbol accordingly:

And that is the end of the code! We deliberately leave MapPoint open at the end for viewing and/or saving by the user.

Here is the resulting map after the pushpins have been classified:

Pushpins classified by the sample F# code

Notice how the whole process has been broken down into small functions. Although we have mixed imperative and functional programming, virtually all of the program consists of many small functional components. The only exception to our functional ideal is the large extract_fields() function.

Although it has not been used here, functional programming could be used to pass custom determination functions. The user/programmer could then call the classifier by simply passing the name of the map file and a function to do the classifying.

Leave a Reply




You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">