For a while now I've had two line items in my "To write about" notebook. They concerned figuring out how to use CFMAP (and Google Maps in general) to center the map on an address. Out of the box you have an immediate address that acts as the center, but I wanted to see how the center could be changed post map load.
My first attempt was to figure out a way to center the map on an marker. I began with an extremely simple script:
<cfmap centeraddress="St. Louis, MO" width="400" height="400" name="mainMap">
<cfmapitem address="New Orleans, LA">
</cfmap>
This generated:

Looking over both the ColdFusion docs and the Google Maps API, I began by adding ajaxOnLoad to the bottom of my script:
<cfset ajaxOnLoad("init")>
And used this for my init:
function init(){
ColdFusion.Map.addEvent("mainMap", "click", clickHandler)
}
This code adds a click handler for the map I created earlier. The click handler does:
function clickHandler(item){
if (item) {
var map = ColdFusion.Map.getMapObject("mainMap")
map.setCenter(item.getLatLng())
}
}
Because I've got a click handler for the entire map, it runs whenever you click on it. However, it only passes event data when you click on one of the markers. Either the center marker (St. Louis) or New Orleans. This feels a bit.... fuzzy to me. I should probably check to see if item is an instance of a marker object. But the important part is the setCenter call. I run this on the map object returned by getMapObject. You can get fancy if you want to - change setCenter to panTo and you get a nice animation. View the Demo
Here is the complete code for that example:
<html>
<head>
<script>
function clickHandler(item){
if (item) {
var map = ColdFusion.Map.getMapObject("mainMap")
// map.setCenter(item.getLatLng())
map.panTo(item.getLatLng())
}
}
function init(){
ColdFusion.Map.addEvent("mainMap", "click", clickHandler)
}
</script>
</head>
<body>
<cfmap centeraddress="St. Louis, MO" width="400" height="400" name="mainMap">
<cfmapitem address="New Orleans, LA">
</cfmap>
</body>
</html>
<cfset ajaxOnLoad("init")>
Ok, that works, but is a bit simplistic. I wanted to step it up a notch and add more interactivity. For my second demo, I created a list of cities. These cities were used as map markers, but I also wanted a simple HTML list as well. Clicking on the text version of the city would center the map. (I skipped making the marker work as well since I had just done that.) To begin, I created my list of cities.
<cfset addresses = ["New Orleans, LA", "San Francisco, CA", "Washington, D.C.", "Houston, TX", "Miami, FL"]>
Next I wrote the code to generate the map and HTML list:
<cfmap centeraddress="St. Louis, MO" width="400" height="400" name="mainMap" showcentermarker="false">
<cfloop index="address" array="#addresses#">
<cfmapitem address="#address#">
</cfloop>
</cfmap>
<p/>
<cfloop index="address" array="#addresses#">
<cfoutput>
<a class="addressDisplay" href="javascript:center('#jsStringFormat(address)#')">#address#</a>
</cfoutput>
</cfloop>
Notice that for each address I'm calling a JavaScript function center. Let's take a look at that:
function center(s){
ColdFusion.Map.setCenter('mainMap',{
address: s
})
}
Yeah - pretty simple, right? Unfortunately, something goes wonky when you run this. The first time I ran my code, I ended up with new, green markers on the locations. This was before I added showcentermarker="false" to my map. I clicked a bunch of cities and each time it simply added green markers. It never actually centered.

When I added showcentermarker="false", it did absolutely nothing. You can see this yourself here. I'm guessing it is just a bug.
I decided to switch to the code I used in the first code sample. But to do that - I needed longitude and latitude. Normally I'd recommend always using those values (see my previous article on this linked to below), but I only had string addresses for now. I was lucky though to find a quick solution on the Google Maps API reference. They had this very simple example of using the Geolocation API:
function center(address){
var geocoder = new GClientGeocoder();
geocoder.getLatLng(
address,
function(point) {
if (!point) {
alert(address + " not found");
} else {
map = ColdFusion.Map.getMapObject("mainMap")
map.setCenter(point);
}
}
)
}
Now the code creates a new instance of GClientGeocoder and runs getLatLng on the address. Notice the use of the inline function to handle the result and set the center. You can see this demo here: View the Demo
And here is the complete script:
<cfset addresses = ["New Orleans, LA", "San Francisco, CA", "Washington, D.C.", "Houston, TX", "Miami, FL"]>
<html>
<head>
<script>
function center(address){
var geocoder = new GClientGeocoder();
geocoder.getLatLng(
address,
function(point) {
if (!point) {
alert(address + " not found");
} else {
map = ColdFusion.Map.getMapObject("mainMap")
map.setCenter(point);
}
}
)
}
</script>
<style>
.addressDisplay {
display: block;
background-color: #e0e0e0;
padding: 10px;
margin: 10px;
width:140px;
}
</style>
</head>
<body>
<cfmap centeraddress="St. Louis, MO" width="400" height="400" name="mainMap" showcentermarker="false">
<cfloop index="address" array="#addresses#">
<cfmapitem address="#address#">
</cfloop>
</cfmap>
<p/>
<cfloop index="address" array="#addresses#">
<cfoutput>
<a class="addressDisplay" href="javascript:center('#jsStringFormat(address)#')">#address#</a>
</cfoutput>
</cfloop>
</body>
</html>
And finally, I decided to get crazy. I knew that Google's Streetview stuff was part of the main API. What I didn't know was how freaking easy it was to use.
panoramaOptions = { latlng:point };
var myPano = new GStreetviewPanorama(document.getElementById("streetView"), panoramaOptions);
In this snippet, point is a valid longitude/latitude pair. The call to GStreetViewPanorama simply creates the streetview within a DOM item. Now, my addresses are pretty vague so this demo ain't great, but you can imagine how cool this would be for your business if you supplied some fuller data points. View the Uber Elite Awesome Demo
Pretty cool, right?

Archived Comments
This is awesome and great to know that using Google Streetview with CFMap is so easy. Thanks for posting Ray!
Thats very cool.
Does CFMap support driving directions?
Nope. But you could do it manually getting the map object via the provided API.
I have a proof of concept done. Blog post in an hour.
Enjoy: http://www.coldfusionjedi.c...
I've found that when you use either the mouse wheel or double click to zoom on the map, the cfmapitems are not redrawn... just a blank map. If you use the map zoom control it works, but who actually uses that? Double click seems like second nature to zoom. Any ideas how to fix this?
For an example: http://www.coldfusionjedi.c...
Use your mouse wheel or double click... the map icons disappear.
I can confirm it does that. Please file a bug report for that: http://cfbugs.adobe.com/cfb...
This was helpful to get me started. I'd love to see something that shows how to integrate the Map API with a control such as cfgrid.
For instance, I've got both a cfgrid and a cfmap on my page. Both are populated with the same records (each record returned in the cfgrid has a lat/long that is displayed on the cfmap).
I can't quite figure out how to do something like hover over a row in the cfgrid and make the map center to the corresponding point... or just temporarily change the color of the icon on the map.
To complicate it, my cfgrid is inside a <cfform> and uses the href, hrefkey, and selectmode='row' attributes. Meaning, a URL is launched when one single clicks on a row. So I can't use the rowclick event of the grid. ugh.... stuck...
Couldn't you simply use cfajaxproxy to bind to the cfgrid? I've got a few blog entries up on that.
Hmm.. Wish I knew. I'll go through your posts, if I can figure out where to start. I'm not sure just yet how to approach it since I'm talking about having the icon dynamically change, etc. But maybe your posts will help me understand the basics.
Ok, feel free to follow up with me via my Contact link.
Here I am late again. Thankyou so much for this detailed explanation and samples. One question, Will this work on CF8 as well, as my host is CF8 still. I am using the CF9 developer for tests though.
cfmap was added in CF9 so no. However, it is not hard to use Google Maps "natively". It is about 5 minutes more work.
This is very cool. I can get it to work perfectly in a stand alone page, but I am having problems displaying the street view in a cflayoutarea. Clicking on the link freezes the browser. Any ideas? Thanks!
It could be a conflict between the JS code used to generate cflayoutarea and the Google stuff. That's the best I can think of now.
how can i update this code from V2 to V3? i'm having problem since v2 was deprecated... please help.
function buildMap()
{
// figure out where service area is.
var geocoder = new GClientGeocoder();
<cfoutput>var zip="#zip#"</cfoutput>
geocoder.getLatLng(zip,
function (point)
{
// create a map
map = new GMap2(mapdiv, {draggableCursor:"auto", draggingCursor:"move"});
// Add a div element for toolips
tooltip = document.createElement("div");
tooltip.className="tooltip";
map.getPane(G_MAP_MARKER_PANE).appendChild(tooltip);
if (!point)
{
alert ("No zip code entered, so providing large scale map.");
map.setCenter(new GLatLng(39,-97), 4); // middle of the country
}
else
map.setCenter(point,11)
map.addControl(new GLargeMapControl()); // Zoom control
map.addMapType(G_PHYSICAL_MAP);
// Create a hierarchical map type control
var hierarchy = new GHierarchicalMapTypeControl();
// make Hybrid the Satellite default
hierarchy.addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", true);
// add the control to the map
map.addControl(hierarchy);
map.addControl(new GScaleControl()); // Scale bar
map.disableDoubleClickZoom();
// Add click listener
GEvent.addListener(map, "click", leftClick);
// read in points for stored polygon
for (var pers=1; pers<4;pers++) // for each perimeter
{
read_poly(pers);
for(var n=0;n<perimeters[pers].length;n++)
leftClick ("init",perimeters[pers][n],pers,n); // produced 1st marker, but then looped in same spot
//marker = markers[0];
if (pers==1) polyColor=RGB2Color(255,0,0);
if (pers==2) polyColor=RGB2Color(0,255,0);
if (pers==3) polyColor=RGB2Color(0,0,255);
drawOverlay(perimeters[pers],polyColor); // creates a polygon from everything in markers
}
})
}
Well I'd begin by asking you which specific portion failed. If it was getX or somesuch, I'd then focus on see what I can use in the v3 API to replace that.
thanks for the reply, the whole functionality is having issue here. it was working fine until last month but now it is showing message that "Google disabled the use of v2 API" something like that.
venkat - I'm not going to rewrite the entire thing for you. What I'd recommend is starting fresh with the v3 API. Please see their getting started guide and read up a bit.
In terms of the error you are getting with CFMAP, we released an update for that. See: http://www.raymondcamden.co...