SemAnt

Saturday, November 11, 2006

SVG specimen maps from SPARQL results

One reason why I've put off adding specimen maps to iSpecies, despite repeated requests, is that Google Maps (my preferred mapping tool) is slow if you have lots of specimen records. I've played with some other tools, notably Map Bureau's Flash-based pointMapper, but what I'd really like is a quick and simple way to display a bunch of specimen records. Because the same issue comes up with SemAnt, I thought it's time to do something about it. I stress that I really like Google Maps, but for some purposes it's overkill. Furthermore, loading hundreds of points will take too long.

So, the idea is to take georeferenced specimen records and put them on a map of the world. Slowly it dawned on me that this was trivially easy. Firstly, take a map of the world drawn using the equirectangular (or plate carrée) projection (Wikipedia provided the example below).



This projection a simple connection between geographic location and pixel position. For example, if the map is scaled to 180 pixels high and 360 pixels wide, then you have a 1 pixel/degree grid. Hence, plotting localities is no harder than plotting a X-Y scatter plot.

Now, all I need to do is take a SPARQL result with latitude and longitudes and draw the localities on this map. One way to do this is to draw the points using SVG, so I can use a XSL transformation to generate the map. If I wanted to support zooming then ideally I'd have the map itself in SVG, but I just want a small world map, so I "cheat" and use a bitmap as the base map. This can be included like this:

<image x="0" y="0" width="360" height="180"
xlink:href="http://...360px-Equirectangular-projection.jpg" />


The trick is to convert latitude and longitude to coordinates on the bitmap. For example, specimen casent0008682-d03 of Melissotarsus emeryi was collected from 31°58'0'' S, 18°51'0'' E, which in decimal values is latitude -31.966667, longitude 18.85. Now, how do I convert these values into a location on a 360 × 180 image? In SVG the coordinates grow from the upper left, whereas on the map shown above 0,0 is in the centre, such that southern latitudes are negative, as are western longitudes. We can use a transform to move the origin of the x- and y-axes to the left 180 pixels, and down 90 pixels, so that the origin of the graph is the intersection of the equator and Greenwich Meridian. We also have to invert the y-axis because in SVG it goes from top to bottom. This diagram shows the difference between SVG and geographical coordinates:



This transformation is achieved by this statement


<g transform="translate(180,90) scale(1,-1)" >
.
.
.
</g>


This idea came from hack #55 in Michael Fitzgerald's book XML Hacks. Here is the XSLT I use.

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:res="http://www.w3.org/2005/sparql-results#" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" exclude-result-prefixes="res xsl">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="/">
<svg>
<xsl:attribute name="width">360px</xsl:attribute>
<xsl:attribute name="height">180px</xsl:attribute>
<rect id="dot" x="0" y="0" width="4" height="4" style="stroke:none; stroke-width:1; fill:solid"/>
<image x="0" y="0" width="360" height="180" xlink:href="http://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Equirectangular-projection.jpg/360px-Equirectangular-projection.jpg"/>
<g transform="translate(180,90) scale(1,-1)">
<xsl:apply-templates select="//res:result"/>
</g>
</svg>
</xsl:template>
<xsl:template match="//res:result">
<use xlink:href="#dot">
<xsl:attribute name="transform">
<xsl:text>translate(</xsl:text>
<xsl:value-of select="res:binding[@name='long']/res:literal"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="res:binding[@name='lat']/res:literal"/>
<xsl:text>)</xsl:text>
</xsl:attribute>
</use>
</xsl:template>
</xsl:stylesheet>


This transforms a SPARQL result that looks something like this:

<result>
<binding name="lat"><literal>10.266666</literal></binding>
<binding name="long"><literal>-84.083336</literal></binding>
</result>


One thing which drove me nuts for a while was that the SVG rendered fine in Safari using Adobe's plugin, but not in Camino, which uses the same rendering engine as Mozilla. Turns out Camino needs http://www.w3.org/2000/svg to be the default namespace, so xmlns="http://www.w3.org/2000/svg" is fine, but it barfs over xmlns:svg="http://www.w3.org/2000/svg". Sigh.

Here is an example SVG file rendered using the XSLT style sheet, but using a different background map, showing the distribution of the ant Azteca constructor. The source SVG is here.



A nice, simple map, with minimal effort.

2 Comments:

  • so the only thing to do with this overview maps is to make them "clickabel" maps to lead back to the max resolution for those interested in more detail, which they could get from google earth: This would allow to see whether Melissotarsus has been collected in a forest or not...

    By Blogger Donat Agosti, at 10:26 am  

  • Yes, it would be easy to add options to get the data in Google Earth KML format, or display a Google Map, or even add the ability to zoom in on this map. At this stage I just want a quick, simple way to display the records.

    By Blogger Rod Page, at 11:08 am  

Post a Comment

Links to this post:

Create a Link

<< Home