6. La librería con la lógica¶
La parte de la lógica de nuestro desarrollo la encontraremos dividida en dos proyectos:
- org.gvsig.landregistryviewer.lib.api
- org.gvsig.landregistryviewer.lib.impl
En el proyecto del API encontraremos principalmente interfaces. Estos interfaces compondrán el API de los componentes de lógica que precisemos desarrollar. EL modelo de clases del ejemplo es el que se muestra en el siguiente diagrama:
![../../../../_images/org.gvsig.visor.api2.png](../../../../_images/org.gvsig.visor.api2.png)
Modelo de clases del API
Tendremos las siguientes entidades:
LandregistryviewerLibrary, que representa al objeto Library. De esta clase señalar los métodos:
doRegistration, que se ejecuta al cargar la librería e informa qué clase de librería es, en nuestro caso la definición de un API, así como qué otras librerías requiere que estén inicializadas antes que esta, la librería de acceso a datos, y la de geometrías. El código que hace esto es:
public void doRegistration() { this.registerAs(landregistryviewerLibrary.class, Library.TYPE.API); this.require(DALLibrary.class); this.require(GeometryLibrary.class); }
doPostInitialize, que se ejecuta cuando se han inicializado todas las librerías. En nuestro caso realiza comprobaciones para verificar que al menos una implementación del API ha sido registrada. El código que hace esto es:
protected void doPostInitialize() throws LibraryException { // Validate if there are any implementation registered. landregistryviewerManager manager = LandregistryviewerLocator.getManager(); if (manager == null) { throw new ReferenceNotRegisteredException( LandregistryviewerLocator.MANAGER_NAME, LandregistryviewerLocator.getInstance() ); } }
Esta clase debera ir acompañada del fichero:
src/main/resources/META-INF/services/org.gvsig.tools.library.Library
En donde exista una linea con el nombre completo (nombre y paquete) de la clase. Esto es usado por el mecanismo de inicialización de librerias de gvSIG para localizar que clases “library” deben ser cargadas e inicializadas al inicio de la aplicación.
Si ha de cambiar el nombre a la clase LandregistryviewerLibrary recuerde editar este fichero para cambiarlo en el tambien ya que el IDE no lo hara por usted.
landregistryviewerLocator, es el locator de nuestra librería, el encargado de suministrarnos la instancia de nuestro manager. Se trata de una clase con métodos estáticos para registrar implementaciones de este API u obtener una implementación de él.
landregistryviewerManager. Un interface. El del manager de la librería. Define métodos para obtener las manzanas y parcelas así como para localizar una manzana dado un punto. Es la entrada a las distintas entidades de nuestro modelo.
landregistryviewerBlock. Un interface que representa a una manzana, y aporta métodos para obtener la geometría que define la manzana o para obtener las parcelas que la constituyen.
landregistryviewerProperty. Un interface que representa una parcela catastral. Tiene métodos para obtener la geometría que la define, su código o municipio.
Vamos a ver qué nos encontramos en la parte de implementación. Observaremos que mientras que en el API lo normal ha sido encontrarnos interfaces, en la implementación nos encontraremos clases. Clases que implementan los distintos interfaces que se definieron en el API. Por convenio, a la implementación de los distintos interfaces que aparecen en el API las llamaremos igual que en el API anteponiéndole el prefijo Default.
El modelo de clases de la implementación para nuestro ejemplo es:
![../../../../_images/org.gvsig.visor.impl2.png](../../../../_images/org.gvsig.visor.impl2.png)
Modelo de clases de la implementación
Vamos a ir viendo las partes más relevantes de la implementación.
LandregistryviewerDefaultImplLibrary. Al igual que en el API, esta clase se encarga de inicializar la librería de la implementación. De esta clase señalar los métodos:
doRegistration, que se encarga de registrar la librería como una implementación del API de landregistryviewerLibrary. Si tuviese otras dependencias distintas de las marcadas en el API, se añadirían aquí para asegurarnos que esas librerías se inicializan antes que esta, pero no es el caso de nuestro ejemplo. Al registrar esta librería como una implementación de landregistryviewerLibrary, se cubren dos funciones. Por un lado la librería del API será inicializada siempre antes que esta implementación, y por otro lado cuando alguna otra librería fije dependencias con el API, el mecanismo de inicialización de librerías se encargará de inicializar la implementación junto con el API para asegurarnos de que dispondremos de una implementación del API que hemos requerido. El código que necesitaremos poner en nuestro ejemplo será:
public void doRegistration() { this.registerAsImplementationOf(LandregistryviewerLibrary.class); }
doInitialize, que se encarga de registrar en el locator del API la implementación del manager que tenemos en esta librería. El código que hace esto es:
protected void doInitialize() throws LibraryException { VisorLocator.registerManager(DefaultlandregistryviewerManager.class); }
DefaultlandregistryviewerManager. De esta clase conviene resaltar principalmente:
El método getBlock. Podemos ver cómo realizar una busqueda de un registro filtrando por una condición espacial:
try { String attrGeomName = blocks.getDefaultFeatureType().getDefaultGeometryAttributeName(); FeatureQuery query = blocks.createFeatureQuery(); query.setFilter( new IntersectsEvaluator(attrGeomName,point) ); set = blocks.getFeatureSet(query); if( set.isEmpty() ) { return null; } it = set.fastiterator(); Feature f = (Feature) it.next(); LandregistryviewerBlock block = new DefaultLandregistryviewerBlock(this,f.getGeometry(attrGeomName)); return block; } catch (DataException e) { ... } finally { if( it != null ) { it.dispose(); } if( set != null ) { set.dispose(); } }
Truco
Puede encontrar información sobre los mecanismos disponibles para el acceso a datos consultando la Guía para el desarrollador de la Librería de Acceso a Datos (DAL)
De aquí resaltar:
- Podemos averiguar qué atributo de la feature es el que contiene la geometría a través del getDefaultGeometryAttributeName del feature type de nuestro store.
- Para realizar una búsqueda de features en un store lo haremos invocando al método getFeatureSet pasando como parámetro una instancia de FeatureQuery en la que se especifiquen las condiciones de filtrado. En ella también se pueden especificar orden o atributos que queremos recuperar en nuestra query.
- La condición de filtro se especifica suministrando un Evaluator al método setFilter de nuestra query.
- Debemos encargarnos de liberar los objetos que creemos, como son iteradores o feature sets. Hay que tener en cuenta que dependiendo del tipo de store con el que estemos trabajando estos pueden tener reservados recursos como conexiones a BBDD, recordsets o conexiones a servidores remotos.
El método openShape. Aquí podemos ver cómo abrir un store basado en shapes ya existentes:
parameters = manager.createStoreParameters("Shape"); parameters.setDynValue("shpfile", shape); parameters.setDynValue("crs", "EPSG:23030"); return (FeatureStore) manager.openStore("Shape", parameters);
Observaremos que para abrir un store lo realizaremos en dos fases. Por un lado crearemos una estructura para albergar los parámetros necesarios para abrir nuestro store, la inicializaremos con los valores adecuados e invocaremos al método openStore del manager de acceso a datos con esos parámetros.
Cada tipo de store tendrá un juego de parámetros específicos de él. Para abrir un shape, deberemos indicarle como mínimo el nombre del fichero y el sistema de referencia en el que se encuentra.
IntersectsEvaluator. Se trata de la clase que evalúa la condición usada en el filtro. Esta clase comprueba si el campo geometría especificado de una feature dada intersecta con una geometría concreta. En su construcción se le suministran el campo que contiene la geometría de la feature y la geometría con la que hay que comprobar si intersecta. De esta clase conviene resaltar:
el método evaluate, encargado de realizar la comprobación:
Geometry op1geom = (Geometry) data.getDataValue(this.op2attrname); return this.op1geom.intersects(op1geom);
Sabiendo cómo se llama el atributo que contiene la geometría podemos obtener esta a través del método getDataValue. Una vez tenemos las dos geometrías podemos invocar el método intersecs de la geometría para comprobar si intersectan.
El método getCQL. Este método devolverá una cadena siguiendo el formato de un where de sql a utilizar como filtro en stores que ataquen a la BBDD sql. El filtro devuelto puede no ser exactamente el mismo que el implementado por el código del método evaluate, actuando a modo de filtro previo a este siempre que el store lo soporte.
DefaultLandregistryviewerBlock. Representa a una manzana de nuestro dominio. Almacena la geometría que da forma a la manzana. La parte más relevante de esta clase es el método getProperties que retorna todas las parcelas que se encuentran sobre esa manzana:
List properties = new ArrayList(); FeatureStore store = this.manager.getProperties(); String attrGeomName = store.getDefaultFeatureType().getDefaultGeometryAttributeName(); FeatureQuery query = store.createFeatureQuery(); query.setFilter( new IntersectsEvaluator(attrGeomName, this.shape) ); set = this.manager.getProperties().getFeatureSet(query); if( set.isEmpty() ) { return null; } it = set.fastiterator(); while( it.hasNext() ) { Feature f = (Feature) it.next(); LandregistryviewerProperty property = new DefaultLandregistryviewerProperty( this.manager, f.getString(PROPERTIES_CODE), f.getGeometry(attrGeomName), f.getInt(PROPERTIES_CREATIONDATE), f.getInt(PROPERTIES_MUNICODE) ); properties.add(property); } return properties;
Podemos observar que utiliza el mismo mecanismo para filtrar las parcelas que usa el manager para recuperar un manzana. En este caso una vez a conseguido el set con las parcelas, lo recorre, recuperando los datos de estas y creando los objetos parcela.
DefaultLandregistryviewerProperty. Se trata de la clase que representa a un parcela. En nuestro ejemplo no tiene apenas lógica, limitándose a almacenar los datos y exponerlos mediante geters.