Archive for the “Technology Guides” Category

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.

Creating GeoRSS Fees in C#

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();
}
}

Comments No Comments »

One of my original intents of registering the beckshome.com domain name was to publish photos of my new baby son or daughter. That was two years and two daughters ago and, until this weekend, photos were nowhere to be found on my blog. I host my blog on the Windows platform and had no desire/time to do any of the following: (a) buy a separate package for image management; (b) cobble together an ASP.NET solution to manage my photos; (c) switch blogging software to a tool like Community Server that has integrated photo management. Furthermore, I already manage my photos on Flickr and I’m more than happy with the service, user experience, and the cost-benefit. What I really needed was a way to integrate my existing Flickr photos into my current .NET-based blog (DasBlog). The pursuit of this goal is what this blog entry is all about.

Being a regular blog reader, what I’ve seen a lot of out there are the Flickr badges. These badges, available in either HTML or Flash versions (like the one in the sidebar of this blog), are pretty slick and can be found pretty much everywhere on the Web. The problem with these badges is that they only offer the opportunity for shallow integration. Click on the badge and bye-bye blog, you’re zipped off to Flickr’s site to look at the photos. Since I aspired to achieve a bit deeper integration, I needed a different approach.

Next thing that I looked into was programmatic access to the Flickr API or a pre-existing solution that I could use wholesale or reproduce with little effort on my part. The Great Flickr Tools Collection has a vast assortment of very interesting tools – none of which quite seemed to meet my needs. I checked out the Flickr.NET API Library, which was written about in a Coding4Fun post and can be found for download here. It’s very well done and, although it probably won’t be the last time I mention this API in my blog, it will be the last time I mention it in this posting.

What I eventually stumbled on was a simple and elegant solution that got me exactly what I wanted by embedding the Flickr slideshow viewer into a custom page on my existing blog. Paul Stamatiou has an excellent post on his blog on how to do just that. By using an iframe and setting some API attributes you can get this up and running very quickly; qualifying this as a super easy hack that just works. Note that only photos marked as public will be displayed.

With the Flickr slideshow viewer up and running, I only needed to add the ability to select between multiple photo groups and I was done. With the actual Flickr viewer taking care of all of the real AJAX work, all that was needed was a bit of light JavaScript to tie this all together. Below you will find the code that does all of the lifting.

<script type="text/javascript">// <![CDATA[
      function changeSlideshow(url, title)
      {
            document.getElementById("SlideShow").src=url;
            document.getElementById("Title").innerHTML=title;
            return false;
      }
// ]]></script>

Clicking on any of the photo group links / thumbnails makes a call to the above function passing the URL for the slideshow in the manner stipulated in Paul’s article for populating the slideshow viewer. The title is also passed so that the title of the page can be updated. You can see this at work on my new photopage. By viewing the page source, you can see the exact mechanism I used to make this work. If you’re have any questions, feel free to drop me a line.

Comments No Comments »