At the end of last week, I released a new ColdFusion CFC wrapper to interact with the Multimap API.
I updated the version to 1.1 this morning to include a new method to obtain static map images.
Multimap... seemingly considered obsolete by some since Google steam-rollered into the geocoding mapping arean, making it even easier for people to integrate and work with geographical information.
I must admit, it had been years since I looked at a Multimap generated object, always defaulting to browse the Goog-monster and it's directions for me.
Google makes it incredibly easy to plot a route between multiple points and pull that route direction information.
However, one thing that found incredibly difficult to do using the Google maps API (perhaps due to lack of/hidden documentation or lack of coffee) was to easily obtain the latitude and longitude of every single point of the returned route. A small fun project I was working on at that time would have benefited from this information.
Multimap to the rescue!
I found, when browsing through the API and playing with some test responses, that the returned information for route searches contains a wealth of data, including the start and end point in lat/lon format for every point of the journey. Exactly what I was looking for.
I was impressed with the results, and what better way to share the benefit than to write something open source so others can at least try it out themselves too.
Instantiation
Instantiating the object requires one parameter, which is the MultiMap API key. To obtain your free key to access the open api, visit the official site here: http://www.multimap.com/openapi/
In the below example, I'm also sending through the optional second parameter, parseOutput, to true. This returns any XML or JSON information in structural form. The alternative 'false' option will return the literal string information.
<!--- set your api key as a variable ---> <cfset strApiKey = '< your API key goes here >' /> <!--- instantiate the object ---> <!--- setting the parseOutput to true to return structural information (XML and deserialized JSON) ---> <cfset objMMapSVC = createObject('component', 'com.coldfumonkeh.map.multimap'). init( apikey=strApiKey, parseOutput=true ) />
There are four functions within the multimap object, each of which interact with a particular service offered through the API:
- geoSearch(): runs a search on the geocoding web service
- getRoute(): runs a search for a route
- getConversion(): converts lat/lon values into Ordnance Survey / Mercator references
- getMap(): generates an image for the map, or returns detailed information for visual representation
Geocoding
The basic geocode search method contains a variety of optional parameters to send through to help refine your search and the returned data.
In this example, we're running a quick search on the UK postal code WC2N:
<cfset xmlObj = objMMapSVC.geoSearch( postalCode='WC2N', countrycode='GB' ) /> <cfdump var="#xmlObj#" label="Geocoding Results" />
The returned data in this case is fairly succinct and to the point, providing the user with the lat/lon coordinates of the 'approved' location:
Routing
The route search returns a great deal of information, covering the overall journey (including distance, duration and bounding box) to stage and step-specific information, such as written directions, duration, lat/lon coordinates etc.
<cfset postalCodeList = 'WC2N, E3' /> <cfset countryCodeList = 'GB, GB' /> <!--- map the route between the two postal codes ---> <cfset xmlObj = objMMapSVC.getRoute( postalCode=postalCodeList, countryCode=countryCodeList, mode='walking') /> <cfdump var="#xmlObj#" label="Route Results" />
A VERY helpful method to use, and the one which fired up my interest in the MultiMap services again.
Conversion
The conversion method does pretty much what it implies. Taking supplied lat/lon coordinates, this will return the x y coordinate equivalents for the same location for use with the Ordnance Survey Grid System. This will also work in reverse, by sending through the x y to obtain their relative lat/lon values.
<cfset latList = '51.50964,51.52854' /> <cfset lonList = '-0.12483,-0.02619' /> <!--- convert the lat/lon into x/y ---> <cfset xmlObj = objMMapSVC.getConversion( lat=latList,lon=lonList ) /> <cfdump var="#xmlObj#" label="Conversion Results" />
The results of which can be seen here:
Static Map Generation
Most, if not all developers have toyed with the Google map integration into some site or other, I'm sure.
JavaScript driven, and dynamically generated within a given DIV element, it is a beautiful, highly extensible system to use, granted.
The Multimap API likes to kick it 'old skool' and generate images for each request using the parameters passed through in the request. This is very reminiscent to me of using the Arc GIS system a few years ago, when I was developing mapping tools for an environmental agency. Not as slick as being able to pan / relocate the map center using a drag and click interface, sure.. but still useful.
In this example, we're creating a static map image of the WC2 area of London using pre-determined lat/lon coordinates.
One thing I did like about these images is the datasource overlay attribute. This queries a MultiMap datasource and overlays pointers/markers directly onto the map image of certain locations / amenities. For instance, below I am asking for the web service to include markers for all ATM machines within the boundary box area of my location.
Again, this may not be as sexy as interactive placeholders as seen on the Google map implementation, but I like the fact that users/ developers have access to their stored information.
<cfset imgObj = objMMapSVC.getMap( lat='51.50964', lon='-0.12483', overlayDataSource='mm.poi.global.general.atm', overlayCount=100, overlayLabel='ATM', maptype='map', zoomFactor=16) /> <cfdump var="#imgObj#" label="Map image response" /> <cfif structKeyExists(imgObj, 'imgPath')> <cfimage action="writeToBrowser" source="#imgObj.imgPath#" /> </cfif>
The map method result (if using the browser to view the API directly) will display the image instantly.
In the CFC wrapper, I developed the return information for this particular method to return a struct of information, which includes the mime type of the file, the location of the image file on the server (which is written from the byteArray information returned from the original service request and saved into the getTempDirectory() location) and the terms and conditions HTML link, which must be displayed near any generated map, according to the API terms and conditions.
So, in the above code example, you can see we are checking for the existence of the 'imgPath' value within the returned struct and displaying that image directly to the browser, which will display the image like so:
Mmmm.. detailed and sexy, I think you'll agree. :)
Getting image information
The getMap() method not only returns static images, but is also incredibly useful for returning information about a static image based upon the parameters you send through.
In this example, I am querying the API using a routeKey paramater, which is essentially the start and end coordinates for a route i wish to display on the map image.
I could easily use the same parameters as in the previous image generation example, only the output parameter is no longer requiring an image; instead, we are requesting either JSON or XML.
<!--- map image call requesting JSON data; no image generated ---> <cfset routeKeylist = 'GB,-0.12483:51.50964;0.29551:51.76776,0' /> <cfset imgObj = objMMapSVC.getMap( routekey=routeKeylist, maptype='map', output='json' ) /> <cfdump var="#imgObj#" label="Map image response" />
I had to streamline and crop the returned structural information, as it was too long to include on a single blog post... (I dont want you scrolling any longer than you need to, dear reader).
This returned information is incredibly detailed and useful. Consider this at first glances as a mapping equivalent of using the getMetaData() method to read a ColdFusion component. You get a lot of core information about the image and the locations used to generate.
Other useful structs returned in this response include the automatically generated URL's to the API webservice to instantly obtain the maps surrounding the central location, i.e to pan east,north-east, south-west etc.
I will be displaying these in action in a future blog post, complete with code to implement the image loading, so stay tuned.
All in all, I have found the MultiMap API incredibly simple to use, and surprisingly detailed.
The moral of the story? If there was one, I'd say that you should never turn your back on something otherwise you may miss out on what it can do for you.
Where can I get it?
The MultiMap API CFC Wrapper is available to download for free right now from http://multimap.riaforge.org/