Monday, March 05, 2007

Tuttocittá

Ho scoperto una cosa curiosa riguardo il sito di Tuttocittá.
Se inviate una query HTTP del tipo

http://www.tuttocitta.it/tcoln/action?msez=500&com=milano&in=via%20solferino&nc=16&_=

(per esempio con curl) riceverete una risposta fatta cosí (ho aggiunto qualche a capo per rendere il tutto piú leggibile):

<code>0</code>
<x>9.187145</x>
<y>
45.4745</y>
<z>
0.6</z>
<zcent>
0.6</zcent>
<fraz></fraz>
<c>
Milano</c>
<com>
Milano (MI)/com>
<prov>
mi</prov>
<topo>
Via Solferino</topo>
<civ>
16</civ>
<cdloc>
51557</cdloc>
<reg>
Lombardia</reg>
<ul></ul>

Insomma, un frammento di XML. Frammento perché non ha un elemento radice. L'origine della cosa é ovviamente l'approccio Ajax della pagina di Tuttocittá.

Bene. Questo si presta ad essere utilizzato in modi interessanti. Se fosse XML e non un frammento tutto sarebbe piú semplice.

Supponiamo per esempio di avere un elenco di indirizzi e volerli geocodificare (recuperare le coordinate geografiche) tutti. E magari avere come risultato un file GPX per poter visualizzare i diversi punti in Google Earth. O ancora inviare i punti al proprio GPS.

Supponiamo inoltre che gli indirizzi siano giá in formato XML. Per esempio così:

<results>
  <match>
    <group id="nome" value="AMANTE CASELLA CENTRO SRL" />
    <group id="indirizzo" value="C.SO MATTEOTTI 61 , TORINO 10121 (TO) 011/540641 " />
    <group id="tipo" value="C.SO" />
    <group id="via" value="MATTEOTTI" />
    <group id="civico" value="61" />
    <group id="citta" value="TORINO" />
    <group id="CAP" value="10121" />
    <group id="prov" value="(TO)" />
    <group id="tel" value="011/540641" />
  </match>
  ...
</results>

(Questo esempio é stato effettivamente prodotto da uno strumento di prova di espressioni regolari con possibilitá di esportare i risultati in XML).

Se l'output di Tuttocittá fosse effettivamente XML potremmo scrivere uno stylesheet XSLT che, facendo leva sulla funcione XPath document() recupera latitudine e longitudine per ogni indirizzo. Cosí non é, ma possiamo scrivere una estensione XSL che ci consenta di trattare opportunamente frammenti XML. Le poche righe di codice C# che seguono servono allo scopo:

using System;
using
System.IO;
using
System.Text;
using
System.Xml;
using
System.Xml.XPath;
using
System.Xml.Xsl;
using
System.Net;
public class DocFrag {
   
public XPathNodeIterator documentFragment(string url) {
       
WebClient wc=new WebClient();
       
string s=wc.DownloadString(url);
       
s="<root>"+s+"</root>";
       
XPathDocument xd=new XPathDocument(new StringReader(s));
       
XPathNavigator xpn=xd.CreateNavigator();
       
return xpn.Select("/");
   
}
}

(Da compilare con il comando "csc -t:library -debug+ -D:DEBUG docfrag.cs")

Con questa ulteriore arma siamo pronti per scrivere lo stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

               xmlns:ext="http://www.docfrag.org">

       <xsl:output indent='yes'/>

 

       <xsl:template match="*"/>

      

       <xsl:template match="/">

               <gpx version="1.1" creator="GPX.XSL"

                               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                               xmlns="http://www.topografix.com/GPX/1/1"

                               xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">

                       <xsl:for-each select="results/match">

                               <xsl:variable name='url'>

                                       <xsl:text>http://www.tuttocitta.it/tcoln/action?msez=500&amp;com=</xsl:text>

                                       <xsl:value-of select="group[@id='citta']/@value"/>

                                       <xsl:text>&amp;in=</xsl:text>

                                       <xsl:value-of select="group[@id='tipo']/@value"/>

                                       <xsl:text>%20</xsl:text>

                                       <xsl:value-of select="group[@id='via']/@value"/>

                                       <xsl:text>&amp;nc=</xsl:text>

                                       <xsl:value-of select="group[@id='civico']/@value"/>

                                       <xsl:text>&amp;_=</xsl:text>

                               </xsl:variable>

 

                               <xsl:variable name='doc' select="ext:documentFragment($url)"/>

 

                               <xsl:choose>

                                       <xsl:when test='$doc/root/x and $doc/root/y'>

                                               <wpt lon='{$doc/root/x}' lat='{$doc/root/y}'>

                                                       <name><xsl:value-of select="group[@id='nome']/@value"/></name>

                                                       <desc><xsl:value-of select="group[@id='indirizzo']/@value"/></desc>

                                               </wpt>

                                       </xsl:when>

                                       <xsl:otherwise>

                                               <xsl:message>Non trovato: <xsl:value-of select="group[@id='indirizzo']/@value"/></xsl:message>

                                       </xsl:otherwise>

                               </xsl:choose>

                       </xsl:for-each>

               </gpx>

       </xsl:template>

</xsl:stylesheet>

Per applicare lo stylesheet utilizzeró l'ottimo nxslt di Oleg Tkachecnko con il comando:

nxslt indirizzi.xml gpx.xsl -af docfrag.dll -ext DocFrag xmlns=http://www.docfrag.org -o p.gpx

...e il gioco é fatto.

del.icio.us tags: , , , ,

No comments: