Posted in ColdFusion | Posted on 12-15-2009 | 4,887 views
About a few months ago a user contacted me with an odd issue with CFMAP. At random times the markers on the map refused to load. Actually they returned the rather odd error that the address didn't exist. Here is an example:

When I tested this myself I saw the same - even when I replaced the test addresses with ones I knew existed (like my own!). Unfortunately I wasn't able to help the user so I wished him luck and forgot about it.
A few days ago I got another email from someone experiencing the exact same issue. This time I decided to dig a bit deeper. I opened up Firebug and took a look at the requests. I noticed something odd. When the code made a geocoding request (ie, translate address so and so to longitude and latitude x and y), the result was different for addresses that failed to work. The result code was 620. After a quick Google search I found this list of geocode status results and bam - I found what 620 meant:
The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly.
If you look at the code ColdFusion generates for the map you can see that all of the geocode requests are run immediately. This then makes sense why the error would occur. You can see this yourself with the following code:
2"1802 San Pedro Avenue, Victoria BC",
3"1804 San Pedro Avenue, Victoria BC",
4"1810 San Pedro Avenue, Victoria BC",
5"1816 Feltham, Victoria BC",
6"1821 San Pedro Avenue, Victoria BC",
7"1823 San Pedro Avenue, Victoria BC",
8"1826 Feltham, Victoria BC",
9"1835 San Pedro Avenue, Victoria BC",
10"1837 San Pedro Avenue, Victoria BC",
11"1883 San Juan, Victoria BC",
12"1888 Feltham, Victoria BC",
13"1904 San Juan, Victoria BC",
14"1909 San Juan, Victoria BC",
15"1913 San Pedro Avenue, Victoria BC",
16"1921 San Fernando Pl, Victoria BC",
17"1972 San Rafael Cres, Victoria BC",
18"4090 San Capri Terrace, Victoria BC",
19"4098 San Capri Terrace, Victoria BC",
20"4101 San Capri Terrace, Victoria BC",
21"4110 Cortez Place, Victoria BC",
22"4120 Gordon Head Rd, Victoria BC",
23"4123 Cortez Ct, Victoria BC",
24"4151 San Mateo Place, Victoria BC"
25]>
26
27<cfmap centeraddress="1802 San Pedro Avenue, Victoria BC" zoomlevel="15" height="600" width="800">
28 <cfloop index="m" array="#addresses#">
29 <cfmapitem address="#m#">
30 </cfloop>
31</cfmap>
Run this a few times and you will get errors on random addresses. Ok, so now that we know the bug involves geocoding, how can we fix it? Well one way would be to geocode the addresses ourselves in a separate process. I've got a CFC for that (Google Geocode) but it too will return an error if you try to geocode too many addresses at once.
The following code runs through the addresses and caches the result in the application scope. I'd probably recommend storing the geocodes in a database instead. Also note the use of sleep(). The number there (500 ms) was a pure guess but it seemed to work fine. If you wanted to be 100% rock solid, you could use a scheduled task to geocode addresses in the background. If an address fails one time it will (probably) get geocoded the next time.
Anyway, I hope this helps and I'm certainly glad I solved this mysery! (Note, in the code sample below I left off the array of addresses. They are the same as the code block above.)
2 <cfset application.longlat = {}>
3</cfif>
4
5<cfset geo = createObject("component", "googlegeocode")>
6<cfset key = "your google maps key goes here">
7<cfloop index="m" array="#addresses#">
8 <cfif not structKeyExists(application.longlat, m)>
9 <cfset result = geo.geocode(key,trim(m))>
10 <cfset sleep(500)>
11 <cfset application.longlat[m] = {longitude=result.longitude, latitude=result.latitude}>
12 </cfif>
13</cfloop>
14
15<cfmap centeraddress="1802 San Pedro Avenue, Victoria BC" zoomlevel="15" height="600" width="800">
16 <cfloop index="x" from="1" to="#arrayLen(addresses)#">
17 <cfset m = application.longlat[addresses[x]]>
18 <cfmapitem address="#m#">
19 <cfmapitem latitude="#m.latitude#" longitude="#m.longitude#" tip="#addresses[x]#">
20 </cfloop>
21</cfmap>


Genius answer - I didn't even realize the geocoding service was a separate function.
I'll apply this right away to my project.
Thanks again
Only thing I would change is a side where this would be processed. That is, I would move it from server to client side. With some nice progress bar ("Processing markers. Please wait...") it would be visually more attractive too.
With increase of number of addresses, this would keep thread hanging for considerably long period.
As for display performance on the client, I strongly recommend markers clustering. There is a nice JS library to do that (http://googlemapsapi.martinpearman.co.uk/readartic...).
But I don't know if you can integrate it with cfmap (we don't use it...). It might be a great addition to cfmap.
More info in this article :
http://www.svennerberg.com/2009/01/handling-large-...
From your example, I understood that is a case of bulk of addresses that need to be processed. As rule of thumb I always look for the way to save server resources as much as possible. One of "few" options is to move workload on client. Of course, having in mind, not to jeopardize their experience.
My only issue with the processing on the client is - you only want to do it one time. So your client needs to send the geocoded data back to the server so it can be persisted.
Sure. After processing (all) addresses, client can send coords back to server in single request....IF user stay long enough on that page. Otherwise, result can be sent back one-by-one.
Pros and cons exist on both sides, but, god didn't gave us second generation of web (aka 2.0) for nothing :))
Anyone else seeing this?
But unfortunately I think riaforge has moved the Google Geocode API from the URL http://googlegeocode.riaforge.org/ to some where else or may be they have just removed it.
Can I get it from else where
Thanks
<cfquery name="qRecentEvents" datasource="funkadelicgrjk24">
SELECT *
FROM tblbookings
WHERE tblbookings.bookingDate < now()
AND tblbookings.bookinglatitude != ""
AND tblbookings.bookinglongitude != ""
AND tblbookings.bookingPostcode != ""
ORDER BY tblbookings.bookingDate DESC
LIMIT 0,20
</cfquery>
<cfajaximport params="#{googlemapkey='ABQIAAAADjCG_E_MrtI5RR9ca6jWaBRIIrxyUpiBF3OCcOJXxijgjehLExTdVid6CC1kqmSc2bcZFQYXpVADfg'}#">
<cfmap name="gmap02"
width="600" height="350"
centeraddress="Birkenhead, Wirral, Merseyside, UK"
doubleclickzoom="false"
overview="false"
scrollwheelzoom="true"
showscale="false"
tip="My Map"
zoomlevel="9">
<cfloop query="qRecentEvents">
<cfmapitem name="#qRecentEvents.bookingID#"
address="#qRecentEvents.bookinglatitude#, #qRecentEvents.bookinglongitude#"
tip="#qRecentEvents.bookingAddressLine1#, #LSDateFormat(qRecentEvents.bookingDate, "ddd, mmmm dd, yyyy")#"/>
</cfloop>
</cfmap>
[Add Comment] [Subscribe to Comments]