I’ve been putting a good deal of time recently into converting GeoGlue from .NET to Rails. One of the things that I’m looking to get into the alpha release is the dynamic creation of podcasts. This is really nothing special since a podcast is little more than a special case of an RSS feed that points at external media files (i.e. audio or video).
I plan on covering the audio/video entry in an upcoming post about the nuances of the Attachment_Fu plugin on Windows. In this post, I’m going to just lay out the code for the podcast creation, since this is nothing more that a simple rxml file. I’ve sprinkled in comments liberally but most of the code should be fairly self explanatory to those familiar with Ruby and RSS feeds.
xml.instruct! :xml, :version=> "1.0", :encoding=> "UTF-8"xml.rss( 'version' => '2.0') do xml.channel do xml.title @podcast.name # Self-referencing link xml.link url_for( :only_path => false) # Important --> RFC-822 compliant datetime xml.pubDate(Time.now.strftime( "%a, %d %b %Y %H:%M:%S %Z")) xml.language "en-us" xml.ttl "40" # User who caused the feed to be generated xml.generator User.find( :first, session[ :user_id]).name xml.description @podcast.description # 'public_filename' is a method from the Attachment_Fu plugin xml.image do xml.url url_for( :controller => @podcast.images[ 0].public_filename, :only_path => false) xml.link url_for( :only_path => false) xml.title @podcast.name xml.width @podcast.images[ 0].width xml.height @podcast.images[ 0].height end @podcast.entries. each do |entry| xml.item do xml.title(entry.title) xml.link(url_for( :controller => entry.audios[ 0].public_filename, :only_path => false)) # User who actually generated the media (i.e. audio) xml.author(entry.user.name) xml.category "Uncategorized" xml.guid(url_for( :controller => entry.audios[ 0].public_filename, :only_path => false)) xml.description(entry.description) # Simplification, you should pull from updated_at/updated_on xml.pubDate(Time.now.strftime( "%a, %d %b %Y %H:%M:%S %Z")) # The enclosure is very important!! # If you use Attachment_Fu, everything you need is included in the model xml.enclosure( :type=>entry.audios[ 0].content_type, :length=>entry.audios[ 0].size.to_s, :url=>url_for( :controller => entry.audios[ 0].public_filename, :only_path => false) ) end end end end
A couple of lessons learned from my experience. Firstly, Apple provides some good resources on generating podcasts. This is especially important since the iTunes crowd is a large and important contingent of the feed consuming world. There are iTunes-specific tags (and a schema) available. These tags are not mandatory (I didn’t use them here) but they will help you produce a richer feed for consumption within iTunes. Secondly, since the RXML file is just another view, make sure to turn off any default layouts that you might have applied to your other views. I’ve included a snippet below to demonstrate how to do this. Check your version of Rails, mileage may vary with exempt_from_layout based upon your release. class ApplicationController < ActionController::Base
# Pick a unique cookie name to distinguish our session data from others' session :session_key => '_trunk_session_id' layout 'default' exempt_from_layout :rxml ... end
My final caveat is not to apply forms-based authentication to your podcast (RXML view). Either make the view public or, if you wish to protect it, do so using HTTP Basic authentication instead. If you’re using both forms-based and HTTP Basic authentication, you’ll probably need to sync the two by using a single LDAP repository. That’s fodder for a completely different post.
The Microsoft Live Search Maps update to include Firefox support that I blogged about a couple of weeks ago was released sooner than I expected. The updated maps API
supporting Firefox has not yet been released but word has it that this is imminent as well. I’ve put together a brief screencast of Live Search Maps running in Firefox. Omitted from the screencast are the features that have been available in Firefox for a while, like bird’s eye view. Focus is given explicitly to navigating with the 3D control.
The Camtasia Studio video content presented here requires JavaScript to be enabled and the latest version of the Macromedia Flash Player. If you are you using a browser with JavaScript disabled please enable it now. Otherwise, please update your version of the free Flash Player by downloading here.
When running
the 3D control, you can turn detailed building rendering on or off from the
options link in the upper right corner of the screen. Detailed rendering chews
up more space but looks a whole lot better. It looks like Microsoft is
definitely taking the geospatial market seriously and is out-innovating Google
in this area. Cross browser 3D support (albeit limited to Windows), bird’s eye
views, and other features are real eye catchers. In the 3D realm, I like
Microsoft’s approach to creating the 3D space on their own. Google might have
overextended the community participation concept with Google Earth’s philosophy of user-created 3d
models. Do we really need 60 different virtual models of the Empire State
Building? That seems just
a bit too confusing. Still, if you look at the number of mashups built using
the respective mapping APIs, Microsoft still has a lot of catching up to do. Either
that or folks just aren’t willing to admit that Microsoft might actually have
something here.
The recent announcement that Google will support GeoRSS in addition to KML as a data format for geographic content in Google Maps is long overdue. This is one of those rare areas where Google trailed both Microsoft and Yahoo and did not seem at all willing to budge. Google's announcement also seals the deal on GeoRSS as the way to syndicate geo-specific data. However, despite the obvious importance of GeoRSS, there is little written material on producing GeoRSS feeds.
 I promised a brief tutorial on creating a GeoRSS feed with my post on Yahoo's Tag Maps. More specifically, my post will focus on a boundary update GeoRSS feed. That is, you pass in the maximum and minimum latitudes and longitudes for your map in question and only data about the points that correspond to that particular latitude / longitude box is actually fetched. Obviously, if the user interacts with the map (i.e. panning or zooming), you can use the map's API and some AJAX'y goodness to make calls to the GeoRSS feed to pick up a new set of points that correspond to the updated map's boundaries.
The code below represents the most rudimentary and explicit way to construct a GeoRSS feed using ASP.NET and C#. For the purposes of illustration, no third party GeoRSS libraries are used. It's all basic I/O, streams, and very manual XML construction. Also note a single monolithic call in Page_Load, lack of exception handling and parameterized queries may or may not be the way you want to do things. Try it out though; it does what it's supposed to do really well. If you have any comments or corrections, just drop me a line.
I plan on posting a follow up in a couple of days with a live GeoRSS feed. I just need to find a nice sized set of simple data that I can load into a database and point my code at. Expect to see this soon.
using System; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Xml;
public partial class BlogGeoRss : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { this.Response.Clear(); this.Response.ContentType = "text/xml"; this.Response.ContentEncoding = System.Text.Encoding.UTF8; System.IO.MemoryStream stream = new System.IO.MemoryStream(); XmlTextWriter XMLWrite = new XmlTextWriter(stream, System.Text.Encoding.UTF8); XMLWrite.WriteStartDocument(); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteStartElement("rss"); XMLWrite.WriteAttributeString("version", "2.0"); XMLWrite.WriteAttributeString("xmlns:georss", "http://www.georss.org/georss"); XMLWrite.WriteAttributeString("xmlns:gml", "http://www.opengis.net/gml"); XMLWrite.WriteWhitespace(Environment.NewLine);
XMLWrite.WriteStartElement("channel"); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("generator", "geoglue.com"); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("title", "GeoGlue GeoRSS Feed"); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("language", "en-us"); XMLWrite.WriteWhitespace(Environment.NewLine);
// Pick up the query strings for the latitude / longitude boundaries float UpperBound = 0F, LowerBound = 0F, LeftBound = 0F, RightBound = 0F; try { UpperBound = float.Parse(Request.QueryString["UpperBound"]); } catch (Exception ex) { }; try { LowerBound = float.Parse(Request.QueryString["LowerBound"]); } catch (Exception ex) { }; try { LeftBound = float.Parse(Request.QueryString["LeftBound"]); } catch (Exception ex) { }; try { RightBound = float.Parse(Request.QueryString["RightBound"]); } catch (Exception ex) { };
// Build the item nodes for each of the specific tours SqlCommand cmd = new SqlCommand("SELECT Name, Description, Latitude, Longitude " + "FROM TOUR WHERE (Latitude < @UpperBound) AND (Latitude > @LowerBound) " + "AND (Longitude > @LeftBound) AND (Longitude < @RightBound)", new SqlConnection(ConfigurationManager.ConnectionStrings["GeoGlueDev"].ConnectionString)); cmd.CommandType = CommandType.Text; cmd.Parameters.Add(new SqlParameter("@UpperBound", SqlDbType.Float)).Value = UpperBound; cmd.Parameters.Add(new SqlParameter("@LowerBound", SqlDbType.Float)).Value = LowerBound; cmd.Parameters.Add(new SqlParameter("@LeftBound", SqlDbType.Float)).Value = LeftBound; cmd.Parameters.Add(new SqlParameter("@RightBound", SqlDbType.Float)).Value = RightBound; cmd.Connection.Open(); SqlDataReader dr = cmd .ExecuteReader();
while (dr.Read()) { XMLWrite.WriteStartElement("item"); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("title", (string)dr["Name"]); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("description", (string)dr["Description"]); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteElementString("georss:point", Convert.ToString(dr["Latitude"]) + " " + Convert.ToString(dr["Longitude"])); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteEndElement(); XMLWrite.WriteWhitespace(Environment.NewLine); } cmd.Connection.Close();
XMLWrite.WriteEndElement(); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteEndElement(); XMLWrite.WriteWhitespace(Environment.NewLine); XMLWrite.WriteEndDocument(); XMLWrite.Flush();
System.IO.StreamReader reader; stream.Position = 0; reader = new System.IO.StreamReader(stream); Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(reader.ReadToEnd()); this.Response.BinaryWrite(bytes); this.Response.End(); } }
The Governor’s office announced an exciting new partnership
between Google Earth, Carnegie Mellon University,
NASA, the Pennsylvania Tourism Office, and the National Civil War museum that
will allow virtual tourists from all over the world to experience Pennsylvania’s Civil War
trails first hand. The partnership will be creating Gigapixel Panoramas
(Gigapans), enabling users to visualize these destinations with a great degree
of detail.  It’s hard to tell what this consortium is going to bring
together exactly, but it sounds very promising. There has been talk of even
introducing a time-based element into the visualization, which would be very
cool, given the nature of the subject. For a quick look at other Gigapan type
work, I’d recommend xRez. I find it hard to
make much out of the landscapes but their panorama of downtown Boston is simply awesome.
On October 4, 2006, the Pennsylvania State Capitol
Building celebrates the
centennial anniversary of its dedication. In honor of this special event, I
have created a GeoCast for the Capitol building, its art, and some surrounding
points of interest. This GeoCast can be downloaded or streamed in MP3 format by
clicking the link below or by going to the Pennsylvania category on GeoGlue.com.
If you are interested in further
information about the Capitol or the centennial celebration activities, the
Capitol Preservation Committee website provides a treasure trove of
information. If you can’t make the trip to Harrisburg but would like to experience the
Capitol’s beauty, a QuickTime virtual tour is available on line as well.
Capitol.mp3 (8.1 MB)
Three Mile Island, the
nuclear power plant which, at least figuratively speaking, is located right in
my back yard, was the site of the nation’s worst nuclear disaster in March of
1979. What better site to select for my first custom Geocast then something so
near and dear to the locals’ hearts.

Sure, the recording quality is not great but it just further
proves that it doesn’t take too much to create a Geocast, convert it to an MP3,
and upload it for consumption. I’ve included a link to the file below but you
can, of course, also find this under the Pennsylvania
category on GeoGlue.com.
Drop me a line and let me know what you think and, if you’re
ever in the neighborhood, make sure to include this tourist attraction on your
itinerary.
TMI.mp3 (8.05 MB)
The first, albeit very rudimentary, version of GeoGlue is
now available online at www.geoglue.com. As
described in previous posts, this release is really nothing more than a
soundseeing mashup with Google maps. The functionality is very basic, allowing
the user to browse for soundseeing tours graphically using maps and a menu
system or to search for tours using a combination of keywords. All tours are
provided via MP3 streaming audio, either from the site that created the tour or
from GeoGlue.com directly. 
We are planning a follow up release in the near future. This
release will make the map-based transitions a bit smoother by using AJAX instead of pure
postbacks. We will also be working on increasing the content base, allowing
users to suggest new soundseeing torus and rolling out a user provisioning
system. Keep your eyes open and, as always, email us at admin@geoglue.com if you have any questions
or suggestions.
A couple of weeks ago, I was asked to describe what GeoGlue was. Although, we have a far-reaching strategic vision for GeoGlue, I can describe the functionality in the initial release using two words – “Soundseeing Mashup”. Now both of these words are fairly new additions to the English language. The mashup concept has gained a good deal of traction through all of the Web 2.0 writeups. Soundseeing, on the other hand, was a term that even I had not heard until just a couple of weeks ago.

Wikipedia, in its very brief description of soundseeing, describes it as an audio tour that uses the ambient sounds and descriptions given by a tour guide to give the listener an accurate depiction of the surroundings. These types of recordings are usually made at tourist points of interest and are commonly distributed through podcasting Soundseeingtours.com provides a collection of links to get you started on understanding soundseeing. The New York Minute Show appears to be a leader in the genre, with a number of podcasts covering popular areas of New York City. The Amateur Traveler is also pretty good, with a diverse geographic focus and a good bit of content collected over the last year or so.
GeoGlue, then, mashes up soundseeing tours with Google Maps. Now I can hear you thinking, “just what the world needs, another Google Maps mashup.” Please bear with us, the mapping component is only one of GeoGlue’s facets. It just happens to be the facet that gets the most visceral reaction from people and is therefore a great feature for our initial release.
In going about assembling some initial content for GeoGlue, what I did discover is that the barriers to entry in soundseeing are fairly low. Not to knock any of the podcasts available out there now but with a small investment of time, anyone could tell a somewhat compelling story or stories about the town they grew up in, work in, or traveled to. We at GeoGlue are counting on this and will be reaching out to you to collect your stories in the not-so-distant future.
Jacob Reimers’ Google Maps Control has been a genuine blessing for me over the last couple of days. After a lot of prototyping with Google and Yahoo maps, I decided to go with Google maps for GeoGlue and keep Yahoo maps open as an option based upon the development of the APIs as well as any potential licensing or usage constraints. After dealing with the Google APIs directly, and feeling the pain of issues such as the well-known Internet Explorer “Operation Aborted” maps loading issue, I was yearning for an intermediary API that had already thoughtfully addressed some of these issues.
The Google Maps Control does just this and more, and hit the sweet spot of platform combinations that I was dealing with - .NET 2.0 and Google Maps v2.0. The component is well documented and its design well thought out. Its naming convention emulates the Google API naming fairly closely, so when in doubt, most standard Google Map documentation will lead you to the answer of how to address the issue with the control’s API. The control also contains methods to support Google’s newly released geocoding functionality as well as support for Yahoo’s geocoding functionality, which by virtue having been around longer, is more likely to be in use in existing applications.
The component is well maintained and aligns well with the newest releases of the Google API. It is closed source but free for all use (Jacob’s words) although no license is included in the distribution. Best of all, it enables you to remove all the Javascript references in your .NET source code and use pure C# / VB.NET. The sample below is a snippet from GeoGlue that replaced 60 odd lines of Javascript code scattered across several files. In brief, it sets the latitude, longitude, and markers and then adds a number of markers to the map from a data source -- all in pure C#.
GoogleMap.Latitude = double.Parse(locationResult.Latitude);
GoogleMap.Longitude = double.Parse(locationResult.Longitude);
GoogleMap.Zoom = 1;
while (ProductResults.read())
{
GoogleMarker gm = new GoogleMarker();
gm.ID = ProductResults["Title"].ToString();
gm.Latitude = double.Parse(ProductResults.["Latitude"]);
gm.Longitude = double.Parse(ProductResults.["Longitude"]);
gm.MarkerText = "<b>" + ProductResults.["Title"].ToString() + "</b><br\\>" + ProductResults.["Description"].ToString();
GoogleMap.Markers.Add(gm);
}
With Suzanne and the kids away for a week, I’ve been holding up my end of the bargain and working to make some significant progress with GeoGlue. After getting hung up quite a while on the nuances of Google and Yahoo maps – not to mention Flash encoding - I chose to take a more lightweight approach to getting a first-cut working product out to production. I’ve revamped the user interface pretty significantly but still many of the tried and true styles still manage to show through.
I’m now engaged in getting the site ready for a production push, albeit in a much scaled-back mode, prior to my departure for Detroit on Friday. I’ve included a fuzzy screen shot to give you and idea of what I’m dealing with. As always, if you’re interested in becoming an alpha adopter, drop me a line at alpha@geoglue.com.
|