Monday, February 4, 2013

Creating a Custom Pop Up with a Spatial Query


The next hurdle I ran into while creating the GeoLinx application was creating a pop up that would display the attributes for more than one feature in the same spatial location.  Each job post is geocoded to a city because very rarely is a full address included in the post.  So each post is typically located at the centroid of the city (or sometimes a specific place like a military base).  Because of this, the points stack on top of each other.  The basic pop up implemented with the Google Maps API will only get the information for the point on top.  To overcome this I had to create by own code to select all of the points within a radius of the user’s click.



First I created a Fusion Tables layer to display the points on the map.  I did this simply by following the documentation.  I did suppress the default Info Window, otherwise two pop ups would appear when the user clicks on the point.  I also included a SQL query to not show posts older than a specified date or that have expired.  You can find many good examples of JavaScript functions that will get the current data or a past date.  Finally, I created a new Info Window that would display the attributes for the selected postings.  An Info Window is simply the normal pop up you see in most Google Maps applications.

var ftLayer = new google.maps.FusionTablesLayer({
              suppressInfoWindows: true,
              query: {
                    select: 'GeoCode',
                    from: table,
                    where: "PostDate > '" + currentDate +
                        "' AND (ExpireDate > '" + todaysDate + 
                        "' OR ExpireDate = '')"
              }
});
ftLayer.setMap(map);

var infoWindow = new google.maps.InfoWindow();

The next step was to create a listener for click event of the Fusion Tables layer.  This simply tells the application to “listen” for the user to click on a point in the layer and then do something.  Once again I followed the documentation for this.  I then built a query to select the attributes I wanted to display.  I used a spatial query to select the features within a certain distance of the users click.  Next it is essential convert the query to a URL format.  Then I completed the query string by including the rest of the URL path and my key.   Finally, I created a Google Visualization query using the Google Visualization API.

google.maps.event.addListener(ftLayer, 'click', function (event) {
      var query = "SELECT Title, GeoCode, Organization, " + 
                   "PostDate, URL FROM " +
                   table +
                   " WHERE ST_INTERSECTS(GeoCode, CIRCLE(LATLNG"
                   event.latLng + ", 5000)) ORDER BY PostDate DESC";
      query = encodeURIComponent(query);
      query = 'http://www.google.com/fusiontables/gvizdata?tq='
               query + '&key=’ + key;

      var gvizQuery = new google.visualization.Query(query);

Once the query was created, I sent the query to the server to get back the result.  I also had to create some HTML formatting to display the results in a tabular format.  The Info Window will take straight HTML.  I cut back some of the HTML formatting below to reduce the length of this post but you can research tables for HTML if you need to.  Using the Google Visualization API, I looped through each row of the returned query and build each row in the table.  I also had to do some formatting on the dates.  The final steps were to set the position of the pop up to the user’s click, feed the HTML code for the table to it, and the open the info window.

      gvizQuery.send(function (response) {
         var content = ...//HTML here

         var numRows = response.getDataTable().getNumberOfRows();

         for (var i = 0; i < numRows; i++) {
               var title = response.getDataTable().getValue(i, 0);
               var loc = response.getDataTable().getValue(i, 1);
               var org = response.getDataTable().getValue(i, 2);
               var pDate = response.getDataTable().getValue(i, 3);
               var url = response.getDataTable().getValue(i, 4);

               pDate = postDate.toString();
               pDate = postDate.replace(
                     '00:00:00 GMT-0500 (Eastern Standard Time)'
                     '');
               pDate = postDate.substr(3, postDate.length);

               content = content +
                    + title + ...//HTML here
                    + loc + ...//HTML here
                    + org + ...//HTML here
                    + pDate + ...//HTML here
          }

          content = content + ...//HTML here

          infoWindow.setPosition(event.latLng);
          infoWindow.setContent(content);
          infoWindow.open(map);
      });
});

Overall, I had a difficult time putting this together because it uses code from three Google APIs, and I couldn’t find examples doing exactly this.  But I did learn a lot that I would use in other parts of the application.  Just a note: I’m sure there are other and better ways to do this.  Feel free to comment if you have better solutions.  The next part of the project was to add search functionality that would center and zoom the map to the address or zip code that the user specifies.