Dynamic google map markers via simple jSon file

27 Jun 2011

After making my previous post on how to simply store comments in a jSon file (and the comment from Brian) I decided to write an additional HOWTO create dynamic markers and store them in a jSon file.

It also includes an automatic geo data lockup via Google GeoCoder and generates a detail html file for-each marker created, which then will be loaded dynamic when the marker is clicked.

Check out the working example or download the code.

So lets get started with the code. First we need to add jQuery and the Google map API:

1
2
3
4
5
<!-- Include jQuery lib for AJAX requests -->
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script> 
 
<!-- Include google map lib -->
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

The HTML code is very basic and contains a Title, Input field for new markers and the placeholder for the map. Make sure to add “loadMap()” to the html body.

1
2
3
4
5
6
<!-- Input field for new markers --> 
<input id="newMarker" type="text" /> 
<input onclick="newMarker();" type="button" value="add city" /> <br class="clear" /> 
 
<!-- Map Placeholder -->
<div id="myMap"></div>

So lets have a look at the JavaScript, first we set some global variables like data path zoom level etc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// data file with markers (could also be a PHP file for dynamic markers)
var newDate = new Date;
var markerFile = 'markers.json?time=' + newDate.getTime();
 
// set default map properties
var defaultLatlng = new google.maps.LatLng(49.00,10.00);
 
// zoom level of the map
var defaultZoom = 2;
 
// variable for map
var map;
 
// variable for marker info window
var infowindow
 
// List with all marker to check if exist
var markerList = {};
 
// set error handler for jQuery AJAX requests
$.ajaxSetup({"error":function(XMLHttpRequest,textStatus, errorThrown) {
	alert(textStatus);
	alert(errorThrown);
	alert(XMLHttpRequest.responseText);
}});
 
// option for google map object
var myOptions = {
	zoom: defaultZoom,
	center: defaultLatlng,
	mapTypeId: google.maps.MapTypeId.HYBRID
}

On page load loadMap is fired which which loads the google map to the myMap DIV, and then fires loadMarkers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Load Map
 */
function loadMap(){
 
	// create new map make sure a DIV with id myMap exist on page
	map = new google.maps.Map(document.getElementById("myMap"), myOptions);
 
	// create new info window for marker detail pop-up
	infowindow = new google.maps.InfoWindow();
 
	// load markers
	loadMarkers();
}

Now we need the markers, loadMarkers loads the existing markers via ajax request directly from the data.json fiel (could of course also be a dynamic PHP files which generates jSon).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Load markers via ajax request from server
 */
function loadMarkers(){
 
	// load marker jSon data
	$.getJSON(markerFile, function(data) {
 
		// loop all the markers
		$.each(data.markers, function(i,item){
 
			// add marker to map
			loadMarker(item);
 
		});
	});
}

Once the markers are loaded they somehow have to go on the map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * Load marker to map
 */
function loadMarker(markerData){
 
	// create new marker location
	var myLatlng = new google.maps.LatLng(markerData['lat'],markerData['long']);
 
	// create new marker
	var marker = new google.maps.Marker({
	    id: markerData['id'],
	    map: map,
	    title: markerData['name'] ,
	    position: myLatlng
	});
 
	// add marker to list used later to get content and additional marker information
	markerList[marker.id] = marker;
 
	// add event listener when marker is clicked
	// currently the marker data contain a dataurl field this can of course be done different
	google.maps.event.addListener(marker, 'click', function() {
 
		// show marker when clicked
		showMarker(marker.id);
 
	});
 
	// add event when marker window is closed to reset map location
	google.maps.event.addListener(infowindow,'closeclick', function() {
		map.setCenter(defaultLatlng);
		map.setZoom(defaultZoom);
	});
}

Each marker has an onclick event which loads the detail marker html file into a google infowindow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Show marker info window
 */
function showMarker(markerId){
 
	// get marker information from marker list
	var marker = markerList[markerId];
 
	// check if marker was found
	if( marker ){
		// get marker detail information from server
		$.get( 'data/' + marker.id + '.html' , function(data) {
			// show marker window
			infowindow.setContent(data);
			infowindow.open(map,marker);
		});
	}else{
		alert('Error marker not found: ' + markerId);
	}
}

Here the logic for lockup new markers and post them via AJAX jSon request to the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 /**
 * Adds new marker to list
 */
function newMarker(){
 
	// get new city name
	var markerAddress = $('#newMarker').val();
 
	// create new geocoder for dynamic map lookup
	var geocoder = new google.maps.Geocoder();
 
	geocoder.geocode( { 'address': markerAddress}, function(results, status) {
 
		// check response status
		if (status == google.maps.GeocoderStatus.OK) {
 
			// Fire Google Goal
			_gaq.push(['_trackPageview', '/tracking/marker-submit']);
 
			// set new maker id via timestamp
			var newDate = new Date;
			var markerId = newDate.getTime();
 
			// get name of creator
			var markerCreator = prompt("Please enter your name","");
 
			// create new marker data object
			var markerData = {
				'id': markerId,
				'lat': results[0].geometry.location.lat(),
				'long': results[0].geometry.location.lng(),
				'creator': markerCreator,
				'name': markerAddress,
			};
 
			// save new marker request to server
			$.ajax({
				type: 'POST',
				url: "data.php",
				data: {
					marker: markerData
				},
				dataType: 'json',
				async: false,
				success: function(result){
					// add marker to map
					loadMarker(result);
 
					// show marker detail
					showMarker(result['id']);
				}
			});
 
		}else if( status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT){
			alert("Marker not found:" + status);
		}
	});
}

The PHP code to save the marker is pretty much the same as in my previous post, and returns the newly create marker data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* get markes from file */
$dataPath = '/var/www/gmap/data/';
$markerDataFile = 'markers.json';
$markerText = file_get_contents($markerDataFile);
 
/* create array list from markers */
$markerList = json_decode($markerText,true);
 
/* check if new marker is posted */
if( !empty($_POST['marker'])  ){
 
        /* get new marker data */
        $markerData =  $_POST['marker'];
 
        /* add additional marker information */
        $markerData['ip'] = $_SERVER['REMOTE_ADDR'];
        $markerData['created'] = time();
 
        /*  create detail marker content file */
        $markerContent = "<h2>" . $markerData['creator'] . "-> " . $markerData['name'] . "</h2>";
        $markerContent .= "<p>" . date("D M j G:i:s T Y") . "</p>";
 
        /* save marker file to server */
        $markerFile = $dataPath . $markerData['id'] . ".html";
        file_put_contents($markerFile  , $markerContent);
 
        /* add new marker to existing list */
        $markerList['markers'][] = $markerData;
 
        /* convert comments to string */
        $markerText = json_encode($markerList);
 
        /* save comment to file */
        file_put_contents($markerDataFile, $markerText);
 
        /* return newly created marker */
        echo json_encode($markerData);
}else{
        echo "Invalid request";
}

This should give a good start on how to dynamically add and display markers via Google map.

Check out the working example or download the code.

8 Comments »

  1. Bravo on your great work on this. Here is more specific detail about what I am trying to do.

    I use a mobile app called Instamapper http://instamapper.com that can run on any mobile device and upload gps data to their server at specified intervals. In my case I am doing this for long bike rides but it would be good for any type of travel where there was wireless data service available. I had aleady been generating google maps of my routes using service from http://GPSVisualizer.com and generating tracks and or markers from the automatically uploaded tracking information from the data feed from instamapper. An example of this can be viewed at http://theregoesbrian.com . What I would like to add to this is the ability to create additional markers on the map that I create from the browser of my phone while moving along my trip bu having a php form that would have text fields that I could enter a label and comment for a marker and then when the script wrote the data to the text file it would also add data from the instamapper feed such as longitude and latitude to finish off the row of data to create the markers from since that location data would be accurate at the most recent time of sync with the server. So essentially doing what you have done only adding this other source of data. Once this is setup one of the form fields can be an image link that I can create from a photo uploaded from my phone to my dropbox account since it’s easy to do it that way. Example map is using a google docs spreadsheet as the data source for the markers since I could do that manually to create the example.

  2. In this case, i would keep using http://instamapper.com (it’s a great application i use it as well), to show the route and the full path of the trip.

    If you want to add custom markers i would use twitter. There you can add your current location, images and a description of what’s happening (or any other mobile app which allows you to post you current location).

    To then generate the map you combine the two data files:

    1. instamapper data for the route
    2. twitter api data for custom marker

    Great idea, would be interesting to build an example on how to combine instamapper and twitter into one custom map.

    Hope you got my point and will look into it once i have a bit more time.

  3. Twitter sounds like an interesting approach to integrate photo, location and message if i could setup a feed with the data i wanted. searching for that I haven’t found anything that is within my capabilities. Do you know of any tutorials on that? The data that I need is right there by accessing http://www.instamapper.com/api?action=getPositions&key=4964919593082806954&num=1&format=json

    and your other post: http://blog.sofasurfer.ch/2011/03/04/php-simple-comments-readwrite-json-data-to-text-file/

    looks like it could use that date to include in the text file it creates.

  4. its nice
    but have you consider this https://developers.google.com/maps/articles/toomanymarkers ?

    anyway thanks for the code :)

  5. I am trying to develop some module with joomla cms. My requirement is similar to what I see here. I would like to know in my development I already use to read data from other available user inputs i just need to load them on some interval.

    so do you have some example where we can se calling json file on some interval to fetch data.

  6. Hey guy, GREAT WORK !

    I’m with problems in my code, using ajax and php and I need some help with my problem.

    If you could help me, check this stackoverflow page:

    http://stackoverflow.com/questions/11651690/load-markers-from-a-database-mysql-on-google-maps-v3#comment15551116_11651690

  7. I have a question that maybe you can help with. I am new to javascript and amatuer at PHP/MySQL. I have a map where I get markers from mySql and draw them on the map. If you click the marker, it pops up an info window with limited info. I would like to add a side bar with more detailed info when the marker is clicked, but I don’t want to download all the data for all the markers on first render.

    So for example: I load 100 markers with three criteria: name, status, & color. I loaded three hundred pieces of info on initial load. When you click the marker, there would be a sidebar on the side of the map with more detailed info for that marker alone. So I click on marker and see: name, status, color, data added, lease owner, lat, lon, slant, etc. I don’t want to load everything on initial render. Trying to keep it lean.

    Do you know how you would accomplish something like this? I’m thinking I need to add the click listener, but I don’t know how to have the click go back and retrieve more info on that specific marker.

    Thank you so much if you can help!
    Jeff P.

  8. If you look at my code you will see:

    // add marker to list used later to get content and additional marker information
    markerList[marker.id] = marker;

    // add event listener when marker is clicked
    // currently the marker data contain a dataurl field this can of course be done different
    google.maps.event.addListener(marker, 'click', function() {

    // show marker when clicked
    showMarker(marker.id);

    });

    This hits the event when the marker is clicked and all you need there is the ID/ref of the data.

    Then you can build what ever function to load additional data:

    /**
    * Show marker info window
    */
    function showMarker(markerId){

    // get marker information from marker list
    var marker = markerList[markerId];

    // check if marker was found
    if( marker ){

    // get marker detail information from server
    $.get( dataRoot + 'data/' + marker.id + '.html' , function(data) {
    // show marker window
    infowindow.setContent(data);
    infowindow.open(map,marker);
    });
    }else{
    alert('Error marker not found: ' + markerId);
    }
    }

RSS feed for comments on this post.

Leave a comment

Switch to our mobile site