Tutorial

Introduction

Avant tout propos, la lecture des documentations de YUI et OpenLayers est fortement recommandée pour réaliser des widgets. De nombreux exemples sont à disposition YUI exemples et OpenLayers exemples. De plus, la communauté autour de ces 2 librairies est très active et de nombreuses réponses se trouvent sur les forums.

Ce tutorial présente plusieurs exemples pour créer son propre widget. Avant de commencer, il convient de s’assurer qu’Eclipse est installé sur votre poste ainsi que le module Maven. Il vous permettra de générer un fichier JAR qui contiendra vos widgets.

Easy geoweb accepte plusieurs fichiers JAR avec des widgets mais chaque widget doit posséder un identifiant unique.

Un widget est un module qui sera chargé dynamiquement dans le portail et qui se compose de 3 fichiers :

  • fichier XML pour décrire les ressources nécessaires (javascript, properties, image) et pour déterminer l’identification du module.
  • fichier JS pour configurer les actions et les interactions du widget avec le portail.
  • fichier CSS pour paramétrer le style du widget.

L’ensemble des sources nécessaires à la réalisation de ce tutorial est fourni avec la documentation.

Projet Maven

Création du projet

Dans Eclipse, créer un nouveau projet Maven.

Choisir Create a simple project (skyp archetype selection). Pour ce tutorial, la description de l'artifact est :

  • Group Id : com.geoconcept.geoweb.product
  • Artifact Id : geoweb-easy-widgets-tutorial
  • Version : 0.0.1-SNAPSHOT
  • Packaging : jar

Une fois le nouveau projet créé, supprimer la classe principale java et le répertoire test et main dans src. Dans le répertoire src, créer un répertoire scripts avec Source Folder (clic droit sur le projet, New → Source Folder).

Ensuite créer un répertoire META-INF dans src. Il contiendra le fichier egw-widgets.txt. A l’intérieur de ce fichier, il est nécessaire d’inscrire une chaine de caractère, par exemple "easy-geoweb widgets module". Ce fichier permet d’identifier et de filtrer les JAR pour conserver uniquement ceux qui possède des widgets.

L’organisation finale est la suivante :

gcweb-reference-img/lbs-integration/geoweb-easy/schema.png

Créer 4 nouveaux répertoires dans src/scripts  :

  • general : contiendra les fichiers des nouveaux widgets. Ce répertoire sera visible dans l’accordéon du Composer. Il existe plusieurs catégories/répertoires disponibles pour le Composer (Annotation/annotation, Informations sur les objets/data, Général/general, Mise en page/layout, Mesures/measure, Modifications des données/modification, Navigation/navigation, Vue/view). En plus de general, ajouter les répertoires annotation et navigation pour réaliser les exemples.
  • resources : contiendra les fichiers de ressources (images, localisation,…).

Dans resources créer :

  • images : contiendra toutes les images utilisées
  • exampleLang.properties : fichier contenant toutes les chaines de localisation par défaut. Pour ajouter de nouvelles langues aux widgets, il faut dupliquer ce fichier en ajoutant au nom du fichier la localisation souhaitée. Par exemple, pour une localisation française, le fichier devra se nommer exampleLang_fr.properties.

Attention, un JAR de widget doit contenir uniquement des fichiers liés aux widgets.

Configuration du POM

Dans le fichier pom.xml, ajouter les éléments suivants afin de construire le projet au format JAR :

<build>
        <plugins>
                <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <configuration>
                                <classesDirectory>src</classesDirectory>
                                <outputDirectory>D:/widgets</outputDirectory>
                                <excludes>
                                        <exclude>**/pom.xml</exclude>
                                </excludes>
                                <archive>
                                        <addMavenDescriptor>false</addMavenDescriptor>
                                </archive>
                        </configuration>
                </plugin>
        </plugins>
</build>

classesDirectory est le chemin d’accès aux fichiers des widgets.

outputDirectory est le chemin dans lequel sera déposé le fichier JAR généré.

Widget afficher une légende

Objectif

Ce widget permet d’afficher la légende de la carte. Il s’agit d’un widget de type bouton. La légende correspond à une image affichée dans un panneau.

Le widget est nommé ExampleLegend.

Initialisation

Créer les 3 fichiers du widget dans le répertoire general :

  • ExampleLegend.xml
  • ExampleLegend.js
  • ExampleLegend.css
ExampleLegend.xml

Cet exemple est le plus simple. Voici la configuration du fichier de description :

<widget id="exampleLegend"
        category="standard"
        position="12"
        icon="resources/images/exampleLegend.png"
        js="general/ExampleLegend.js"
        module="geoconcept-widget-example-legend"
        display="true"
        lang="resources/exampleLang">
</widget>
  • id : identifiant unique
  • icon : chemin d’accès à la ressource image pour l’afficher comme bouton.
  • js : chemin d’accès au fichier de configuration de l’action du widget.
  • module : nom du module qui sera appelé dans le widget manager pour charger l’instance du widget.
  • display : booléen pour déterminer si le widget sera affiché dans le Composer. Utiliser false pour créer un sous-widget qui proposera des fonctionnalités commune à d’autres widgets.
  • lang : chemin d’accès au fichier de localisation.

Pour plus d’informations, voir la description des fichiers XML dans le chapitre des widgets.

ExampleLegend.js

Le fichier js permet de construire un nouveau module. Le widget créé est un bouton simple, l’action sera executée à chaque fois que l’utilisateur effectuera une pression sur le bouton. La méthode appelée suite à un clic souris est trigger:function(). Cette méthode est définie dans WidgetBase mais il convient ici de la surcharger afin d’afficher l’image de la légende. Voici la méthode finale :

trigger:function() {
        var panelNode = this.createPanel(this._("widget.exampleLegend.popup.title"));

        var linkNode = Y.Node.create(this.getImageHtml());
        panelNode.appendChild(linkNode);

        this.showPanel();
},

Les méthodes _(key), createPanel(name) et showPanel() appartiennent à l'API de base d’un widget. Chaque widget possède une fenêtre (panel) qui est liée avec un identifiant unique. Il est possible de créer d’autres fenêtres mais il faudra les définir directement de le widget. La clé de localisation doit être ajoutée au fichier properties du jar.

La partie HTML de l’intérieur de la fenêtre est défini par la méthode getImageHtml(). Un noeud est ensuite créé puis ajouté à la fenêtre :

getImageHtml:function() {
        var imageUrl = "/" + this.getContextUrl() + "/combo?resources/images/sample_legende.png";
        var html = "<img src='" + imageUrl + "' class='imageLegend'>";
        return html;
}

Dans cette méthode, la balise HTML est créée. La méthode getContextUrl() appartient à l'API de base d’un widget. L’image est stockée dans les ressources du widget. Une classe est associée afin de définir la position de l’image dans la fenêtre.

A présent, voici la description complète de ce widget :

YUI.add("geoconcept-widget-example-legend", function(Y) {

        /* ExampleLegend class constructor */
        function ExampleLegend(config) {
                ExampleLegend.superclass.constructor.apply(this, arguments);
        }

        /*
         * Required NAME static field, to identify the Widget class and
         * used as an event prefix, to generate class names etc. (set to the
         * class name in camel case).
         */
        ExampleLegend.NAME = "exampleLegend";

        Y.extend(ExampleLegend, Y.GCUI.WidgetButton,  {
                initializer: function(config) {
                        ExampleLegend.superclass.initializer.apply(this);
                },

                destructor: function() {
                        ExampleLegend.superclass.destructor.apply(this);
                },

                configureAction: function() {
                        ExampleLegend.superclass.configureAction.apply(this);
                },

                /*
                 * Action after click on button
                 */
                trigger:function() {
                        var panelNode = this.createPanel(this._("widget.exampleLegend.popup.title"));

                        var linkNode = Y.Node.create(this.getImageHtml());
                        panelNode.appendChild(linkNode);

                        this.showPanel();
                },

                /*
                 * Get HTML description for image legend
                 * return string html
                 */
                getImageHtml:function() {
                        var imageUrl = "/" + this.getContextUrl() + "/combo?resources/images/sample_legende.png";
                        var html = "<img src='" + imageUrl + "' class='imageLegend'>";
                        return html;
                }
        });
        Y.namespace("GCUI");
        Y.GCUI.ExampleLegend = ExampleLegend;

}, "1.0.0", {requires: ["project-widgetbutton"] });
ExampleLegend.css

Dans cet exemple, une classe a été ajoutée pour définir les marges autour de la légende pour améliorer son positionnement dans la fenêtre.

.imageLegend {
        margin: 5px;
}
Ressources

Voici les images nécessaires à la réalisation de ce widget.

gcweb-reference-img/lbs-integration/geoweb-easy/sample_legende.png
gcweb-reference-img/lbs-integration/geoweb-easy/exampleLegend.png

Placer ces images dans le répertoire src/scripts/resources/images.

Par défaut, un bouton possède 2 clés de localisation, label et description. label est utilisé dans l’accordéon du Composer et description est affiché lorsque la souris survole le bouton.

Attention les clés doivent obligatoirement commencer par widget.(id du widget).(fin de la clé). Pour ce widget, la clé du label sera widget.exampleLegend.label

Voici les clés de base à placer dans le fichier properties :

widget.exampleLegend.label=Exemple de l\u00E9gende
widget.exampleLegend.description=Afficher la l\u00E9gende principale

Voici la clé pour afficher un titre dans la fenêtre :

widget.exampleLegend.popup.title=L\u00E9gende

Widget dessiner un point

Objectif

Ce widget permet de dessiner un point sur la carte. Il s’agit d’un widget de type bouton poussoir, en cliquant dessus, l’utilisateur active la fonctionnalité. Une propriété dans le Composer permettra de déterminer l’image stockée en base de données à afficher après un clic sur la carte.

Le widget est nommé ExampleDrawPOI.

Initialisation

Créer les 3 fichiers du widget dans le répertoire annotation :

  • ExampleDrawPOI.xml
  • ExampleDrawPOI.js
  • ExampleDrawPOI.css
ExampleDrawPOI.xml

Cet exemple permet de rajouter une propriété au widget. Voici la configuration du fichier de description :

<widget id="exampleDrawPOI"
                category="standard"
                position="13"
                icon="resources/images/exampleDrawPOI.png"
                js="annotation/ExampleDrawPOI.js"
                module="geoconcept-widget-example-draw-poi"
                display="true"
                lang="resources/exampleLang">
        <properties>
                <property id="defaultImage" type="combobox">
                        <params value="poi_blue_small"/>
                </property>
        </properties>
</widget>
  • properties : permet d’ajouter des propriétés au widget.
  • property : détail d’une propriété composé d’un id unique et d’un type.
  • params : paramètre par défaut pour la propriété correspondante. Cette balise n’est pas obligatoire dans le cas où il n’y a pas de valeur par défaut.

Pour plus d’informations, voir la description des fichiers XML dans le chapitre des widgets.

ExampleDrawPOI.js

Le widget créé est un bouton de type TOOL, lorsque l’utilisateur clique sur le bouton, il reste activé. Un seul bouton de type TOOL peut être activé à la fois, tous les autres boutons de ce type sont alors désactivés. Les deux méthodes utilisés sont activate() et deactivate(). A l’activation, le widget appelle une fonction pour activer l'écouteur d’un clic sur la carte. Pour la désactivation, l'écouteur sera supprimé. Voici les 2 méthodes de base du widget :

activate:function() {
        ExampleDrawPOI.superclass.activate.apply(this);
        this.createPoiListener();
},

deactivate:function() {
        ExampleDrawPOI.superclass.deactivate.apply(this);
        this.removeCreatePoiListener();
},

La création d’un écouteur suite à un clic souris sur la carte est basée sur l’API Javascript

createPoiListener:function() {
        var wExampleDrawPOI = this;

        function clickListener(dynMap){}

        clickListener.prototype.onSelect = function(x,y,xpx,ypx){
                wExampleDrawPOI.createObject(x,y);
        };

        var listener = new clickListener();
        DynMapAddMouseSelectionEventListener(this.getMap(), "clickOnMap", listener)
},

La méthode s’appuie sur la fonction DynMapAddMouseSelectionEventListener dans laquelle il est possible de lui passer un écouteur (listener) en paramètre. Un clic souris active la fonction createObject(x,y) du widget. Les coordonnées x et y permettront de positionner l’objet sur la carte.

L’ajout du point se fait dans la couche objet de l’API Javascript. L’image affichée pour représenter le point est définie à partir d’une url.

var imageUrl = this.getHost() + this.getContextUrl() + "/Image/showImage.do?name=" +
                                                        this.getPropertyValue(ExampleDrawPOI.IMAGE_PROPERTY_NAME);

Les méthodes getHost(), getContextUrl() et getPropertyValue(name) appartiennent à l'API de base d’un widget. ExampleDrawPOI.IMAGE_PROPERTY_NAME est une constante définie dans le module de la façon suivante :

ExampleDrawPOI.IMAGE_PROPERTY_NAME = "defaultImage";

getPropertyValue(name) est la méthode permettant de rechercher la valeur d’une propriété. Chaque widget peut bénéficier de propriétés qu’il faut définir dans son fichier de description. Tous les widgets possèdent par défaut une propriété sur le choix des groupes ayant accès à ce widget dans le cas d’un portail privé. Les widgets de type bouton possèdent en plus une propriété pour définir si le widget est activé par défaut. Les autres propriétés sont créées automatiquement en fonction du fichier de description mais si le type de propriété n’existe pas, il est alors possible d’utiliser la méthode addProperties(groups).

Pour récupérer les images de la base de données, il faut faire appel à la méthode getImages de l’API REST. Le résultat est retourné dans la méthode onSuccess() ou onError(), en cas de succès ou d'échec de la recherche.

this.getEGWApi().getImages({
        onSuccess:function(response) {
        },
        onError:function(response) {
        }
});

A la suite de la récupération des images, il faut associer le résultat à la liste déroulante de la propriété en utilisant la méthode setComboboxOptions(propertyId, data)

this.setComboboxOptions(ExampleDrawPOI.IMAGE_PROPERTY_NAME, images);

A présent, voici la description complète de ce widget :

YUI.add("geoconcept-widget-example-draw-poi", function(Y) {

        /* ExampleDrawPOI class constructor */
        function ExampleDrawPOI(config) {
                ExampleDrawPOI.superclass.constructor.apply(this, arguments);
        }

        /*
         * Required NAME static field, to identify the Widget class and
         * used as an event prefix, to generate class names etc. (set to the
         * class name in camel case).
         */
        ExampleDrawPOI.NAME = "exampleDrawPOI";
        ExampleDrawPOI.IMAGE_PROPERTY_NAME = "defaultImage";

        Y.extend(ExampleDrawPOI, Y.GCUI.WidgetButton,  {
                initializer: function(config) {
                        ExampleDrawPOI.superclass.initializer.apply(this);
                        this.setType(Y.GCUI.WidgetButton.TYPE_TOOL);
                        this.getImages();
                },

                destructor: function() {
                        ExampleDrawPOI.superclass.destructor.apply(this);
                },

                configureAction: function() {
                        ExampleDrawPOI.superclass.configureAction.apply(this);
                },

                activate:function() {
                        ExampleDrawPOI.superclass.activate.apply(this);
                        this.createPoiListener();
            },

            deactivate:function() {
                ExampleDrawPOI.superclass.deactivate.apply(this);
                this.removeCreatePoiListener();
            },

            createPoiListener:function() {
                        var wExampleDrawPOI = this;

                        function clickListener(dynMap){}

                        clickListener.prototype.onSelect = function(x,y,xpx,ypx){
                                wExampleDrawPOI.createObject(x,y);
                        };

                        var listener = new clickListener();
                        DynMapAddMouseSelectionEventListener(this.getMap(), "clickOnMap", listener)
                },

                // Remove create poi listener
                removeCreatePoiListener:function() {
                        DynMapRemoveListener(this.getMap(),"click","clickOnMap");
                },

            // add points from list of citysite
                createObject:function(x,y) {
                        var map = this.getMap();
                        var imageUrl = this.getHost() + this.getContextUrl() + "/Image/showImage.do?name=" +
                                                        this.getPropertyValue(ExampleDrawPOI.IMAGE_PROPERTY_NAME);
                        var poi = {
                        mapx : x,
                        mapy : y,
                        imgsrc : imageUrl
                    };
                    map.objectLayer.addObject(poi);
                        map.objectLayer.refresh();
                },

                /**
                 * Method: Get images on geoweb server. Call egw api rest method
                 */
                getImages : function() {
                        var wExampleDrawPOI = this;

                        this.getEGWApi().getImages({
                                onSuccess : function(response) {
                                        // For images properties, we add the images of geoweb
                                        // database
                                        var result = response.result;
                                        var images = [""];

                                        for (i = 0; i < result.length; i++) {
                                                var image = result[i];
                                                images.push(image.name);
                                        }

                                        wExampleDrawPOI.setComboboxOptions(ExampleDrawPOI.IMAGE_PROPERTY_NAME, images);
                                },
                                onError : function(response) {
                                        alert("Poi getImages error " + response.status + ", "
                                                        + response.message);
                                }
                        });
                }
        });
        Y.namespace("GCUI");
        Y.GCUI.ExampleDrawPOI = ExampleDrawPOI;

}, "1.0.0", {requires: ["project-widgetbutton"] });
ExampleDrawPOI.css

Dans cet exemple, nous n’utiliserons pas de style par défaut.

Ressources

Voici l’image nécessaire à la réalisation de ce widget.

gcweb-reference-img/lbs-integration/geoweb-easy/exampleDrawPOI.png

Voici les clés de base à placer dans le fichier properties :

widget.exampleDrawPOI.label=Exemple de points
widget.exampleDrawPOI.description=Cr\u00E9er un point sur la carte

Pour l’intitulé de la propriété du choix de l’image, il est nécessaire d’ajouter la clé suivante :

widget.exampleDrawPOI.defaultImage=Images :

Pour plus d’information sur les ressources, se référer au widget ExampleLegend.

Widget afficher les coordonnées X et Y

Objectif

Ce widget permet d’afficher les coordonnées X et Y suite à un clic sur la carte.

Le widget est nommé ExampleXY.

Initialisation

Créer les 3 fichiers du widget dans le répertoire navigation :

  • ExampleXY.xml
  • ExampleXY.js
  • ExampleXY.css
ExampleXY.xml

Voici la configuration du fichier de description :

<widget id="exampleXY"
        category="standard"
        position="14"
        icon="resources/images/exampleXY.png"
        js="navigation/ExampleXY.js"
        module="geoconcept-widget-example-xy"
        display="true"
        lang="resources/exampleLang">
</widget>

Pour plus d’informations, voir la description des fichiers XML dans le chapitre des widgets.

ExampleXY.js

Le widget créé est de type basique. Il ne possède aucune forme, c’est au développeur de réaliser son interface. L’ihm du widget est à définir dans la méthode getWidget(). Pour l’affichage dans l’outil de conception, le widget doit être désactivé. Pour cela, 2 méthodes sont disponibles dans l'API de base d’un widget, isPortal() et getWrapper(). La 1ère détermine si l’application est affichée dans le portail ou l’outil de conception. La seconde permet de récupérer le masque que l’on pourra superpositionner pour désactiver le widget.

getWidget: function() {
        var htmlContent = "<div class='xyBG'><div class='xyField'><label>"+this._("widget.exampleXY.x") +
        "</label></br><input type='text' id='x' size='15' disabled='disabled'/></div>" +
    "<div class='xyField'><label>" + this._("widget.exampleXY.y") +
    "</label></br><input type='text' id='y' size='15' disabled='disabled'/></div>" +
    "<div class='xyButton egw-btn'><button id='searchXY'>" + this._("widget.exampleXY.button") + "</button></div>";

        if (!this.isPortal()) {
                htmlContent = htmlContent + this.getWrapper();
        }
        htmlContent += "</div>";
        return ("<div id='"+this.getId()+"'>"+htmlContent+"</div>");
},

Comme pour l’exemple ExampleDrawPOI, ce widget utilise un écouteur lors du clic souris. L’activation se fera lors du clic sur le bouton searchXY. L'évènement positionné dans la fonction configureAction() activera l'écouteur. La fonction configureAction() est appelée suite à la construction du widget dans l’ihm.

configureAction: function() {
        ExampleXY.superclass.configureAction.apply(this);
        if (this.isPortal()) {
                Y.one("#searchXY").on("click", this.clickOnMapListener, this);
        }
},

Pour désactiver l'écouteur, la fonction surchargée est reinitView(). Cette fonction peut être appelée par l'évènement Y.fire("reinitView") (utilisé notamment par le widget Erase fourni par défaut).

reinitView: function() {
        this.removeClickOnMapListener();
},

Suite à l’activation de ce widget, chaque clic sur la carte permettra d’afficher les coordonnées x et y de la souris.

setXY: function(x,y) {
        var map = this.getMap();
        Y.one("#x").set("value", x*map.precision);
        Y.one("#y").set("value", y*map.precision);
}

A présent, voici la description complète de ce widget :

YUI.add("geoconcept-widget-example-xy", function(Y) {

        /* ExampleXY class constructor */
        function ExampleXY(config) {
                ExampleXY.superclass.constructor.apply(this, arguments);
        }

        /*
         * Required NAME static field, to identify the Widget class and
         * used as an event prefix, to generate class names etc. (set to the
         * class name in camel case).
         */
        ExampleXY.NAME = "exampleXY";

        Y.extend(ExampleXY, Y.GCUI.WidgetBase,  {
                initializer: function(config) {
                        ExampleXY.superclass.initializer.apply(this);
                },

                destructor: function() {
                        ExampleXY.superclass.destructor.apply(this);
                },

                configureAction: function() {
                        ExampleXY.superclass.configureAction.apply(this);
                        if (this.isPortal()) {
                                Y.one("#searchXY").on("click", this.clickOnMapListener, this);
                        }
                },

                reinitView: function() {
                        this.removeClickOnMapListener();
                },

                getWidget: function() {
                        var htmlContent = "<div class='xyBG'><div class='xyField'><label>"+this._("widget.exampleXY.x") +
                        "</label></br><input type='text' id='x' size='15' disabled='disabled'/></div>" +
                    "<div class='xyField'><label>" + this._("widget.exampleXY.y") +
                    "</label></br><input type='text' id='y' size='15' disabled='disabled'/></div>" +
                    "<div class='xyButton egw-btn'><button id='searchXY'>" + this._("widget.exampleXY.button") + "</button></div>";

                        if (!this.isPortal()) {
                                htmlContent = htmlContent + this.getWrapper();
                        }
                        htmlContent += "</div>";
                        return ("<div id='"+this.getId()+"'>"+htmlContent+"</div>");
                },

                clickOnMapListener: function() {
                        var wExampleXY = this;

                        function clickListener(){}

                        clickListener.prototype.onSelect = function(x,y,xpx,ypx){
                                wExampleXY.setXY(x,y);
                        };

                        var listener = new clickListener();
                        DynMapAddMouseSelectionEventListener(this.getMap(), "getXY", listener)
                },

                // Remove create poi listener
                removeClickOnMapListener: function() {
                        DynMapRemoveListener(this.getMap(),"click","getXY");
                },

                setXY: function(x,y) {
                        var map = this.getMap();
                        Y.one("#x").set("value", x*map.precision);
                        Y.one("#y").set("value", y*map.precision);
                }
        });
        Y.namespace("GCUI");
        Y.GCUI.ExampleXY = ExampleXY;

}, "1.0.0", {requires: ["project-widgetbase"] });
ExampleXY.css

Dans cet exemple, le widget n'étant pas un bouton, le fichier css doit être enrichit pour personnaliser l’outil. Ci-dessous se trouve une des possibilités d’affichage du widget.

.xyField {
        padding: 0 10px;
        float: left;
}

.xyButton {
        float: left;
        padding: 13px 0px 0px;
}

.xyBG {
        width: 100%;
        height: 40px;
}

.xyField label{
        font-weight: bold;
}
Ressources

Voici l’image nécessaire à la réalisation de ce widget.

gcweb-reference-img/lbs-integration/geoweb-easy/exampleXY.png

Voici les clés de base à placer dans le fichier properties :

widget.exampleXY.label=Exemple de coordonn\u00E9es
widget.exampleXY.description=R\u00E9cup\u00E9rer les coordonn\u00E9es \u00E0 partir d\'un clic

Pour les autres textes du widget, il est nécessaire d’ajouter les clés suivantes :

widget.exampleXY.button=Activer
widget.exampleXY.x=X
widget.exampleXY.y=Y

Pour plus d’information sur les ressources, se référer au widget ExampleLegend.