<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://www.jenitennison.com/blog" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>visualisation</title>
 <link>http://www.jenitennison.com/blog/taxonomy/term/49</link>
 <description>The taxonomy view with a depth of 0.</description>
 <language>en</language>
<item>
 <title>SPARQL &amp; Visualisation Frustrations: Aggregation and Projection</title>
 <link>http://www.jenitennison.com/blog/node/127</link>
 <description>&lt;p&gt;Today, I&amp;#8217;m going to moan about the lack of features in SPARQL that are necessary to do many kinds of data analysis and visualisation. Going from raw data, held in RDF, to data like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;em&gt;average&lt;/em&gt; traffic flow along the M5&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;total&lt;/em&gt; amount claimed by each MP&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;number of&lt;/em&gt; corporate insolvency notices published each day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;cannot be done with SPARQL on its own. These calculations involve &lt;a href=&quot;http://www.w3.org/TR/sparql-features/#Aggregates&quot;&gt;aggregation, grouping&lt;/a&gt; and &lt;a href=&quot;http://www.w3.org/TR/sparql-features/#Project_expressions&quot;&gt;projection&lt;/a&gt; which are planned for SPARQL vNext, but not here yet (at least, not in any standard way or in every triplestore).&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the pretty graph to illustrate today&amp;#8217;s rant:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/files/insolvency-smooth.jpg&quot; alt=&quot;Corporate insolvency notices per day from the London Gazette since 1st May 2008, averaged over 20 days&quot; style=&quot;width: 100%&quot; /&gt;&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;The graph shows the number of notices of certain types placed in the &lt;a href=&quot;http://www.london-gazette.co.uk/&quot;&gt;London Gazette&lt;/a&gt; each day. The notices it summarises are those related to &lt;a href=&quot;http://www.insolvency.gov.uk/compulsoryliquidation/whatiscompulsoryliquidation.htm&quot;&gt;companies being liquidated&lt;/a&gt;, indicated by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.london-gazette.co.uk/issues/recent/10/corp-insolvency-winding-up-court/petitions-companies/start=1&quot;&gt;winding-up petitions&lt;/a&gt; indicating compulsory liquidation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.london-gazette.co.uk/issues/recent/10/corp-insolvency-winding-up-members/resolution/start=1&quot;&gt;members resolutions for winding-up&lt;/a&gt; indicating members&amp;#8217; voluntary liquidation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.london-gazette.co.uk/issues/recent/10/corp-insolvency-winding-up-creditors/resolution/start=1&quot;&gt;creditors resolutions for winding-up&lt;/a&gt; indicating creditors&amp;#8217; voluntary liquidation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.london-gazette.co.uk/issues/recent/10/corp-insolvency-administration/appointments/start=1&quot;&gt;appointment of administrators&lt;/a&gt; indicating companies going into administration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The graph is a version of:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/files/insolvency-raw.jpg&quot; alt=&quot;Corporate insolvency notices per day from the London Gazette since 1st May 2008&quot; style=&quot;width: 100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;with each data point averaged over 20 days. (The raw data spikes every Wednesday, presumably due to notices building up over the weekend and taking two days to appear in the Gazette.) It shows how the number of creditors&amp;#8217; voluntary liquidations (indicating companies that go insolvent and are unable to pay their creditors) doubled from around 30/day in May 2008 to around 60/day in the Spring of this year, but seems to be falling again (as far as we can tell; the data is not up-to-date).&lt;/p&gt;

&lt;p&gt;This data is brought to you by the RDFa embedded by &lt;a href=&quot;http://www.tso.co.uk/&quot;&gt;TSO&lt;/a&gt; in the notices on the London Gazette website and the scraping of said data into the &lt;a href=&quot;http://api.talis.com/stores/datagovuk&quot;&gt;datagovuk datastore&lt;/a&gt; held on the &lt;a href=&quot;http://www.talis.com/platform/&quot;&gt;Talis platform&lt;/a&gt;, for both of which we have &lt;a href=&quot;http://www.opsi.gov.uk/&quot;&gt;OPSI&lt;/a&gt; to thank.&lt;/p&gt;

&lt;p&gt;The visualisation is brought to you by a touch of experimental &amp;#8220;AJAR&amp;#8221; in &lt;a href=&quot;http://code.google.com/p/rdfquery&quot;&gt;rdfQuery&lt;/a&gt; and the graphing power of &lt;a href=&quot;http://code.google.com/p/flot/&quot;&gt;Flot&lt;/a&gt;. Here are the lengths I have to go to to get the pretty graph:&lt;/p&gt;

&lt;p&gt;First, I use rdfQuery to request a list of London Gazette issues since 1st May 2008. The SPARQL for the request is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;PREFIX corp-insolvency: &amp;lt;http://www.gazettes-online.co.uk/ontology/corp-insolvency#&amp;gt;
PREFIX g: &amp;lt;http://www.gazettes-online.co.uk/ontology#&amp;gt;
PREFIX xsd: &amp;lt;http://www.w3.org/2001/XMLSchema#&amp;gt;

CONSTRUCT {
  ?issue a g:Issue .
  ?issue g:hasPublicationDate ?date .
}
WHERE {
  ?issue a g:Issue .
  ?issue g:hasPublicationDate ?date .
  FILTER ( ?date &amp;gt; &quot;2008-05-01&quot;^^xsd:date ) .
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a &lt;code&gt;CONSTRUCT&lt;/code&gt; request because the resulting RDF/XML can be loaded into rdfQuery for querying. I &lt;em&gt;could&lt;/em&gt; do a &lt;code&gt;SELECT&lt;/code&gt; query and request JSON as the output format, but I&amp;#8217;m doing a kind of end-to-end RDF thing here. So I use rdfQuery to make the request, load the result into an rdfQuery object, query it, and iterate over the results.&lt;/p&gt;

&lt;p&gt;For each of the returned issues (all 293 of them), I make a &lt;em&gt;separate&lt;/em&gt; request for all the relevant notices within that issue. The SPARQL looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;PREFIX corp-insolvency: &amp;lt;http://www.gazettes-online.co.uk/ontology/corp-insolvency#&amp;gt;
PREFIX g: &amp;lt;http://www.gazettes-online.co.uk/ontology#&amp;gt;

CONSTRUCT {
  ?notice a ?type
}
WHERE {
  ?notice g:isInIssue $issue .
  { ?notice a corp-insolvency:MembersResolutionsForWindingUpNotice } UNION
  { ?notice a corp-insolvency:CreditorsResolutionsForWindingUpNotice } UNION
  { ?notice a corp-insolvency:AppointmentOfAdministratorNotice } UNION
  { ?notice a corp-insolvency:PetitionsToWindUpCompaniesNotice } .
  ?notice a ?type .
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once I&amp;#8217;ve got the RDF for those notices, I can use rdfQuery to select just those of a particular type, then count how many there are and use the result to plot the graph.&lt;/p&gt;

&lt;p&gt;Creating the graph involves 294 requests to the Talis store via the proxy that I&amp;#8217;m using to get around the cross-site scripting issues, each of which takes (in my experience) between 200ms and 4s. So it&amp;#8217;s pretty server-intensive for both the Talis servers and my proxy server (which is why I&amp;#8217;m not actually going to make the page available generally). It&amp;#8217;s also slow.&lt;/p&gt;

&lt;p&gt;What I &lt;em&gt;want&lt;/em&gt; to do is to be able to make four SPARQL requests that return RDF that summarise the number of notices of each of the different types on each date (or in each issue). I &lt;em&gt;want&lt;/em&gt; to write SPARQL queries that look something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;PREFIX corp-insolvency: &amp;lt;http://www.gazettes-online.co.uk/ontology/corp-insolvency#&amp;gt;
PREFIX g: &amp;lt;http://www.gazettes-online.co.uk/ontology#&amp;gt;

CONSTRUCT {
  ?issue a g:Issue .
  ?issue g:hasPublicationDate ?date .
  ?issue corp-insolvency:membersResolutionsForWindingUpNotices COUNT(?notice) .
}
WHERE {
  ?issue a g:Issue .
  ?issue g:hasPublicationDate ?date .
  ?notice g:isInIssue ?issue .
  ?notice a corp-insolvency:MembersResolutionsForWindingUpNotice .
}
GROUP BY ?issue
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Four requests would be &lt;em&gt;so&lt;/em&gt; much better than 294.&lt;/p&gt;

&lt;p&gt;The thing of it is that this kind of facility is available as standard in SQL, the &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/querylanguage.html#Group_By&quot;&gt;Google Visualisation API&amp;#8217;s simple query language&lt;/a&gt;, or in the &amp;#8220;reduce&amp;#8221; part of &lt;a href=&quot;http://en.wikipedia.org/wiki/MapReduce&quot;&gt;map/reduce&lt;/a&gt;. If we&amp;#8217;re to think of triplestores as a serious alternative to either relational or non-relational databases, and SPARQL as a serious alternative to either SQL or &lt;a href=&quot;http://en.wikipedia.org/wiki/Nosql&quot;&gt;NoSQL&lt;/a&gt;, then it really must support these operations. And Real Soon.&lt;/p&gt;

&lt;p&gt;In the meantime, I think the lesson for the publishers of linked data is to provide aggregated values for the obvious kinds of aggregations that people might want to do over your data. In the London Gazette data, that would be the counts of the various kinds of notices it contains. In the traffic flow data it would be the average, minimum and maximum traffic flow over each of the measured days, at each hour over the known dates and overall for each point.&lt;/p&gt;

&lt;p&gt;On a more philosophical note, it strikes me that the concept of aggregation contradicts the Open World assumption. I can only know that the number of members&amp;#8217; winding-up order notices was exactly 30 if I know that I know of &lt;em&gt;all&lt;/em&gt; the members&amp;#8217; winding-up order notices that exist. Pragmatically, in many cases this is going to be just fine, because we know that the datasets that we&amp;#8217;re using are complete (our World is Closed), but it does slightly concern me that it&amp;#8217;s impossible to do much useful data analysis without contradicting one of the fundamental tenets of the Semantic Web.&lt;/p&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/127#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/51">sparql</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <pubDate>Sat, 12 Sep 2009 22:42:45 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">127 at http://www.jenitennison.com/blog</guid>
</item>
<item>
 <title>More Crime</title>
 <link>http://www.jenitennison.com/blog/node/125</link>
 <description>&lt;p&gt;I wrote &lt;a href=&quot;http://www.jenitennison.com/blog/node/123&quot;&gt;previously&lt;/a&gt; about a visualisation using &lt;a href=&quot;http://www.homeoffice.gov.uk/about-us/publications/non-personal-data/&quot;&gt;Home Office data&lt;/a&gt; to navigate around categories of offences. The second interesting set of data from the Home Office that I found, tucked away in a small link on a page about &lt;a href=&quot;http://www.crimereduction.homeoffice.gov.uk/toolkits/dr0202.htm&quot;&gt;Crime Reduction Toolkits&lt;/a&gt; was a &lt;a href=&quot;http://www.homeoffice.gov.uk/rds/pdfs/100years.xls&quot;&gt;spreadsheet of recorded crime statistics&lt;/a&gt; between 1898 and the present day. Each column is a different category of offence (I won&amp;#8217;t say class because they don&amp;#8217;t map onto the Classes from the spreadsheet of notifiable offences).&lt;/p&gt;

&lt;p&gt;This time I wanted to try out the &lt;a href=&quot;http://www.omnipotent.net/jquery.sparkline/&quot;&gt;jQuery sparklines&lt;/a&gt; plug-in to illustrate how crime notifications have changed over time. The resulting page is available at &lt;a href=&quot;http://www.jenitennison.com/visualisation/crime.html&quot;&gt;http://www.jenitennison.com/visualisation/crime.html&lt;/a&gt;; here&amp;#8217;s a screenshot for Bigamy:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/files/crime.jpg&quot; alt=&quot;Summary statistics for rate of Bigamy within the UK&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;(I chose Bigamy because there are some interesting humps in the data, roughly aligned with the two World Wars, which demonstrate the value of looking at timelines.)&lt;/p&gt;

&lt;p&gt;I got this working by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cleaning up and processing the spreadsheet into RDF/XML&lt;/li&gt;
&lt;li&gt;putting the RDF/XML on my server in &lt;a href=&quot;http://www.jenitennison.com/data/scheme/crime/&quot;&gt;http://www.jenitennison.com/data/scheme/crime/&lt;/a&gt;, with more or less the same &lt;code&gt;.htaccess&lt;/code&gt; as I &lt;a href=&quot;http://www.jenitennison.com/blog/node/123&quot;&gt;used previously&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;creating a web page that included jQuery sparklines and &lt;a href=&quot;http://code.google.com/p/rdfquery&quot;&gt;rdfQuery&lt;/a&gt; as libraries and populates the page with details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the source code for &lt;code&gt;crime.html&lt;/code&gt; if you just go and look at it, but the relevant piece for populating the sparkline is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$.get(selected, null, function (rdfXml) {
  var values = {}, sparkline = [],
    ...
    counts = $.rdf()
      .load(rdfXml)
      .prefix(&#039;rdf&#039;, &#039;http://www.w3.org/1999/02/22-rdf-syntax-ns#&#039;)
      .prefix(&#039;crime&#039;, &#039;http://www.jenitennison.com/data/ontology/crime#&#039;)
      .where(&#039;&amp;lt;&#039; + selected + &#039;&amp;gt; crime:count ?count&#039;)
      .where(&#039;?count crime:startYear ?year&#039;)
      .where(&#039;?count rdf:value ?value&#039;)
      .each(function () {
        ...
        values[this.year.value] = this.value.value;
      });
  ...
  for (v in values) {
    sparkline.push([v, values[v]]);
  }
  $(&#039;#sparkline&#039;)
    .sparkline(sparkline, { 
      chartRangeMin: 0,
      lineColor: &#039;#999&#039;,
      fillColor: &#039;#EEE&#039;,
      spotColor: &#039;blue&#039;,
      minSpotColor: &#039;green&#039;,
      maxSpotColor: &#039;red&#039;
    });
}, &#039;xml&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(There is a reason for first creating an object representation of the values and then generating the array-of-arrays that the sparkline needs, but it&amp;#8217;s in the elided code so you&amp;#8217;ll have to look at the original source to see.)&lt;/p&gt;

&lt;p&gt;jQuery sparklines are ridiculously easy to create. I&amp;#8217;m looking forward to using more of the great variety of visualisations that they support.&lt;/p&gt;

&lt;p&gt;Now, the big problem with the data is that the ways in which crimes are classified and notified has changed over time. If you look at the &lt;a href=&quot;http://www.homeoffice.gov.uk/rds/pdfs/100years.xls&quot;&gt;original spreadsheet&lt;/a&gt; you&amp;#8217;ll see a bunch of notes that describe three kinds of changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes in the codes used for particular offences: for example Arson changed from classification 51 to 56 in 1934. I&amp;#8217;m not using the Home Office codes at the moment anyway, but this is going to be something to be wary of when I start doing so.&lt;/li&gt;
&lt;li&gt;Changes when a code is split into separate codes: for example &amp;#8220;Other wounding&amp;#8221; (8) split into &amp;#8220;Other wounding&amp;#8221; (8A), &amp;#8220;Possession of weapons&amp;#8221; (8B) and &amp;#8220;Harassment&amp;#8221; (8C) in 1998. In this case, offences stop being recorded under the original category and start being recorded under the new ones.&lt;/li&gt;
&lt;li&gt;Changes in whether a crime is notifiable: for example &amp;#8220;Cruelty to or neglect of children&amp;#8221; (11) became notifiable in 1998 (according to the notes; according to the data, at least some instances were notified up to 1952, but then there was a gap).&lt;/li&gt;
&lt;li&gt;Changes in how crimes are recorded: for example &amp;#8220;The introduction of the Sexual Offences Act 2003 in May 2004 resulted in substantial changes to the sexual offences.  This means that sexual offences data for 2004/05 are not comparable with those for previous years.&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are undoubtedly other effects that aren&amp;#8217;t listed in the notes, either in the legislation that covers a particular crime, or other environmental factors such as being at war (as shown above).&lt;/p&gt;

&lt;p&gt;What I&amp;#8217;d really like to do is to indicate these events in the sparkline and in the data table. Unfortunately it involves the translation of the loose notes from the spreadsheet into handcrafted RDF/XML, which is a little tedious. It&amp;#8217;s also frustrating that there&amp;#8217;s no good means of identification for the categories of offences. I&amp;#8217;ve ended up arbitrarily naming them &amp;#8216;A&amp;#8217; to &amp;#8216;FZ&amp;#8217; which is somewhat unsatisfactory.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s worth noting that although I have a closed data set I&amp;#8217;m explicitly using the Linked Data paradigm to go from a list of categories of crimes to retrieving information about a particular category (because the identifier for a category is a URI). If I weren&amp;#8217;t using RDF, and wanted to split up the data in the way that I have for manageability, I&amp;#8217;d have to document that particular properties contain pointers to information held at other locations. (Kris Zyp has attempted to &lt;a href=&quot;http://www.json-schema.org/draft-hyperschema-02.txt&quot;&gt;formalise this in a kind of schema for JSON&lt;/a&gt;, but I have no idea how much support for this there is.)&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve also used &lt;a href=&quot;http://www.w3.org/TR/skos-primer/&quot;&gt;SKOS&lt;/a&gt; to describe the categories, which is nice because all I have to tell you is that &lt;code&gt;http://www.jenitennison.com/data/scheme/crime/&lt;/code&gt; is a SKOS Concept Scheme and if you know SKOS you&amp;#8217;ll know how to locate the top concepts in that scheme, nice human readable labels for them, and so on.&lt;/p&gt;

&lt;p&gt;But if you want to reuse the counts of offences you will still have to actually look at the data to find the name of the property that I&amp;#8217;ve used to go from a category to a count, and for the years and values themselves. These semantics are local to this particular application and the only way you can know them is by being told, just as it would be if I were using JSON.&lt;/p&gt;

&lt;p&gt;So using RDF has bought us some things &amp;#8212; a level of understanding about reaching data and a common vocabulary for organising concept schemes &amp;#8212; but certainly not everything. It should be no surprise to anyone that it is not a magic bullet.&lt;/p&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/125#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/41">jQuery</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/40">rdfQuery</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <pubDate>Sun, 23 Aug 2009 20:26:49 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">125 at http://www.jenitennison.com/blog</guid>
</item>
<item>
 <title>Offence Hierarchy Visualisation with rdfQuery and JIT</title>
 <link>http://www.jenitennison.com/blog/node/123</link>
 <description>&lt;p&gt;The &lt;a href=&quot;http://www.homeoffice.gov.uk/&quot;&gt;Home Office&lt;/a&gt; recently &lt;a href=&quot;http://www.homeoffice.gov.uk/about-us/publications/non-personal-data/&quot;&gt;opened up some of its data&lt;/a&gt;, mostly in the form of PDF reports and Excel spreadsheets. Right after, I went on holiday and offline (!) for a week, so I set myself the task of putting together some visualisations of the data using two client-side visualisation libraries that I liked the look of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.omnipotent.net/jquery.sparkline/&quot;&gt;jQuery sparklines&lt;/a&gt; which I think look simply gorgeous and which follow the jQuery tradition of being incredibly easy to put on a page&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://thejit.org/&quot;&gt;the JavaScript InfoVis Toolkit (JIT)&lt;/a&gt; which can be used to create some very attractive and interactive visualisations for hierarchical information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a quick summary, I ended up with solutions that use an HTML page with &lt;a href=&quot;http://code.google.com/p/rdfquery&quot;&gt;rdfQuery&lt;/a&gt; code that pulls in static RDF/XML files and performs queries on them to create the particular formats that the two client-side libraries require.&lt;/p&gt;

&lt;p&gt;The first one I&amp;#8217;m going to talk about is a &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html&quot;&gt;visualisation of types of offences&lt;/a&gt; using JIT. There&amp;#8217;s a screenshot below to give you a flavour, but you&amp;#8217;d be better off actually &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html&quot;&gt;visiting the page&lt;/a&gt; because it&amp;#8217;s interactive: mousing over and clicking on the labels enables you to navigate around the hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;Visualisation of Criminal Damage offences&quot; src=&quot;/blog/files/offences.jpg&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;The data for this visualisation comes from a &lt;a href=&quot;http://www.homeoffice.gov.uk/rds/pdfs09/countnotif09.xls&quot;&gt;spreadsheet of notifiable offences&lt;/a&gt;, available amongst a bunch of interesting information about &lt;a href=&quot;http://www.homeoffice.gov.uk/rds/countrules.html&quot;&gt;counting rules for recording crime&lt;/a&gt;. The columns are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home Office code, which is split into a major and minor part (eg 6/4)&lt;/li&gt;
&lt;li&gt;Mode (&amp;#8216;I&amp;#8217; for indictable, &amp;#8216;E&amp;#8217; for triable-either-way or &amp;#8216;S&amp;#8217; for summary)&lt;/li&gt;
&lt;li&gt;Max sentence (eg life, 15, 3m, fine)&lt;/li&gt;
&lt;li&gt;Class (eg &amp;#8220;Violence Against The Person&amp;#8221;)&lt;/li&gt;
&lt;li&gt;Subclass (eg &amp;#8220;Endangering Railway Passengers&amp;#8221;)&lt;/li&gt;
&lt;li&gt;Offence (eg &amp;#8220;Destroying, damaging etc. a Channel Tunnel train or the Tunnel system or committing acts of violence likely to endanger safety of operation&amp;#8221;)&lt;/li&gt;
&lt;li&gt;Act(s) (eg &amp;#8220;Channel Tunnel Act 1987 Sec 1(7)&amp;#8221;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a couple of things that I find particularly interesting about this data.&lt;/p&gt;

&lt;p&gt;First, it includes references to legislation! Since my day job at &lt;a href=&quot;http://www.tso.co.uk/&quot;&gt;TSO&lt;/a&gt; is currently all about publishing legislation as linked data, I find this really exciting! I haven&amp;#8217;t done anything with those links yet, but I aim to.&lt;/p&gt;

&lt;p&gt;Second, you would have thought that the Home Office code would be tied to a particular Subclass or Offence, but it&amp;#8217;s not. The same Subclass can have multiple codes, but two Offences can have the same Home Office code. There doesn&amp;#8217;t seem to be a natural way of identifying the Offences, except through their (often long) descriptive name. The terminology for the Offence often comes straight out of a piece of legislation, but sometimes it&amp;#8217;s simply common law.&lt;/p&gt;

&lt;p&gt;On the other hand, the offence Classes have reasonably short labels like &amp;#8220;Burglary&amp;#8221; and &amp;#8220;Drug Offences&amp;#8221; which can be turned into URIs like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://www.jenitennison.com/data/scheme/offence/drug-offences
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The fact that Class-Subclass-Offence defines a hierarchy of concepts led me to think that &lt;a href=&quot;http://www.w3.org/TR/skos-primer/&quot;&gt;SKOS&lt;/a&gt; would be a good ontology to use to model it. The Classes and Subclasses can be plain old &lt;code&gt;skos:Concept&lt;/code&gt;s but the Offences need to have their own type so that extra information, such as the maximum sentence that applies to the offence, can be associated with it.&lt;/p&gt;

&lt;p&gt;So if you look at &lt;a href=&quot;http://www.jenitennison.com/data/scheme/offence/drug-offences&quot;&gt;http://www.jenitennison.com/data/scheme/offence/drug-offences&lt;/a&gt; you&amp;#8217;ll see RDF/XML that includes the triples:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;http://www.jenitennison.com/data/scheme/offence/drug-offences&amp;gt;
  a skos:Concept ;
  skos:topConceptOf &amp;lt;http://www.jenitennison.com/data/scheme/offence/&amp;gt; ;
  skos:prefLabel &quot;Drug Offences&quot;@en ;
  skos:narrower
    [ 
      a skos:Concept ;
      skos:inScheme &amp;lt;http://www.jenitennison.com/data/scheme/offence/&amp;gt; ;
      skos:broader &amp;lt;http://www.jenitennison.com/data/scheme/offence/drug-offences&amp;gt; ;
      skos:prefLabel &quot;Trafficking in controlled drugs&quot;@en ;
      skos:narrower
        [
          a crime:Offence ;
          skos:inScheme &amp;lt;http://www.jenitennison.com/data/scheme/offence/&amp;gt; ;
          skos:prefLabel &quot;Manufacturing a scheduled substance&quot;@en ;
          crime:maxSentence &quot;P14Y&quot;^^xsd:yearMonthDuration
        ],
        [
          a crime:Offence ;
          skos:inScheme &amp;lt;http://www.jenitennison.com/data/scheme/offence/&amp;gt; ;
          skos:prefLabel &quot;Supplying a scheduled substance to another person&quot;@en ;
          crime:maxSentence &quot;P14Y&quot;^^xsd:yearMonthDuration
        ],
        ...
    ],
    [
      a skos:Concept ;
      skos:inScheme &amp;lt;http://www.jenitennison.com/data/scheme/offence/&amp;gt; ;
      skos:broader &amp;lt;http://www.jenitennison.com/data/scheme/offence/drug-offences&amp;gt; ;
      skos:prefLabel &quot;Possession of controlled drugs&quot;@en ;
      skos:narrower
        ...
    ],
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;ll notice that I&amp;#8217;ve used blank nodes in the above rather than constructing an identifier for each Subclass or Offence. This makes things simpler because it means I can easily publish the dataset as a few flat files. An alternative would have been to use hash URIs, I suppose, but anyway this is the way I went. The (big) disadvantage is that it means the individual offences themselves aren&amp;#8217;t referenceable. So I might work on that, especially if I migrate the data over to data.gov.uk rather than just using it to try out a visualisation.&lt;/p&gt;

&lt;p&gt;The URI for the concept scheme is &lt;a href=&quot;http://www.jenitennison.com/data/scheme/offence/&quot;&gt;http://www.jenitennison.com/data/scheme/offence/&lt;/a&gt;. The slash on the end is entirely the result of trying to make Apache serve the static files correctly. As it is, I have one RDF/XML file for each Class of offence, plus an &lt;code&gt;index.rdf&lt;/code&gt; within the same (&lt;code&gt;offence&lt;/code&gt;) directory, with the &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;AddType application/rdf+xml .rdf
DirectoryIndex index.rdf
RewriteEngine On
RewriteRule ^([^\.]+)$ $1.rdf [L]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The concept scheme file itself contains a list of the top concepts in the scheme (the Classes) and their labels. This serves as a useful entry point to the data.&lt;/p&gt;

&lt;p&gt;So to the code. To create the visualisation, I needed to construct a Javascript structure that adhered to the &lt;a href=&quot;http://thejit.org/docs/files/Loader-js.html#Loader.loadJSON&quot;&gt;JIT Input JSON Structure&lt;/a&gt;. Basically, each &amp;#8220;node&amp;#8221; within the visualisation needed to have an &lt;code&gt;id&lt;/code&gt;, a &lt;code&gt;name&lt;/code&gt; and a number of &lt;code&gt;children&lt;/code&gt;. This structure needed to be constructed from the RDF/XML for a particular offence Class, ie that held within a particular RDF/XML document. The RDF/XML document can be accessed using the standard &lt;a href=&quot;http://docs.jquery.com/Ajax/jQuery.get#urldatacallbacktype&quot;&gt;&lt;code&gt;$.get()&lt;/code&gt; jQuery method&lt;/a&gt;. This passes the DOM for the document into the callback function passed as the third argument, which can then invoke &lt;a href=&quot;http://www.jenitennison.com/rdfquery/symbols/jQuery.rdf.html#load&quot;&gt;rdfQuery&amp;#8217;s &lt;code&gt;$.rdf.load()&lt;/code&gt; method&lt;/a&gt; to load the triples encoded in the RDF/XML into an rdfQuery object that can then operate over those triples.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the relevant part of the code, in which &lt;code&gt;view&lt;/code&gt; is the URI for the particular offence class and &lt;code&gt;ht&lt;/code&gt; is a JIT HyperTree instance:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$.get(view, null, function (rdfXml) {
  var rdf, offences = {};
  rdf = $.rdf()
    .load(rdfXml)
    .prefix(&#039;skos&#039;, &#039;http://www.w3.org/2004/02/skos/core#&#039;)
    .prefix(&#039;crime&#039;, &#039;http://www.jenitennison.com/data/ontology/crime#&#039;);
  rdf
    .where(&#039;&amp;lt;&#039; + view + &#039;&amp;gt; skos:prefLabel ?label&#039;)
    .each(function () {
      offences.id = view;
      offences.name = this.label.value;
      offences.data = {
        &#039;$color&#039;: &#039;#0CC&#039;,
        &#039;type&#039;: &#039;class&#039;
      };
      offences.children = rdf
        .where(&#039;&amp;lt;&#039; + view + &#039;&amp;gt; skos:narrower ?subclass&#039;)
        .where(&#039;?subclass skos:prefLabel ?label&#039;)
        .map(function () {
          return {
            id: this.subclass.id,
            name: this.label.value,
            data: {
              &#039;type&#039;: &#039;subclass&#039;
            },
            children: rdf
              .where(this.subclass + &#039; skos:narrower ?offence&#039;)
              .where(&#039;?offence skos:prefLabel ?label&#039;)
              .where(&#039;?offence crime:maxSentence ?sentence&#039;)
              .map(function () {
                var sentence;
                if (this.sentence.datatype.toString() === &#039;http://www.w3.org/2001/XMLSchema#token&#039;) {
                  sentence = this.sentence.value;
                } else if (this.sentence.value &amp;gt; 12) {
                  sentence = this.sentence.value / 12 + &#039; years&#039;;
                } else {
                  sentence = this.sentence.value + &#039; months&#039;;
                }
                return {
                  id: this.offence.id,
                  name: this.label.value,
                  data: {
                    &#039;type&#039;: &#039;offence&#039;,
                    &#039;sentence&#039;: sentence
                  },
                  children: []
                };
              })
              .get()
          };
        })
        .get();
    });
  ht.loadJSON(offences);
  ht.refresh();
}, &#039;xml&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can look at the rest of the code simply by viewing source on &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html&quot;&gt;&lt;code&gt;offences.html&lt;/code&gt;&lt;/a&gt; if you want to. It&amp;#8217;s mostly the same as the &lt;a href=&quot;http://thejit.org/Jit/Examples/Hypertree/example1.html&quot;&gt;HyperTree animation example&lt;/a&gt; but with a bit of refactoring particularly to add some jQuery goodness.&lt;/p&gt;

&lt;p&gt;Some random thoughts having done this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;rdfQuery is really good to use, even if I do say so myself. It provides a very flexible way of creating data structures based on RDF accessed from elsewhere, particularly because you have the full power of Javascript at your fingertips.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JIT itself is OK to work with, though it doesn&amp;#8217;t have the ease of use that it could have. The visualisation&amp;#8217;s reasonably attractive, but my attempts to do clever things with the size of nodes to reflect the severity of the sentence proved fruitless.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HyperTree visualisation works far better for smallish hierarchies (eg for &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html?offences=http%3A%2F%2Fwww.jenitennison.com%2Fdata%2Fscheme%2Foffence%2Fcriminal-damage&quot;&gt;Criminal Damage&lt;/a&gt;) than for large ones (eg &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html?offences=http%3A%2F%2Fwww.jenitennison.com%2Fdata%2Fscheme%2Foffence%2Fviolence-against-the-person&quot;&gt;Violence Against The Person&lt;/a&gt; or, if you have the patience, &lt;a href=&quot;http://www.jenitennison.com/visualisation/offences.html?offences=http%3A%2F%2Fwww.jenitennison.com%2Fdata%2Fscheme%2Foffence%2Fother-offences&quot;&gt;Other Offences&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Offence hierarchy itself is a bit of a mess. There are 738 &amp;#8216;Other Offences&amp;#8217; compared with 453 offences categorised within the other Classes, some of which contain only a handful of Offences. If this visualisation shows anything, it&amp;#8217;s how disorganised the offences are. Even more so if you take into account some of the other data that&amp;#8217;s been made available which I&amp;#8217;ll post about another time and shows a completely different classification. I wonder if there&amp;#8217;s data or other visualisations that would help identify where it could be rationalised.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The data is also out of date. I was surprised to see that it said that under the Piracy Act 1837 Section 2, Piracy with violence (one of the many &amp;#8216;Other Offences&amp;#8217;) still attracted a death penalty. But looking at the &lt;a href=&quot;http://www.statutelaw.gov.uk/documents/1837/88/ukpga/c88/2&quot;&gt;relevant Section on the Statue Law Database&lt;/a&gt; it appears that the death penalty was replaced with life imprisonment by &lt;a href=&quot;http://www.statutelaw.gov.uk/content.aspx?LegType=All+Legislation&amp;amp;searchEnacted=0&amp;amp;extentMatchOnly=0&amp;amp;confersPower=0&amp;amp;blanketAmendment=0&amp;amp;sortAlpha=0&amp;amp;PageNumber=0&amp;amp;NavFrom=0&amp;amp;parentActiveTextDocId=1570287&amp;amp;ActiveTextDocId=1570337&amp;amp;filesize=10394&quot;&gt;Section 36 of the Crime and Disorder Act 1998&lt;/a&gt;. Getting better links into the legislation itself might help identify similar problems with the offence data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I don&amp;#8217;t know how easy it would have been to create this visualisation if I hadn&amp;#8217;t been hosting the data myself. Danny Ayers put together a helpful post recently in which he listed the various ways of &lt;a href=&quot;http://blogs.talis.com/n2/archives/770&quot;&gt;getting around the restrictions in doing cross-domain Ajax&lt;/a&gt;, which I&amp;#8217;ll no doubt draw on if and when I need to do that.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/123#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/40">rdfQuery</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <pubDate>Sun, 16 Aug 2009 20:00:50 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">123 at http://www.jenitennison.com/blog</guid>
</item>
<item>
 <title>SPARQL &amp; Visualisation Frustrations: Linked Data</title>
 <link>http://www.jenitennison.com/blog/node/121</link>
 <description>&lt;p&gt;I&amp;#8217;ll start with the problem. To create the graphs I showed in &lt;a href=&quot;http://www.jenitennison.com/blog/node/120&quot;&gt;my last post&lt;/a&gt;, I wanted to split MPs into groups based on their party affiliation. Ideally, I wanted the Google Visualisation query to look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;select mp, additionalCosts, totalTravel, totalBasic 
where party = &#039;Conservative&#039; 
order by totalClaim desc 
limit 25
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;because this is reasonably easy to understand and for a developer to create without having to know any magic URIs.&lt;/p&gt;

&lt;p&gt;The party affiliation for an MP is given in the RDF supplied within the &lt;a href=&quot;http://guardian.dataincubator.org/&quot;&gt;Talis store&lt;/a&gt; as a pointer to one of the resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://dbpedia.org/resource/Labour_Party_(UK)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://dbpedia.org/resource/Conservative_Party_(UK)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://dbpedia.org/resource/Liberal_Democrats&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, if you visit &lt;a href=&quot;http://dbpedia.org/resource/Conservative_Party_(UK)&quot;&gt;http://dbpedia.org/resource/Conservative_Party_(UK)&lt;/a&gt; then you&amp;#8217;ll see precious few properties and none of them give you access to the string &amp;#8216;Conservative&amp;#8217;. If you look at &lt;a href=&quot;http://dbpedia.org/resource/Liberal_Democrats&quot;&gt;http://dbpedia.org/resource/Liberal_Democrats&lt;/a&gt;, you&amp;#8217;ll see plenty of properties, one of which is &lt;code&gt;dbpprop:partyName&lt;/code&gt;. But trying to query on &lt;code&gt;dbpprop:partyName&lt;/code&gt; within the Talis data store gives me nothing, because that information hasn&amp;#8217;t been imported into the particular store that this SPARQL query is running on.&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;What I did in &lt;code&gt;utils.php&lt;/code&gt; was extend the parsing of the &lt;code&gt;tq&lt;/code&gt; parameter, which is supposed to be in the Google Visualisation query language, to understand &lt;code&gt;&amp;lt;URI&amp;gt;&lt;/code&gt; as a reference to a resource. In other words, you can create a query like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;select mp, additionalCosts, totalTravel, totalBasic 
where rParty = &amp;lt;http://dbpedia.org/resource/Conservative_Party_(UK)&amp;gt; 
order by totalClaim desc 
limit 25
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and this will be mapped to a SPARQL query that looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT ?mp ?additionalCosts ?totalTravel ?totalBasic 
WHERE {
  ...
  FILTER (?rParty = &amp;lt;http://dbpedia.org/resource/Conservative_Party_(UK)&amp;gt;)
}
ORDER BY desc(?totalClaim)
LIMIT 25
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I don&amp;#8217;t like having done this, because I don&amp;#8217;t want Data Sources that happen to be SPARQL queries to look any different from other Data Sources. Introducing a new syntax for URI literals isn&amp;#8217;t really on.&lt;/p&gt;

&lt;p&gt;The superficial fix is to &lt;strong&gt;always provide basic labelling information for the resources referenced within a triplestore&lt;/strong&gt;. In this case, Leigh actually did include an &lt;code&gt;rdfs:label&lt;/code&gt; property for each of the party URIs within the Guardian store, so it was possible to use the query I wanted to use after all (though it took some experimentation to find this out).&lt;/p&gt;

&lt;p&gt;But underlying this is a bigger issue. Much is made of linked data &amp;#8212; that you can find out more about a particular thing by resolving the link to that thing &amp;#8212; but the best illustrations of the power and benefits of the semantic web tend to revolve around analysis and visualisations of moderately large amounts of data using SPARQL. And SPARQL (as yet) only runs on individual triplestores, which do not contain the entire semantic web. Every SPARQL query is limited by what has been loaded into the particular triplestore that is queried.&lt;/p&gt;

&lt;p&gt;Now, one of the &amp;#8220;time-permitting&amp;#8221; requirements for SPARQL 1.1 is &lt;a href=&quot;http://www.w3.org/TR/sparql-features/#Basic_federated_query&quot;&gt;Federated Queries&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Federated query is the ability to take a query and provide solutions based on information from many different sources. It is a hard problem in its most general form and is the subject of continuing (and continuous) research. A building block is the ability to have one query be able to issue a query on another SPARQL endpoint during query execution.&lt;/p&gt;
  
  &lt;p&gt;Time-permitting, the SPARQL Working Group will define the syntax and semantics for handling a basic class of federated queries in which the SPARQL endpoints to use in executing portions of the query are explicitly given by the query author.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That&amp;#8217;s certainly &amp;#8220;a building block&amp;#8221;, but it can&amp;#8217;t be the only method. For many data publishers, it&amp;#8217;s going to be far far simpler to publish their data as linked data in RDF/XML than it is to provide a SPARQL endpoint for that data. We can ask organisations like &lt;a href=&quot;http://www.talis.com/platform&quot;&gt;Talis&lt;/a&gt; to crawl our data and provide a SPARQL endpoint for it, and hope that the SPARQL Working Group have time to address federated search, but really we need tools that make it easy to aggregate, analyse and visualise linked data directly rather than through a triplestore silo.&lt;/p&gt;

&lt;p&gt;So how about it?&lt;/p&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/121#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/46">linked data</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/51">sparql</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/47">Talis</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <pubDate>Mon, 03 Aug 2009 20:36:34 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">121 at http://www.jenitennison.com/blog</guid>
</item>
<item>
 <title>Map Visualisation of MPs Travel Expenses</title>
 <link>http://www.jenitennison.com/blog/node/119</link>
 <description>&lt;p&gt;During &lt;a href=&quot;http://www.guardian.co.uk/media/pda/2009/jul/31/hacking-opensource1&quot;&gt;Guardian Hack Day 2&lt;/a&gt;, &lt;a href=&quot;http://www.ldodds.com/&quot;&gt;Leigh&lt;/a&gt; ported the &lt;a href=&quot;http://mps-expenses.guardian.co.uk/&quot;&gt;Guardian&amp;#8217;s MP&amp;#8217;s Expenses data&lt;/a&gt; into &lt;a href=&quot;http://guardian.dataincubator.org/&quot;&gt;Talis&lt;/a&gt;. Most wonderfully, this gives a &lt;a href=&quot;http://api.talis.com/stores/guardian/services/sparql&quot;&gt;SPARQL endpoint&lt;/a&gt; that can be used to query the data. I thought I&amp;#8217;d try to use the same approach as I &lt;a href=&quot;http://www.jenitennison.com/blog/node/113&quot;&gt;blogged about recently&lt;/a&gt;, using a SPARQL query as a &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html&quot;&gt;Data Source&lt;/a&gt; for a &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/gallery.html&quot;&gt;Google Visualisation&lt;/a&gt; of the MP&amp;#8217;s expenses data.&lt;/p&gt;

&lt;p&gt;To cut to the chase, here&amp;#8217;s a screenshot of &lt;a href=&quot;http://www.jenitennison.com/visualisation/mp-travel.html&quot;&gt;the result&lt;/a&gt; (follow the link for the more interactive version):&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;Map of travel expenses for the 100 MPs with the lowest majorities&quot; src=&quot;/blog/files/mp-travel.jpg&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;I created this visualisation with the same general approach as I &lt;a href=&quot;http://www.jenitennison.com/blog/node/113&quot;&gt;explained last time&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, I&amp;#8217;ve been working on the visualisation &lt;code&gt;utils.php&lt;/code&gt;, which is a reasonably simple PHP script that exposes a SPARQL endpoint as a Google Visualisation Data Source. Requests to a Data Source use a special &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/querylanguage.html&quot;&gt;query language&lt;/a&gt; to indicate the information that should be included, how it should be sorted, how many rows of data there should be, and so on.&lt;/p&gt;

&lt;p&gt;Previously, &lt;a href=&quot;/blog/files/utils.php_2.txt&quot;&gt;&lt;code&gt;utils.php&lt;/code&gt;&lt;/a&gt; only understood the &lt;code&gt;select&lt;/code&gt; portion of the &lt;code&gt;tq&lt;/code&gt; parameter which contains this query; I&amp;#8217;ve expanded it to understand (somewhat limited versions of) the &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;where&lt;/code&gt;, &lt;code&gt;order by&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt; parts of the query, which of course have equivalents in &lt;a href=&quot;http://www.w3.org/TR/rdf-sparql-query/&quot;&gt;SPARQL&lt;/a&gt;. Since these parts of the Google Visualisation query language are pretty close to SPARQL, this is actually just a bunch of string munging, which isn&amp;#8217;t particularly interesting, so just &lt;a href=&quot;/blog/files/utils.php_2.txt&quot;&gt;grab hold of it&lt;/a&gt; if you want to use it.&lt;/p&gt;

&lt;p&gt;Second, I created a PHP script (&lt;a href=&quot;/blog/files/mp-travel.php.txt&quot;&gt;&lt;code&gt;mp-travel.php&lt;/code&gt;&lt;/a&gt;) specifically for the MPs expenses data that pulls out the parts that I&amp;#8217;m interested in and exposes them as variables which can be used in the query language. This is what the file looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php
  include &quot;utils.php&quot;;
  proxy(&#039;?rMP a &amp;lt;http://guardian.dataincubator.org/ns/MemberOfParliament&amp;gt; .
         ?rMP &amp;lt;http://xmlns.com/foaf/0.1/name&amp;gt; ?mp .
         ?rMP &amp;lt;http://guardian.dataincubator.org/ns/mp-expenses/majority&amp;gt; ?majority .
         ?rMP &amp;lt;http://dbpedia.org/property/constituency&amp;gt; ?rConstituency .
         ?rConstituency rdfs:label ?constituency .
         ?rConstituency &amp;lt;http://www.w3.org/2003/01/geo/wgs84_pos#lat&amp;gt; ?lat .
         ?rConstituency &amp;lt;http://www.w3.org/2003/01/geo/wgs84_pos#long&amp;gt; ?long .
         ?rMP &amp;lt;http://guardian.dataincubator.org/ns/mp-expenses/total-travel&amp;gt; ?totalTravel .&#039;,
        &#039;desc(?totalTravel)&#039;, 
        &#039;guardian&#039;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second argument to the &lt;code&gt;proxy()&lt;/code&gt; function is the default ordering (&lt;code&gt;desc(?totalTravel)&lt;/code&gt;) and the third is the name of the Talis data store that&amp;#8217;s being used (&lt;code&gt;guardian&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The first argument is a query which determines the variables that are exposed by the Data Source. This Data Source exposes the variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mp&lt;/code&gt;: the name of the MP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;majority&lt;/code&gt;: the majority that they have in their constituency&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constituency&lt;/code&gt;: the name of the constituency&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lat&lt;/code&gt;, &lt;code&gt;long&lt;/code&gt;: the latitude and longitude of the constituency (presumably the centre of it)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;totalTravel&lt;/code&gt;: the total amount claimed for travel by the MP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rMP&lt;/code&gt;: the URI used to identify the MP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rConstituency&lt;/code&gt;: the URI used to identify the constituency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Third, I created an &lt;a href=&quot;/blog/files/mp-travel.html&quot;&gt;HTML document&lt;/a&gt; that used the Google Visualisation API to create the map visualisation that I&amp;#8217;ve shown above. The really important lines are:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var query = new google.visualization.Query(&#039;http://www.jenitennison.com/visualisation/data/mp-travel&#039;);
query.setQuery(&#039;select lat, long, totalTravel, mp order by majority limit 100&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first line shows the URL for the Data Source, which is essentially a pointer to the &lt;code&gt;mp-travel.php&lt;/code&gt; script. The second line shows the query that&amp;#8217;s sent to the Data Source: &amp;#8220;&lt;code&gt;select lat, long, totalTravel, mp order by majority limit 100&lt;/code&gt;&amp;#8221;.&lt;/p&gt;

&lt;p&gt;Put together, when you load &lt;a href=&quot;http://www.jenitennison.com/visualisation/mp-travel.html&quot;&gt;http://www.jenitennison.com/visualisation/mp-travel.html&lt;/a&gt;, you create a &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/gallery/geomap.html&quot;&gt;Google Visualisation GeoMap&lt;/a&gt; which uses as its data the result of the SPARQL query&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT ?lat ?long ?totalTravel ?mp
WHERE {
  ?rMP a &amp;lt;http://guardian.dataincubator.org/ns/MemberOfParliament&amp;gt; .
  ?rMP &amp;lt;http://xmlns.com/foaf/0.1/name&amp;gt; ?mp .
  ?rMP &amp;lt;http://guardian.dataincubator.org/ns/mp-expenses/majority&amp;gt; ?majority .
  ?rMP &amp;lt;http://dbpedia.org/property/constituency&amp;gt; ?rConstituency .
  ?rConstituency rdfs:label ?constituency .
  ?rConstituency &amp;lt;http://www.w3.org/2003/01/geo/wgs84_pos#lat&amp;gt; ?lat .
  ?rConstituency &amp;lt;http://www.w3.org/2003/01/geo/wgs84_pos#long&amp;gt; ?long .
  ?rMP &amp;lt;http://guardian.dataincubator.org/ns/mp-expenses/total-travel&amp;gt; ?totalTravel .
}
ORDER By ?majority
LIMIT 100
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;on the SPARQL endpoint at &lt;a href=&quot;http://api.talis.com/stores/guardian/services/sparql&quot;&gt;http://api.talis.com/stores/guardian/services/sparql&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s hoping you can reuse the Data Source or the code that was used to make it. Let me know if you do!&lt;/p&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/119#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/47">Talis</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <enclosure url="http://www.jenitennison.com/blog/files/utils.php_2.txt" length="4151" type="text/plain" />
 <pubDate>Fri, 31 Jul 2009 22:33:13 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">119 at http://www.jenitennison.com/blog</guid>
</item>
<item>
 <title>Creating Google Visualisations of Linked Data</title>
 <link>http://www.jenitennison.com/blog/node/113</link>
 <description>&lt;p&gt;&lt;em&gt;Update: For the people who couldn&amp;#8217;t read the post because the graph didn&amp;#8217;t have 0 as its x-axis minimum, here is the version of the graph that does. I haven&amp;#8217;t removed the other version, since doing so would make the comments confusing and I think it&amp;#8217;s interesting to compare the two.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/files/LononBoroughBarchart2.jpg&quot; alt=&quot;London Borough Life Expectancy Bar Chart with Y-Axis Minimum at 0&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are idealists who immediately see the publication of Open Data as a Good Thing, and leap up and down (metaphorically or physically) shouting &amp;#8220;Raw Data Now&amp;#8221;. There are also a whole bunch of people who need to &amp;#8220;see the shiny&amp;#8221;. They need to understand &lt;em&gt;why&lt;/em&gt; publishing Open Data is a Good Thing, and most particularly what the benefit is going to be to &lt;em&gt;them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is understandable. Publishers bear the cost of the development of URI schemes, XML formats, RDF ontologies, and other infrastructure for serving data, and the ongoing maintenance cost of domain resolution, bandwidth usage and user support. Even publishers with a public-service remit (who may not need to see &lt;em&gt;monetary&lt;/em&gt; payback) need to be convinced that there will be some kind of return on the investment.&lt;/p&gt;

&lt;p&gt;One result of making data available is that it enables you and others to easily construct nice visualisations over the data, and maybe spot useful patterns within it. This is particularly useful for public sector information because it can provide feedback on how effective a particular policy has been or where more resources need to be spent.&lt;/p&gt;

&lt;p&gt;So I thought it would be worthwhile trying to explore how to create visualisations of some data, starting with the &lt;a href=&quot;http://spreadsheets.google.com/ccc?key=t3bns85prAbiChLmFhlcB1Q&quot;&gt;London Borough data&lt;/a&gt; that I&amp;#8217;ve &lt;a href=&quot;http://www.jenitennison.com/blog/node/109&quot;&gt;published using Talis&lt;/a&gt;.&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;Nowadays, there are a bunch of visualisation libraries around. My first experiment is going to be using the &lt;a href=&quot;http://code.google.com/apis/visualization/&quot;&gt;Google Visualisation API&lt;/a&gt;, which offers a &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/gallery.html&quot;&gt;range of different, reasonably pretty, visualisations&lt;/a&gt; that work cross-browser using either SVG or VML. In this post, I&amp;#8217;m going to use the &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/gallery/barchart.html&quot;&gt;barchart visualisation&lt;/a&gt;, to create a chart of male and female life expectancy in the London Boroughs. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/files/LondonBoroughBarchart.jpg&quot; alt=&quot;London Borough life expectancy bar chart&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One of the interesting things about the Google Visualisation API is that you can provide the data for every visualisation using a &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html&quot;&gt;Data Source&lt;/a&gt;. A Data Source is any web page that, in the face of particular requests, returns JSON in a particular format. The format basically encodes a table with named (and typed) columns and a number of rows containing cells which have values for each of those columns.&lt;/p&gt;

&lt;p&gt;For the London Borough data, the JSON needs to look something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;google.visualization.Query.setResponse({
  version:0.6,
  status:&#039;ok&#039;,
  reqId:0,
  table:{
    cols:[
      {id:&#039;label&#039;, type:&#039;string&#039;},
      {id:&#039;maleLE&#039;, type:&#039;number&#039;},
      {id:&#039;femaleLE&#039;, type:&#039;number&#039;}
    ],
    rows:[
      {c:[
        {v:&#039;Barking &amp;amp; Dagenham&#039;},
        {v:76.3},
        {v:80.3}]},
      {c:[
        {v:&#039;Barnet&#039;},
        {v:79.5},
        {v:83.6}]},
      {c:[
        {v:&#039;Bexley&#039;},
        {v:78.7},
        {v:82.7}]},
      ...
    ]}
})
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we already know, from my &lt;a href=&quot;http://www.jenitennison.com/blog/node/110&quot;&gt;last couple&lt;/a&gt; &lt;a href=&quot;http://www.jenitennison.com/blog/node/111&quot;&gt;of blog posts&lt;/a&gt;, how to query the Talis platform through its &lt;a href=&quot;http://n2.talis.com/wiki/Store_Sparql_Service&quot;&gt;SPARQL endpoint&lt;/a&gt;, and how to use &lt;a href=&quot;http://n2.talis.com/wiki/Transformation_Service&quot;&gt;Talis&amp;#8217; transformation service&lt;/a&gt; to invoke an XSLT transformation over the XML that we get back from that. The same principle applies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a SPARQL query that will return the information we want&lt;/li&gt;
&lt;li&gt;create an XSLT transformation that will tidy the result into the format we want it&lt;/li&gt;
&lt;li&gt;set up a PHP page to access the relevant URI and pass through the results&lt;/li&gt;
&lt;li&gt;create an .htaccess file so that invoking the PHP can be done without revealing it&amp;#8217;s PHP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, the SPARQL query. As I said, a Data Source is essentially a table of named columns. While we could do a &lt;code&gt;DESCRIBE&lt;/code&gt; or &lt;code&gt;CREATE&lt;/code&gt; query, a &lt;code&gt;SELECT&lt;/code&gt; query is a lot closer match to the tabular layout that the Data Source needs because it&amp;#8217;s also essentially a table of named columns (the variables you select). For the data that we want in the table, an appropriate SPARQL query would be:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT ?label ?maleLE ?femaleLE
WHERE {
  ?borough a &amp;lt;http://www.jenitennison.com/ontology/data#LondonBorough&amp;gt; .
  ?borough &amp;lt;http://www.w3.org/2000/01/rdf-schema#label&amp;gt; ?label .
  ?borough &amp;lt;http://www.jenitennison.com/ontology/data#maleLifeExpectancy&amp;gt; ?maleLE .
  ?borough &amp;lt;http://www.jenitennison.com/ontology/data#femaleLifeExpectancy&amp;gt; ?femaleLE .
}
ORDER BY ?label
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Plugging this query into the Talis SPARQL endpoint (have a go at &lt;a href=&quot;http://api.talis.com/stores/rdfquery-dev1/services/sparql&quot;&gt;http://api.talis.com/stores/rdfquery-dev1/services/sparql&lt;/a&gt; if you like) gives a response in the &lt;a href=&quot;http://www.w3.org/TR/rdf-sparql-XMLres/&quot;&gt;SPARQL Query Results Format&lt;/a&gt; which looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;sparql xmlns=&quot;http://www.w3.org/2005/sparql-results#&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;variable name=&quot;label&quot;/&amp;gt;
    &amp;lt;variable name=&quot;maleLE&quot;/&amp;gt;
    &amp;lt;variable name=&quot;femaleLE&quot;/&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;results&amp;gt;
    &amp;lt;result&amp;gt;
      &amp;lt;binding name=&quot;label&quot;&amp;gt;
        &amp;lt;literal&amp;gt;Barking &amp;amp;amp; Dagenham&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;maleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;76.3&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;femaleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;80.3&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
    &amp;lt;/result&amp;gt;
    &amp;lt;result&amp;gt;
      &amp;lt;binding name=&quot;label&quot;&amp;gt;
        &amp;lt;literal&amp;gt;Barnet&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;maleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;79.5&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;femaleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;83.6&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
    &amp;lt;/result&amp;gt;
    &amp;lt;result&amp;gt;
      &amp;lt;binding name=&quot;label&quot;&amp;gt;
        &amp;lt;literal&amp;gt;Bexley&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;maleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;78.7&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
      &amp;lt;binding name=&quot;femaleLE&quot;&amp;gt;
        &amp;lt;literal datatype=&quot;http://www.w3.org/2001/XMLSchema#decimal&quot;&amp;gt;82.7&amp;lt;/literal&amp;gt;
      &amp;lt;/binding&amp;gt;
    &amp;lt;/result&amp;gt;
    ...
  &amp;lt;/results&amp;gt;
&amp;lt;/sparql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second step &amp;#8212; transforming this SPARQL result into JSON &amp;#8212; just take a little bit of XSLT (1.0, remember, because that&amp;#8217;s all the Talis&amp;#8217; Transformation Service can manage). My aim in this post is to show that anyone, even if they don&amp;#8217;t have write access to a Talis data store, can create these visualisations, so I&amp;#8217;ve just put the XSLT on my site at &lt;a href=&quot;http://www.jenitennison.com/visualisation/data/SRXtoGoogleVisData.xsl&quot;&gt;http://www.jenitennison.com/visualisation/data/SRXtoGoogleVisData.xsl&lt;/a&gt;. I&amp;#8217;m not going to duplicate it here; it&amp;#8217;s generic enough for reuse should you want to.&lt;/p&gt;

&lt;p&gt;The third step is to create some PHP that handles a query from the Google Visualisation. The requests will include &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html#requestformat&quot;&gt;two parameters&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tqx&lt;/code&gt; defines details about how the data should be returned, such as its format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tq&lt;/code&gt; defines a query in the &lt;a href=&quot;http://code.google.com/apis/visualization/documentation/querylanguage.html&quot;&gt;Google Visualisation API query language&lt;/a&gt; that identifies precisely the data that should be returned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want this PHP to be reusable, so I&amp;#8217;ve created a &lt;code&gt;utils.php&lt;/code&gt; that looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php
  $store = &#039;rdfquery-dev1&#039;;

  function proxy($filter, $order) {
    global $store;
    $tqx = $_GET[&#039;tqx&#039;];
    $tq = $_GET[&#039;tq&#039;];

    // Parse tq parameter
    if ($tq) {
      $select = stristr($tq, &#039;select &#039;);
      $select = substr($select, 7);
      $select = explode(&#039;,&#039;, $select);
      foreach ($select as $var) {
        $var = trim($var);
        $vars[] = &quot;?$var&quot;;
      }
      $vars = implode(&#039; &#039;, $vars);
    } else {
      $vars = &#039;*&#039;;
    }
    $sparql = &quot;SELECT $vars WHERE { $filter } ORDER BY $order&quot;;

    $params = array(&#039;query&#039; =&amp;gt; $sparql, &#039;output&#039; =&amp;gt; &#039;xml&#039;);
    $query = http_build_query($params);
    $rdfURL = &quot;http://api.talis.com/stores/$store/services/sparql?$query&quot;;

    // URL for the transformation
    $params = array(&#039;xml-uri&#039; =&amp;gt; $rdfURL, 
      &#039;xsl-uri&#039; =&amp;gt; &quot;http://www.jenitennison.com/visualisation/data/SRXtoGoogleVisData.xsl&quot;,
      &#039;tqx&#039; =&amp;gt; $tqx);
    $query = http_build_query($params);
    $txURL = &quot;http://api.talis.com/tx?$query&quot;;

    $resource = fopen($txURL, &#039;rb&#039;);
    header(&#039;Content-Type: application/json&#039;, true);
    fpassthru($resource);
    return;
  }
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Things to note here are about the processing of the &lt;code&gt;tq&lt;/code&gt; and &lt;code&gt;tqx&lt;/code&gt; parameters that are sent by the Google Visualisation to control what and how the data appears. The &lt;code&gt;tqx&lt;/code&gt; parameter gets passed through into the stylesheet as a parameter, and parsed there. The &lt;code&gt;tq&lt;/code&gt; parameter is used to construct the SPARQL query itself, specifically which variables will get included within the result. The rest of the SPARQL query &amp;#8212; the filter and the ordering &amp;#8212; are set in the code which calls the &lt;code&gt;proxy()&lt;/code&gt; function, which is in &lt;code&gt;london-borough.php&lt;/code&gt; within the same directory, and looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php
  include &quot;utils.php&quot;;
  proxy(&#039;?borough a &amp;lt;http://www.jenitennison.com/ontology/data#LondonBorough&amp;gt; .
         ?borough &amp;lt;http://www.w3.org/2000/01/rdf-schema#label&amp;gt; ?label .
         ?borough &amp;lt;http://www.jenitennison.com/ontology/data#maleLifeExpectancy&amp;gt; ?maleLE .
         ?borough &amp;lt;http://www.jenitennison.com/ontology/data#femaleLifeExpectancy&amp;gt; ?femaleLE .&#039;, 
        &#039;?label&#039;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code defines the names of the variables that can be used within the &lt;code&gt;tq&lt;/code&gt; parameter, and therefore selected and displayed within the graph. So for example, if I request:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://www.jenitennison.com/visualisation/data/london-borough?tq=select+label,+maleLE
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;this translates into the SPARQL query:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT ?label ?maleLE
WHERE {
  ?borough a &amp;lt;http://www.jenitennison.com/ontology/data#LondonBorough&amp;gt; .
  ?borough &amp;lt;http://www.w3.org/2000/01/rdf-schema#label&amp;gt; ?label .
  ?borough &amp;lt;http://www.jenitennison.com/ontology/data#maleLifeExpectancy&amp;gt; ?maleLE .
  ?borough &amp;lt;http://www.jenitennison.com/ontology/data#femaleLifeExpectancy&amp;gt; ?femaleLE .
}
ORDER BY ?label
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At the moment, the PHP just uses the &lt;code&gt;select&lt;/code&gt; portion of the &lt;code&gt;tq&lt;/code&gt; parameter to determine which data to display. It would be possible to map other aspects of the Google Visualisation query language onto SPARQL, but this will do for now.&lt;/p&gt;

&lt;p&gt;The final step is to amend the .htaccess to do a basic rewrite to prevent people from having to put .php at the end of the URI because I don&amp;#8217;t like URIs that indicate the technology they&amp;#8217;re using. In this case it looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_rewrite.c&amp;gt;
  RewriteEngine on
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d

  RewriteRule ^(.+) $1.php [L,QSA]
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;as this gives me flexibility later on to add more PHP files that can do similar things.&lt;/p&gt;

&lt;p&gt;So now we have a Data Source that can provide the label, male life expectancy and female life expectancy for the London Boroughs. Using it requires a copy and tweak of an example from Google:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot; src=&quot;http://www.google.com/jsapi&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      google.load(&quot;visualization&quot;, &quot;1&quot;, {packages:[&quot;barchart&quot;]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        // Replace the data source URL on next line with your data source URL.
        var query = new google.visualization.Query(&#039;http://www.jenitennison.com/visualisation/data/london-borough&#039;);
        query.setQuery(&#039;select label, maleLE, femaleLE&#039;);
        // Send the query with a callback function.
        query.send(handleQueryResponse);
      };
      function handleQueryResponse(response) {
        if (response.isError()) {
          alert(&#039;Error in query: &#039; + response.getMessage() + &#039; &#039; + response.getDetailedMessage());
          return;
        }

        var data = response.getDataTable();
        data.setColumnLabel(1, &#039;Male Life Expectancy&#039;);
        data.setColumnLabel(2, &#039;Female Life Expectancy&#039;);
        var chart = new google.visualization.BarChart(document.getElementById(&#039;chart_div&#039;));
        chart.draw(data, {
          width: 600, 
          height: 450, 
          is3D: false, 
          title: &#039;Life Expectancy in London Boroughs&#039;,
          axisFontSize: 10,
          colors: [&#039;#4162B5&#039;, &#039;#CF413A&#039;]});
      };
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;chart_div&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;drawChart()&lt;/code&gt; function is where the URL to the Data Source gets set. It&amp;#8217;s actually done in two parts:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var query = new google.visualization.Query(&#039;http://www.jenitennison.com/visualisation/data/london-borough&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;sets the base URI to be used, and:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;query.setQuery(&#039;select label, maleLE, femaleLE&#039;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;sets the value of the &lt;code&gt;tq&lt;/code&gt; parameter without you having to worry about escaping the special characters it may contain.&lt;/p&gt;

&lt;p&gt;After getting the data, I set the column labels in the code itself: they aren&amp;#8217;t provided in the Data Source because it&amp;#8217;s easier and more generic this way. Plus I set a bunch of other display options within the HTML page, so it seems like the right place for it.&lt;/p&gt;

&lt;p&gt;The result of all this is a graph that you can see at &lt;a href=&quot;http://www.jenitennison.com/visualisation/london-borough-life-expectancy.html&quot;&gt;http://www.jenitennison.com/visualisation/london-borough-life-expectancy.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to do something similar, feel free to grab hold of &lt;code&gt;utils.php&lt;/code&gt;. You can either reuse my hosted copy of &lt;code&gt;SRXtoGoogleVisData.xsl&lt;/code&gt; or move it onto your own site. Then all you have to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;adjust the graph variable in &lt;code&gt;utils.php&lt;/code&gt;, and the location of &lt;code&gt;SRXtoGoogleVisData.xsl&lt;/code&gt; if you need to&lt;/li&gt;
&lt;li&gt;create another PHP file similar to &lt;code&gt;london-borough.php&lt;/code&gt; that defines a filter and an ordering over a set of data&lt;/li&gt;
&lt;li&gt;tweak your .htaccess if you want to&lt;/li&gt;
&lt;li&gt;create an HTML page that references your Data Source to create a Google Visualisation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My current plan is to continue to refine &lt;code&gt;utils.php&lt;/code&gt; and &lt;code&gt;SRXtoGoogleVisData.xsl&lt;/code&gt; to make it easy to create SPARQL-backed visualisations. More soon.&lt;/p&gt;
</description>
 <comments>http://www.jenitennison.com/blog/node/113#comments</comments>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/31">rdf</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/47">Talis</category>
 <category domain="http://www.jenitennison.com/blog/taxonomy/term/49">visualisation</category>
 <enclosure url="http://www.jenitennison.com/blog/files/utils.php_1.txt" length="1160" type="text/plain" />
 <pubDate>Thu, 23 Jul 2009 21:37:33 +0000</pubDate>
 <dc:creator>Jeni</dc:creator>
 <guid isPermaLink="false">113 at http://www.jenitennison.com/blog</guid>
</item>
</channel>
</rss>

