jQuery and Google Maps #2: AJAX Storing and Retrieving Points

Continuing the series on jQuery and Google Maps, I will teach you how to store and retrieve points with using AJAX and a server-side language. This tutorial will use PHP/MySQL on the server, but it is basic enough that re-writing in another language should not be difficult.

View Final Demo

First, let me share with you the design-pattern behind the tutorial. My design pattern has two steps. The first is to use a simple HTML form to create new locations by posting the data to the server via AJAX. The second step is to fetch those locations from the server also via AJAX. Sound simple? Well, then lets get started…

Note: This tutorial builds on the first tutorial’s code, so all code I am showing you here will be added onto it.

Step #1: Create the, “Add Location” Form

To allow users to add locations to the map, let’s create a basic form. This will include the parts of an address, as well as the name of the location.

<form id="add-point"action="map-service.php"method="POST">
  <input type="hidden"name="action"value="savepoint"id="action">
  <fieldset>
    <legend>Add a Point to the Map</legend>
    <div class="error" style="display:none;"></div>
    <div class="input">
      <label for="name">Location Name</label>
      <input type="text"name="name"id="name"value="">
    </div>
    <div class="input">
      <label for="address">Address</label>
      <input type="text"name="address"id="address"value="">
    </div>
    <button type="submit">Add Point</button>
  </fieldset>
</form>

A couple things to note about the form:

  • The form’s action is pointed to map-service.php, which is where we will process the form data.
  • A hidden input <input type="hidden"name="action"value="savepoint"id="action"> will be used on the server to flag that we want to save a point to the database. This is just a personal preference on how to do things, there are many other ways to flag the intended action.
  • An empty div with class error <div class="error"style="display:none;"></div> is placed in the form to be used in a later step to display errors.

Step #2: Add Styles to the Form

By adding a few CSS rules to our page, we will set our form next to the map and spruce up the form a bit.

#add-point { float:left; } 
div.input { padding:3px 0; } 
label { display:block; font-size:80%; } 
input, select { width:150px; } 
button { float:right; } 
div.error { color:red; font-weight:bold; }

Step #3: Geoencode Address Before Submiting Data

3a) Override default form submit

At this point we’ll override the form’s default submit action by selecting the form $("#add-point") , then using jQuery’s submit event method . This method accepts a function that will run on submit of the form.

$("#add-point").submit(function(){ 
  geoEncode(); 
  return false; 
});

3b) Add GeoCoder

Then, inside the submit we will post the form data with AJAX using jQuery’s ajax post method .

var geo = new GClientGeocoder(); 
var reasons=[]; 
reasons[G_GEO_SUCCESS] = "Success"; 
reasons[G_GEO_MISSING_ADDRESS] = "Missing Address"; 
reasons[G_GEO_UNKNOWN_ADDRESS] = "Unknown Address."; 
reasons[G_GEO_UNAVAILABLE_ADDRESS] = "Unavailable Address"; 
reasons[G_GEO_BAD_KEY] = "Bad API Key"; 
reasons[G_GEO_TOO_MANY_QUERIES] = "Too Many Queries"; 
reasons[G_GEO_SERVER_ERROR] ="Server error";

3c) Get geocode from address

function geoEncode() { 
  var address = $("#add-point input[name=address]").val(); 
  geo.getLocations(address, function (result){ 
    if (result.Status.code == G_GEO_SUCCESS) { 
      geocode = result.Placemark[0].Point.coordinates; savePoint(geocode); 
    } else { 
      var reason="Code"+result.Status.code; 
      if (reasons[result.Status.code]) { 
        reason = reasons[result.Status.code] 
      } 
      $("#add-point .error").html(reason).fadeIn(); 
      geocode = false; 
    } 
  }); 
}

Step #4: Submit Data to Server

function savePoint(geocode) { 
  var data = $("#add-point :input").serializeArray(); 
  data[data.length] = { name:"lng", value: geocode[0] }; 
  data[data.length] = { name:"lat", value: geocode[1] }; 
  $.post($("#add-point").attr('action'), data, function(json){ 
    $("#add-point .error").fadeOut(); 
    if (json.status =="fail") { 
      $("#add-point .error").html(json.message).fadeIn(); 
    }
    if (json.status =="success") { 
      $("#add-point :input[name!=action]").val(""); 
      var location = json.data; addLocation(location); zoomToBounds(); 
    } 
  }, "json"); 
}

The $.post method accepts parameters.

  1. URL to post data to: $(this).attr('action') will get the action attribute from the form that was submitted in the previous step.
  2. Data in name, value pairs i.e. { name:"inputname", value:"inputvalue"} we will get all the inputs using the :input selector in jQuery, then use the serialize array function to turn those inputs into name, value pairs. Then add the two geocode name/value pairs to the data object.
  3. Function to run after AJAX response is received. This function has one parameter which contains the response of the AJAX request.
  4. Type of data to be returned (optional). In this case we will use JSON.

Step #5: Use PHP on the Server to Process the Form

Once the data is posted with jQuery, we can handle it on the server with PHP.

5a) Check the action and validate the name

Let’s simply check if the action variable is posted as,“savepoint”. Then validate that the name has the proper characters with a regular expression and also that it is not empty. If any data is invalid, let’s call a fail method (defined in next sub-step) with the message we want to show to the user.

<?php if ($_POST['action'] == 'savepoint') { 
  $name = $_POST['name']; 
  if(preg_match('/[^ws]/i', $name)) { 
    fail('Invalid name provided.'); 
  } 
  if(empty($name)) { 
    fail('Please enter a name.'); 
  } 
} ?>

Save the file as map-service.php or whatever you named your form’s action attribute.

5b) Output the error message as a JSON object

Our fail function will use PHP’s die method to stop the script from executing and output an error message to the client. Since the front-end (jQuery) is expecting a JSON object, we want to make sure to always send back a JSON response. To output JSON with PHP, you simply pass an array into the json encode method (json_encode is PHP 5.2+ only, if you are using less than 5.2 then use the JSON PHP library ).

function fail($message) { 
  die(json_encode(array('status' => 'fail', 'message' => $message))); 
}

For the JSON array we want to use the JSEND specification for sending back a response. Basically, you have a key/value pair of status equals success or fail. That way the response can easily be checked on the front-end. I’m deviating from the JSEND spec a little bit by only sending a string back instead of a key/value pair of messages.

Using Firebug and Firefox , we can inspect the Ajax requests easily within the browser.

You can see here I submitted the form without entering a name and it sent me back an error message in the form of JSON.

Step #6: Display the Error Messages with jQuery

Hopping back to the jQuery code, we will write the error handling.

Inside the post code, we will first use the hide method to hide the error div in case it is already displaying. Then check if json.status is showing,“fail”. If it is, we’ll place the json.message inside the error div with jQuery’s html attribute method and then fade it in with the fade in method .

$("#add-point .error").hide(); 
if (json.status =="fail") { 
  $("#add-point .error").html(json.message).fadeIn(); 
}

Step #7: Create a Database and Store the Locations

Using SQL, create a database table named locations which has a“name”,“latitude”,“longitude“and an“id“in it. If you need help with this, you will have to consult w3schools php and mysql for more help.

7a) Create the table with SQL

CREATE TABLE `locations` ( 
  `id` int(11) unsigned NOT NULL auto_increment, 
  `name` varchar(100) default NULL, 
  `lat` float(15,11) default NULL, 
  `lng` float(15,11) default NULL, 
  PRIMARY KEY (`id`) 
)

7b) Insert name and location into the database with PHP and MySQL

We will use PHP and MySQL to insert the new location into the database. Directly after, we will either flag a success or fail message to the user.

$query ="INSERT INTO locations SET name='$_POST[name]', lat='$lat', lng='$lng'"; 
$result = map_query($query); 
if ($result) { 
  success(array('lat' =>$_POST['lat'], 'lng' =>$_POST['lng'], 'name' =>$name)); 
} else { 
  fail('Failed to add point.'); 
}

If you noticed, I created a custom function called map_query to abstract out the database stuff. Here is the function definition. Make sure to update the,“MYSQL“stuff with your credentials.

function map_query($query) { 
  mysql_connect('MYSQL_HOST', 'MYSQL_USER', 'MYSQL_PASSWORD') OR die(fail('Could not connect to database.')); 
  mysql_select_db ('MYSQL_DATABASE'); return mysql_query($query); 
}

I also created a similar method to“fail“called“success“which looks like:

function success($data) { 
  die(json_encode(array('status' =>'success', 'data' =>$data))); 
}

An example of a succesful response in firebug:

Step #8: Map the New Point

Going back to the jQuery code, we can now add the success response handling. The response is a JSON object with“lat”,“lng“and“name“properties. I’ll give you the code inside the success handling, then later show you what each custom function is doing.

if (json.status =="success") { 
  $("#add-point :input[name!=action]").val(""); 
  var location = json.data; 
  addLocation(location); 
  zoomToBounds(); 
}

After a location is successfully added to the database, we want to clear the form to prevent duplicate entry. Do this by selecting the inputs with the :input selector. Then we need to filter out theaction input, do this by using the attribute not equal selector [name!=action] .

My addLocation(location) function is simply our code from the last tutorial placed into a function to be reusable later.

function addLocation(location) { 
  var point = new GLatLng(location.lat, location.lng); 
  var marker = new GMarker(point); 
  map.addOverlay(marker); 
  bounds.extend(marker.getPoint()); 
  $("<li />")
    .html(location.name) 
    .click(function(){ 
      showMessage(marker, location.name); 
    }) 
    .appendTo("#list"); 
  GEvent.addListener(marker,"click", function(){ 
    showMessage(this); 
  }); 
}

It has a few things you might want to note:

  • using location.name, location.lat and location.lng means that we will be passing in a location object with those properties to the function.
  • Ignore bounds.extend(marker.getPoint()); and zoomToBounds for now or skip to
  1. quickly to find out what they do.

Step #9: Load and Display the Locations from in the Database

When the page initially loads, we want to load all of our stored points. The simplest way to do this (in my opinion) is to do a GET request to fetch a JSON object from the server after the page loads.

To make a,“GET“request to the server, we can use jQuery’s getJson method . We will send the server a,“get“variable called action with value,“listpoints”.

$.getJSON("php/map-service.php?action=listpoints", function(json) { 
  // do stuff in step #11 
});

Step #10: Get the Locations from the Database

Simply check the,“GET“action in the PHP and run this code to fetch the locations records. Pretty straight-forward code here. We are creating an array of points and then sending them back to the client as JSON.

if ($_GET['action'] == 'listpoints') { 
  $query ="SELECT * FROM locations"; 
  $result = map_query($query); 
  $points = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { 
    array_push($points, array('name' =>$row['name'], 'lat' =>$row['lat'], 'lng' =>$row['lng'])); 
  } 
  echo json_encode(array("Locations"=>$points)); 
  exit; 
}

Step #11: Display the Locations

Iterate through the JSON object that contains the locations inside the getJson response function.

if (json.Locations.length>0) { 
  for (i=0; i<json.Locations.length; i++) { 
    var location = json.Locations[i]; 
    addLocation(location); 
  } 
  zoomToBounds(); 
}

Here we are simply looping through the Locations inside the json object and calling out addLocation method.

Step #12: Zoom Map to Show all Points

Let’s define that mysterious zoomToBounds method that I keep calling and not telling you what it does.

First, define a variable called,“bounds“above the getJson method.

var bounds = new GLatLngBounds();

Note that in step #8’s addLocation method we are extending the bounds with bounds.extend(marker.getPoint()); This takes the bounds and adds each point to it. Then we can set the map to show all the points later by using the bounds that contains each point on the map.

function zoomToBounds() { 
  map.setCenter(bounds.getCenter()); 
  map.setZoom(map.getBoundsZoomLevel(bounds)-1); 
}

This function sets the center of the map, and the zoom minus 1 to set the map’s viewport based on that bounds variable we declared and extended earlier.

Congrats! You just create a Google Maps mashup that you can add and remove points to dynamically with Ajax, PHP and MySQL.

View Final Demo

Download Code from Github

Comments

  1. Mohammed says

    Great
    I have a suggestion. Can you explain how to allow the user to click the point that he/she wants to save on the map.

  2. says

    Excellent post! I’ve been looking for something like this for awhile since I’ve been unable to make it myself. I am curious, like Mohammed, what if the map is being used for an area like a bike or canoe trail? I realize that its just geocoding that one point and having those variables stored away like the main bulk of this tutorial, but finding that one point based on a click event seems elusive to me.

    Good work though!

  3. joyce says

    thank for sharing its work well. I have try to modify but i got a question how if i wan to change the code from passing form input value to javascript variable. Is it possible? i have try this

    var finalpoint="1.234,23.552";
    var data = $("#add-point").serializeArray();
    data[data.length]={name:"sam", value:finalpoint};
    alert (data[0]);
    $.post($("#add-point").attr('action2'), data, function(json){

  4. Mini0n says

    Hi there! Nice tutorials you have here!
    Will you make some more on this subject, jQuery+Google Maps? It was very nice.

    Thanks for your explanations. =)

  5. says

    One issue – IE does not seem to understand that the li:hover applies to the list. Is this perhaps because they are added after the fact?

  6. Henry says

    is it possible to do this with a google map i already saved (its about 150 points of data that i dont wanna type in again.)

  7. Wolfgang says

    Thank you for this great tutorial !!!!
    I learned so much and wrote a Joomla sample.
    Keep on writing !

  8. says

    I am having some trouble with this. Basically when I add a new point by address or lat/lng it shows up on the map in the correct place but updates the database with 0.00000000000 for both lat and long.

    if i then refresh the page the pointer moves to 0.00000000000,0.00000000000 on the map which is obviously no good. I have tested this with IE, Chrome and Firefox and all do the same thing

    any ideas as to why this is happening?

    Cheers

  9. says

    1) Check what coordinates are being sent to the backend by inspecting the POST request with Firebug.
    2) Verify the names of the POST variables are retrieved on the backend by echoing(logging) the variables.
    3) Verify the SQL is sending out the correct column names into the database, try echoing(logging) the entire SQL statement.

  10. says

    Thank you Marc

    In the map-services.php file the lat and lng variables were not being collected from the post. I added the $_POST[] to these variables and it worked like a charm.

    Do you plan to update this with extra function like draggable pointers that update once dropped and a close button inside the message div?

    Thank you for a great piece of code, has served my purposes well

  11. Dexter says

    Great tutorial! For further development, do you plan to add on-click adding markers and dragable pointers?

  12. Daan says

    Thanks! This was exaclty what I needed. I’ve modified it to include urls so one can click on the messages and go to a website. Keep going with this stuff!

  13. Emanuele says

    Hi, I’ve the latest stable build of jQuery and I’m using you script. The problem is this:

    function showMessage(marker, text){
      var markerOffset = map2.fromLatLngToDivPixel(marker.getPoint());
      $("#message").hide().fadeIn()
        .css({ top:markerOffset.y, left:markerOffset.x })
        .html(text);
    }

    .css({ top:markerOffset.y, left:markerOffset.x }) doesn’t work like you want, in my page, the #message doesn’t “follow” my point in the map if I go down and up :(

  14. Serial says

    Hi,

    I’m having a little problem getting this to work. It won’t store the points in my database. It doesn’t seem to connect to the php file at all. Ive checked the response with firebug and it does make the point after pressing the button. But after that nothing. I used the example files and only edited the database connect stuff and the link to the php file. I’m sure the error is not in the php file.

    Any idea what it could be ? I’m testing locally.

  15. Serial says

    Just to clarify.. I’m getting nothing when checking console with firebug. Nothing happens when I press add point. But when I check the JS at net I do get a correct output.

  16. says

    in: Step #7: Create a Database and Store the Locations

    Using SQL, create a database table named locations which has a “name”, “latitude”, “longitude” and an “id” in it. If you need help with this, you will have

    This is incorrect as map-service.php in the demo is referencing “name”, “lat”, “lng” and “ip”

  17. says

    First off wicked tutorial!!!! But ran into some trouble setting up my own get data and displaying it ;
    I have set up my own way of getting the location( this by clicking on the map and it get’s the code(i have tested it and it returns the data ineed from database)…. I am trying to use the json method to display the saved data.. so i modified the index html matching the fields of my database table…
    But i get nothing

    The field i wanna call up are the following “title”, “lat” and “lng”
    i haven’t stored the ip because i don’t need it…

    i don’t need the form to enter the data because i have separated them ( so input and display seperate )

    what i am goin to do is to search in the database on the display page example: mapengo.anandsueman.com so you get an idea

    what am i doing wrong here… please help …

    this is my out from database with json ;
    Array ( ) {“Locations”:[{“title”:“Megastores”,“lat”:“52.065605”,“lng”:“4.317820”},{“title”:“some title hogeschool”,“lat”:“52.067062”,“lng”:“4.324284”},{“title”:“Bart Osso”,“lat”:“52.068172”,“lng”:“4.328916”}]}

  18. Feelicjan says

    Great tutorial! BTW, can anyone tell me how to change the script so that points are still there when i reload the site? At this point, reloading the website means that all the points disappear even though they are in the database. Can anyone give me a hint? Regards.

  19. says

    thanks for good code.. But i have one question can someone tell me how i remove the previous mark on Google map Because when i change the address i want to show the current mark not old one. please help me soon
    thanks advance

  20. says

    Awesome idea and even better with the tutorials 8]

    Just one bug that i found – Clicking on the location gives the text, on zooming in/out the text displayed stays there but it is in the incorrect location now. This could be fixed by detecting double click and removing the text.

    Just an idea to look into 8]

  21. says

    Hi Marc,

    Will be looking forward to any future developments 8] In the meantime, time to start learning ajax for me 8]

    Not sure if you did this on purpose or this is how it works with different versions of php/MySQL, in the tutorial you have above in Step #7b you have:

    $query = "INSERT INTO locations SET name='$_POST[name]', lat='$lat', lng='$lng'";

    However, in the zip downloadable file the supplied the code for that line is

    $query = "INSERT INTO locations SET name='$_POST[name]', lat='$_POST[lat]', lng='$_POST[lng]', ip='$ip'";

    I kept getting the Failed to add point. message until i changed this around to what you had in the tutorial.

    Thanks again. 8]

  22. techno says

    I am new to php and it would be really helpful if you tell me in which files should i put steps 2, 3, 4, 5

  23. sanv says

    very good tutorial, thanks.

    I have one question, after initially loading the points from the database I want to periodically check if the database has been updated and fetch the new data. I have written the PHP script that returns the new data however, I am not sure on how to implement it in the html page with jquery. E.g. I want to check for new data every 5mins.

    Thanks in advance.

  24. Graham says

    This is SOOOOOOOOOOOoooo cool.

    I do have a question if i may…

    How could you edit this so instead of loading the markers from the post.. How can i have this just keep updating the from the DB?

    I have my marker data in the DB and gets updated externally ( not from the form you supplied )

    How can i have it just update the map with the DB markers without refreshing the whole page?

    Thanks man. This has saved my life.

  25. says

    hi! great tutorial!
    i’m trying to draw polylines using your code but i’m a bit in trouble… i’ve build a function on your initialization functions…

    $(function(){
    var points = new Array();
    $.getJSON("insert.php?action=listpoints", function(json) {
        if (json.Locations.length > 0) {
            for (i=0; i<json .Locations.length; i++) {
                var punto = json.Locations[i];
                            addLocation(punto);
    var temp_lat = eval(punto.lat);
    var temp_lon = eval(punto.lng);
                points.push(new GLatLng(temp_lat, temp_lon));
            }
        }
    });
    map.addOverlay(new GPolyline(points, "#ff0000", 10));
    });

    this doesn’t works… but if write:

     var points = [new GLatLng(43.26,-80.15), new GLatLng(43.19,-79.98), new GLatLng(43.25,-79.67),
            new GLatLng(43.10,-79.46), new GLatLng(43.20,-79.23), new GLatLng(43.20,-78.99), new GLatLng(43.24,-78.82)];
          map.addOverlay(new GPolyline(points, "#ff0000", 10));

    …the line is drawn! Maybe is json not parsing properly?

    thank you

  26. says

    This is a very good example!! Thanks for your work!
    I have the following problem, I can store the point with no problem. And they keep showing (they are stroeed in the db).
    But if I refresh the page, he does not load the existing points in the db?
    I get no erros in firebug….
    Can you help me on this?

    Best regards,
    Jorge, from PT

  27. says

    i need a little help, if insert data success, then i want to redirect to another page after that. which line i must change… please help, iam newbie in json. thanks

  28. Bas says

    Marc,

    I’ve finally got this thing up and running, however it will not retrieve previously added points from the database. The database is still filled with data, but the website does not load them.

    any thoughts on what could cause this?

    Cheers!

  29. Mike says

    Thanks for this tutorial!
    I would like to save information on every street, where user can place comments. How do I make every street have it’s own page? And add comments that will show every time someone clicks on a street (in the bubble area)?

    Cheers

  30. Lilia says

    Thank you very much Marc; your Google map series is great. I saw your Google map talk at the jQuery Summit and our entire team was very impressed. You’re doing great stuff.

    Any chance you will update your code to use the Google Maps Javascript API v3? This tutorial only works if you load the version 2 of the Google maps API, but it’s been deprecated since May 2010. I built our university’s latest beta map based off of this method and the entire thing crashes when I load the v3 API :( Working to re-write it with the v3 API, but just an FYI for future tutorials.

    Thanks again!

  31. Tar says

    I keep getting the error ‘failed to add point’ and have used all of the source code and simply added in my credentials to allow access to the database. Does anyone know why this occurs

  32. adam says

    I also get ‘failed to add point’. I believe its becuase the tutorial uses v2 of the API andthishas been deprecated as stated by Lilia.

    This is anawesome tutorial, please can you update the tutorial to work with v3.

  33. says

    I would like to create this kind of map for one voluntary project of the Luxembourg Red-Cross.
    My idea is that I will create one administration form where I can add and remove location of the Red-Cross projects and link to the info text of the project under the map and on the other, will be public display map that will display map, list of projects on right side and when certain point is clicked under the map will be displayed different info text.

  34. says

    I have problem that I can not simply get information from the database that is stored without having to add it again.

    Is it any form that can be utilized to pull database info and as well I will include in form info column that I will pull down.

    Thank you really good work

  35. says

    I found the trick it was everything OK- It was pulling info but from different location php/map-service.php and with me were two documents in same folder

  36. says

    how it will be the best to pull some for example Africa map, and corresponding info of the project at the button of the page.

    Sorry for writing three posts for such a short period
    Thank you everyone for helping me this out

  37. Dejan says

    Hi,
    Very good tutorial of course. I modified some things, but i have a little problem with #message div! About problem:
    - Height can’t be display:auto or inline-content, or table so everything in one line.If i set width fixed it shows like fixed and if sentence is longer than pixel drop to next line.
    I try many different solutions for automatich height (like very thing in one line), but nothing works ok.
    Example: i write : Here is a party today. Div shows :
    Here Is
    A Party
    today.

    Any solution?

  38. Lymoon says

    Hi it’s the first day of using google api, so i go a problem of this article that it does not show marker after adding point but in the database was inserted. Please anyone help me…!

  39. CrOMaX says

    $(“#add-point”).submit(function(){

    geoEncode();

    return false;

    });

    In this code, when I put return true it doesn’t works, what’s the problem?

  40. adda says

    Hi,
    I am from Algeria and I salvation for your information because I work on a web application “to create event on google maps” and I ask you to please explain the map file service.php louse-connect has my database. thank you

  41. says

    I have found this article very useful for my project. Is it possible to add radius suppose that 20 miles with the address and I can get all zip code and lat and lng from that query?

    Any help would be very appreciated!

  42. says

    Great tutorial!

    I am new to the programming world, and recently GMaps we’re updated to version 3… Can you make the same tutorial for that version, or at least notify about changes and where exactly… Thank You!

  43. rizwan says

    Nice idea but demo is not working properly.. i want to view the inserted address side and click the address that location pop up the on map can you help me

  44. mo riza says

    awesome tut. after inserted database point the marker on map after refresh the page map was cleared. why?