Integrándolo con gvSIG ---------------------- Hasta ahora hemos visto cómo crear nuestros componentes, lógica e interface de usuario, usando las librerías de gvSIG para acceder a los datos geográficos o para presentarlos, así como una forma simple de crear una pequeña aplicación que los utilice. Vamos a ver ahora como integraríamos esas funcionalidades en la aplicación gvSIG. Si observamos los proyectos que tenemos en nuestro workspace veremos que aun hay uno sobre el que no hemos trabajado, **org.gvsig.visor.app.mainplugin** . Es aquí donde está implementado nuestro plugin. Antes de ver el código del plugin comentar un detalle. Cuando describíamos lo que tenía que hacer nuestro plugin, dijimos que debía presentar un *splash* personalizado. Veamos primero como podemos hacer esto. En la carpeta *"src/main/resources"* encontraremos una carpeta *theme*, y dentro de esta un fichero *andami-theme.xml*. Este fichero es el encargado de especificar al framework de andami que *splash* se debe presentar así como si hay que utilizar alguna imagen de fondo en el MDI de la aplicación o los iconos de las ventanas de gvSIG. Andami, al arrancar, buscará en las carpetas de los plugins uno que tenga la carpeta *theme* y dentro este fichero y cuando encuentre uno lo utilizará. El fichero xml de nuestro ejemplo contiene: .. code-block:: xml Por defecto las rutas que aparezcan en el fichero se interpretarán relativas a la ubicación de este fichero, disponiendo de una variable *GVSIG_INSTALL* que apuntará a la carpeta en la que está instalado gvSIG. En el ejemplo podemos ver como en el tag *Splash* no se indica ruta para el fichero *"splash.png"*, usándose el fichero que hay en la carpeta del plugin mientras que en el tag *Icon* se usa la variable *GVSIG_INSTALL* para hacer referencia al fichero que hay en el tema por defecto de Andami. Podemos arrancar gvSIG y comprobar que sale el *splash* indicado en nuestro fichero *andami-theme.xml*. Una vez visto como podemos cambiar el *splash*, podemos echar un vistazo a la extensión de nuestro plugin. Vamos a ver ahora como se añaden menus y botones a gvSIG. En la carpeta:: src/main/resources-plugin/ En contraremos el fichero *"config.xml"*. Contendra: .. code-block:: xml Vamos a ir viendo parte por parte que tenemos en el. Las primeras lineas dentro del tag *plugin-config*... .. code-block:: xml ... ... Se encargan de: - Tag **depends**. Indica que este plugin depende del plugin *org.gvsig.app.mainplugin*. ¿ Esto que quiere decir ? principalmente dos cosas, por un lado que el nuestro plugin se inicializara siempre despues de *org.gvsig.app.mainplugin*, y que se incluira en el classpath de nuestro plugin el classpath que tenga el plugin *org.gvsig.app.mainplugin*. Con esto nos aseguramos que desde nuetro plugin tendremos acceso a todos los componentes definidos en *org.gvsig.app.mainplugin* y los plugins de los que este dependa. Es importante señalar que podemos indicar que dependemos de mas de un plugin, de forma que podamos tener acceso no solo a los componentes de *org.gvsig.app.mainplugin*, si no ademas, por ejemplo, a los del layout. Para esto simplemente repetiremos este tag indicando en cada uno un plugin del que dependamos. - Tag **resourceBundle**. Indica donde ir a buscar los recurso de internacionalizacion de nuestro plugin. - Tag **libraries**. Indica donde ha de ir a buscar los jars que tenga nuestro plugin. Normalmente el classpath de nuestro plugin estara compuesto, por la ruta a la carpeta de nuestro plugin, y la ruta indicada en este tag. Lo siguiente que encontraremos es el tag **extensions**. Un plugin contiene extensiones... asociadas cada una de ellas a una clase que implementaremos en nuestro codigo. En esta seccion del *config.xml*, se definen que estensiones se van a cargar de nuestro plugin. Asi vemos que se definen dos extensiones que se corresponderan con las clases *LandRegistryViewerExtension* y *DisableEditingExtension*. En cada una de estas extensiones se definiran todas las acciones que precisemos y que luego se asociaran a menus o botones. Por ejemplo, en *LandRegistryViewerExtension* se define la accion *view-show-land-regisytry-information*: .. code-block:: xml ... ... Que luego es usada para definir una entrada de menu y un boton en la barra de botones de gvSIG: .. code-block:: xml ... ... Vamos ahora a ver el código de nuestro *plugin*. Veremos que existen tres clases: - *LandRegistryViewerExtension*. - *PropertiesOfBlockListener*. - *DisableEditingExtension*. Vamos a ir viendo el codigo de estas tres clases. La clase *LandRegistryViewerExtension* es la encargada de gestionar la integración del codigo que hemos visto en las librerias de la logica y el interface de usuario con gvSIG. Es la encargada de gestionar las herramientas que nuestro plugin añade a gvSIG en forma de menus y botones en la barra de herramientas. Los métodos mas relevantes que vamos a encontrar en la clase *LandRegistryViewerExtension* son: - **initialize**. Se invoca al cargar la extensión. Aquí nos limitaremos a registrar servicios que ofrezca nuestra extensión. En nuestro caso nos limitaremos a registrar en el tema de iconos de la aplicacion las imagenes que vaya a usar nuestro plugin: .. code-block:: java PluginsManager manager = PluginsLocator.getManager(); IconThemeHelper.registerIcon("action", "view-show-land-regisytry-information", this); - **postInitialize**. Se invoca durante la inicialización de los plugins, una vez invocado al método *initialize* de todas las extensiones. Esto nos garantiza que cuando se ejecuta estarán disponinles prácticamente todos los servicios de gvSIG. Aprovecharemos este método para: - Crear el manager de la parte de lógica de nuestra librería. .. code-block:: java try { manager = LandRegistryViewerLocator.getManager(); - Inicializar los *stores* a través de la función *initializeStores*. .. code-block:: java this.initializeStores(); - Y por último crear y mostrar la venta con nuestra vista, a través del método *createViewWindow*. .. code-block:: java viewWindow = this.createViewWindow(); } catch (LoadLayerException e) { } Vamos a ver con un poco más de detalle cómo se realiza esto. Antes de empezar lo primero que haremos será obtener una referencia del objeto *aplicacion* y del manager de *proyectos*: .. code-block:: java ApplicationManager application = ApplicationLocator.getManager(); ProjectManager projectManager = application.getProjectManager(); Ademas de estos dos *managers* obtendremos tambien el *manager* encargado de gestionar la internacionalización de la aplicación, que usaremos para traducir algunas de las cadenas que vamos a utilizar. .. code-block:: java I18nManager i18nManager = ToolsLocator.getI18nManager(); Una vez disponemos de estas referencias, podemos crear nuestra vista: .. code-block:: java // 1. Create a new view and set the name. ViewManager viewManager = (ViewManager) projectManager.getDocumentManager(ViewManager.TYPENAME); ViewDocument view = (ViewDocument) viewManager.createDocument(); view.setName(i18nManager.getTranslation(MY_VIEW_NAME)); // Setting view's projection to shapefile's known CRS view.getMapContext().setProjection(CRSFactory.getCRS("EPSG:23030")); Para crear la vista, pediremos al *ProjectManager* que nos devuelva el manager de vistas, y a este le pediremos una nueva instancia del documento vista. Debemos recordar aquí, que una de las principales funcionalidades de los *manager* es actuar a modo de factorías para obtener instancias de los objetos que gestiona ese *manager*. Una vez disponemos del documento vista, le asignaremos el nombre que nosotros consideremos oportuno. Con la vista ya creada, procederemos a ver cómo hemos de hacer para añadir a ésta las capas que necesitemos. Para ello crearemos una capa con las manzamas, esto lo realizaremos a través del método *createLayer*, indicándole el nombre de la capa y el *store* en que queremos que se base: .. code-block:: java // 2. Create a new layer with the blocks FLyrVect layer = (FLyrVect) application.getMapContextManager().createLayer( i18nManager.getTranslation("_Blocks"), this.manager.getBlocks()); // Add a new property to the layer to identify. layer.setProperty("ViewerLayer", Boolean.TRUE); Con la capa ya creada, añadiremos al *mapa* de la vista la nueva capa: .. code-block:: java // 3. Add this layer to the mapcontext of the new view. view.getMapContext().getLayers().addLayer(layer); Añadiremos al proyecto corriente la vista: .. code-block:: java // 4. Add the view to the current project. projectManager.getCurrentProject().add(view); Y por último nos encargaremos de presentar la ventana asocida a la vista que acabamos de crear: .. code-block:: java // 5. Force to show the view's window. IView viewWindow = (IView) viewManager.getMainWindow(view); application.getUIManager().addWindow(viewWindow, GridBagConstraints.CENTER); try { application.getUIManager().setMaximum((IWindow) viewWindow, true); } catch (PropertyVetoException e) { logger.info("Can't maximize view.", e); } Una vez ya tenemos mostrada la ventana de la vista, precisaremos registrar en el componente gráfico del mapa la nueva herramienta que aportamos: .. code-block:: java // 6. Register my tool in the mapcontrol of the view. PropertiesOfBlockListener listener = new PropertiesOfBlockListener(); viewWindow.getMapControl().addBehavior(TOOL_NAME, new PointBehavior(listener)); Con todo esto tendremos inicializado nuestro plugin. - *execute*. Este metodo será invocado cada vez que el usuario interactue con las opciones de menú o botones que se configuraron en el fichero *config.xml* de nuestro plugin. En nuestro caso, se configuró para que se dispare este evento cuando el usuario quisiese activar la herramienta de información sobre parcelas catastrales de una manzana. Así que el código que tendríamos que tener ahí debe corresponderse con esto: .. code-block:: java if (ACTION_SETINFOTOOL.equalsIgnoreCase(actionCommand)) { // Set the tool in the mapcontrol of the active view. ApplicationManager application = ApplicationLocator.getManager(); if (application.getActiveWindow() != viewWindow) { return; } viewWindow.getMapControl().setTool(TOOL_NAME); } Observaremos que lo primero que hacemos es comprobar si el comando que recibimos es el correspondiente a la activación de nuestra herramienta, y que fijamos en el *config.xml*. Esto es debido a que en una misma extensión podemos agrupar varias herramientas, indicando nombres de comando distintos en el *config.xml* para cada una de ellas. Una vez sabemos que se está tratando de activar nuestra herramienta de información, comprobaremos si está activa la ventana de nuestra vista, ya que sobre otras vistas u otros tipos de documento, no debemos hacer nada. Y por último, nos dedicaremos a activar nuestra herramienta en el mapa de la vista. Herramenta que habíamos registrado en el metodo *createViewWindow*. - *isVisible*. En este método deberemos informar si los menús y botones asociados a nuestra herramienta deben estar visibles. En nuestro caso dejaremos visible nuestra herramienta siempre que la ventana activa sea la de nuestra vista: .. code-block:: java ApplicationManager application = ApplicationLocator.getManager(); return application.getActiveWindow() == viewWindow; - *isEnabled*, que devolveremos siempre 'true', ya que nuestra herramienta estará activa siempre que esté visible, y allí ya pusimos las comprobaciones necesarias. Si la logica de nuestra herramienta permite que no esté activa en algunos casos para los que sí se permite que esté visible, será aquí donde deberemos realizar esas comprobaciones. Para terminar vamos a comentar muy de pasada como hemos creado la herramienta sobre la vista. Una herramienta es la conjuncion de dos componentes, un *Behavior* y un *Listener*. El *Behavior* es el encargado de interactuar con el usuario para recoger la informacion que se precise de este. En nuestro caso hemos usado un *PointBehavior*, es decir, este componente se encarga de recoger un *click* del usuario y una vez recogido le pasa la informacion al *Listener* para que este la procese. En gvSIG podemos encontrarnos ya definidos algunos tipos de *Behaviors*, como pueden ser: - CircleBehavior, captura de usuario la informacion necesaria para definir un circulo. - MoveBehavior, captura del usuario la informacion para desplazar la vista sobre el mapa. - PointBehavior, captura un click del usuario - PolylineBehavior - PolygonBehavior - RectangleBehavior - MouseWheelBehavior Siendo en general suficientes para la gran mayoria de herramientas que precisemos. Nosotros deberemos de implementar el *Listener* que se encargue de realizar la operacion que queramos con los datos capturados por el *Behavior*. Vamos a echarle un vistazo a nuestra clase *Listener*, *PropertiesOfBlockListener*. .. code-block:: java public class PropertiesOfBlockListener extends AbstractPointListener { private static final Logger logger = LoggerFactory.getLogger(PropertiesOfBlockListener.class); private JLandRegistryViewerBlockPanel blockPanel = null; public void point(PointEvent event) throws BehaviorException { ApplicationManager application = ApplicationLocator.getManager(); I18nManager i18nManager = ToolsLocator.getI18nManager(); LandRegistryViewerManager logicManager = LandRegistryViewerLocator.getManager(); LandRegistryViewerSwingManager guiManager = LandRegistryViewerSwingLocator.getSwingManager(); LandRegistryViewerBlock block; try { block = logicManager.getBlock(event.getMapPoint()); if (block == null) { return; } if( this.blockPanel == null ) { this.blockPanel = guiManager.createJLandRegistryViewerBlockPanel(block); JComponent panel = blockPanel.asJComponent(); ToolsSwingLocator.getWindowManager().showWindow( panel, i18nManager.getTranslation("_Block_registry_information"), WindowManager.MODE.TOOL ); panel.addAncestorListener(new AncestorListener() { public void ancestorRemoved(AncestorEvent arg0) { blockPanel = null; } public void ancestorMoved(AncestorEvent arg0) { } public void ancestorAdded(AncestorEvent arg0) { } }); } else { this.blockPanel.setLandRegistryViewerBlock(block); } } catch (LandRegistryViewerException e) { logger.warn("Can't show block registry information",e); application.message( i18nManager.getTranslation("_Cant_show_block_registry_information"), JOptionPane.WARNING_MESSAGE ); } } } Básicamente derivaremos de AbstractPointListener sobre-escribiendo el método point que es llamado para realizar la acción asociada a esta herramienta. Este método se limitará a llamar a nuestro método getBlocks a partir de la posición del mapa sobre la que se hizo clic, y si se obtiene alguna manzana en ese punto se creará nuestro panel para presentar esa información en una ventana. Dos cosas a destacar: - Cuando consultemos al parámetro event por las coordenadas del punto sobre el que se ha hecho clic se ha de hacer a través del método *getMapPoint* para asegurarnos que nos lo devuelve en coordenadas del mapa y no de pantalla. - Cuando creemos el panel para presentar la informacion, nos quedaremos escuchando el evento *ancestorRemoved*, para saber cuando el usuario cierre la ventana y asi poder reutilizar la ventana mientras este abierta y saber que hay que abrir una nueva en caso de que el usuario la cierre.