Shaun Mccran

My digital playground

15
J
A
N
2011

Dynamically adding markers to Google maps

Following on from a previous article I wrote about (Google maps panning the next step in my Google mapping project is to be able to add markers to a Google map dynamically.

This article deals with how to translate a location into a latitude and longitude using Google, and how to send and add markers from a database into a Google maps via a remote service, using AJAX and JSON.

There is a full example of the finished application here: Demo of dynamically adding markers to Google maps

Much like the previous article about Google map panning we have to load the JQuery and the Google maps API, using an API key related to our domain.

view plain print about
1<s/cript type="text/javascript"
2src="http://www.google.com/jsapi?key= your API key here">

3</script>
4
5<s/cript type="text/javascript">
6    google.load("jquery", '1.3');
7    google.load("maps", "2.x");
8</script>

Next we will pick a point on the map, and set it as the centre, with a predefined zoom level. Below that we are creating references to the Google API 'bounds' object, and the 'Geocoder' object. I am also creating an Array of response codes for remote service responses.

Next the getJSON() JQuery method fires on page load. This communicates with our remote service, in this case a CFC (ColdFusion) and looks for a function called 'listpoints'. I'll drop the code for that in later, but it returns a JSON object of data that contains the name of a location and its lat-long values.

view plain print about
1<s/cript type="text/javascript">
2    $(document).ready(function(){
3
4    // set the element 'map' as container
5    var map = new GMap2($("#map").get(0));
6
7    // set the lat long for the center point
8    var cheltenham = new GLatLng(51.89487062921521,-2.084484100341797);    
9    // value 1 is the center, value 2 is the zoom level
10    map.setCenter(cheltenham, 9);
11
12    var bounds = new GLatLngBounds();
13    var geo = new GClientGeocoder();
14
15    // status codes
16    var reasons=[];
17        reasons[G_GEO_SUCCESS] = "Success";
18        reasons[G_GEO_MISSING_ADDRESS] = "Missing Address";
19        reasons[G_GEO_UNKNOWN_ADDRESS] = "Unknown Address.";
20        reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address";
21        reasons[G_GEO_BAD_KEY] = "Bad API Key";
22        reasons[G_GEO_TOO_MANY_QUERIES] = "Too Many Queries";
23        reasons[G_GEO_SERVER_ERROR] = "Server error";
24
25    // initial load points
26    $.getJSON("map-service.cfc?method=listpoints", function(json) {
27        if (json.Locations.length >
0) {
28            for (i=0; i<json.Locations.length; i++) {
29                var location = json.Locations[i];
30                addLocation(location);
31                }
32            zoomToBounds();
33            }
34        });

The page display itself is very small, there are only three elements, the 'map' div, where our map function will be inserted, a 'list', which will populated with a list of our locations and a 'form' to allow a user to submit new locations.

view plain print about
1<div id="wrapper">
2
3    <div id="map"></div>
4    <ul id="list"></ul>
5    <div id="message" style="display:none;"></div>
6
7<form id="add-point" action="map-service.cfc?method=accept" method="post">
8<input type="hidden" name="action" value="savepoint" id="action">
9
10        <fieldset>
11            <legend>Add a Point to the Map</legend>
12            <div class="error" style="display:none;"></div>
13            <div class="input">
14            <label for="name">Location Name</label>
15            <input type="text" name="name" id="name" value="">
16            </div>
17
18            <div class="input">
19            <label for="address">Address</label>
20            <input type="text" name="address" id="address" value="">
21            </div>
22
23            <button type="submit">Add Point</button>
24        </fieldset>
25</form>
26
27</div>

All the data in my example is stored in a mySQL database. The script to create it is below. There are only a few fields, and they are reasonably easy to recognise.

view plain print about
1CREATE TABLE `locations` (
2 `intId` int(11) NOT NULL AUTO_INCREMENT,
3 `varName` varchar(100) DEFAULT NULL,
4 `varLat` varchar(25) DEFAULT NULL,
5 `varLong` varchar(25) DEFAULT NULL,
6 `varAddress` varchar(55) DEFAULT NULL,
7 PRIMARY KEY (`intId`)
8) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;

At this point if you were build this app you would have a page displaying a map and a series of map points from a database (if you had some in the table).

The next few code snippets are the parts that accept the form values, and translate them into useful map data to store then display them.

A user will enter the place label and location, this is then geo coded and sent to the remote service to be saved in our database. Here we can perform any validation and actually store the data. The same service returns the newly saved data to our map and we dynamically add the new marker to the map.

The first new function is a JQuery selector triggered when a user submits the form. It simply fires the geoEncode() function below it. The geoCode function tries to lookup the location the user entered from the form. If there is an error the corresponding message is displayed, otherwise the savePoint() function fires, using the location values.

view plain print about
1$("#add-point").submit(function(){
2    geoEncode();
3    return false;
4});
5
6function geoEncode() {
7var address = $("#add-point input[name=address]").val();
8    geo.getLocations(address, function (result){
9    if (result.Status.code == G_GEO_SUCCESS) {
10    geocode = result.Placemark[0].Point.coordinates;
11    savePoint(geocode);
12            } else {
13        var reason="Code "+result.Status.code;
14    if (reasons[result.Status.code]) {
15        reason = reasons[result.Status.code]
16                }
17    $("#add-point .error").html(reason).fadeIn();
18        geocode = false;
19            }
20        });
21    }

The savePoint() function serialises the inputs from the form and performs the Geo encoding. Now we try and post the form. If it fails we show a message, otherwise we run the addLocation() and zoomToBounds() functions. (almost there, stick with it!).

view plain print about
1function savePoint(geocode) {
2    var data = $("#add-point :input").serializeArray();
3     data[data.length] = { name: "lng", value: geocode[0] };
4     data[data.length] = { name: "lat", value: geocode[1] };
5
6    $.post($("#add-point").attr('action'), data, function(json){
7        $("#add-point .error").fadeOut();
8
9        if (json.status == "fail") {
10            $("#add-point .error").html(json.message).fadeIn();
11            }
12        if (json.status == "success") {
13            $("#add-point :input[name!=action]").val("");
14                var location = json.data;
15                addLocation(location);
16                zoomToBounds();
17                }
18            }, "json");
19        }

The addLocation() function below creates a map marker object and adds it to the map. It also adds the new location to the 'list' we created in the display layer above.

The zoomToBounds() function zooms to the new marker, and sets the center of the map on it.

The last function is a click event that pans the map to the marker and displays the marker label when a user clicks on one of the list item locations, or the marker itself.

view plain print about
1function addLocation(location) {
2    var point = new GLatLng(location.lat, location.lng);
3    var marker = new GMarker(point);
4        map.addOverlay(marker);
5        bounds.extend(marker.getPoint());
6                    
7        $("<li />")
8        .html(location.name)
9        .click(function(){
10        showMessage(marker, location.name);
11                        })
12
13        .appendTo("#list");
14    
15            GEvent.addListener(marker, "click", function(){
16            showMessage(this, location.name);
17            });
18        }
19
20function zoomToBounds() {
21    map.setCenter(bounds.getCenter());
22    map.setZoom(7); // map.getBoundsZoomLevel(bounds)-1
23                }
24
25$("#message").appendTo( map.getPane(G_MAP_FLOAT_SHADOW_PANE) );
26
27
28function showMessage(marker, text){
29    var markerOffset = map.fromLatLngToDivPixel(marker.getPoint());
30
31        map.panTo(marker.getLatLng());
32
33    $("#message").hide().fadeIn()
34        .css({ top:markerOffset.y, left:markerOffset.x })
35        .html(text);
36        }
37    });
38</script>

The server side code used in this example is below. I have used ColdFusion to query a database and return JSON objects to the map, but you can use any server side language.

view plain print about
1<cffunction name="accept" access="remote" output="true" returntype="void" hint="handler for google maps api">
2    <cfquery datasource="database-name">
3        INSERT INTO locations
4        (varName, varlat, varlong, varAddress)
5        VALUES(
6<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.name#">,
7<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.lat#">,
8<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.lng#">,
9<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.address#">)
10    </cfquery>
11
12    <cfset response = '{"status":"success","data":{"lat":"#arguments.lat#","lng":"#arguments.lng#","name":"#arguments.name#"}}'>
13        <cfoutput>#response#</cfoutput>
14    </cffunction>
15
16<cffunction name="listpoints" access="remote" output="true" returntype="void" hint="returns data to a Google map request">
17
18    <cfquery datasource="database-name" name="qGetMarkers">
19        SELECT intId, varName, varlat, varlong, varAddress
20        FROM locations
21        ORDER BY intId
22    </cfquery>
23
24    <cfoutput>{"Locations":[<cfloop query="qGetMarkers">{"name":"#qGetMarkers.varName#","lat":"#qGetMarkers.varlat#","lng":"#qGetMarkers.varlong#"}<cfif qGetMarkers.currentRow LT qGetMarkers.recordcount>,</cfif></cfloop>]}</cfoutput>
25    </cffunction>

ColdFusion purists will note that I'm not returning the data in the most efficient JSON way, but I'm running this code on ColdFusion server 7, so I can't specify a returnformat of JSON.

The next (and last) article in this series will bring AJAX polling, map panning and dynamically adding markers together to create the finished dynamic Google map application.

There is a full example of the finished application here: Demo of dynamically adding markers to Google maps

Related Blog Entries

TweetBacks
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Robert Varga's Gravatar If you need to add multiple markers on Google maps, and you are scared from all the code you see on that page .), give a try this service :
http://www.mapsofall.com/map-generator/ - you can add as many markers (addresses) as possible, and map will center and zoom itself. Then you can paste it to your contacts page. How simple!
# Posted By Robert Varga | 09/11/2012 06:13
Tài's Gravatar Dear Shaun McCran
I'm in Vietnam, and i'm a student also
Now in my university, i'm codeing my project about the google map api!
And i search in the internet, and i found this page belong to you!
I have the trouble with this, i read and i see but i can not actualize again...
I have the trouble about the json, i can not understand it, about the ajax bla bla...
I think i very happy if you would like to send me this code... to help me completed this project..
you think you can send me all this code please.!
Thank you for your reading
# Posted By Tài | 11/11/2012 19:36
Shaun McCran's Gravatar @Robert,

Thanks for the link. That is handy if you just want to generate a map with a set of markers on it, but what if I want to have a more dynamic map that updates with user input? You really need control of the data feed at a local level to ensure that you can edit the pins to suit.

Nice site though.

@Tai

What seems to be the problem? Are you looking for a downloadable version of the code? If you view the demo (http://www.mccran.co.uk/examples/maps/recording.cf...) you can just view the source and see how it fits together.

If it doesn't work out then email me, and I'll send you a zip file of all the code. You'll still need to build the database and deploy it, for it to work though.
# Posted By Shaun McCran | 19/11/2012 04:31
Rizwan Mo's Gravatar i am junior web developer i want to addGoogle map user input marker it using php. i am not understand json. please let me know how can add with php mysql with Database code. ASAP
# Posted By Rizwan Mo | 23/11/2012 02:13
Shaun McCran's Gravatar Hi Rizwan,

PHP performs much the same function as ColdFusion (server side scripting and database connectivity) so there shouldn't be much difference in the general flow of the script.

Understanding JSON (JavaScript Object Notation) is quite key to using AJAX functions and passing data around (server to client and client to client functions). I'd read up in here: http://www.w3schools.com/json/default.asp

The example here uses a mysql database, so this is easily do-able with the technology you are using. I'd go through the Coldfusion and replace each part with the PHP equivalent. The key is generating the JSON from a query.
# Posted By Shaun McCran | 23/11/2012 02:25
Rizwan Mo's Gravatar i tried so many time but i couldn't define php founction to ColdFusion please can you send me the php mysql code replcing ColdFusion
# Posted By Rizwan Mo | 23/11/2012 02:43
Shaun McCran's Gravatar Hi Rizwan

I'm sorry but I don't have any serious knowledge of PHP. I could probably pull it apart and convert it, but I think it might be worth you examining the Coldfusion to see if it makes sense to you. You may find you like it!
# Posted By Shaun McCran | 23/11/2012 05:14
Rizwan Mo's Gravatar Hi Shaun
i was tried to code but it is not working can you send me the full source code to my email
# Posted By Rizwan Mo | 29/11/2012 21:12
Rizwan Mo's Gravatar Hi shaun
Your Map is not loading... can you send zip file above script
# Posted By Rizwan Mo | 30/11/2012 02:19
Lindsa's Gravatar We are always looking for this type of covered bridge burns park info and the details are mostly using to http://www.dissertationworld.biz/ develop the website. Every website is too informative and too intellectual to know about this.
# Posted By Lindsa | 01/11/2015 22:39
Commercial Plumbing's Gravatar We are always looking for this type of covered bridge burns park info and the details are mostly using to
# Posted By Commercial Plumbing | 23/11/2015 03:43
the venus factor diet youtube's Gravatar We are always looking for this type of covered bridge burns park info and the details are mostly using to
# Posted By the venus factor diet youtube | 24/11/2015 03:55
Kelowna Siding Contractor's Gravatar We are always looking for this type of covered bridge burns park info and the details are mostly using to
# Posted By Kelowna Siding Contractor | 26/11/2015 03:31
tncc course's Gravatar Rides are offered through December 28, and the hot chocolate and whipped cream is served...
# Posted By tncc course | 28/11/2015 03:47
Check Out Your URL's Gravatar I just thought it may be an idea to post in case anyone else was having problems researching but I am a little unsure if I am allowed to put names and addresses on here.
# Posted By Check Out Your URL | 13/12/2015 22:19
Xareltos Side Effects Runied your Life?'s Gravatar I'm able to bookmark your site and show the kids check out up here generally. I m fairly positive there likely to be informed a great deal of new stuff here than anyone.
# Posted By Xareltos Side Effects Runied your Life? | 19/12/2015 02:21
dedicated server reseller's Gravatar That is handy if you just want to generate a map with a set of markers on it, but what if I want to have a more dynamic map that updates with user input? You really need control of the data feed at a local level to ensure that you can edit the pins to suit.
# Posted By dedicated server reseller | 19/12/2015 23:21
improve Amazon seo's Gravatar f you subscribe, any new posts to this thread will be sent to your email address.
# Posted By improve Amazon seo | 21/12/2015 00:08
Assistant Nj's Gravatar Great Post This is also a very good post which I really enjoyed reading. It is not everyday that I have the possibility to see something like this..Thank You!
# Posted By Assistant Nj | 21/12/2015 02:42
los angeles tax lawyers's Gravatar This type of message always inspiring and I prefer to read quality content, so happy to find good place to many here in the post, the writing is just great, thanks for the post
# Posted By los angeles tax lawyers | 22/12/2015 04:28
research paper for sell's Gravatar Firstly create a CurrencyFormatter object, and specify the currency symbol, and any other formatting parameters that you require.
# Posted By research paper for sell | 26/12/2015 02:20
lipoflavonoid's Gravatar This blog is so nice to me. I will continue to come here again and again. Visit my link as well. Good luck...
# Posted By lipoflavonoid | 26/12/2015 05:38
supreme garcinia cambogia reviews's Gravatar Google map panning we have to load the JQuery and the Google maps API, using an API key related to our domain.
# Posted By supreme garcinia cambogia reviews | 27/12/2015 05:37
this link's Gravatar Good to become visiting your weblog again, it has been months for me. Nicely this article that i've been waited for so long.
# Posted By this link | 28/12/2015 02:00
get more Information's Gravatar I am agreeing with you this post is really informative for everyone. I want to say Arthur have shared Great and informative stuff. thanks for sharing this wonderful post.
# Posted By get more Information | 28/12/2015 07:00
check this out's Gravatar This protection capability created by the Total Hi-Perf 4T Super Plus is made such that measured for consistency lubrication in every component of the machine.
# Posted By check this out | 28/12/2015 23:19
this link's Gravatar Your site and show the kids check out up here generally. I m fairly positive there likely to be informed a great deal of new stuff here than anyone.
# Posted By this link | 29/12/2015 01:42
adult traffic's Gravatar I’m now working with WordPress for a couple of with this blogs and forums nonetheless wanting to switch one of them over to your stand akin to you for a trial offer perform.
# Posted By adult traffic | 04/01/2016 23:27
taxi from delhi to jaipur's Gravatar WordPress for a couple of with this blogs and forums nonetheless wanting to switch one of them over to your stand akin to you for a trial offer perform.
# Posted By taxi from delhi to jaipur | 05/01/2016 02:18
amazon product research help's Gravatar I have feel that this blog is really have all those quality that qualify a blog to be a one.I wanted to leave a little comment to support you and wish you a good continuation.
# Posted By amazon product research help | 09/01/2016 01:02
Back to top