Google Maps Demo

Below is the process and code that I used to create this map showing Instagram pictures from a road trip I took starting in Nashville, TN and ending in Portland, OR. This exercise demonstrates:

  • API Development
  • Consumption of JSON Data
  • Object-Oriented Principles

Live Demo Page

The Process

  1. First, I gathered the Instagram pictures I wanted to show on my map. Because I didn’t properly tagged the original Instagram posts with any location info, or anything that would distinguish the posts from my other posts, I had to build my own custom results set using the results set from Instagram as a foundation. I employed Instafeed.js to help build my results set. Here is the final JSON object.
  2. Next, I got my App Key from Google and built a “Hello, World!” test to make sure everything worked.
  3. Finally, I created two javascript classes, MyList and MyMap. MyList is a glorified array of Map Markers, with an internal pointer that tracks the “current” index. MyMaps is a wrapper class for the google.maps.Map object, adding UI controls.

The Code

Class: MyList

/***************** CLASS: MyList **********************/
/* * 
    Stores a list of pbjects, and a a reference pointing to a selected list item
    Functions:
        add - Adds item to the list
        saveAll - Adds an array of items to the list
        count - returns number of items in list
        getAll - returns entire list
        get - gets the item at a specified index
        indexOf - returns index number of specified object, making that object the new selection
        next - returns the next object in the list
        previous - returns the previous object in the list
        triggerMarkerIcon - toggles marker icon of the selected index;
            Parameter toggle - boolean. False for default red flag; true for green flag
    */
function MyList() {
    this.list = [];
    this.pointer = -1;	
}

MyList.prototype.add = function(obj) { return this.list.push(obj); };
MyList.prototype.saveAll = function(array) { if(array.constructor === Array) this.list = array; };
MyList.prototype.count = function() { return this.list.length; };
MyList.prototype.getAll = function() { return this.list; };
MyList.prototype.get = function(i) {
    if(i > -1 && i < this.list.length) {
        this.triggerMarkerIcon();
        this.pointer = i;
        this.triggerMarkerIcon(true);			
        return this.list[i];
    }
};
MyList.prototype.indexOf = function(marker) {
    this.triggerMarkerIcon();
    for(var i = 0; i < this.list.length; i++) {
        if(this.list[i] === marker) {
            this.pointer = i;
            this.triggerMarkerIcon(true);
            return i;
        }
    }
    return -1;
};
MyList.prototype.next = function() {
    this.triggerMarkerIcon();
    this.pointer++;
    if(this.pointer == this.list.length) this.pointer = 0;
    this.triggerMarkerIcon(true);
        
    return this.list[this.pointer];
};
MyList.prototype.previous = function() {
    this.triggerMarkerIcon();
    this.pointer--;
    if(this.pointer < 0) this.pointer = this.list.length - 1;
    this.triggerMarkerIcon(true);
        
    return this.list[this.pointer];
};
MyList.prototype.triggerMarkerIcon = function(toggle) {
    var icon = red_icon;
    if(toggle) icon = green_icon;
    
    if(this.pointer > -1 && this.list[this.pointer]) {
        this.list[this.pointer].setIcon(icon);
    }
};

Class: MyMaps

/***************** CLASS: MyMap **********************/
/* * 
    Wrapper class for Google Maps Object. 
    customized to show a list of Instagram images, each marked on a map
    Functions:
        changeMarker: activates the marker chosen by the user. Displays infoWindow
        addMarker: creates a custom marker object, including the content for the marker, 
            extrracted from the data object.
            Expands the bounds of the map.
        initControls: Adds the 'Next' and 'Previous' click event handlers allowing the user to go back
            and forth between markers
        initMap: initializes Map, stores the markers in a list
    */
function MyMap() {
    this.map = undefined;
    this.el = undefined;
    this.infoWindow = new google.maps.InfoWindow();
    this.bounds = new google.maps.LatLngBounds();
    this.markers = new MyList();
}

MyMap.prototype.changeMarker = function(marker, origin) {

    if(!origin)
        this.markers.indexOf(marker);

    this.infoWindow.setContent(marker.content);
    this.infoWindow.open(this.map, marker);	
};

MyMap.prototype.addMarker = function(mark) {
    var self = this, date, date_string, latlng, gMarker;
    if (mark.location.lat != undefined && mark.location.long != undefined) {
        
        date = new Date(mark.created_time * 1000);
        date_string = monthnames[date.getMonth()] + ' ' + date.getDate();
                    
        latlng = new google.maps.LatLng(mark.location.lat, mark.location.long);
        
        gMarker = new google.maps.Marker({
            position: latlng,
            map: self.map,
            title: date_string
        });
        
        gMarker.content = "<div class='text-center' style='max-width: 300px;'><strong>"+date_string+"</strong><br/><a href='"+mark.link+"' target='_blank'><img src='"+mark.images.low_resolution.url+"'/></a><br/>"+mark.caption.text + "<br/><strong>Filter:</strong> " + mark.filter + "</div>";
        
        google.maps.event.addListener(gMarker, 'click', function() { 
            self.changeMarker(gMarker);
        });
        
        this.markers.add(gMarker);
        
        this.bounds.extend(latlng);			
    }	
}

MyMap.prototype.initControls = function() {
    
    var self = this;
    
    $('.next-marker').click(function(e) {
        e.preventDefault();
        self.changeMarker(self.markers.next(), this);	
    });
    $('.previous-marker').click(function(e) {
        e.preventDefault();
        self.changeMarker(self.markers.previous(), this);	
    });
};

MyMap.prototype.initMap = function(id, data) {
    
    this.el = $(id)[0];
    this.map = new google.maps.Map(this.el, {zoom: 8});
    
    var self = this;
    
    for(var i = 0; i < data.length; i++) {
        this.addMarker(data[i]);
    }
    
    this.map.fitBounds(self.bounds);
    this.changeMarker(this.markers.next());
    
};

$(document).ready()

map_to_pdx = new MyMap();
	
$(function() {
    
    if(!pdx_trip_data_uri) {
        console.log("ERROR. No input.");
    }
    else {
    	//#map-container is hidden on small screens
        if($('#map-container').is(':visible')) {
            //Get Data from JSON Object
            var jqxhr = $.getJSON(pdx_trip_data_uri.path);
            
            jqxhr.done(function(data) {
                if(data.length > 0) {
                    map_to_pdx.initMap('#map-canvas', data);
                    map_to_pdx.initControls();
                }
            });
        }
    }
});

Links

Special Thanks To: