Posts Tagged ‘google maps’

Mapping NYC subway stations

I previously wrote about showing the transit layer with the Google Maps API, this is somewhat of a continuation, but narrower in scope; here I’ll talk about showing custom markers for New York City subway stations, making use of data from NYC Open Data (formerly the NYC Data Mine).

The reason for doing this at all, given that a Google Map already shows subway stations, is that the default indicators are fairly inflexible:

  • You can’t use a custom icon to change how they look
  • You can’t fire off custom event handlers when the user interacts with them

NYC subway stations near city hall

(this is true for all of the point of interest indicators: parks, schools, etc.)

Replacing the point of interest indications with markers turned out to be fairly simple and the data from NYC Open Data was (relatively) clean and readily usable, a pleasant surprise given my previous experience. For this little project, I exported the Subway Stations dataset; it can be exported in a number of formats, but JSON is probably the easiest to work client-side. With the data readily available as a JSON file, it can be loaded simply with an AJAX call to get the file and, once loaded, the subway stations can be plotted on the map by iterating through the list of stations in the JSON data.

It’s worth taking a look at the format of the JSON data, as the indexing of the nodes isn’t all that clear. There’s a meta element and data element at the root, the data element contains a zero-indexed array of subway stations, and each subway station contains a zero-indexed array of attributes, notably:

  • 10 = station name
  • 12 = dash-delimited list of train lines
  • 9 = object with latitude, longitude, and geometry field
    (Note that these values are referenced by the field name, not a numeric index).

NYC Open Data, Subway Station, JSON format

The code to get the JSON data (using jQuery’s .ajax), iterate through the array of subway stations, extract the relevant pieces of information about the stations, and create map markers for them is shown below. A label is attached to the marker by making use of Marc Ridey’s Label class.

$.ajax({
url:
'http://whatever.com/subway-stops.json',
success:
function (ret)
{
for(var i=0; i<ret.data.length; i++)
{
// extract station name, latitude, longitude, and dash-delimited list of train lines at station
var stationName = ret.data[i][10];
var lat = ret.data[i][9]['latitude'];
var lon = ret.data[i][9]['longitude'];
var trainLines = (ret.data[i][12]).split('-');

// make comma-delimited list of train lines
var trainLinesLbl = '';
for(var k=0; k<trainLines.length; k++)
{
trainLinesLbl += trainLines[k];
if(k < trainLines.length-1)
{
trainLinesLbl +=
',';
}                                    
}

// create marker
marker = new google.maps.Marker({
"position": new google.maps.LatLng(lat, lon),
"map": map,
"title": stationName + " [" + trainLinesLbl + "]",
"icon": "http://whatever.com/marker-subway.png"
});

// create label for marker
// uses Label created by Marc Ridey
var label = new Label({ map: map });
label.bindTo(
'position', marker, 'position');
label.bindTo(
'text', marker, 'title');                                                     
}
}                    
});

With some minor styling to the label and an icon from Map Icons Collection, here’s my result:

NYC subway stations with custom markers

Showing the transit layer with the Google Maps API

While Google Maps has a very useful transit layer (showing subway lines, bus stops, etc.) available when zoomed in on a city, this layer is unfortunately not exposed via the Google Maps API. However, as demonstrated on BlinkTag Inc. by Brendan Nee, it’s possible to load the transit layer as a custom tile layers, pulling the transit layer images directly from Google’s servers.

// add transit overlay
var transitOptions = {
getTileUrl:
function (coord, zoom)
{
return "http://mt1.google.com/vt/lyrs=m@155076273,transit:comp|vm:&" + "hl=en&opts=r&s=Galil&z=" + zoom + "&x=" + coord.x + "&y=" + coord.y;
},

tileSize:
new google.maps.Size(256, 256),
isPng:
true
};

var transitMapType = new google.maps.ImageMapType(transitOptions);
map.overlayMapTypes.insertAt(0, transitMapType);    

Google Maps, Transit Layer, Subway - NYC, City Hall


However, there are 2 issues you may quickly notice:

1. Custom styling applied to the base layer is lost.
This is because full image tiles are loaded, which completely obscures the lower layer. A solution to this is to find and copy the apistyle and style URL parameters when the base layer is loaded (you can do this by looking at the GET requests with a tool like Firebug). You then simply add these paremeters to the URL returned by the getTileUrl() function.

getTileUrl: function (coord, zoom)
{
return "http://mt1.google.com/vt/lyrs=m@155076273,transit:comp|vm:&" + "hl=en&opts=r&s=Galil&z=" + zoom + "&x=" + coord.x + "&y=" + coord.y + "&apistyle=s.t%3A3%7Cp.h%3A%23C5C5C5%7Cp.s%3A-100%7Cp.l%3A37%7Cp.v%3Aon%2Cs.t%3A35%7Cp.h%3A%23F284FF%7Cp.s%3A100%7Cp.l%3A-9%7Cp.v%3Aon%2Cs.t%3A81%7Cp.v%3Aoff&s=Gal&style=api%7Csmartmaps";
},

Google Maps, Transit Layer, Subway - NYC, City Hall

2. The large subway stop markers are useless
In New York City at least, it’s impossible to identify train lines by color alone, so it’s fairly important to see the letter or number identifier for trains at the different stations. The map has both large and small markers for each station, but only the small markers shows this information. I couldn’t figure out a way to get the larger marker to show the letters/numbers, but by changing “vm:” to “vm:1” you can completely remove the large markers from the map. However, this also shrinks the size of the lines indicating the train routes.

getTileUrl: function (coord, zoom)
{
return "http://mt1.google.com/vt/lyrs=m@155076273,transit:comp|vm:1&" + "hl=en&opts=r&s=Galil&z=" + zoom + "&x=" + coord.x + "&y=" + coord.y + "&apistyle=s.t%3A3%7Cp.h%3A%23C5C5C5%7Cp.s%3A-100%7Cp.l%3A37%7Cp.v%3Aon%2Cs.t%3A35%7Cp.h%3A%23F284FF%7Cp.s%3A100%7Cp.l%3A-9%7Cp.v%3Aon%2Cs.t%3A81%7Cp.v%3Aoff&s=Gal&style=api%7Csmartmaps";
},

Google Maps, Transit Layer, Subway - NYC, City Hall

There is a third issue that’s pretty noticeable as well: bus stops do not how the identifier of the buses that stop at them. I’ve yet to find a way to show them.

Map directions request on webOS

Very simple, but not well documented.

First, here’s a typical call to launch the maps app and display a position (latitude, longitude + title) on the map:

this.controller.serviceRequest("palm://com.palm.applicationManager", {
method:
"open",
parameters: {
id:
"com.palm.app.maps",
params: {
query: escape(latlong) +
"(" + encodeURIComponent(title) + ")"
}
}
});

To launch the maps app and bring up the directions panel instead, replace query with daddr:

this.controller.serviceRequest("palm://com.palm.applicationManager", {
method:
"open",
parameters: {
id:
"com.palm.app.maps",
params: {
daddr: escape(latlong) +
"(" + encodeURIComponent(title) + ")"
}
}
});

google directions on webOS

Local search on dotspott

Local search is now available on the dotspott web client; allowing you to search for local venues and add them to your list of spotts.

dotspott local search for cocoa bar

The Google Maps API v3, which is used by dotspott, doesn’t really allow for local search and the Local Search API itself is deprecated (however, as per Google’s deprecation policy, it should be available until Nov. 2013). What I did was use gmaps-api-v3-googlebar, which allows adding a google-bar like control to the map.

One interesting thing I needed to do that wasn’t directly possible with gmaps-api-v3-googlebar update a few other things after one of the search results were selected. I wanted to avoid touching the gmaps-api-v3-googlebar code, so I did this by getting a reference to the existing event handler, then overwriting it with a new function, which called the previous event handler function.

Here’s an example where we grab the reference to the existing selectResult event handler (searchResultPre), overwrite with a new event handler, call the previous handler (binding to window.gbar, the instance of window.jeremy.jGoogleBar; binding is necessary b/c the event handler references this internally), then add some new functionality where we modify a paragraph element in the DOM (id = map_position) to show the position of the local marker that is selected.

selectResultPre = window.jeremy.jGoogleBar.prototype['selectResult'];
window.jeremy.jGoogleBar.prototype[
'selectResult'] = function (result)
{
selectResultPre.call(window.gbar, result);

    
var searcher = window.jeremy.gLocalSearch.searchers[0];
    
var results = searcher['results'];
    
var lmarker = result['marker'];

    $(
'#map_position').text(lmarker.getPosition().toString());
}

oh, and yes, this is using jQuery; $(‘#map_position’) should have given it away.

Attaching a title to a position on Google Maps

One of the neat little things I discovered when doing the hotspotdot webOS app is how to show the custom, user-entered title of the location, as shown below.

Google map location on webOS

This is done by specifying the title in the query, in parentheses, after the coordinates. The full, escaped query URL for the location above is as follows:

http://maps.google.com/maps?q=40.7044120969184,-73.99009709643556%28Empire%20Fulton%20Ferry%20State%20Park%29

The Javascript code for the webOS app would look something like this (latlong and title are variables that hold the latitude, longitude coordinate pair and title, respectively):

this.controller.serviceRequest("palm://com.palm.applicationManager", { method: "open", parameters: { id: "com.palm.app.maps", params: { query: escape(latlong) + "%28" + escape(title) + "%29" } } });

This works in the browser as well, so the URL shown above would display the following,

custom title on Google maps position

Not quite fulfilling…

Apparently I’m not good at reading nor following rules.

my app is better contest, rejection email

I’m guessing this has to do with the fact that I used Google Maps for hotspotdot instead of Microsoft’s Bing Maps, with the Google logo plainly visible on the map.

It never crossed my mind that this might be an issue. Trademark or not, using components from a variety of vendors is pretty much the norm for any sort of web development or web design.

hotspotdot

hotspotdot is my entry into Microsoft’s My App is Better Challenge. In a nutshell, hotspotdot allows you to locate and tag “hot spots” that are important to you, allowing you to create a personal database of hot spots.

It’s a very simple app, utilizing PHP, SQL Server 2008, jQuery, jQuery UI, and the Google Maps API. I wrapped thing up in under 2 weeks, but in order to make the August 25th deadline there were a few features I didn’t get a chance to put in, such as preloaders, icons on the map, and searching by tag. I’ll probably devote some time to these in the coming weeks.

hotspotdot login

The goal of the contest was to create something showing off the power of SQL Server and Microsoft’s new PHP for SQL Server 2.0 drivers. I used the new PDO driver which was pretty sweet – very simple, elegant API. Two things in particular that impressed me were transactions (very nice for doing multiple inserts or deletions) and a consistent and well designed exception model (makes error handling much easier and especially powerful when combined with transactions as failed queries don’t effect the database [code jumps to exception handler before commit() is called], so no chance of junk being inserted). Error messages, in general, were also much more descriptive than those I’ve encountered with MySQL.

Finding a server proved difficult. I got a shared hosting solution, but the host was unable (though I suspect unwilling) to install the PHP for SQL drivers. So I ended up getting a virtual private server, but this only came with SQL Server Express and I used some features (see below) that prevented a migration. In order to avoid purchasing an SQL Server 2008 license (way out of my budget), my final solution was use the shared hosting server for the DB and the VPS for everything else. Hence the reason for the site being located at the my. subdomain, which maps to the IP of the VPS.

SQL Server 2008 is a fine system (despite some annoyances with the management studio)… though it’s really just a solid database system, which isn’t bad, but there’s nothing really impressive or creative about that. It’s not really leaps and bounds above a cheap solution like MySQL. The contest was about SQL Server, so I tried to do something that utilized a fairly unique aspect of the system: the geography data type. I wasn’t too impressed. It simply holds a (longitude, latitude) pair in a certain format, nothing more. The one big advantage of having a vector type like this would be doing comparisons based on distance but, as far as I could tell, this isn’t supported (my queries failed). Worse yet, in SQL Server Management Studio the display of the geography type is in hexadecimal, making things very cryptic… I couldn’t help but wonder why this is any better than using 2 columns and storing the longitude and latitude values independently. That said, the idea of richer/more-complex data types within a relational database is a pretty cool idea and it would interesting to see it taken further and beyond its current, primitive state.

hotspotdot map

hotspotdot map

Leave a comment if you find a bug or have any questions, comments, etc.