|
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.
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.
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.
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.
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.
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!).
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.
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.
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
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!
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
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.
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.
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!
i was tried to code but it is not working can you send me the full source code to my email
Your Map is not loading... can you send zip file above script