At the Forge - Creating Mashups
This map is a nice start, but far from what we want to accomplish. And, one of the biggest impediments is the fact that Google Maps expects to get longitude/latitude pairs. Amazon's Web service does return information about third-party vendors, but it provides us with city and state information. So, we need a way to translate city and state names into latitude and longitude.
The easiest way to do this is to rely on someone else, who can translate an address into a longitude/latitude pair. Such geocoder services exist as Web services on the Internet; some of them are freely available, and others charge money. One of the best-known free geocoder services is at geocoder.us. To use this geocoder, we simply use a REST-style URL, as follows: http://geocoder.us/service/rest?address=ADDRESS, replacing ADDRESS with the place we want to go. For example, to find my house, we would say, http://geocoder.us/service/rest?address=9120+Niles+Center+Road+Skokie+IL.
The geocoder service returns an XML document that looks like this:
<rdf:RDF>
<geo:Point rdf:nodeID="aid77952462">
<dc:description>9120 Niles Center Rd, Skokie IL 60076</dc:description>
<geo:long>-87.743874</geo:long>
<geo:lat>42.046517</geo:lat>
</geo:Point>
</rdf:RDF>
Because the longitude and latitude are nicely compartmentalized inside of the XML, it's easy to extract it in our program and then insert it into the JavaScript that we generate. However, from looking through the geocoder.us documentation, it doesn't seem as though it is able to handle city names (that is, without street addresses).
Luckily, at least one free geocoder service handles city names, returning a similarly styled XML document. We submit the name of a city as follows, once again using a REST-style request: http://brainoff.com/geocoder/rest?city=Skokie,IL,US.
We get the following result:
<rdf:RDF>
<geo:Point>
<geo:long>-87.762660</geo:long>
<geo:lat>42.034680</geo:lat>
</geo:Point>
</rdf:RDF>
As you can see, the longitude and latitude points we got back from this query are slightly different. If we were looking to create a map for driving directions, this would be of greater importance. But, we already know that we'll be looking at the entire map of the United States for this application, and that being blocks away, or even two miles away, won't make any difference.
Listing 2. mashup2.rhtml
<% require 'net/http' %>
<% require 'rexml/document' %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script
src="http://maps.google.com/maps?file=api&v=1&key=
↪ABQIAAAAQQK9JhAXQ9eq-G55q\
↪gu1ExTnGAXa-Bs2i826H4DeSQaC3Vqy-xSjDFaTYAO0N5YPQWIEeUbqJMhhbA"
type="text/javascript"></script>
</head>
<body>
<h2>Here is your map</h2>
<div id="map" style="width: 400px; height: 400px"></div>
<script type="text/javascript">
var map = new GMap(document.getElementById("map"));
map.centerAndZoom(new GPoint(-87.740070, 42.037030), 13);
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
<% final_list = [ ] %>
<% cities = ["Skokie,IL,US", "Longmeadow,MA,US",
"Somerville,MA,US", "Old+Westbury,NY,US"] %>
<% cities.each_with_index do |city, index| %>
<% geocoder_response =
Net::HTTP.get_response('brainoff.com',
"/geocoder/rest/?city=#{city}") %>
<% xml = REXML::Document.new(geocoder_response.body) %>
<% longitude = xml.root.elements["/rdf:RDF/geo:Point/geo:long"].text
%>
<% latitude = xml.root.elements["/rdf:RDF/geo:Point/geo:lat"].text %>
<% final_list << {'city' => city, 'longitude' => longitude,
'latitude' => latitude } %>
var myMarker<%= index %> = new GMarker(new GPoint(<%= longitude %>,
<%= latitude %>));
map.addOverlay(myMarker<%= index %>);
<% end %>
</script>
<body>
<h2>Your cities</h2>
<table border="1">
<tr>
<th>City</th>
<th>Longitude</th>
<th>Latitude</th>
</tr>
<% final_list.each do |city| %>
<tr>
<td><%= city['city'] %></td>
<td><%= city['longitude'] %></td>
<td><%= city['latitude'] %></td>
</tr>
<% end %>
</table>
</body>
</html>
We can now update our ERB file, such that it has an array of cities, rather than longitude/latitude pairs, as you can see in Listing 2. We begin the file by importing two Ruby classes that will be needed to handle this additional functionality:
<% require 'net/http' %> <% require 'rexml/document' %>
Although our starting (and centering) point begins at the same longitude/latitude location, we begin at zoom level 13, which will be large enough to show all of the cities.
We then define four cities, putting them in an array called cities, showing four of the US cities in which I have lived. Notice that each element of this array is a string containing a city name, state abbreviation and US (for United States). Also note that when the city name has a space, we must replace it with a + sign (or %20), so the Web service request works appropriately:
<% cities = ["Skokie,IL,US", "Longmeadow,MA,US",
"Somerville,MA,US", "Old+Westbury,NY,US"] %>
We then iterate through these cities, using each as the argument to our Web service geocoder:
<% geocoder_response =
Net::HTTP.get_response('brainoff.com', "/geocoder/rest/?city=#{city}") %>
The results of the geocoder Web service are in XML, as we saw earlier. To extract the results of this query from the XML, we use the REXML library that comes with Ruby. This allows us to retrieve the geo:long and geo:lat elements, and then grab the textual contents of the elements:
<% longitude = xml.root.elements["/rdf:RDF/geo:Point/geo:long"].text %> <% latitude = xml.root.elements["/rdf:RDF/geo:Point/geo:lat"].text %>
Having done the hard work, we now insert the appropriate JavaScript:
var myMarker<%= index %> = new GMarker(new GPoint(<%= longitude %>,
<%= latitude %>));
map.addOverlay(myMarker<%= index %>);
Along the way, we collect city names and locations into an array named final_list. We can then use this to produce a list at the end of the document:
<% final_list.each do |city| %>
<tr>
<td><%= city['city'] %></td>
<td><%= city['longitude'] %></td>
<td><%= city['latitude'] %></td>
</tr>
<% end %>
Sure enough, this produces a page with a Google map showing all of those locations, and with a list at the bottom.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- Dynamic DNS—an Object Lesson in Problem Solving
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- Tech Tip: Really Simple HTTP Server with Python
- myip
4 min 13 sec ago - Keeping track of IP address
1 hour 55 min ago - Roll your own dynamic dns
7 hours 8 min ago - Please correct the URL for Salt Stack's web site
10 hours 20 min ago - Android is Linux -- why no better inter-operation
12 hours 35 min ago - Connecting Android device to desktop Linux via USB
13 hours 3 min ago - Find new cell phone and tablet pc
14 hours 2 min ago - Epistle
15 hours 30 min ago - Automatically updating Guest Additions
16 hours 39 min ago - I like your topic on android
17 hours 25 min ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




Comments
crime mashup
Another crime mashup for you:
http://www.newhavencrimelog.org/
Error
I get this error when I try to run the code from listing 3:
undefined method `text' for nil:NilClass
(
NoMethodError
)
The error is from this line:
next if geocoder_xml.root.nil?
Thanks for writing great articles. At the Forge is one of my favorite articles.
Error
Hi, cbier. Sorry that it took a while to respond, but I only realized recently that people were asking questions of me on this forum!
The error message tells us that we're trying to invoke a method -- the "text" method -- on nil. Now, we obviously don't have a literal nil in the code, so that means our variable must have a nil value, and we're trying to invoke an unknown method on it.
My guess, and I can't prove it, is that Amazon gave you back a city for which there wasn't any geocoder information. (Maybe the city name was spelled incorrectly?) Thus you got nil back. But then you tried to invoke a method on it. Where? I'm not sure, but I'm guessing that we needed to do a bit more testing -- checking that geocoder_response wasn't nil, and that geocoder_xml wasn't nil, either.
Reuven
Senior Columnist, Linux Journal
Senior Columnist, Linux Journal