ReLucBlog - SIG, MOZILLA & NTIC

Aller au contenu | Aller au menu | Aller à la recherche

jeudi 18 septembre 2008

Créer son nsIProtocolHandler pour afficher un buffer (char *)

Dans le cadre de la réalisation de mon premier composants XPCOM en C++, j'ai dû résoudre le problème suivant : Comment récupérer un buffer pour le manipuler en JavaScript et l'afficher via la définition d'un nouveau protocol ?

Voici mes découvertes :

nsIBinaryOutputStream

L'objet JavaScript suivant permet d'écriture dans un output stream :

Components.classes"@mozilla.org/binaryoutputstream;1"
  .createInstance(Components.interfaces.nsIBinaryOutputStream);

Un tel objet permet de récupérer le buffer générer au sein de mon composant C++ via la méthode writeByteArray qui prend en paramètre le buffer (char *) et la taille de celui-ci. J'ai donc créer une méthode saveToStream qui prend en paramètre un objet implémentant l'interface nsIBinaryOutputStream.
Par exemple pour écrire dans un fichier :

// création d'un fichier local
var aFile = Components.classes"@mozilla.org/file/local;1".createInstance(Components.interfaces.nsILocalFile);
aFile.initWithPath( "/tmp/buffer");
// création du OutputStream associé au fichier
var stream = Components.classes"@mozilla.org/network/file-output-stream;1".createInstance(Components.interfaces.nsIFileOutputStream);
stream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0);
// création du binary output stream
var bos = Components.classes"@mozilla.org/binaryoutputstream;1".createInstance(Components.interfaces.nsIBinaryOutputStream);
// cette étape va permettre d'écrire dans l'output stream du fichier
bos.setOutputStream(stream);
// écriture du buffer dans l'output stream du fichier
myComponent.saveToStream(bos);
// il n'y a plus qu'à fermer les flux pour finaliser l'écriture du buffer dans le fichier
bos.close();
stream.close();

Grâce au Binary Output Stream je suis capable d'écrire le buffer générer dans mon composant XPCOM dans un fichier.

nsIChannel et nsIPipe

Dans Mozilla, il est possible de définir son propre protocol. La principale étape est de définir la méthode newChannel qui prend en paramètre un objet implémentant l'interface nsIURI et doit retourner un objet implémentant l'interface nsIChannel. Dans mon cas c'est dans cette méthode que j'utilise mon composant et donc que je récupère un objet implémentant l'interface nsIOutputStream. Or un nsIChannel est basé sur un objet implémentant une interface nsIInputStream. Il fallait trouver le moyen d'obtenir le input stream associé à l'output stream.

L'objet JavaScript suivant permet de lié input stream et output stream :

Components.classes"@mozilla.org/pipe;1".createInstance(Components.interfaces.nsIPipe);

Enfin il fallait faire de l'input stream obtenu un objet implémentant l'interface nsIChannel, c'est ce que permet l'objet suivant :

Components.classes"@mozilla.org/network/input-stream-channel;1".createInstance(Components.interfaces.nsIInputStreamChannel);

La méthode newChannel s'implémente ainsi :

myProtocolHandler.prototype.newChannel = function(aURI) {
     // création du pipe
     var pipe = Components.classes"@mozilla.org/pipe;1".createInstance(Components.interfaces.nsIPipe);
     pipe.init(true,true, 0, 0, null);
     // création du binary output stream
     var bos = Components.classes"@mozilla.org/binaryoutputstream;1".createInstance(Components.interfaces.nsIBinaryOutputStream);
     // cette étape va permettre d'écrire dans l'output stream du pipe
     bos.setOutputSTream(pipe.outputStream);
     // écriture du buffer dans l'output stream du pipe
     myComponent.saveToStream(bos);
     // fermeture des output stream
     bos.close();
     pipe.outputStream.close();
     // il reste plus qu'à créer le channel
     var channel = Components.classes"@mozilla.org/network/input-stream-channel;1".createInstance(Components.interfaces.nsIInputStreamChannel);
     channel.setURI(aURI);
     channel.contentStream = pipe.inputStream;
     channel.QueryInterface(Components.interfaces.nsIChannel);
     return channel;
}

C'est bien mais c'est synchone...

nsIPipe et nsIThreadManager

La dernière étape consiste à faire en sorte que la création du buffer et l'écriture soit asynchrone. Ce qu'il faut savoir c'est que le pipe permet d'écrire dans un thread et de lire le résultat dans un autre, de plus tant que rien n'est écrit dans l'output stream du pipe rein n'est lu, enfin lorsque l'output stream est clos le channel et donc la requête qui a été créer à partir de l'input stream du pipe est considéré comme fini.

Il faut d'abord définir un thread d'exécution :

var workingThread = function(pipe, myComponent) {
   this.pipe = pipe;
   this.component = myComponent;
};
workingThread.prototype = {
  run: function() {
    try {
      var bos = Cc"@mozilla.org/binaryoutputstream;1".createInstance(Ci.nsIBinaryOutputStream);
      bos.setOutputStream(this.pipe.outputStream);
      this.component.saveToStream(bos);
      bos.close();
      this.pipe.outputStream.close();
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Ci.nsIRunnable) ||
        iid.equals(Ci.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

Ensuite il suffit de déclarer un objet global permettant l'exécution en fond de tâche :

background = Components.classes"@mozilla.org/thread-manager;1".getService().newThread(0);

Il ne reste plus qu'à réécrire la méthode newChannel du protocol handler :

myProtocolHandler.prototype.newChannel = function(aURI) {
     // création du pipe
     var pipe = Components.classes"@mozilla.org/pipe;1".createInstance(Components.interfaces.nsIPipe);
     pipe.init(true,true, 0, 0, null);
     // appel en fond de tâche de la création du buffer et de l'écriture
     background.dispatch(new workingThread(pipe, myComponent), background.DISPATCH_NORMAL);
     // il reste plus qu'à créer le channel
     var channel = Components.classes"@mozilla.org/network/input-stream-channel;1".createInstance(Components.interfaces.nsIInputStreamChannel);
     channel.setURI(aURI);
     channel.contentStream = pipe.inputStream;
     channel.QueryInterface(Components.interfaces.nsIChannel);
     return channel;
}

En espérant que ça puisses vous servir ;-)

mercredi 17 septembre 2008

Diagramme UML d'OpenLayers 2.7

La prochaine version d'OpenLayers est en vue, la Release Candidate 2 a été publié. Pour l'occasion un diagramme UML est disponible. Donc si vous vous demandiez à quoi ressemblait l'architecture d'OpenLayers, je vous conseille d'y jeter un coup d'oeil.

mardi 16 septembre 2008

Effets SVG sur du HTML

Après l'intégration des tranformations CSS3, Robert O'Callahan annonce la possibilité d'utiliser les effets SVG sur du contenu HTML. Le Web Ouvert va vraiment devenir sympa ;-)

jeudi 11 septembre 2008

Géolocaliser un utilisateur d'application Web

Actuellement pour permettre à un utilisateur de se géolocaliser, il faut demander l'adresse de celui-ci. Avec Firefox 3.1 ou la nouvelle version de Google Gears, il sera possible d'obtenir la géolocalisation simplemet en demandant au navigateur!

La prochaine version de Firefox, disponible avant la fin de l'année, implémente les premières versions de l'API de Géolocalisation. Cette nouvelle norme du W3C est encore en cours d'élaboration. L'objectif de cette norme est de permettre aux applications Web de demander la position sur Terre de l'utilisateur, sans que celui-ci ai forcément besoin de saisir une adresse ou sa position.

Techniquement ceci se traduit par :

  • l'ajout de méthodes au DOM accésible en JavaScript ;
  • l'ajout d'une interface d'abstraction des fournisseurs de position.

Cette structure résout 2 problèmes :

  • Comment puis-je fournir en temps réelle ma position à une application Web ?
  • Comment fournir sa position quand on a pas de GPS ?

Donc le développeur d'application Web pourra simplement interroger le navigateur sur la position de l'utilisateur mais aussi s'abonner pour connaître toutes les modifications de position. Pour bien voir l'intérêt d'une telle API, prennons le cas de Google Maps. Sur l'IPhone 3G, il est possible de se positionner sur fond Google Maps. Mais vous n'utiliser pas Safari ni le Google Maps que vous consultez sur navigateur Web. Donc Google a dût développer une application qui ai accès au système GPS de l'IPhone et aux données de Google Maps. Avec l'API de Géolocalisation, plus besoin de développer une nouvelle application. Ils suffit d'ajouter quelques lignes de JavaScriptcool pour que l'application puisse savoir où se trouve l'utilisateur.

Il reste tout de même à ajouter au navigateur la possibilité de répondre à cette demande de position! Dans ce qui a été imaginé, le navigateur pourra utiliser différentes sources de position : GPS, bornes wifi, bornes téléphoniques, etc... Chez Mozilla, l'ajout de ces fournisseurs de position pourra se faire via des extensions. Pour Google Gears, il sera possible de proposer un service de geolocalisation.

Donc cette API a été intégré au futur Firefox, mais aussi au futur navigateur mobile de Mozilla : Fennec. Il est évident qu'une telle API doivent être intégré à Fennec, mais elle est tout aussi intéressante dans Firefox. Tout d'abord par ce que les objets mobiles ne sont pas que les smartphones ou téléphones portables, il y a aussi les netbooks et les tablets PC. Ensuite on peut imaginer des applications Web contextuelles tout aussi intéressantes pour un utilisateur sur poste fixe, par exemple pour consulter les évènements prêts de chez soi ou les bonnes affaires des magasins du quartiers.

Cette nouvelle API offre de nouvelles perspectives de développement d'application de Web Mapping mais aussi de Web SIG.

Pour en savoir plus :

mardi 9 septembre 2008

Evolution des données libres d'OpenStreetMap

GeoFabrik vous permet de consulter l'évolution des données cartographiques libres (OpenStreeMap) de certaines région, comme par exemple Paris :

Données cartographiques libres animées de Paris

Ces animations sont disponibles sous forme de GIF ou via une interface menu d'un slider.

Via OpenGeoData.

vendredi 5 septembre 2008

OpenLayers 2.7 RC1 aujourd'hui

La première Release Candidate de la version 2.7 d'OpenLayers devrait être annoncé aujourd'hui.

Au moment où j'écris ces lignes, 96% des tickets de la version 2.7 ont été clos. Il reste encore 7 tickets.
4 devrait être inclus dans cette Release candidate :

  • l'ajout d'un protocole WFS, Review ;
  • l'ajout d'un protocole SQL.Gears pour pouvoir utiliser Google Gears et donc OpenLayers en mode déconnecté, Review ;
  • l'ajout d'un contrôle de mesure, Commit ;
  • l'amélioration du gestionnaire de dessin (sketch handler), Commit.

Les 3 derniers nécessite encore du travaille :

Dans ce qui est déjà intégré à cette Release Candidate, il y a 2 choses intéressantes que vous pouvez déjà tester :

  • un affichage en profondeur des objets géographiques en fonction de leur position sur l'axe des x et y, exemple ;
  • le support des well-known graphics pour les géométries de type point, exemple.

Enfin un dernier point qui m'intéresse particulièrement, c'est la correction d'un bug des couches Google.