Interface graphique de traitement de données RDF, avec une application géographique

Annexes

Cédric Kiss,
Ingénieur IMAC, promotion ING2003,
Université de Marne-la-Vallée.
Projet SWAD-Europe, IST
The World Wide Web Consortium,
mai à octobre 2003.
 
IST Logo

Version 2.0 – septembre 2003

Sommaire des annexes

Annexe A : Le code source du script

Voici le code source du script.

<?xml version="1.0" encoding="iso-8859-1"?>
<svg xml:lang="fr"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ced="./cedricExtensions"
xmlns:a3="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
height="100%" width="100%"
onload="init(evt)" onzoom="resizeOnZoom()"
>
<!-- I removed the a3:scriptImplementation="Adobe" statements because I found it incredibly slow with hi-res data.
The embedded parses takes 1.5 secs to do the _same_ job as Adobe's one in 1 hour... so I made my choice..... :o]
-->
<!-- viewBox="0 0 1000 536" -->

<title>Zones du monde</title>

<desc xml:lang="fr">
Cédric Kiss -- SWAD-Europe / W3C -- Un projet IST -- 2003.
Cette carte repère des zones, localise des personnes,
et affiche en RDF quelles zones sont communes à une position (entrée à la souris).
</desc>


<!-- Analyseur syntaxique RDF de Jim Ley
<script xlink:href="rdfparser.seeAlso.js" type="text/ecmascript" a3:scriptImplementation="Adobe"/>
-->

<!-- Crypteur SHA1 pour les adresses mail -->
<script xlink:href="sha1.js" type="text/ecmascript"/>
<!-- a3:scriptImplementation="Adobe" -->


<defs>
<style type="text/css"><![CDATA[
circle{stroke-width: 0.2px; opacity: 0.6;}
text{font-size: 7pt;}
.countryOutlines{opacity: .5; fill: orange; stroke: none;}
.countryOutline1{opacity: .5; fill: orange; stroke: none;}
.countryOutline2{opacity: .5; fill: yellow; stroke: none;}
.countryOutline3{opacity: .5; fill: purple; stroke: none;}
.countryOutline4{opacity: .5; fill: red; stroke: none;}
.countryOutline5{opacity: .5; fill: blue; stroke: none;}
.airportIcao{opacity: 0.6; fill: yellow;}
.airportIata{opacity: 0.6; fill: red;}
.actor{opacity: 0.7; fill: white; stroke: black;}
.graduation{font-family: courier new; font-weight: bold; font-size: 22pt;}
.texteTravail{font-size: 30pt;}
#airportName{font-size: 10pt;}
#actorsAtAirport{font-size: 8pt;}
#credits{fill: black;}
#scriptResults{font-family: courier new; font-size: 10pt;}
]]></style>

<rect id="bouton" height="20" width="40" fill="red" rx="2" stroke="black" />

<g id="compteur">
<path id="compteur_fond" d="M -100 0 L 0 0 L 100 0 A 100 100 0 0 0 -100 0 Z" fill="white"/>
<path id="compteur_remplissage" stroke="white" fill="orange" d="" />
<path id="compteur_contour" d="M -100 0 L 0 0 L 100 0 A 100 100 0 0 0 -100 0 Z" stroke="black" stroke-width="1.6" fill="none"/>
<text x="-95" y="-5" class="graduation">0%</text>
<text x="95" y="-5" class="graduation" style="text-anchor: end;">100%</text>
<path id="aiguille" transform="scale(1,1.7)" fill="yellow" stroke="black"
d="M 0 -2 l -92 0 l 0 -3 l -15 5 l 15 5 l 0 -3 l 92 0 A 3 3 0 0 0 0 -2 Z" />
<text x="112" y="-52" class="texteTravail">Travail en cours :
<tspan x="112" y="0" id="texteTravailEnCours" class="texteTravail">N/A.</tspan>
</text>
</g>

</defs>


<metadata>
author: Cédric Kiss - http://ckiss.net/cedric
created: May to August 2003
homepage: http://www.w3.org/2001/sw/Europe/200306/geo/readme_carte_zones_monde_rdf.html
Part of W3C SWAD-Europe project.
</metadata>


<!--
'onclick' attributes :
traceOutlines(evt) to draw manually the outlines of a country on the map (deprecated)
locateMousePoint(evt) to discover the region of a given point
-->
<g id="world_map" onclick="locateMousePoint(evt)">

// (fr) On imagine que le monde a une largeur de 1000 pixels, et on l'affiche
// (en) Assume the world's width is 1000 pixels and start drawing !
<rect id="mapBackground" x="0" y="0" width="1000" height="500" fill="lightblue"/>
<image opacity="0.8" xlink:href="images/cartes/Day_lrg.jpg" width="1000" height="500"/>
<image opacity="0.535" xlink:href="images/cartes/utmworld_ced.png" x="-20" y="17" width="1052.4" height="479.6"/>

<g id="world_outlines" style="visibility: visible;">
<zoneType>Planete</zoneType>
<title>Planete Terre</title>

<g id="europe_outlines">
<zoneType>Continent</zoneType>
<title>Europe</title>

<!-- (fr) Note : les 'd' doivent tenir sur une seule ligne -->
<!-- (en) Notice: 'd' descriptions should be single-lined -->

<g id="france_outlines">
<zoneType>Pays</zoneType>
<title>France</title>

<g id="france_outlines_lowres">
<!-- (fr) Utiliser lorsque aucune autre donnée n'est disponible -->
<!-- (en) Use when there is no other data available -->
<g>
<zoneType>Autres</zoneType>
<path id="fralt000001" class="countryOutlines" d="M 508.25 134.25 L 496.0625 129.5625 L 496.5625 122.25 L 492.9375 118.8125 L 486.875 116.9375 L 487.125 115.5 L 494.9375 115.0625 L 494.9375 112.25 L 499.8125 113.4375 L 504.8125 108.625 L 507.3125 108.4375 L 521.1875 113.9375 L 520 118 L 516.375 121.5625 L 518.5625 123.3125 L 520.125 128.9375 L 517.4375 131 L 510.75 129.25 L 508.25 130.8125 Z"/>
</g>

<g>
<zoneType>Région</zoneType>
<title>PACA</title>
<path class="countryOutlines" d="M 520.2 128.8125 L 517.575 131 L 511.375 129.5375 L 512.1875 126.625 L 518.875 124.4375 Z"/>
</g>

<g>
<zoneType>Autres</zoneType>
<title>Corsica</title>
<path id="fralt000002" class="countryOutlines" d="M 526.1875 131.25 L 526.25 133.5625 L 525.25 135.4375 L 524.0625 133.875 L 524.1875 132.125 L 525.4375 132 Z"/>
</g>
</g>
</g>

<g id="spain_outlines">
<zoneType>Pays</zoneType>
<title>Spain</title>
<g>
<!-- Use when there is no other data available -->
<path class="countryOutlines" d="M 496.125 129.4375 L 477.5625 129 L 474.4375 130.6875 L 476 133.0625 L 482.5625 133.3125 L 480.375 146.9375 L 484.4375 150.1875 L 486.75 148.1875 L 494.4375 147.875 L 499.4375 142.75 L 500.6875 138.375 L 505.5 135.8125 L 508.1875 134.25 Z"/>
</g>
<!-- order doesnt matter -->
<g>
<zoneType>Région</zoneType>
<title>Galicia</title>
<path idd="slfqhblh" class="countryOutlines" d="M 482.5 129.125 L 477.375 129 L 474.375 130.6875 L 475.9375 132.9375 L 482.625 133.0625 Z" />
</g>
</g>

<g id="unitedKingdom_outlines">
<zoneType>Pays</zoneType>
<precision>0</precision>
<title>United Kingdom</title>
<!-- Use when there is no other data available -->
<g>
<zoneType>Île</zoneType>
<title>Main Island</title>
<path id="unitedKingdom_main" class="countryOutlines" d="M 485.1875 111.375 L 501.5625 109.375 L 504.1875 104.6875 L 492.375 94.25 L 494.6875 90.1875 L 488.75 90.1875 L 491.5625 87.5 L 486.0625 87.6875 L 481.875 93.875 L 486.5625 98.4375 L 491.6875 100.3125 L 491.25 102.5 L 487.375 101.5625 L 485.9375 106.875 L 489.5625 107.3125 Z"/>
</g>
</g>

<g id="poland_outlines">
<zoneType>Pays</zoneType>
<title>Poland</title>
<g id="poland_outlines_alt">
<!-- Use when there is no other data available -->
<path id="poland_main" class="countryOutlines" d="M 563.5625 99.4375 L 548.5625 97.875 L 541.0625 99.9375 L 540.4375 107.6875 L 555.3125 114.125 L 561.375 114.125 L 565.8125 108.5 Z"/>
</g>
</g>

</g>

<g>
<zoneType>Continent</zoneType>
<title>Asia</title>
<g type="pays">
<zoneType>Pays</zoneType>
<title>Russie</title>
<path class="countryOutlines" d="M 582 56 L 577 93 L 631 133 L 635.5 108.5 L 695.5 98 L 792 133 L 840.5 99.5 L 872 129.5 L 892 99.5 L 881 94.5 L 948 78 L 934.5 93.5 L 938 104.5 L 960.5 81 L 998.5 69.5 L 999.5 57 L 813 42 L 786 33.5 L 695.5 43 L 617 62 L 581 54.5 Z"/>
</g>
</g>
</g>
</g>






<g id="dessins">
</g>

<g id="carteActors">
<!-- Contient les éléments graphiques pour la localisation des acteurs -->
</g>

<g id="carteAirports">
<!-- Contient les éléments graphiques pour la localisation des aéroports -->
</g>



<g id="textes">
<text x="10" y="490" id="yourCurrentLocation">Vous avez choisi cette position :
<tspan id="yourCurrentLocationName">Aucune</tspan>
<tspan id="yourCurrentLocationCoords">()</tspan>
</text>
<text x="10" y="475" id="line1">M </text>
<text id="airportName"> </text>
<text id="actorsAtAirport"> </text>
</g>


<g id="credits">
<rect x="0" y="500" width="1000" height="36" fill="#d0d0d0" rx="4"/>
<a xlink:href="http://www.w3.org/2001/sw/Europe/">
<image xlink:href="images/logos/swadelogo2.png" x="896" y="502" height="32" width="28" />
</a>
<a xlink:href="http://www.w3.org/">
<image xlink:href="images/logos/w3c.gif" x="930" y="502" height="32" width="59" />
</a>
</g>


<g id="results">
<text x="10" y="20" id="scriptResults"> </text>
</g>



<text x="10" y="515" style="font-size: 9pt;">En développement
<tspan x="10" y="530"><a xlink:href="http://ckiss.net/cedric" style="fill: blue;">Cédric Kiss</a>, août 2003</tspan>
</text>


<use xlink:href="#compteur" transform="translate(250,533) scale(0.3)"/>


<g id="boutons">
<g transform="translate(530,506) scale(0.5)" onclick="afficheActors()">
<use xlink:href="#bouton"/>
<text x="46" y="20" style="font-size: 20pt;">Afficher les acteurs</text>
</g>

<g transform="translate(530,522) scale(0.5)" onclick="afficheAirportsCoords()">
<use xlink:href="#bouton"/>
<text x="46" y="20" style="font-size: 20pt;">Afficher les aéroports</text>
</g>

<g transform="translate(700,506) scale(0.5)" onclick="afficheResults()">
<use xlink:href="#bouton"/>
<text x="46" y="20" style="font-size: 20pt;">Afficher les résultats</text>
</g>

<g transform="translate(700,522) scale(0.5)" onclick="afficheStatsChargement()">
<use xlink:href="#bouton"/>
<text x="46" y="20" style="font-size: 20pt;">Bilan des fichiers chargés</text>
</g>
</g>





<script type="text/ecmascript"><![CDATA[
// a3:scriptImplementation="Adobe"

// === Début du script ====================================================

var svgDocu;
var mapWidth;
var mapHeight;

/*
// Create RDF object
myRDF=new RDF();
myAirports=new RDF();

// Déclaration des Namespaces
foafNS="http://xmlns.com/foaf/0.1/"
aptNS="http://xmlns.com/wordnet/1.6/";
contactNS="http://www.w3.org/2000/10/swap/pim/contact#";
airNS="http://www.megginson.com/exp/ns/airports#";
*/

// the lower (=1) the better (thus the slower)
var precision=20;

// Pas encore fait
var arrayMesSubZones;
// Colorado --> www.whitehouse.gov ; Corse --> www.elysee.fr

var mesAirportsCoords=new Array();
// 1:{IATA,ICAO,Nom,(43N,2E)}...

var mesActors=new Array();
// mailto:cedric2003@ckiss.net --> Coords( ... , ... , ... )
// c125e43d1e4526784fe8... --> Coords(...)

var foafFilesToLoad=new Array();

// Dernières coordonnées cliquées
var lastClickedCoords;

var proximityResults=new Array();
// actor / zone / type_zone
// "vous etes dans zone, c'est à dire le meme type_zone que actor."

// Éléments graphiques...
var diametrePointsAirports=4.5;
var diametrePointsActors=2.2;

// Gestion du chargement de données externes sur des codes aéroport inconnus
var externalAdvicesOnAirports=0;

var conditionElement;
var conditionValue;
// <foaf:interest>Machin</foaf:interest>

// (fr) Filtrage des personnes selon un critère (laisser conditionValue vide pour ne pas filtrer)
// (en) You can set a condition to include a person to the graph or not (leave conditionValue blank to include everyone)
conditionElement='foaf:interest';
conditionValue='';
// Exemple : ceux qui auront 'w3' contenu dans leur 'foaf:interest'

var nombreFichiersFoafCharges=0;
var nombreFichiersFoafNonCharges=0;
var nombreFichiersAreaCharges=0;
var nombreFichiersAreaNonCharges=0;
var nFichiersFoafEnCours=0;
var nFichiersFoafTotal=0;
var nombreAirportCoordsExternes=0;
var nombreAirportCoordsExternesRecus=0;

// The path to your (PHP?) proxy (change this to match your config)
// Sur mon laptop
//var pathToProxy='http://localhost/rdfig/proxy.php';
// Sur le dossier public
var pathToProxy='http://www.w3.org/2001/sw/Europe/200306/geo/proxy.php';


function init(evt){
svgDocu=evt.getTarget().getOwnerDocument();

if(conditionValue)
svgDocu.getElementById('line1').firstChild.nodeValue='Filtrage des personnes selon '+conditionElement+' : '+conditionValue+'.';
else
svgDocu.getElementById('line1').firstChild.nodeValue='(Pas de filtrage des personnes)';

// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement du script';

// Dimensions de la carte (utile pour les conversions)
mapHeight=svgDocu.getElementById('mapBackground').getAttribute('height');
mapWidth=svgDocu.getElementById('mapBackground').getAttribute('width');

// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement des aéroports';

// (fr) Afficher les aéroports
// 4 aéroports
//getURL('airports-test.rdf',drawAirportsFromFile);
// 8000 aéroports ?
getURL('airports.rdf.gz',drawAirportsFromFile);

// Un peu de temporisation
setTimeout('initsuite1()',3000)
}


function initsuite1(){
// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement des pays';

// Les outlines des pays
//getURL('outlines/belgium2pts.rdf',drawAreaFromFile);
//getURL('outlines/germany2pts.rdf',drawAreaFromFile);
getURL('outlines/usa_outlines.rdf',drawAreaFromFile);
getURL('outlines/europe_outlines.rdf',drawAreaFromFile);

// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement des fichiers FOAF';

// Get an individual Foaf file?
//myRDF.getRDFURL(pathToProxy+'?url=http://ckiss.net/cedric/foaf.rdf',getFoafActorInfo)
//myRDF.getRDFURL(pathToProxy+'?url=http://www.petebarrwatson.com/pm/foaf.rdf',getFoafActorInfo)

// Get a set of Foaf files
//loadFOAFScutter(pathToProxy+'?url=http://jibbering.com/scutter/');
// (Fichier local)
loadFOAFScutter('scutter_20030806.xml');
}


function loadFOAFScutter(url){
//alert('getAllFOAFFromFile : connexion à '+url);
getURL(url,readFOAFScutter);
}


function readFOAFScutter(urlRequestStatus){

if(!urlRequestStatus.success) {
alert('Impossible de charger le scutter de fichiers FOAF !');
return(false);
}

var msg=urlRequestStatus.content;
//alert('File loaded : '+msg.substr(0,80)+'...');

var externNode = parseXML(msg,svgDocu);

// Nombre de fichiers à charger
nFichiersFoafTotal+=externNode.firstChild.getElementsByTagName('rdfs:seeAlso').length

try{
// A toujours marché
var myFiles=externNode.firstChild.getElementsByTagName('rdfs:seeAlso');
}catch(erreur){
// Je le mets au cas où ?
var myFiles=externNode.firstChild.nextSibling.getElementsByTagName('rdfs:seeAlso');
}

for(var a=0; a<myFiles.length; a++){
currentFile=myFiles.item(a).getAttribute('rdf:resource');

// Si on ne l'a déjà pas chargée
//if(foafFilesToLoad.isInArray(currentFile)==-1){
foafFilesToLoad.push(currentFile);
getURL(pathToProxy+'?url='+currentFile,getFoafActorInfo);
//}

// Si on ne veut pas charger plus de 'n' fichiers...
if(a==140){
//break;
}
}
}


function afficheActorNamesAtAirport(code){
// Affiche sur la carte la liste des Actors à un Airport donné

var afficheActorNames='';
var nActeurs=0;

for(var a=0; a<mesActors.length; a++){
if(mesActors.item(a).aptCode==code){
afficheActorNames+=mesActors.item(a).name+', ';
nActeurs++;
}
}

afficheActorNames=afficheActorNames.substring(0,afficheActorNames.length-2);

if(nActeurs){
// On retrouve les coords
var coords=getAirportCoords(code);
if(coords){
var ax=longitudeToSVGCoordinatesMercator(coords.lon);
var ay=latitudeToSVGCoordinatesMercator(coords.lat);
}

var scale=svgDocu.documentElement.getCurrentScale();
svgDocu.getElementById('actorsAtAirport').setAttribute('x',ax+(5/scale));
svgDocu.getElementById('actorsAtAirport').setAttribute('y',ay+(13/scale));
}

svgDocu.getElementById('actorsAtAirport').firstChild.nodeValue=afficheActorNames;
}


function afficheResults(){
if(!lastClickedCoords){
alert('afficheResults : il faut cliquer d\'abord quelque part !');
return(false);
}

//alert('afficheResults : Proximity Test...\n'+lastClickedCoords);

lastClickedCoords.proximityTest();
}


function afficheLastClickedCoords(){
if(lastClickedCoords.lat){
//alert('afficheLastClickedCoords : '+lastClickedCoords.lat+','+lastClickedCoords.lon);
svgDocu.getElementById('yourCurrentLocationCoords').firstChild.nodeValue='('+lastClickedCoords.lat+','+lastClickedCoords.lon+')';
}else
window.alert('Vous n\'avez pas cliqué \nlastClickedCoords='+lastClickedCoords);
}


function resizeOnZoom(){
var scale=svgDocu.documentElement.getCurrentScale();

var lesCircles=svgDocu.getElementById('carteAirports').getElementsByTagName('circle');
// Taille des points des aéroports
for(var i=0; i<lesCircles.length; i++)
{
lesCircles.item(i).setAttribute('r',(diametrePointsAirports/scale));
}

// Taille du texte nommant l'aéroport
var taille=(10/scale);
svgDocu.getElementById('airportName').setAttribute('style','font-size: '+taille+'pt;');

// Taille du texte nommant les personnes à l'aéroport
var taille=(8/scale);
svgDocu.getElementById('actorsAtAirport').setAttribute('style','font-size: '+taille+'pt;');

var lesCircles=svgDocu.getElementById('carteActors').getElementsByTagName('circle');
// Taille des points des acteurs
for(var i=0; i<lesCircles.length; i++)
{
lesCircles.item(i).setAttribute('r',(diametrePointsActors/scale));
// Pourquoi ça ne marche pas ???
//lesCircles.item(i).setAttribute('stroke-width',(0.5/scale));
}
}


function getAirportCoords(code){
// Renvoie les Coords() d'un aéroport désigné par son code IATA ou ICAO

var codeType=getCodeType(code);

var coordsFound=false;
for(var a=0; a<mesAirportsCoords.length; a++){
//alert('on compare '+code+' à '+mesAirportsCoords[a].icao+' ou à '+mesAirportsCoords[a].iata);
if(codeType=='iata' && mesAirportsCoords[a].iata){
if(code==mesAirportsCoords[a].iata){
coordsFound=mesAirportsCoords[a].coords;
break;
}
}
else
{
if(code==mesAirportsCoords[a].icao){
coordsFound=mesAirportsCoords[a].coords;
break;
}
}
}

if(!coordsFound){
// On a un code d'aéroport mais on ne trouve pas les coordonnées...
//alert('Pas de coordonnées trouvées pour l\'aéroport de code '+codeType+' : <'+code+'>... GoTo JLAL');

// (fr) Ajouter au nombre d'aéroports externes demandés
externalAdvicesOnAirports++;

// (fr) Demander au Jim Ley Airport Locator
getURL(pathToProxy+'?url=http://jibbering.com/foaf/airports.1?'+code,getAirportCoordsFromAirportLocator);
nombreAirportCoordsExternes++;

// Temporisation pour que 2 fichiers ne soient pas 'confondus' (bug d'ecmascript)
setTimeout('rien()',2000);
}

//alert('coordsFound : '+coordsFound);
return(coordsFound);
}


function getAirportCoordsFromAirportLocator(urlRequestStatus){
// On accède un service en ligne qui envoie des fichiers RDF à la demande !

// On décrémente le compteur
externalAdvicesOnAirports--;

if(!urlRequestStatus.success) {
//alert('Impossible de charger le fichier RDF du Jim Ley Airport Locator !');
return(false);
}

var msg=urlRequestStatus.content;

//alert('JimLeyAirportLocator-FileLoaded: '+msg.substr(0,200)+'...\nlength : '+msg.length);

// (fr) On va tenter de trouver les coordonnées de cet aéroport
var coordsAirport=false;

if(msg.substr(0,6)=='erreur'){
// Could not reach destination, or any other error my proxy could have encountered...
return(false);
}

var externNode = parseXML(msg,svgDocu);
//alert('msg='+msg);

// Permet de 'sauter' jusqu'à 100 lignes de commentaires : <!-- blah blah -->
// Si quelqu'un a une meilleure idée, qu'il me la donne !
var myAirport=false;
for(var a=0; a<100; a++){
try{
myAirport=externNode.childNodes.item(a).getElementsByTagName('airport:Airport');
}catch(erreur){}
if(myAirport){
//alert('trouvé après '+a+' lignes de commentaires');
break;
}
}

if(myAirport.length){
myAirport=myAirport.item(0);

//alert('loca : '+myAirport.getElementsByTagName('airport:location').item(0).firstChild.nodeValue);
try{
if(myAirport.getElementsByTagName('pos:lat').length){
var lat=myAirport.getElementsByTagName('pos:lat').item(0).firstChild.nodeValue;
var lon=myAirport.getElementsByTagName('pos:long').item(0).firstChild.nodeValue;
}
}catch(erreur){
//alert('getAirportCoordsFromAirportLocator : Encore une erreur : '+erreur+'\nmsg='+msg);
return(false);
}

nombreAirportCoordsExternesRecus++;

if(myAirport.getElementsByTagName('airport:location').length){
if(myAirport.getElementsByTagName('airport:location').item(0).firstChild){
try{
var name=myAirport.getElementsByTagName('airport:location').item(0).firstChild.nodeValue;
}catch(erreur){
alert('Erreur airport:location : '+erreur);
alert('myAirport : '+myAirport);
alert('1 myAirport.getElementsByTagName(airport:location) : '+myAirport.getElementsByTagName('airport:location'));
//alert('2 myAirport.getElementsByTagName(airport:location) : '+printNode(myAirport.getElementsByTagName('airport:location')));
alert('1 myAirport.getElementsByTagName(airport:location).item(0) : '+myAirport.getElementsByTagName('airport:location').item(0));
alert('2 myAirport.getElementsByTagName(airport:location).item(0) : '+printNode(myAirport.getElementsByTagName('airport:location').item(0)));
name="Nom inconnu (1)";
}
}
}else
var name="Nom inconnu (2)";

// (fr) Le code de l'aéroport n'est pas dans le RDF ! Je le récupère à partir du nom de fichier... :-(
var about=myAirport.getAttribute('rdf:about');
//alert('code : '+about);
var code=about.substr(about.indexOf('?')+1);
var codeType=getCodeType(code);
//alert('code donc : '+code+' ... '+codeType);
coordsAirport=new Coords(lat,lon);

if(codeType=='iata'){
var iata=code;
var icao=null;
}
else
{
var iata=null;
var icao=code;
}

// Ajouter les résultats au tableau
var apt=new Airport(name,coordsAirport,iata,icao);

// Faire des vérifs pour savoir si l'aéroport n'était pas dans
//  le tableau, ou s'il y était mais mal renseigné

var uniqueName=true;
for(var a=0; a<mesAirportsCoords.length; a++){
//alert('on compare '+name+' et '+mesAirportsCoords[a].name);
if(name==mesAirportsCoords[a].name){
uniqueName=false;
break;
}
}

if(uniqueName){
// Test supplémentaire : le code existe-t-il déjà dans le tableau ?
var uniqueCode=true;
for(var a=0; a<mesAirportsCoords.length; a++){
//alert('on compare '+name+' et '+mesAirportsCoords[a].name);
if(name==mesAirportsCoords[a].name){
uniqueCode=false;
break;
}
}
}

if(uniqueName==false){
// Modif au rang 'a' s'il manquait les coordonnées
if(!mesAirportsCoords[a].coords){
mesAirportsCoords[a].coords=coordsAirport;
}
// Modif au rang 'a' s'il manquait le code
if(codeType=='iata'){
if(!mesAirportsCoords[a].iata)
mesAirportsCoords[a].iata=code;
}else{
if(!mesAirportsCoords[a].icao)
mesAirportsCoords[a].icao=code;
}
}else if(uniqueCode==false){
// Modif au rang 'a' s'il manquait les coordonnées
if(!mesAirportsCoords[a].coords)
mesAirportsCoords[a].coords=coordsAirport;
if(!mesAirportsCoords[a].name)
mesAirportsCoords[a].name=name;
}else{
mesAirportsCoords.push(apt);
apt.draw();
}

// On update 'rétrospectivement' la table des Actors
if(externalAdvicesOnAirports==0){
// Si on n'attend plus d'autre fichier, on peut traiter les acteurs qui restaient sans coordonnées
resolveActorsAvecAirportSansCoords();
}
}
}


function getCodeType(code){
// (en) Detects 3-letter codes (iata) and 4-letter codes (icao)
if(code.length==3){
var codeType='iata';
}else if(code.length==4){
var codeType='icao';
}else{
window.alert(code+' n\'est pas un code d\'aéroport valable !');
return(false);
}
return(codeType);
}


function getAirportName(code){
// Renvoie les Coords() d'un aéroport désigné par son code IATA ou ICAO

// (en) Detects 3-letter codes (iata) and 4-letter codes (icao)
if(code.length==3){
var codeType='iata';
}else if(code.length==4){
var codeType='icao';
}else{
window.alert(code+' n\'est pas un code d\'aéroport valable !');
return(false);
}

var nameFound=false;
for(var a=0; a<mesAirportsCoords.length; a++){
//alert('on compare '+code+' à '+mesAirportsCoords[a].icao+' et à '+mesAirportsCoords[a].iata);
if(codeType=='iata'){
if(code==mesAirportsCoords[a].iata){
nameFound=mesAirportsCoords[a].name;
break;
}
}
else
{
if(code==mesAirportsCoords[a].icao){
nameFound=mesAirportsCoords[a].name;
break;
}
}
}

//alert('nameFound : '+nameFound);
return(nameFound);
}


function getImmediateChildrenByTagName(node,givenTagName){
// (fr) Trouve les éléments 'givenTagName' _immédiatement_ fils d'un node
var myNodeList=new Array();
var mesTags=node.getElementsByTagName(givenTagName);

for(var a=0; a<mesTags.length; a++){
if(mesTags.item(a).parentNode==node){
myNodeList.push(mesTags.item(a));
//if(givenTagName=='foaf:mbox')
//alert('Accepté : '+printNode(mesTags.item(a)));
//}else{
//if(givenTagName=='foaf:mbox')
//alert('Refusé : '+printNode(mesTags.item(a)));
}
}
return(myNodeList);
}


function getFoafActorInfo(urlRequestStatus){
// Nombre de fichiers à charger
nFichiersFoafEnCours++;
actualiseCompteurFichiersCharges(nFichiersFoafEnCours,nFichiersFoafTotal);

// État du script
if(nFichiersFoafEnCours==nFichiersFoafTotal)
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement des fichiers FOAF terminé.';
else
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Chargement des fichiers FOAF ('+nFichiersFoafEnCours+'/'+nFichiersFoafTotal+')';

if(!urlRequestStatus.success) {
//alert('Impossible de charger le fichier FOAF !');
nombreFichiersFoafNonCharges++;
return(false);
}

var msg=urlRequestStatus.content;
//alert('getFoafActorInfo : FileLoaded: '+msg.substr(0,80)+'...\nlength : '+msg.length);

if(msg.substr(0,6)=='erreur'){
// Could not reach destination, or any other error my proxy could have encountered...
nombreFichiersFoafNonCharges++;
return(false);
}
else
nombreFichiersFoafCharges++;

var externNode = parseXML(msg,svgDocu);
//alert('msg='+msg);

// Permet de 'sauter' jusqu'à 100 lignes de commentaires : <!-- blah blah -->
// Si quelqu'un a une meilleure idée, qu'il me la donne !
var myPersons=false;
for(var a=0; a<100; a++){
try{
myPersons=externNode.childNodes.item(a).getElementsByTagName('foaf:Person');
}catch(erreur){}
if(myPersons){
//alert('trouvé après '+a+' lignes de commentaires');
break;
}
}

var coords,myLat,myLon,iata,icao,mailbox;

for(var a=0; a<myPersons.length; a++){
// On regarde d'abord si la Person est intéressante pour notre programme

// Note : ici on recherche conditionValue dans TOUT le RDF de conditionElement, c.-à.-d., <foaf:interest><rdf:Description .... </>
//  C'est assez laid ; il faudra pouvoir rechercher dans les Attributs et les Valeurs mais pas dans les Éléments.
if(conditionValue){
conditionFound=false;
// Recherche de l'élément qui fait la condition ?
//condition=myPersons.item(a).getElementsByTagName(conditionElement);
// Maintenant
condition=getImmediateChildrenByTagName(myPersons.item(a),conditionElement);
if(condition.length){
//alert('Condition trouvée : '+condition.length+' élément(s) \n');
for(var b=0; b<condition.length; b++){
//alert(printNode(condition.item(b)));
if(printNode(condition.item(b)).indexOf(conditionValue)!==-1){
conditionFound=true;
//alert('condition '+conditionValue+' trouvée dans\n'+printNode(condition.item(b)));
break;
}
}
}
}else{
// Sans condition, c'est toujours d'accord
conditionFound=true;
}

// On n'ajoute la personne que si elle répond à notre condition
//  (ou s'il n'y a pas de condition définie)
if(conditionFound){

// Recherche des coordonnées géographiques
coords=false;

// Si l'utilisateur fournit ses coordonnées
// <geo:location><geo:Position geo:lat="52.422400" geo:lon="-0.803000"/></geo:location>
position=false;
position=myPersons.item(a).getElementsByTagName('geo:location');
if(position.length){
try{
position=position.item(0).getElementsByTagName('geo:Position');
}catch(erreur){
/*
alert('getFoafActorInfo : Erreur '+erreur+'\n(1) position='+position);
alert('msg='+msg);
alert('(2) position.item(0)='+position.item(0));
alert('(3) printNode(position)='+printNode(position));
alert('(4) printNode(position.item(0))='+printNode(position.item(0)));
*/
}
if(position.length){
try{
myLat=position.item(0).getAttribute('geo:lat');
myLon=position.item(0).getAttribute('geo:lon');
coords=new Coords(myLat,myLon);
//alert('getFoafActorInfo : on a une geo:position !!\n'+coords);
}catch(erreur){
//alert('encore une erreur : '+erreur);
}
}
}

iata='';
icao='';

// Pas nécéssaire maintenant ?
if(!coords){
// Recherche en fonction de l'aéroport le plus proche
nearest=getImmediateChildrenByTagName(myPersons.item(a),'contact:nearestAirport');
if(nearest.length){
// Recherche du code de l'aéroport ; du style <wn:Airport air:iata="NCE"/>
//apt=nearest.item(0).getElementsByTagName('wn:Airport');
apt=getImmediateChildrenByTagName(nearest.item(0),'wn:Airport');

/*
// (Besoin d'une ontologie pour traiter wn:Airport <==> airport:Airport)
if(apt.length!=1)
alert('Avertissement :\nnearestAirport demandé ; pourtant, nombre d\'aéroports='+apt.length);
*/

// Récupération des coordonnées de la personne par son aéroport
if(apt.length){

myLat=apt.item(0).getAttribute('pos:lat');
myLon=apt.item(0).getAttribute('pos:long');

if(myLat && myLon && !coords){
// Si les coordonnées sont déjà fournies
coords=new Coords(myLat,myLon);
alert('getFoafActorInfo : myLat && myLon : pas encore testé\n'+coords);
}else{
// Si les coordonnées ne sont pas fournies, on regarde dans nos tables
iata=apt.item(0).getAttribute('air:iata');
icao=apt.item(0).getAttribute('air:icao');

//alert('code IATA : '+iata+'\ncode ICAO : '+icao);

if(iata){
if(!coords)
coords=getAirportCoords(iata);
//alert('Recherche avec iata='+iata);
}

if(!coords){
if(icao){
if(!coords){
coords=getAirportCoords(icao);
//alert('Recherche avec icao='+icao);
}
}
}
}
}
}
}

// Sans coordonnées géographiques, pas d'intérêt à garder les personnes
if(coords || iata || icao){
// On associe les acteurs aux coordonnées géographiques

name='';
if(getImmediateChildrenByTagName(myPersons.item(a),'foaf:name').length){
name=getImmediateChildrenByTagName(myPersons.item(a),'foaf:name').item(0).firstChild.nodeValue;
}
else if(getImmediateChildrenByTagName(myPersons.item(a),'foaf:givenname').length){
name=getImmediateChildrenByTagName(myPersons.item(a),'foaf:givenname').item(0).firstChild.nodeValue;
}
else if(getImmediateChildrenByTagName(myPersons.item(a),'foaf:nick').length){
name=getImmediateChildrenByTagName(myPersons.item(a),'foaf:nick').item(0).firstChild.nodeValue;
}
else if(getImmediateChildrenByTagName(myPersons.item(a),'foaf:surname').length){
name=getImmediateChildrenByTagName(myPersons.item(a),'foaf:surname').item(0).firstChild.nodeValue;
}

if(iata)
aptCode=iata;
else if(icao)
aptCode=icao;
else
aptCode='';

photoUri='NotSet';
foafUri='NotSet';

// On rentre toutes les adresses mail de la personne (car ce sont ses identifiants)
//  et sans mettre celles des autres, naturellement

//alert('on get les mailbox');

// GRR mesMailboxesNonCryptees=myPersons.item(a).getImmediateChildrenByTagName('foaf:mbox');
mesMailboxesNonCryptees=getImmediateChildrenByTagName(myPersons.item(a),'foaf:mbox');

for(var b=0; b<mesMailboxesNonCryptees.length; b++){
mailbox=mesMailboxesNonCryptees.item(b).getAttribute('rdf:resource');
//mailbox=hex_sha1(mailbox);

if(coords || aptCode){
newActor=new Actor(name,mailbox,coords,aptCode,photoUri,foafUri);
//alert('Nom de la personne : '+name+'\nmailbox : '+mailbox+'\niata : '+iata+'\nicao : '+icao+'\naptCode retenu : '+aptCode+'\nCoords : \n'+coords);

// Vérification qu'il n'est pas déjà dans le tableau (code -1 ; sinon : position)
if(newActor.actorIsInArray(mesActors)==-1){
mesActors.push(newActor);
if(coords)
newActor.draw();
}
}
}

//mesMailboxesCryptees=myPersons.item(a).getElementsByTagName('foaf:mbox_sha1sum');
// Maintenant
mesMailboxesCryptees=getImmediateChildrenByTagName(myPersons.item(a),'foaf:mbox_sha1sum');

for(var b=0; b<mesMailboxesCryptees.length; b++){
mailbox=mesMailboxesCryptees.item(b).firstChild.nodeValue;
if(coords || aptCode){
newActor=new Actor(name,mailbox,coords,aptCode,photoUri,foafUri);
//alert('Nom de la personne : '+name+'\nmailbox : '+mailbox+'\niata : '+iata+'\nicao : '+icao+'\naptCode retenu : '+aptCode+'\nCoords : \n'+coords);

// Vérification qu'il n'est pas déjà dans le tableau
if(newActor.actorIsInArray(mesActors)==-1){
mesActors.push(newActor);
if(coords)
newActor.draw();
}
}
}
}
}
}
}


function actualiseCompteurFichiersCharges(nFichiersFoafEnCours,total){
// (fr) Angle de l'aiguille, en degrés (100% correspond à 180 degrés)
var rota=((nFichiersFoafEnCours/total)*100)*1.8;

svgDocu.getElementById('aiguille').setAttribute('transform','rotate('+rota+') scale(1,1.7)');

// (fr) En radians...
var rota=rota/180*Math.PI;
var x1=-100*Math.cos(rota);
var y1=-100*Math.sin(rota);

var myPath='M -100 0 L 0 0 L ';
myPath+=x1+' '+y1;
myPath+=' A 100 100 0 0 0 -100 0 Z';

svgDocu.getElementById('compteur_remplissage').setAttribute('d',myPath);
}


function afficheAirportsCoords(){
var alertMesAirportsCoords='Liste des '+mesAirportsCoords.length+' AirportsCoords\n=====================\n';

for(var a=0; a<mesAirportsCoords.length; a++){

if(mesAirportsCoords[a].iata)
alertMesAirportsCoords+=mesAirportsCoords[a].iata+' \t';
else
alertMesAirportsCoords+=' \t';

if(mesAirportsCoords[a].icao)
alertMesAirportsCoords+=mesAirportsCoords[a].icao+' \t';
else
alertMesAirportsCoords+=' \t';

alertMesAirportsCoords+=mesAirportsCoords[a].name+' \t('+mesAirportsCoords[a].coords.lon+','+mesAirportsCoords[a].coords.lat+')\n';

if(a%30==29){
alert(alertMesAirportsCoords+'\n[Continuer...]');
alertMesAirportsCoords='';
}
}

alert(alertMesAirportsCoords+'\n[Fin]');
}


function afficheActors(){
// On affiche le plus de coordonnées possible (même si tous les aéroports 'externes' ne nous sont pas encore parvenus)
resolveActorsAvecAirportSansCoords();

var alertMesActors='Liste des '+mesActors.length+' Actors\n====================\n';
for(var a=0; a<mesActors.length; a++){
//alertMesActors+=mesActors[a].name+' : \t('+mesActors[a].coords.lat+','+mesActors[a].coords.lon+')\n';
alertMesActors+=mesActors[a].name+' \t<'+mesActors[a].mbox+'> : \t'+mesActors[a].aptCode+'\t('+mesActors[a].coords.lat+','+mesActors[a].coords.lon+')';
alertMesActors+='\n';
}
alert(alertMesActors);
}


function resolveActorsAvecAirportSansCoords(){
var coords,codeActor,codeType=false;
for(var a=0; a<mesActors.length; a++){
// Si on a le code mais pas les coordonnées
if(mesActors[a].aptCode && !mesActors[a].coords){
codeActor=mesActors[a].aptCode;
codeType=getCodeType(codeActor);

if(!codeType)
continue;

// et pas break ?

coords=false;
for(var b=0; b<mesAirportsCoords.length; b++){
if(codeType=='iata'){
if(codeActor==mesAirportsCoords[b].iata){
coords=mesAirportsCoords[b].coords;
break;
}
}else{
if(codeActor==mesAirportsCoords[b].icao){
coords=mesAirportsCoords[b].coords;
break;
}
}
}

if(coords){
// On a trouvé les coords de l'aéroport ! Remplacer dans mesActors
mesActors[a].coords=coords;
mesActors[a].draw();
}
}
}

//if(codeType)
//window.alert('Résolution des actorsAvecAirportSansCoords terminée.');
}


function drawAreaFromFile(urlRequestStatus){
if(!urlRequestStatus.success) {
nombreFichiersAreaNonCharges++;
alert('drawAreaFromFile : Impossible de charger le fichier de régions !');
return(false);
}

var msg=urlRequestStatus.content;
var externNode = parseXML(msg,svgDocu);

if(externNode.firstChild){
nombreFichiersAreaCharges++;

var mesAreas=externNode.firstChild.getElementsByTagName('area:Area');

// Toutes les lignes de coordonnees
for(var a=0; a<mesAreas.length; a++){
//alert(a+') '+mesAreas.item(a).getElementsByTagName('area:name').item(0).firstChild.nodeValue);

// Le title de la zone
zoneTitle=mesAreas.item(a).getElementsByTagName('area:name');
if(zoneTitle.length)
zoneTitle=zoneTitle.item(0).firstChild.nodeValue;
else
zoneTitle='not set'

// Nouvelle Area --> nouveau SVGGElement
var ng=svgDocu.createElement('g');

mesSubAreas=mesAreas.item(a).getElementsByTagName('area:SubArea');
if(mesSubAreas.length){
//alert(mesSubAreas.length+' subAreas');
for(var b=0; b<mesSubAreas.length; b++){
if(mesSubAreas.item(b).getElementsByTagName('rdfs:seeAlso').length){
subFile=mesSubAreas.item(b).getElementsByTagName('rdfs:seeAlso').item(0).getAttribute('rdf:resource');
if(subFile){
//alert(b+') '+subFile);

getURL('outlines/'+subFile,drawAreaFromFile)
}
}
}
}

mesOutlines=mesAreas.item(a).getElementsByTagName('area:points');
for(var b=0; b<mesOutlines.length; b++){
points=mesOutlines.item(b).firstChild.nodeValue;

// On met tout de la forme "45.3;24.6;34.6"...
points=points.trim();
// On supprime les retours à la ligne
points=points.replace(/\n+/g,' ');
// On comprime les mutiples espaces
points=points.replace(/ +/g,';');

arrayPoints=points.split(';');
//alert(arrayPoints.length+' points');

// On détermine quel sera le 'path' issu du fichier
pathString='M ';

// Système pour avoir à tracer moins de points
cpt=0;

/*
if(arrayPoints.length/2>100000)
precision=5000;
else if(arrayPoints.length/2>10000)
precision=500;
else if(arrayPoints.length/2>1000)
precision=50;
else if(arrayPoints.length/2>100)
precision=5;
else
precision=1;
*/

for(var c=0; c<arrayPoints.length; c++){
if(c%2){
// Moins de points
//if(cpt%precision==1)
pathString+=latitudeToSVGCoordinatesMercator(arrayPoints[c])+' L ';
}else{
// Moins de points
cpt++;
//if(cpt%precision==1)
pathString+=longitudeToSVGCoordinatesMercator(arrayPoints[c])+' ';
}
}

// On retire le trailing 'L ' et on met 'Z' à la place pour finir le 'd'
pathString=pathString.substr(0,pathString.length-2);
pathString+='Z';

//alert('Nouveau pathstring pour '+zoneTitle+' ('+pathString.length+' cara)\n'+pathString);

var nti=svgDocu.createElement('title');
var nt=svgDocu.createTextNode(zoneTitle);
nti.appendChild(nt);

var np=svgDocu.createElement('path');

// Des couleurs différentes pour les régions
var randomLayout=(parseInt(Math.random()*5)+1)
//np.setAttribute('class','countryOutlines');
np.setAttribute('class','countryOutline'+randomLayout);
np.setAttribute('d',pathString);

ng.appendChild(np);
ng.appendChild(nti);

svgDocu.getElementById('world_outlines').appendChild(ng);
}
}









}
else{
nombreFichiersAreaNonCharges++;
alert('drawAreaFromFile : Un fichier qui sert à rien ?\n'+msg);
}
}


function addArea(mesAreas){

}


function drawAirportsFromFile(urlRequestStatus){
// Charge en mémoire (array) les informations sur les aéroports.
// Parcourt le fichier et dessine les aéroports sur la carte du monde.

if(!urlRequestStatus.success) {
alert('Impossible de charger le fichier des aéroports !');
return(false);
}

var msg=urlRequestStatus.content;
//alert('File loaded : '+msg.substr(0,8)+'...');

var externNode = parseXML(msg,svgDocu);

// Marche pour airports
//alert('n aero : '+externNode.firstChild.nextSibling.getElementsByTagName('apt:Airport').length);
// Marche pour airports-test
//alert('n aero : '+externNode.firstChild.getElementsByTagName('apt:Airport').length);

try{
// A marché (pour airports.rdf)
var myAirports=externNode.firstChild.nextSibling.getElementsByTagName('apt:Airport');
} catch(erreur){
// Marche s'il n'y a pas de commentaire <!- -> avant le <rdf:RDF>
var myAirports=externNode.firstChild.getElementsByTagName('apt:Airport');
}

var latitude,longitude,apt,aptName,aptCoords;

for(var a=0; a<myAirports.length; a++){
//alert('n°'+a+' : '+myAirports.getElementsByTagName('apt:latitude').item(0));
if(myAirports.item(a).getElementsByTagName('apt:latitude').length){
latitude=myAirports.item(a).getElementsByTagName('apt:latitude').item(0).firstChild.nodeValue;
longitude=myAirports.item(a).getElementsByTagName('apt:longitude').item(0).firstChild.nodeValue;

aptName=myAirports.item(a).getElementsByTagName('apt:name').item(0).firstChild.nodeValue;

//alert('longitude='+longitude+'\nlatitude='+latitude);

latitude=transformMinutesCoordsToDecimalCoords(latitude);
longitude=transformMinutesCoordsToDecimalCoords(longitude);

aptCoords=new Coords(latitude,longitude);

if(myAirports.item(a).getElementsByTagName('apt:iata').length){
//apt=new Airport(aptName,aptCoords,'airportIata');
iata=myAirports.item(a).getElementsByTagName('apt:iata').item(0).firstChild.nodeValue;
icao=myAirports.item(a).getElementsByTagName('apt:icao').item(0).firstChild.nodeValue;
}else{
//apt=new Airport(aptName,aptCoords,'airportIcao');
iata=null;
icao=myAirports.item(a).getElementsByTagName('apt:icao').item(0).firstChild.nodeValue;
}

// On ajoute ce qu'on sait dans l'Array
//mesAirportsCoords.push(iata,icao,aptCoords);

apt=new Airport(aptName,aptCoords,iata,icao);
mesAirportsCoords.push(apt);

//alert('new apt \n'+apt.coords+'\nlongitude='+longitude+'\nlatitude='+latitude);

apt.draw();
//return(true);
}
}
}


function afficheInfosAirport(code){
var scale=svgDocu.documentElement.getCurrentScale();

// On retrouve le nom
var name=getAirportName(code);
if(!name)
name='Code '+code;
svgDocu.getElementById('airportName').firstChild.nodeValue=name;

// On retrouve les coords
var coords=getAirportCoords(code);
if(coords){
var ax=longitudeToSVGCoordinatesMercator(coords.lon);
var ay=latitudeToSVGCoordinatesMercator(coords.lat);
}

svgDocu.getElementById('airportName').setAttribute('x',ax+(5/scale));
svgDocu.getElementById('airportName').setAttribute('y',ay+(4/scale));
}


function test(){
alert('Test!');
}


function rien(){
return(true);
}


function afficheStatsChargement(){
alert('Statistiques de chargement :\n==========================\n\
Fichiers FOAF chargés :\t'+nombreFichiersFoafCharges+'\nFichiers FOAF non chargés :\t'+nombreFichiersFoafNonCharges+'\
\nFichiers de pays chargés :\t'+nombreFichiersAreaCharges+'\nFichiers de pays non chargés :\t'+nombreFichiersAreaNonCharges+'\
\nCoordonnées d\'aéroports demandées :\t'+nombreAirportCoordsExternesRecus+'\nCoordonnées d\'aéroports reçues :\t'+nombreAirportCoordsExternesRecus);
}


Coords.prototype.proximityTest=function(){

// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Test en cours...';

// Prendre ma position (inclusion dans différentes zones)

// Le geoArea d'un acteur donné, à qui je me comparerai
var otherGeoArea;
// Le nom de la zone commune ?
var fgeoarea='';
// Le résultat (un array d'arrays)
var result=new Array();
// Initialisation
proximityResults=Array();

// Premièrement, on fixe les positions de tous les acteurs possibles
resolveActorsAvecAirportSansCoords();

var myGeoArea=this.getGeoArea();
//alert('proximityTest commence, pour mon clic = \n'+this.toString()+'\ndans la zone : '+myGeoArea);

if(!myGeoArea){
window.alert('Impossible de poursuivre : votre position d\'origine n\'est pas connue ou pas définie !');
return(false);
}

// Prendre toutes les autres positions des gens
var arrayActors=new Array();

for(var a=0; a<mesActors.length; a++){
// Réinitialisation des anciennes infos de zones (nouvel acteur)
var myZone='Autres';
fgeoArea='';

//alert('proxTest : acteur suivant à tester : '+mesActors.item(a).name+' ('+a+'/'+mesActors.length+')');

if(mesActors.item(a).coords && mesActors.item(a).coords.lat){

// Maintenant on rapatrie les geoAreas et on cherche les similitudes
otherGeoArea=mesActors.item(a).getGeoArea();

// État du script
actualiseCompteurFichiersCharges(a,mesActors.length);
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Test en cours ('+(a+1)+'/'+(mesActors.length+1)+')';

//if(otherGeoArea.length)
//alert('proxTest : GeoArea trouvée pour l\'acteur '+mesActors.item(a).name+' : \n'+otherGeoArea);

// Attention : myGeoArea est un array d'arrays
//  (un array de Continent > Pays > Zone1 > Zone2)
for(b=0; b<myGeoArea.length; b++){
// (et dans le sens inverse du tableau : de + en + précis)
for(c=myGeoArea[b].length-1; c>=0; c--){

// Pour chaque zone de l'acteur testé,
//  on compare 2 à 2 avec mes zones à moi
for(d=0; d<otherGeoArea.length; d++){
// Attention, otherGeoArea est aussi un array d'arrays

for(e=0; e<otherGeoArea.length; e++){
//alert('on a '+otherGeoArea[e].length+' zones a comparer');

//fgeoArea='non defini';
for(f=0; f<otherGeoArea[e].length; f++){
//alert('on compare '+otherGeoArea[e][f]+' et '+myGeoArea[b][c]);
if(otherGeoArea[e][f]==myGeoArea[b][c]){
// Même zone !
fgeoArea=myGeoArea[b][c];

//alert('vous et l\'acteur '+mesActors.item(a).name+' êtes dans une même zone\net son title est : '+myGeoArea[b][c]);

// Retrouver l'element à partir du title...
// On teste tous les <title> de world_map
myZone='Autres';
var lesTitles=svgDocu.getElementById('world_map').getElementsByTagName('title');
for(g=0; g<lesTitles.length; g++){
//alert(g+'. nouveau title : '+lesTitles.item(g).firstChild.nodeValue);
if(lesTitles.item(g).firstChild.nodeValue==myGeoArea[b][c]){
// On l'a trouvé
myZone=lesTitles.item(g).parentNode.getElementsByTagName('zoneType').item(0).firstChild.nodeValue;
//alert('donc la zone est '+myZone);
break;
}
}
//alert('meme zone et c\'est du type : '+myZone);
}
}
}
}
}
}

//if(fresult){
if(fgeoArea){
try{
//alert('Vous êtes dans le même '+myZone.toLowerCase()+' que '+mesActors.item(a).name+' ('+fgeoArea+')');
proximityResults.push(mesActors.item(a).name,fgeoArea,myZone.toLowerCase());
}catch(erreur){
//alert('Vous êtes dans le même '+myZone.toLowerCase()+' que '+mesActors.item(a).name+' (erreur : '+erreur+')');
proximityResults.push(mesActors.item(a).name,'--Erreur--',myZone.toLowerCase());
}
}
}
/*
else{
alert('on ne cree pas cette personne sans coordonnees !');
alert(mesActors.item(a));
}
*/
}

// État du script
svgDocu.getElementById('texteTravailEnCours').firstChild.nodeValue='Test terminé.';
actualiseCompteurFichiersCharges(1,1);

//alert('fin proxtest...\nlength='+proximityResults.length);

var showProximityResults='';
for(var a=0; a<proximityResults.length; a+=3){
showProximityResults+=' '+proximityResults[a]+'\t'+proximityResults[a+1]+'\t'+proximityResults[a+2]+'\n';
//alert(proximityResults[a]);
}

formateResultsEnRDF(proximityResults);

alert('Résultats de proximityTest() :\n===================\n'+showProximityResults);
}


function formateResultsEnRDF(arrayResults){

// On efface les anciens résultats et on recommence
svgDocu.getElementById('results').removeChild(svgDocu.getElementById('scriptResults'));
//var txtr=svgDocu.getElementById('scriptResults');
var txtr=svgDocu.createElement('text');
txtr.setAttribute('id','scriptResults');

var yLigne=15;
var interligne=12;

ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode('<rdf:RDF>'));
txtr.appendChild(ntspan);

yLigne+=interligne;
yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode('  <!-- Situation des personnes par rapport a votre position actuelle -->'));
txtr.appendChild(ntspan);

for(var a=0; a<arrayResults.length; a+=3){

// Pour chaque nouveau <tspan>, une nouvelle ligne

yLigne+=interligne;
yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode(' <Person>'));
txtr.appendChild(ntspan);

yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode('   <name>'+arrayResults[a]+'</name>'));
txtr.appendChild(ntspan);

yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode('   <sameZoneType xml:lang="fr">'+arrayResults[a+2]+'</sameZoneType>'));
txtr.appendChild(ntspan);

yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode('   <sameZoneName xml:lang="fr">'+arrayResults[a+1]+'</sameZoneName>'));
txtr.appendChild(ntspan);

yLigne+=interligne;
ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);
ntspan.appendChild(svgDocu.createTextNode(' </Person>'));
txtr.appendChild(ntspan);
}

yLigne+=interligne;

ntspan=svgDocu.createElement('tspan');
ntspan.setAttribute('x','5');
ntspan.setAttribute('y',yLigne);

ntspan.appendChild(svgDocu.createTextNode('</rdf:RDF>'));
txtr.appendChild(ntspan);

svgDocu.getElementById('results').appendChild(txtr);
}


function Airport(name,coords,iata,icao){
// Constructeur de position géographique
this.name=name;
if(coords)
this.coords=coords;
else
this.coords='';

this.iata=iata;
this.icao=icao;
}


Object.prototype.isInArray=function(arr){
// (fr) Vérifie si un Object est contenu dans un tableau d'Objects

//alert('dans Object.prototype.isInArray\n---\nThis : '+this+'\nLength : '+arr.length+'\n---\nArray : '+arr);

var unique=true;

for(var a=0; a<arr.length; a++){
//alert('on compare '+this+' et '+arr[a]);

if(this==arr[a]){
//alert('Object.prototype.isInArray : trouvé à la position '+a);
unique=false;
break;
}
}

if(unique)
return(-1);
else
return(a);
}


Object.prototype.actorIsInArray=function(arr){
// (fr) Vérifie si un acteur semble contenu dans un tableau d'autres acteurs
//  en fonction de son nom et son adresse mail

//alert('dans Object.prototype.isInArray\n---\nThis : '+this+'\nLength : '+arr.length+'\n---\nArray : '+arr);

var unique=true;
var mailboxToLookFor=this.mbox;
var mailboxInArray;

if(mailboxToLookFor.indexOf('@')!=-1){
// Si ce n'est pas déjà fait, on crypte pour comparer
mailboxToLookFor=hex_sha1(mailboxToLookFor);
}

for(var a=0; a<arr.length; a++){
mailboxInArray=arr[a].mbox;
if(mailboxInArray.indexOf('@')!=-1){
// On crypte pour comparer
mailboxInArray=hex_sha1(mailboxInArray);
}

//alert('on compare '+this.name+' et '+arr[a].name);

if(this.name==arr[a].name || mailboxToLookFor==mailboxInArray){
//alert('Object.prototype.isInArray : trouvé à la position '+a);
unique=false;
break;
}
}

if(unique)
return(-1);
else
return(a);
}


Airport.prototype.draw=function(){
if(!this.coords || this.coords.lat==undefined){
//alert('pas de coords pour l\'aéroport '+this.name);
return(false);
}

var ax=longitudeToSVGCoordinatesMercator(this.coords.lon);
var ay=latitudeToSVGCoordinatesMercator(this.coords.lat);

nc=svgDocu.createElement('circle');
nc.setAttribute('cx',ax);
nc.setAttribute('cy',ay);
nc.setAttribute('r',diametrePointsAirports);

if(this.iata)
nc.setAttribute('class','airportIata');
else
nc.setAttribute('class','airportIcao');

// Affiche le nom quand on clique
if(this.iata)
nc.setAttribute('onmouseover','afficheInfosAirport(\''+this.iata+'\');afficheActorNamesAtAirport(\''+this.iata+'\')');
else
nc.setAttribute('onmouseover','afficheInfosAirport(\''+this.icao+'\');afficheActorNamesAtAirport(\''+this.icao+'\')');

nc.setAttribute('onclick','locateMousePoint(evt)');

svgDocu.getElementById('carteAirports').appendChild(nc);
}


function Actor0(name,coords){
// Constructeur de personnes
this.name=name;
if(coords)
this.coords=coords;
else
this.coords='';
}


function Actor(name,mbox,coords,aptCode,photoUri,foafUri){
// Constructeur de personnes
this.name=name.trim();
this.mbox=mbox.trim();
this.aptCode=aptCode.trim();
this.photoUri=photoUri.trim();
this.foafUri=foafUri.trim();
if(coords)
this.coords=coords;
else
this.coords='';
}


function Coords(lat,lon,alt){
// Constructeur de positions géographiques
this.lat=lat;
this.lon=lon;
this.alt=alt;
}


Array.prototype.item=function(n){
if(n>=this.length)
alert('Cet élément n\'existe pas : item('+n+')');
else
return this[n];
}


Array.prototype.toString=function(){
result=new String('Array('+this.length+'){');
for(a=0; a<this.length; a++){
result+=this[a];
if(a!=this.length-1 && this.length>1)
result+=',';
}
result+='}';
return result;
}


Array.prototype.afficheAvecVirgules=function(){
return this.join(', ');
}


Coords.prototype.toString=function(){
// Formatage des coordonnées (pour affichage dans un alert box)
return '===COORDS======\nLatitude : '+this.lat+'\nLongitude : '+this.lon+'\nAltitude : '+this.alt;
}


Actor.prototype.toString=function(){
// Formatage des coordonnées (pour affichage dans un alert box)
return '===ACTEUR======\nNom : '+this.name+'\nMail : '+this.mbox+'\nLatitude : '+this.coords.lat+'\nLongitude : '+this.coords.lon+'\nAltitude : '+this.coords.alt;
}


Coords.prototype.draw=function(){
var ax=longitudeToSVGCoordinatesMercator(this.lon);
var ay=latitudeToSVGCoordinatesMercator(this.lat);

nc=svgDocu.createElement('circle');
nc.setAttribute('fill','red');
nc.setAttribute('cx',ax);
nc.setAttribute('cy',ay);
nc.setAttribute('r','4');

svgDocu.getElementById('dessins').appendChild(nc);
}


Actor.prototype.draw=function(){
if(this.coords){
//return this.coords.draw();
var ax=longitudeToSVGCoordinatesMercator(this.coords.lon);
var ay=latitudeToSVGCoordinatesMercator(this.coords.lat);

nc=svgDocu.createElement('circle');
nc.setAttribute('class','actor');
nc.setAttribute('cx',ax);
nc.setAttribute('cy',ay);
nc.setAttribute('r',diametrePointsActors);

//Oui mais... un acteur en cache un autre ?
nc.setAttribute('onclick','alert(\''+this.name+'\')');

svgDocu.getElementById('carteActors').appendChild(nc);
}else
alert('Impossible de dessiner l\'acteur : pas de coordonnées.');
}


Coords.prototype.getGeoArea=function(){

var ay=latitudeToSVGCoordinatesMercator(this.lat);
var ax=longitudeToSVGCoordinatesMercator(this.lon);

//alert('getGeoArea\ngoto locatePoint pour les points SVG '+ax+' et '+ay);
return locatePoint(ax,ay);
}


Actor.prototype.getGeoArea=function(){
//alert('on getGeoArea des coords d\'un acteur...\n'+this.name);
return this.coords.getGeoArea();
}


function locateCoords(coords){
alert('on locateCoords '+coords.toString());
var ax=longitudeToSVGCoordinatesMercator(this.lon);
var ay=latitudeToSVGCoordinatesMercator(this.lat);

return locatePoint(ax,ay);
}


Coords.prototype.locateCoords=function(){
alert('on locateCoords '+this.toString());
var ax=longitudeToSVGCoordinatesMercator(this.lon);
var ay=latitudeToSVGCoordinatesMercator(this.lat);

return locatePoint(ax,ay);
}


function transformMinutesCoordsToDecimalCoords(coord){
// Transforme 49-01N en 49.16667

// Attention, il y a aussi 49-01-24N !

// Ne marche plus avec le MSIE JS parser
//var lettre=coord.substr(-1);
// Donc :
var lettre=coord.substr(coord.length-1,1);

var debut=coord.substr(0,coord.indexOf('-'));
var fin=coord.substr(coord.indexOf('-')+1,coord.substr(coord.indexOf('-')+1).length-1);
if(fin.indexOf('-')!=-1){
// Format 64-13-19W --> 64-1319W
//alert('un - dans fin : '+fin+'\n'+fin.indexOf('-'));
fin=fin.substr(0,fin.indexOf('-'))+fin.substr(fin.indexOf('-')+1);
}
var deccoord=Math.round(debut)+'.'+Math.round(fin*10000*(10/6));
if(lettre=='S' || lettre=='W'){
deccoord=-deccoord;
}
//alert('Deb '+debut+'\nFin '+fin+'\nLet '+lettre+'\nDec '+deccoord+'\nlettre '+lettre)
return(deccoord);
}


function getActorCoordsById(actorId){
return getActorCoords(svgDocu.getElementById(actorId));
}


function getActorsByName(name){
name=name.toLowerCase();
var arrayActors=new Array();
var tmp;
var myActors=svgDocu.getElementsByTagName('acteur');
for(var a=0; a<myActors.length; a++){
if(myActors.item(a).getElementsByTagName('name')){
nameLoop=myActors.item(a).getElementsByTagName('name').item(0).firstChild.nodeValue().toLowerCase();
if(name==nameLoop){
tmp=new Actor(name,getActorCoords(myActors.item(a)));
arrayActors.push(tmp);
}
}
}
return arrayActors;
}


function getActorCoords(actor){
if(actor.getElementsByTagName('location'))
{
// On cherche les coords, et l'aéroport à défaut...
if(actor.getElementsByTagName('location').item(0).getElementsByTagName('coords').length)
{
var lat=actor.getElementsByTagName('location').item(0).getElementsByTagName('coords').item(0).getElementsByTagName('lat').item(0).firstChild.nodeValue();
var lon=actor.getElementsByTagName('location').item(0).getElementsByTagName('coords').item(0).getElementsByTagName('lon').item(0).firstChild.nodeValue();
return new Coords(lat,lon);
}
else if(actor.getElementsByTagName('location').item(0).getElementsByTagName('nearestAirport').length)
{
var code=actor.getElementsByTagName('location').item(0).getElementsByTagName('nearestAirport').item(0).firstChild.nodeValue();
return(getAirportCoords(code));
}
}
return false;
}


function latitudeToSVGCoordinatesMercator(lat){
// Rappel : il ne faut pas de '°' -- c'est un float

// Si il y a un S ou un W (ça peut arriver) on met le signe 'moins'

if(lat==undefined){
//alert('undefined, break!');
return(false);
}

try{
// Ne marche plus pour MSIE JS parser
//var lettre=lat.substr(-1);
var lettre=lat.substr(lat.length-1,1);
}catch(erreur){
var lettre=null;
//alert('Erreur '+erreur+' pour lat='+lat);
}

if(lettre=='N'){
lettre='Nord';
//alert('Donc la latitude doit etre '+lat.substr(0,lat.length-1));
// On supprime la lettre de fin
lat=lat.substr(0,lat.length-1);
}else if(lettre=='S'){
lettre='Sud';
//alert('Donc la latitude doit etre '+lat.substr(0,-1)+'\n ou '+lat.substr(0,lat.strlen-1)+'\n ou '+lat.substring(0,-1));
// On supprime la lettre de fin et on prend l'opposé
lat=-lat.substr(0,lat.length-1);
}else{
lettre=null;
}

if(isNaN(lat))
alert('latitudeToSVGCoordinatesMercator : on transforme '+lat+' en une coordonnée SVG \nLettre : '+lettre+'\nlat.length='+lat.length);

return (mapHeight/2)-(mapHeight)*(lat/180);
}


function longitudeToSVGCoordinatesMercator(lon){

// Il semble qu'il y ait des problèmes avec les substringuages de nombres négatifs
try{
//var lettre=lon.substr(-1);
//var lettre=lat.substr(-1);
var lettre=lon.substr(lon.length-1,1);
//var lettre=lon.substring(-1);
}catch(erreur){
// Ca arrive quand lon=-0.45 par exemple
var lettre=null;
//alert('Erreur '+erreur+' pour lon='+lon);
}

// Si il y a un S ou un W (ça peut arriver) on met le signe 'moins'
if(lettre=='E'){
lettre='East';
//alert(lettre+' - Donc la longitude doit etre '+lon.substr(0,lon.length-1));
// On supprime la lettre de fin
lon=lon.substr(0,lon.length-1);
}else if(lettre=='W'){
lettre='West';
//alert(lettre+' - Donc la longitude doit etre '+lon.substr(0,-1)+'\n ou '+lon.substr(0,lon.strlen-1)+'\n ou '+lon.substring(0,-1));
// On supprime la lettre de fin et on prend l'opposé
lon=-lon.substr(0,lon.length-1);
}else{
lettre=null;
}

//if(lettre)
//alert('longitudeToSVGCoordinatesMercator : Il y a une lettre.\nOn transforme '+lon+' en une coordonnée SVG \nLettre : '+lettre);

return (mapWidth/2)+(mapWidth)*(lon/360);
}


function SVGCoordinatesToLatitudeMercator(y){
var lat=-(y/(mapHeight/2)-1)*90;
if(lat<0)
var lettre="S";
else
var lettre="N";
return(Math.abs(Math.round(lat*10000)/10000)+lettre);
}


function SVGCoordinatesToLongitudeMercator(x){
// hmm var lon=-(x/(mapWidth/2)-1)*90;
var lon=-(x/(mapWidth/2)-1)*180;
if(lon<0){
var lettre="E";
// je m'a gourré
//var lettre="W";
//alert('SVGCoordinatesToLongitudeMercator : C\'est à l\'ouest ; x='+x);
}else{
var lettre="W";
}
return(Math.abs(Math.round(lon*10000)/10000)+lettre);
}


function locateMousePoint(evt){
// (fr) Calculer les coordonnées de la souris
// (en) Get the cursor's coordinates
var svgDocu=evt.getTarget().getOwnerDocument();
var svgDocEl=svgDocu.getDocumentElement();
var scale=svgDocEl.getCurrentScale();
var trans=svgDocEl.getCurrentTranslate();

var x=evt.getClientX();
var y=evt.getClientY();

var nx=x/scale+((0.0-trans.x)/scale);
var ny=y/scale+((0.0-trans.y)/scale);

// je ne m'en sers plus
//svgDocu.getElementById('position').firstChild.nodeValue='Détermination en cours...';
svgDocu.getElementById('yourCurrentLocationName').firstChild.nodeValue='Détermination en cours...';

// Et on s'en vante
lastClickedCoords=new Coords(SVGCoordinatesToLatitudeMercator(ny),SVGCoordinatesToLongitudeMercator(nx));

//alert('Coords locatemousepoint : '+lastClickedCoords+'\na partir de '+nx+','+ny+'\n(nx doit etre bon, pas lon)');

var texteArea=locatePoint(nx,ny);

// Copier-coller
if(texteArea){
var texte='';
for(var a=0; a<texteArea.length; a++){
texte+=texteArea[a].afficheAvecVirgules();
if(a<texteArea.length-1)
texte+=' ou ';
}
}
else
{
var texte='Aucune';
}

svgDocu.getElementById('yourCurrentLocationName').firstChild.nodeValue=texte;
afficheLastClickedCoords();

return texteArea;
}


function locatePoint(nx,ny){
// (fr) Découvrir la région à partir d'un point (x,y) en coordonnées 'SVG'
// (en) Discover the region from a (x,y) point

//alert('locatePoint pour '+nx+','+ny);

if(isNaN(nx) || isNaN(ny)){
alert('locatePoint : mauvais chiffres : '+nx+','+ny);
return(false);
}
var arrayFoundTitles=testEachGroup(svgDocu.getElementById('world_outlines'),new Array(),nx,ny);

//arrayFoundTitles.push('pouet');
//alert('locatepoint arrayfoundtitles='+arrayFoundTitles);

// (fr) On teste si un title n'est pas deja contenu dans un autre title plus précis ; si oui, on l'efface.
// (en) Test if a title isn't already contained into another more precise title ; if yes, drop it.
// (Ex : 'Europe' && 'Europe>France' => 'Europe>France').
// on les compare 2 à 2
for(var a=0; a<arrayFoundTitles.length; a++){
// Test chaque position complete possible (France, Europe en est une...)
//alert('nouvelle boucle avec '+arrayFoundTitles[a]);
for(var b=0; b<arrayFoundTitles.length; b++){
// Teste d'abord France, ensuite Europe...
if(a==b || arrayFoundTitles[a]=='--' || arrayFoundTitles[a]=='')
break;
sousChaine1=arrayFoundTitles[a].join(', ');
sousChaine2=arrayFoundTitles[b].join(', ');
//alert('on va tenter de trouver '+sousChaine1+' et le comparer a '+sousChaine2);
if(sousChaine1.substr(sousChaine1.length-sousChaine2.length)==sousChaine2){
//alert(sousChaine2+' est contenu dans '+sousChaine1);
arrayFoundTitles[b]=new String('--');
}
}
}
// (fr) On construit un résultat propre à afficher à l'utilisateur
// (en) Reconstruction of a clean result for human display
var foundTitlesFinal=new Array();
var afficheArrays='';
for(var a=0; a<arrayFoundTitles.length; a++)
{
if(arrayFoundTitles[a]!='--'){
afficheArrays+='['+a+'] '+arrayFoundTitles[a]+'\n';
foundTitlesFinal.push(arrayFoundTitles[a]);
}
}

if(afficheArrays){
//alert('Résultat de la résolution : \n'+afficheArrays);
var texte='';
for(var a=0; a<foundTitlesFinal.length; a++){
texte+=foundTitlesFinal[a].afficheAvecVirgules();
if(a<foundTitlesFinal.length-1)
texte+=' ou ';
}
}
else
{
var texte='Aucune';
}

// (fr) Retourne un array de positions possibles (sur un même pixel, on peut être en espagne ET en france...)
return foundTitlesFinal;
}


function testEachGroup(fromId,foundTitles,nx,ny){
// (fr) Teste tous les éléments 'g' pour savoir si le point choisi est contenu dans ces groupes,
//  et renvoie le texte 'title' associé

var newFoundTitle;
//alert('on commence avec '+fromId+'\nid : '+fromId.id);
//alert('qui a enfants '+fromId.childNodes);

// (fr) Pour JScript, childNodes() marche ; pas pour Adobe où il faut remplacer par childNodes.
for(var a=0; a<fromId.childNodes.length; a++){
// (en) If it's a path: test if our point is inside it
//  If it's a g: call the function recursively
//  If no other children: stop

//alert(fromId.childNodes.item(a));
if(fromId.childNodes.item(a).tagName=='path'){
//alert('c est un path');

// On utilise les bounding boxes pour éliminer les pays dont on est sûr qu'ils ne sont pas sous la souris
if(nx < fromId.childNodes.item(a).getBBox().x 
|| nx > (fromId.childNodes.item(a).getBBox().x + fromId.childNodes.item(a).getBBox().width)
|| ny < (fromId.childNodes.item(a).getBBox().y)
|| ny > (fromId.childNodes.item(a).getBBox().y + fromId.childNodes.item(a).getBBox().height))
{
// On est en-dehors des bounding-boxes
}
else
{
// On est dans les bounding-boxes, donc on lance le test
if(isInsidePath(nx,ny,fromId.childNodes.item(a))){
//alert('trouve dans '+fromId.childNodes.item(a).getAttribute('id'))
newFoundTitle=getAllTitlesFor(fromId.childNodes.item(a),new Array());
foundTitles.push(newFoundTitle);
//alert('Position 1 trouvee : '+newFoundTitle);
}
}
}
else if(fromId.childNodes.item(a).tagName=='g'){
//alert('c est un g');
// (en) It's a 'g' element -> Try to find a title recursively
testEachGroup(fromId.childNodes.item(a),foundTitles,nx,ny);
}
}
return(foundTitles);
}


// === Ceci est plutôt géométrique ========================================


function getAllTitlesFor(pathHandle,arrayTitles){
// (en) Get all the parent elements' 'title' values, for a given id

//alert(' --> recherche des titres pour '+pathHandle.id+' ('+arrayTitles+')');

// (en) Exit when we meet the root element
if(pathHandle.getAttribute('id')=='world_outlines'){
return(arrayTitles);
}

if(pathHandle.getElementsByTagName('title').length){
//alert('** il y a un title : '+pathHandle.getElementsByTagName('title').item(0).firstChild.nodeValue+'\nLe parent est : '+pathHandle.getElementsByTagName('title').item(0).parentNode.id+'\net on voudrait : '+pathHandle.id);
// (en) If the n-level child element's parent matches pathHandle, then it is a 1-level child
//  (which is the correct position for a 'title' element)
if(pathHandle==pathHandle.getElementsByTagName('title').item(0).parentNode){
//moreString=', '+pathHandle.getElementsByTagName('title').item(0).firstChild.nodeValue;
arrayTitles.push(pathHandle.getElementsByTagName('title').item(0).firstChild.nodeValue);
}
//else...
// (en) The child's parent doesn't match pathHandle...
//   then we're not dealing with the same 'generation' (we went too far in the DOM tree)
//   So: do nothing, loop
}

if(pathHandle.parentNode){
pathHandle=pathHandle.parentNode;
return getAllTitlesFor(pathHandle,arrayTitles);
}
}


function isInsidePath(x,y,path){
var pathm=path.getAttribute('d');

// (fr) Ne marche que pour les 'L' dans l'attribut 'd' des paths
//  (les courbes de Bézier, etc. pas encore fait, on ne le fera que si nécéssaire)
// (en) Works only for 'L's inside path's 'd' attribute
//  (Bezier curves, etc. not yet done, I wonder if it will ever be, depends of how we use it)

// (fr) Variables pour parcourir la chaine de caractères...
// (en) String parsing variables...
var compteur=0;
var pointX;
var pointY;
var previousPointX="non";// (fr) Valeur par défaut quand le calcul n'est pas encore fait
// (en) Default value when not yet computed
var previousPointY;

// (fr) Pour se souvenir du premier point
// (en) Keep the first point in mind
var firstPointX;
var firstPointY;

var nbIntersections=0;

// (fr) On saute le M qu'on trouve au début du 'd' du path
// (en) At the beginning of the path's 'd', there is always a M, which we will skip
var remainingPath=pathm.substr(1,pathm.length).trim();

// (fr) Pour chaque extrémité, on cherche à savoir si le segment croise la ligne horizontale
//  virtuelle à droite de notre point initial (x,y)
// (en) For each extremity, we will see if the segment crosses the virtual horizontal line
//  at right from our initial (x,y) point
while(remainingPath.length && compteur<1000000){
// variable 'compteur' is just here to prevent the 'while' from looping eternally
posFirstPoint=remainingPath.indexOf(' ');
pointX=remainingPath.substr(0,posFirstPoint);
posSecondPoint=remainingPath.indexOf('L');

// (en) At the end
if(remainingPath.indexOf('L')==-1)
posSecondPoint=remainingPath.indexOf('Z');
pointY=remainingPath.substr(posFirstPoint,posSecondPoint-posFirstPoint).trim();
remainingPath=remainingPath.substr(posSecondPoint+1,remainingPath.length).trim();

// (fr) Calcule s'il y a une intersection
// (en) Compute if there is a crossing
if(previousPointX!="non"){
if(lookForIntersection(x,y,pointX,pointY,previousPointX,previousPointY))
nbIntersections++;
}else{
firstPointX=pointX;
firstPointY=pointY;
}

compteur++;
//window.alert('pointX : '+pointX+'\npointY : '+pointY+'\nremainingPath : '+remainingPath);
previousPointX=pointX;
previousPointY=pointY;
}

if(compteur>=999999){
alert('Il semble que isInsidePath ait fait plus de boucles que souhaité...\nremainingpath='+remainingPath);
}

// At the end, perform the test with the first parsed points
if(lookForIntersection(x,y,pointX,pointY,firstPointX,firstPointY))
nbIntersections++;

//window.alert('isinsidepath terminé\nNombre d\'intersections : '+nbIntersections+'\nNombre de calculs : '+compteur);
if(nbIntersections%2!=1){
// Odd number of intersections => point not inside the path
return false;
}
else {
// Even number of intersections => point inside the path
return true;
}
}

function lookForIntersection(px,py,x1,y1,x2,y2){
var foundOne=false;
var xIntersection;
//alert('Intersection?\nx1 : '+x1+'\ny1 : '+y1+'\nx2 : '+x2+'\ny2 : '+y2);
if(x1>=px || x2>=px){
if(x1>=px && x2>=px){
// (en) Case 1 : all 2 extremities of the segment are at point P's right side
if(y1>=py && y2<py){
// Top to bottom
foundOne=true;
//window.alert('Oui ! 1-1');
}
else if(y2>=py && y1<py){
// Bottom to top
foundOne=true;
//window.alert('Oui ! 1-2');
} //else window.alert('Non 1');
}
else {
// (en) Case 2
if(!((y1>py && y2>py) || (y1<py && y2<py)))
{
// (en) Find the x-coord of the segment when y = pointY.
xIntersection=(1.0*x1+((py-y1)*(x2-x1)/(y2-y1)));
if(xIntersection>px){
foundOne=true;
//window.alert('Oui ! 2 '+xIntersection);
} //else window.alert('Non ! 2 '+xIntersection);
} //else window.alert('Non ! 3');
}
} //else window.alert('Non ! 4');
return foundOne;
}


function traceOutlines(evt){

alert('attention ! Ne marche plus, normalement');

// (en) For each clic, append the coordinates of this point to the string defining the path's 'd' element
var svgDocu=evt.getTarget().getOwnerDocument();
var svgDocEl=svgDocu.getDocumentElement();
var scale=svgDocEl.getCurrentScale();
var trans=svgDocEl.getCurrentTranslate();

var x=evt.getClientX();
var y=evt.getClientY();

// Coordonnées sur le graphe
var nx=x/scale+((0.0-trans.x)/scale);
var ny=y/scale+((0.0-trans.y)/scale);

var newText=svgDocu.createTextNode(nx+' '+ny+' L ');
svgDocu.getElementById('line1').appendChild(newText);

// Coordonnées réelles
var lat=SVGCoordinatesToLatitudeMercator(ny);
var lon=SVGCoordinatesToLongitudeMercator(nx);

var newText=svgDocu.createTextNode(lat+' '+lon+' ');
svgDocu.getElementById('line3').appendChild(newText);
}


String.prototype.trim=function(){
// Create the prototype for trim() on the String object

// Skip leading and trailing whitespace and return everything in between
var x=this;
x=x.replace(/^\s*(.*)/, "$1");
x=x.replace(/(.*?)\s*$/, "$1");
return x;
}


// === Fin du script ==================================
]]></script>

</svg>

Annexe B : Fichier FOAF complet

Mon fichier foaf.rdf personnel.

<?xml version="1.0" encoding="iso-8859-1"?>
<rdf:RDF
xmlns:wot="http://xmlns.com/wot/0.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:rss="http://purl.org/rss/1.0/"
xmlns="http://xmlns.com/foaf/0.1/"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:lang="http://purl.org/net/inkel/rdf/schemas/lang/1.1#"
xmlns:wn="http://xmlns.com/wordnet/1.6/"
xmlns:air="http://www.megginson.com/exp/ns/airports#"
xmlns:contact="http://www.w3.org/2000/10/swap/pim/contact#"
xmlns:dc="http://purl.org/dc/elements/1.1/">

<foaf:Person rdfs:id="cedric">
<foaf:name>Cédric Kiss</foaf:name>
<foaf:title>Mr</foaf:title>
<foaf:firstName>Cédric</foaf:firstName>
<foaf:surname>Kiss</foaf:surname>
<foaf:nick>Cédric</foaf:nick>
<foaf:mbox_sha1sum>2da7e65b06d1b74f51c8d18a3c0b27cc360e2fe6</foaf:mbox_sha1sum>
<foaf:homepage rdf:resource="http://ckiss.net/cedric/"/>
<foaf:depiction rdf:resource="http://ckiss.net/copains/photos/photo_donindiano4.jpg"/>
<foaf:phone rdf:resource="tel:06-73-69-68-84"/>
<foaf:workplaceHomepage rdf:resource="http://www.w3.org/"/>
<foaf:schoolHomepage rdf:resource="http://www.u-paris2.fr/html/formations/infocom/imac_inge.htm#ing"/>

<contact:nearestAirport>
<wn:Airport air:iata="NCE"/>
</contact:nearestAirport>

<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:charles@w3.org"/>
<foaf:firstname>Charles</foaf:firstname>
<foaf:familyname>McCathieNevile</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:firstname>Karima</foaf:firstname>
<foaf:familyname>Boudaoud</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:caroline@w3.org"/>
<foaf:firstname>Caroline</foaf:firstname>
<foaf:familyname>Baron</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:coralie@w3.org"/>
<foaf:firstname>Coralie</foaf:firstname>
<foaf:familyname>Mercier</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:bert@w3.org"/>
<foaf:firstname>Bert</foaf:firstname>
<foaf:familyname>Bos</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:carine@w3.org"/>
<foaf:firstname>Carine</foaf:firstname>
<foaf:familyname>Bournez</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:ylafon@w3.org"/>
<foaf:firstname>Yves</foaf:firstname>
<foaf:familyname>Lafon</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:alex@w3.org"/>
<foaf:firstname>Alexandra</foaf:firstname>
<foaf:familyname>Lavirotte</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:danield@w3.org"/>
<foaf:firstname>Daniel</foaf:firstname>
<foaf:familyname>Dardailler</foaf:familyname>
</foaf:Person>
</foaf:knows>
<foaf:knows>
<foaf:Person>
<foaf:mbox rdf:resource="mailto:rigo@w3.org"/>
<foaf:firstname>Rigo</foaf:firstname>
<foaf:familyname>Wenning</foaf:familyname>
</foaf:Person>
</foaf:knows>

<lang:masters>fr</lang:masters>
<lang:masters>en</lang:masters>

</foaf:Person>

</rdf:RDF>

Annexe C : Présentation par e-mail à la liste de diffusion

Voici le mail envoyé à la liste www-rdf-interest.

Hi RDF enthusiasts,

As part of the SWAD-Europe project [1], I have been working with Charles McCathieNevile to provide a graphical interface to RDF location information for the RDF community.

The purpose is to determine if some people are located in the same "area" as you ("Alice is in the same state; Bob is in the same city"...) as you travel. This could be as acurate as you wish, but on the other hand you will have to build your own data sets (in RDF, hopefully, so that they could be re-useable).

The graphics are displayed in SVG, W3C's XML-based 2D vector format; the whole process is done client-side. RDF location information comes from the plethora of FOAF [2] files around the web, and can be described either with their nearestAirport or with real lat/long co-ordinates.

You can also restrict the number of persons, to better suit your needs. For example: you may only want to keep people whose interests involve 'Accessibility' (for that, you will need to make a minor edit to the source code).

Although it's still under development, the main idea is here. You can find files, source code, screenshots and other useful information about this project at:

http://www.w3.org/2001/sw/Europe/200306/geo/readme_carte_zones_monde_rdf.html

Cheers,


[1] http://www.w3.org/2001/sw/Europe - SWAD-Europe
[2] http://rdfweb.org/ - Friend of a Friend project

Cédric Kiss
___________________________________________________
SWAD-Europe - http://www.w3.org/2001/sw/Europe/
W3C - INRIA Sophia Antipolis, France

Annexe D : Création du sommaire avec XSL

Voici la feuille de style XSL qui a servi à générer la table des matières à partir du fichier HTML (on peut certainement faire beaucoup mieux, mais c'est néanmoins utilisable) :

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
  <body>
    <h2>Sommaire :</h2>

      <ul>
<xsl:apply-templates/>
      </ul>

  </body>
  </html>
</xsl:template>


      <xsl:template match="div">
        <xsl:apply-templates select="h2|h3|h4"/>
      </xsl:template>

      <xsl:template match="h2">
          <li><b><xsl:value-of select="."/></b></li>

      </xsl:template>

      <xsl:template match="h3">
         <ul>
           <li><xsl:value-of select="."/></li>
         </ul>

      </xsl:template>

      <xsl:template match="h4">
          <ul><ul>
            <li><i><xsl:value-of select="."/></i></li>
          </ul></ul>

      </xsl:template>

</xsl:stylesheet>

Annexe E : Captures d'écran commentées

Voici l'écran qui apparaît une fois le script chargé :

Fenêtre du script
Vue globale de l'affichage du script.

Cet affichage dépend du nombre du nombre d'aéroports, d'acteurs et de pays chargés.

Comme c'est un graphique SVG, on peut zoomer pour afficher plus de détails dans une région donnée. Lorsqu'on passe la souris sur un élément, s'affichent les informations sur l'aéroport et sur les acteurs présents.

Détail du script
Détail du script (zoom vur la France).