Google Maps API: Create a store locator

Today I am trying to make a store locator using google maps' api. The store locator is to be set up like so: two areas, one with a map containing all the stores in a given area (measured in a selectable radius from a center point), and one area with a list of all the stores on the map, their information, and of course a link to their website. When a person clicks on the name of the store on the store list, it centers upon the store in the map, and opens an infoWindow above the store marker.

I have a javascript variable to which I have taken pains to assign some json data from a php script (which is selecting this data on the fly from a database)

locations = [{"siteurl":"http:\/\/localhost.localdomain\/5","address":"260 Test St","city":"Brooklyn","state":"New York","zip_code":"11206"},{"siteurl":"http:\/\/localhost.localdomain\/4","address":"3709 Testing St.","city":"Austin","state":"Texas","zip_code":"78705"}]; 

Now, I know there are 5 different functions I need to run, listed below with their apparent use:

  1. geocoder.getLocations : Used to convert address data (from the json object) into latitude and longitude data object
  2. addElementToList : Used to add address information to the list of stores, and bind the centerOnStore function to onclick
  3. centerOnStore when a store list item is clicked in the list area, this function center's upon the store that has been clicked on in the map area. This function also opens an infoWindow above the centered upon store.
  4. placeMarker the function to place a marker on the map, called once the geocoder returns latitudeLongitude objects
  5. eventListener this is tied up somehow in the clicking of a list item and it's further centering the map upon the store in question

Well, i am out of my league it would appear. I am just now learning about javascript closures, and I think these may be necessary, but I can't quite understand them. I need to figure out some way to get all these functions into a working order, passing information back and forth to each other, and create a store locator

.


Here is what I've got so far, but there is something very wrong with it.

    var map = null;
    var geocoder = null;
    var locations = null;
    var center_on = null;
    var zoom_level = null;
    var markerList = [];

    function initialize()
    {
            if(GBrowserIsCompatible())
            {
                    // Assign vars
                    map = new GMap2(document.getElementById("map_canvas"));
                    geocoder = new GClientGeocoder();
                    locations = <?php echo(json_encode($my_vars['locations'])); ?>;
                    center_on = "<?php echo($my_vars['center_on']); ?>";
                    zoom_level = <?php echo($my_vars['zoom_level']); ?>;
                    var currentLocation = 0;

                    geocoder.getLatLng(center_on, function(myPoint)
                    {
                            map.setCenter(myPoint, zoom_level);
                    });
                    map.setUIToDefault();


                    var list = document.getElementById('center_list');

                    for(var i = 0; i < locations.length; i++)
                    {
                            var address = locations[i]['address'] + ', ' + locations[i]['city'] + ' ' + locations[i]['state'] + ', ' + locations[i]['zip_code'];
                            geocoder.getLocations(address, addAddressToMap);
                    }

            }




            function addAddressToMap(response) {

                    if (!response || response.Status.code != 200) {
                            currentLocation++;
                    } else {
                    var place = response.Placemark[0];
                    var point = new GLatLng(place.Point.coordinates[1],
                            place.Point.coordinates[0]);
                    marker = new GMarker(point);
                    GEvent.addListener(marker, 'click', function(){
                            this.openInfoWindowHtml("<strong>" + place.address + "</strong><br /><a href=''siteurl'] + "'>'siteurl'] + "</a>");
                    });
                    map.setCenter(point, 13);
                    markerList.push(marker);
                    map.addOverlay(marker);
                    li = document.createElement('li');

                    li.innerHTML = "<strong>" + place.address + "</strong>";
                    li.setAttribute('onclick', 'center_on_center(' + place.Point.coordinates[1] + ',' + place.Point.coordinates[0] + ')');
                    li.setAttribute('id', 'center_');
                    li.style.fontSize = '1.4em';
                    document.getElementById('center_list').appendChild(li);
                    // alert(currentLocation) here says 0,0,0,0
                    currentLocation++;
                    // alert(currentLocation) here says 1,2,3,4
                    }
            }
    }

I am sorry for the wall of code. I can't think anymore. I had no idea this would be so difficult. No idea at all.

if I alert currentLocation in the line before I increment it, it's always 0. but If I alert it in the line after I increment it, it's '1,2,3,4' etc. This goes against everything I know about computers.


ANSWERS:


Forget about closures for a moment. You can dive into those once you get a working app. I think you're goal at this point to should be to just get something that accomplishes what you want.

To me, it seems like the only piece you're missing is the idea of a callback function. For instance, addElementToList would be passed as the callback argument to geocoder.getLocaitons. The way it works is that when getLocations() finishes, it calls addElementToList and supplies the result from getLocations() as an argument to addElementToList. The code for addElementToList will then add your store location to the map as a marker and add a new element to your html list with the store's name or address or whatever.

Take a look at this blog post for a simple example using a callback: Introducing Google's Geocoding Service.

The last part, centering on a specific store, can be done (as you suggested) with event listeners. You can set up a listener for clicks on the markers and also for clicks on your list. When you add a marker, you can also add an event listener on it. It'd be nice if you could set one listener for all markers on the map but I'm not familiar enough with google's API to know if this is possible.


What is your source for that information? placeMarker certainly doesn't ring a bell. The Google Maps API reference (complete with examples!) is available at


Based on your comment to @Rushyo's answer- it seems like you know enough about Javascript and the Google Maps API to construct those functions. I'm a little confused as to what you're looking for.

I would suggest however, that you add lat/lon coordinates to your database in the first place. You shouldn't have to geocode the addresses every time the map is loaded.

Update: In response to your comment below, here is the code you referenced - along with the addAddressToMap() function called by the Geocoder. It creates a marker for each address and adds it to the array markerList. You can then access the markers in that array later, since we initialized it outside the scope of the addAddressToMap() function.

for(var i = 0; i < locations.length; i++) { 
    var address = locations[i]['address'] + ', ' + locations[i]['city'] + ' ' + locations[i]['state'] + ', ' + locations[i]['zip_code'];
    geocoder.getLocations(address, addAddressToMap); 
}

var markerList = new array();

function addAddressToMap(response) {
    if (!response || response.Status.code != 200) {
        alert("\"" + address + "\" not found");
  } else {
      place = response.Placemark[0];
      point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
      marker = new GMarker(point);
      markerList.push(marker);
      map.addOverlay(marker);
  }
}

Update 2: In response to the code you posted in your question above, you're probably getting random numbers in currentLocation because of the asynchronous nature of the Geocoder. Remember that your getLocations() function will send requests for every location in the array before it gets any responses back.


I'm creating a new answer, since my other answer is getting messy.

In order to get proper closure, you'll need to create a separate function to make the geocoder request. The following code will allow you to assign the desired infoWindow text to each marker.

for(var i = 0; i < locations.length; i++) {
    var address = locations[i]['address'] + ', ' + locations[i]['city'] + ' ' + locations[i]['state'] + ', ' + locations[i]['zip_code'];
    var text = locations[i]['address']; // or whatever you want the text to be
    getLocation(address, text);
}

...

function getLocation(address, text) {
    geocoder.getLocations(address, function(response) {
        var place = response.Placemark[0];
        var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
        marker = new GMarker(point);
        marker.bindInfoWindowHtml(text); // note that if you want to use GEvent.addListener() instead - you'll need to create another function to get proper closure
        map.addOverlay(marker);
    });
}

For more info on closure in Google maps, see these questions:



 MORE:


 ? Blacklisting specific roads from Google Maps/Mapquest?
 ? google map api help
 ? Google Maps Roads API: Making 'snap to road' more forgiving
 ? Implementing Google maps in mobile
 ? Implementing Google maps in mobile
 ? Implementing Google maps in mobile
 ? Using maps on Windows Mobile
 ? How can I implement a Wizard form a Windows Mobile application?
 ? How to implement a SOAP client in C# (specifically for Windows Mobile)?
 ? google maps in jquery mobile