Friday, July 22, 2005

GeoURL To Google Earth client using KML

I'm going to show how to combine the GeoURL RSS feed with Google Earth client using JSTL.

In my last blog entry, I showed how you could get a web service and Google Earth client to communicate using a KML file dynamically generated by JSP. Now I'm going to extend this approach so that a list of blog in the immediate vicinity of a location are displayed inside the Google Earth client by making use of the GeoURL RSS feed.

Essentially this is quite easy because in the last entry we already produced a script that could calculate the centre co-ordinates of a Google Earth map from details passed to it from the client application. Since we have the central map co-ordinates we can query the GeoURL feed for blogs in that vicinity. The final stage is to convert the RDF feed returned into the KML format that the Google Earth client requires.

In my XSLT I borrow a few tricks from one of my *favourite* XSLT related articles Never Mind the Namespaces: An XSLT RSS Client (which is incidentally very useful if you want to create an XSLT that can transform multiple RSS formats). There are a couple of namespaces knocking about in the RDF feed that GeoURL returns and for my purposes I'd be happier ignoring them. So here is the XSL stylesheet which will transform the GeoURL RDF into KML format.


<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://earth.google.com/kml/2.0">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes" />

<xsl:template match="/">
<kml xmlns="http://earth.google.com/kml/2.0">
<Folder>
<name>GeoURL</name>
<open>1</open>
<xsl:apply-templates select="*[local-name()='RDF']" />
</Folder>
</kml>
</xsl:template>

<xsl:template match="*[local-name()='RDF']">
<xsl:apply-templates select="*[local-name()='item']" />
</xsl:template>

<xsl:template match="*[local-name()='item']">
<Placemark>
<description>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="*[local-name()='link']" />
</xsl:attribute>
<xsl:value-of select="*[local-name()='title']" />
</xsl:element>
</description>
<name>
<xsl:value-of select="*[local-name()='title']" />
</name>
<LookAt>
<longitude>
<xsl:value-of select="*[local-name()='longitude']" />
</longitude>
<latitude>
<xsl:value-of select="*[local-name()='latitude']" />
</latitude>
<range>540.68</range>
<tilt>0</tilt>
<heading>3</heading>
</LookAt>
<Point>
<coordinates>
<xsl:value-of select="*[local-name()='longitude']" />,<xsl:value-of select="*[local-name()='latitude']" />,0
</coordinates>
</Point>
</Placemark>
</xsl:template>
</xsl:stylesheet>

It is simply now a matter of creating an KML document describing the "Network Link".


<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<NetworkLink>
<name>GeoURL Blogs Link</name>
<open>1</open>
<Url>
<href>http://localhost/GoogleEarth/geourlRSSFetch.jsp</href>
<viewRefreshMode>onStop</viewRefreshMode>
<viewRefreshTime>5</viewRefreshTime>
</Url>
<visibility>1</visibility>
</NetworkLink>
</kml>

...and modifying the JSP I previously created to make the GeoURL feed request and perform the XSL transformation.


<%@ page contentType="application/vnd.google-earth.kml+xml; charset=UTF-8" %><%--
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %><%--
--%>
<%@ taglib prefix="x" uri="http://java.sun.com/jstl/xml_rt" %><%--
--%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt_rt" %><%--
--%>
<%@ taglib uri="http://jakarta.apache.org/taglibs/string-1.1" prefix="str" %><%--
--%>
<c:if test="${empty applicationScope.geo2kmlXSLT}"><%--
--%>
<c:import var="geo2kmlXSLT" url="geourl2kml.xsl" scope="application" /><%--
--%>
</c:if><%--

longitude_west, latitude_south, longitude_east, latitude_north

--%>
<c:choose><%--
--%>
<c:when test="${!empty param.BBOX}"><%--
--%>
<str:split separator="," var="bbox"><%--
--%>
<c:out value="${param.BBOX}" /><%--
--%>
</str:split><%--
--%>
<c:set var="longitude_west" value="${bbox[0]}"/><%--
--%>
<c:set var="latitude_south" value="${bbox[1]}"/><%--
--%>
<c:set var="longitude_east" value="${bbox[2]}"/><%--
--%>
<c:set var="latitude_north" value="${bbox[3]}"/><%--
--%>
<c:set var="userlon" value="${(longitude_east - longitude_west)/2 + longitude_west}"/><%--
--%>
<c:set var="userlat" value="${(latitude_north - latitude_south)/2 + latitude_south}"/><%--
--%>
</c:when><%--
--%>
<c:otherwise><%--
--%>
<c:set var="userlon" value="-2.6018"/><%--
--%>
<c:set var="userlat" value="51.4595"/><%--
--%>
</c:otherwise><%--
--%>
</c:choose><%--

http://geourl.org/near/?lat=51.4595&long=-2.6018;format=rss10

--%>
<c:set var="long"><c:out value="${userlon}" /></c:set><%--
--%>
<c:set var="lat"><c:out value="${userlat}" /></c:set><%--
--%>
<c:set var="fetchurl">http://geourl.org/near/?lat=<c:out value="${lat}"/>&amp;long=<c:out value="${long}"/>&amp;format=rss10</c:set><%--
--%>
<c:import var="geourl_rss" url="${fetchurl}"/><%--

--%>
<x:transform xml="${geourl_rss}" xslt="${geo2kmlXSLT}"/>

3 comments:

Mark McLaren said...

damn...and i thought i had just created something original with my GeoURL to Google Earth transformation...and then i see it's old hat. never mind, it was a good coding/XSLT exercise nonetheless.
Note: Comment imported. Original by Patrick H. Lauke website: http://www.splintered.co.uk at 2005-08-15 17:08

Mark McLaren said...

Do you happen to know how to read the BBOX parameters passed by GE using a Perl CGI script?
Note: Comment imported. Original by Jim Bellenger at 2005-08-16 21:50

Mark McLaren said...

It's been a while since I did any Perl, something like this should work:





use CGI;

$query = new CGI;

@values = $query->param('BBOX');





This should create an array with a length of 4 items containing:





longitude_west, latitude_south, longitude_east, latitude_north




Note: Comment imported. Original by markmc website: http://content.mark-mclaren.info/ at 2005-08-16 22:46