Personal tools
gvSIG Desktop
gvSIG Desktop

Cached time 11/21/13 11:24:39 Clear cache and reload

 

Dentro de este apartado veremos:

  • Como recorrer los fenómenos de un shape.
  • Como manejarse con un conjunto de fenómenos.
  • Acciones asociadas a un Feature para consultar los valores de sus atributos.

Veamos ahora a seguir este documento con un ejemplo simple. Accederemos a un fichero shape y recorreremos todos sus fenómenos.

Lo primero que tendremos que hacer será conseguir una instancia del DataManager del API de acceso a datos. Esto se consigue mediante la llamada al método estático, getDataManager de la clase DALLocator.

manager = DALLocator.getDataManager();

Una vez conseguido la instancia del DataManager , utilizaremos este para conseguir una instancia de los parámetros que necesitamos para acceder al almacén de nuestro fichero shape. Para eso invocaremos al método createStoreParameters indicándole el tipo de almacén al que queremos acceder.

params = manager.createStoreParameters("Shape");

En el caso de un almacén de tipo shape, el único parámetro que necesitaremos será el nombre del fichero al que queremos acceder. Los parámetros se comportan como un Map, así que asignaremos a la clave shpfilename, el nombre del fichero al que queremos acceder.

params.setDynValue("shpfilename","data/prueba.shp");

Una vez tenemos los parámetros del almacén al que queremos acceder preparados, le indicaremos al manager que nos cree un DataStore acorde a esos parámetros que nos permita acceder a nuestro fichero. Esto se hará llamando al método createStore pasándole como argumento los parámetros que ya hemos preparado. Esta llamada nos devolverá un FeatureStore , y ya con el podremos acceder a sus fenómenos.

store = (FeatureStore)manager.createStore(params);

Cuando ya disponemos del store, podemos utilizar este para acceder a sus fenómenos. La forma mas simple de hacer esto seria visitar el almacen.

store.accept( new Visitor() {
    public void visit(Object obj) {
      Feature feature = (Feature)obj;
      ...
    }
  }
);

Como alternativa a este metodo podemos usar el método getFeatureSet, que nos devolverá un conjunto de fenómenos, FeatureSet , con el que podremos operar.

features = store.getFeatureSet();

Es importante entender que estas son las unicas formas de acceder a los fenómenos de un almacén. Además estas nos permiten dado un almacén de datos acceder de forma simultanea a sus fenómenos invocando desde varias partes de la aplicación al método accept o getFeatureSet para obtener distintos conjuntos de datos. Como norma general no deberemos usar un conjunto de fenómenos, FeatureSet . desde varias partes de la aplicación simultáneamente. Si necesitamos acceder a los datos del almacén, allá donde se necesite, se creará un nuevo conjunto de datos.

Los principales métodos de un FeatureSet son:

  • getDefaultFeatureType
  • getFeatureTypes
  • isEmpty
  • getSize
  • dispose
  • iterator
  • fastiterator

Hacer una mención especial sobre los dos últimos métodos, iterator y fastiterator. El primero, iterator, devuelve una nueva instancia de Feature cada vez que se invoca al método next. El segundo, fastiterator devuelve siempre la misma instancia de Feature al invocar al método next, alterando el valor de esta de forma que está cargada con los valores del nuevo fenómeno. Este comportamiento nos ahorra crear tantos objetos Feature como elementos tenga el conjunto, pero deberemos tener especial cuidado en no guardarnos una referencia a los objetos Feature así obtenidos ya que irán alterando su valor conforme se itere sobre el conjunto.

El FeatureSet, ademas de los metodos para iterar sobre el tambien nos permite visitarlo, siendo siempre recomendable utilizar este mecanismo frente al recorrido mediante un iterador.

Volviendo al ejemplo sobre el que estábamos trabajando, podemos obtener un iterador sobre el conjunto de todas los fenómenos y recorrerlos. En el ejemplo accederemos al valor del atributo NOMBRE del fenómeno y lo sacaremos por la salida estándar.

DisposableIterator it = features.iterator();
while( it.hasNext() ) {
  feature = (Feature)it.next();
  System.out.println(feature.getString("NOMBRE"));
}
it.dispose();

Tenemos también la opción de saltar directamente a una posición, pidiendo el DisposableIterator con un índice. Podemos usar el DisposableIterator devuelto de la misma forma que con el DisposableIterator normal, aunque el primer elemento devuelto será el que ocupa la posición correspondiente al índice indicado:

DisposableIterator it = features.iterator(100);
while( it.hasNext() ) {
  feature = (Feature)it.next();
  System.out.println(feature.getString("NOMBRE"));
}
it.dispose();

Por último una vez hemos terminado de trabajar con el conjunto de fenómenos y el almacén debemos informarles de ello, para que este libere todos los recursos que esté utilizando.

features.dispose();
store.dispose();

Es importante tener en cuenta algunas consideraciones.

Los elementos de un FeatureSet son siempre del tipo Feature , pero si no hemos indicado nada al respecto cuando creamos el conjunto, no deberemos asumir que todos tienen la misma estructura, ya que dependiendo del tipo de almacén podemos encontrarnos con que la estructura de estos puede cambiar de un elemento a otro. Más adelante explicaremos como filtrar los conjuntos de fenómenos de forma que el tipo de elementos sea único dentro de este.

Otro punto a tener en cuenta está relacionado con la implementación del recorrido de los elementos de un conjunto. No deberemos asumir nada al respecto de esto. El API no fija nada respecto a como debe implementar cada almacén el recorrido de sus elementos. Puede haber almacenes que carguen todos sus elementos en memoria para ser recorridos mientras que otros sólo mantengan en memoria el elemento que se va a devolver o incluso otros que utilicen algoritmos de cache específicos para acelerar su acceso. En general la implementación de los distintos almacenes que lleva de base esta librería tiende a ser lo mas óptima posible, balanceando la carga en memoria y la velocidad de acceso, e implementándose de una u otra forma en función de las características del almacén.

Por último es importante invocar a la acción dispose del conjunto de fenómenos cuando dejemos de trabajar con él, así como de los DisposableIterator cuando dejemos de usarlos. Dependiendo del tipo de almacén, esto puedo liberar recursos como ficheros abiertos en disco o conexiones a BBDD. Así mismo cuando hayamos creado nosotros el almacén, debemos de encargarnos de invocar a la acción dispose sobre este. Hay que tener cuidado de no invocar a la acción dispose sobre un almacén asociado a una capa de gvSIG. Será la propia capa de gvSIG la encargada de hacerlo cuando no lo necesite.

Veamos todo el código del ejemplo junto:

DataManager manager;
DataStoreParameters params;
FeatureStore store;
FeatureSet features;
Feature feature;

manager = DALLocator.getDataManager();
params = manager.createStoreParameters("Shape")
params.setDynValue("shpfilename","data/prueba.shp");
store = (FeatureStore)manager.createStore(params);
features = store.getFeatureSet();

DisposableIterator it = features.iterator();
while( it.hasNext() ) {
  feature = (Feature)it.next();
  System.out.println(feature.getString("NOMBRE"));
}
it.dispose();
features.dispose();
store.dispose();

Aunque siempre se pueden utilizar iteradores para recorrer los elementos del almacen, es recomendable cuando sea posible utilizar visitors. Estos nos garantizan que se libreran los recursos de forma automatica sin tener que ir invocando a los metos dispose del iterador o el conjunto de fenomenos. El codigo usando visitors quedaria algo como:

DataManager manager;
DataStoreParameters params;
FeatureStore store;
FeatureSet features;
Feature feature;

manager = DALLocator.getDataManager();
params = manager.createStoreParameters("Shape")
params.setDynValue("shpfilename","data/prueba.shp");
store = (FeatureStore)manager.createStore(params);
store.accept( new Visitor() {
    public void visit(Object obj) {
      Feature feature = (Feature)obj;
      System.out.println(feature.getString("NOMBRE"));
    }
  }
);
store.dispose();

Hasta aquí hemos visto como conseguir usar el API para llegar a obtener un objeto Feature . Pero, ¿ qué es un Feature y qué servicios nos ofrece ?

La clase Feature representa a un fenómeno dentro del almacén. En él se aglutina toda la información, ya sea alfanumérica o vectorial. Un Feature se comporta como un Map muy especializado en el que las key hacen referencia a los nombres de los atributos del fenómeno y los value de estas al valor de estos atributos. Cada atributo tiene su tipo, por lo que no hay limitación en cuanto a cuantos atributos de tipo vectorial puede tener la definición de una Feature .

En Feature nos encontraremos un método get para acceder a los valores de los atributos del fenómeno. Para facilitar el acceso se han añadido métodos de utilidad que nos devuelven ya tipos concretos en lugar de Object. Así, en el ejemplo anterior, se usa el getString para obtener el valor del atributo NOMBRE como un String.

Los métodos de acceso a los valores de los atributos de un Feature son:

  • get, retorna el valor del atributo indicado como un Object.
  • getInt, retorna el valor del atributo como un valor de tipo int.
  • getBoolean, retorna el valor del atributo como un valor de tipo boolean.
  • getLong, retorna el valor del atributo como un valor de tipo long.
  • getFloat, retorna el valor del atributo como un valor de tipo float.
  • getDouble, retorna el valor del atributo como un valor de tipo double.
  • getDate, retorna el valor del atributo como un valor de tipo Date.
  • getString, retorna el valor del atributo como un valor de tipo String.
  • getByte, retorna el valor del atributo como un valor de tipo byte.
  • getGeometry, retorna el valor del atributo como un valor de tipo Geometry.
  • getFeature, retorna el valor del atributo como un valor de tipo Feature .
  • getArray, retorna el valor del atributo como un valor de tipo Object[].

Para los tipos básicos, si el valor del atributo pedido no es del tipo pedido se intentará convertir a ese tipo y en caso de que no se pueda se lanzará una excepción.

Conviene comentar sobre los últimos tres métodos.

En lo que se refiere al acceso a datos, los valores de datos de tipo vectorial los trata como objetos de tipo Geometry . Este tipo de datos esta definido en la librería de geometrías de gvSIG, org.gvsig.fmap.geom para más información sobre las funcionalidades alrededor de este tipo consulte la documentación de esta librería (ver en documentos relacionados).

En lo que respecta al método getFeature, está pensado para dar soporte a fuentes de datos en las que un fenómeno dentro de la fuente de datos tiene un atributo que a su vez es una estructura compleja, con sus propios atributos y valores. La forma de recuperar ese atributo compuesto, por referirnos a él de alguna forma, será a través de este método.

En lo que respecta al método getArray viene a cubrir el hueco de qué pasa cuando un atributo de un fenómeno está compuesto por una tupla de valores. Para acceder a esta tupla de valores se usará este método que nos la presentará como un array.

Ahora unas consideraciones sobre rendimientos. En general es recomendable usar el nombre del atributo para acceder a el valor de estos, ya que, para cada consulta podemos variar tanto en orden como en cantidad los atributos de la Feature que deseamos. Sin embargo en ocasiones puede resultar mas óptimo usar su índice para acceder a él. Todos los métodos de acceso que acabamos de comentar están sobrecargados de forma que podemos usar bien el nombre o bien su índice.

Veamos como podía usarse esta forma de acceso a los atributos en el ejemplo anterior.

DisposableIterator it = features.iterator();
while( it.hasNext() ) {
  feature = (Feature)it.next();
  featureType = feature.getType();
  index = featureType.getIndex("NOMBRE");
  System.out.println(feature.getString(index));
}
it.dispose();

Evidentemente, el cambio, así introducido no ha supuesto ninguna mejora en el rendimiento. Ahora bien si asumimos que estamos trabajando con un almacén de datos que sólo soporta un tipo de Feature , como es nuestro caso, podemos optimizar algo mas el acceso.

featureType = store.getDefaultFeatureType();
index = featureType.getIndex("NOMBRE");

DisposableIterator it = features.iterator();
while( it.hasNext() ) {
  feature = (Feature)it.next();
  System.out.println(feature.getString(index));
}
it.dispose();

Esto sí que puede resultar en una ganancia considerable en rendimientos cuando estemos accediendo a un almacén con un numero muy grande de fenómenos.

Cabe resaltar que hemos utilizado en el ejemplo un par de métodos nuevos. Por un lado podemos ver como dada un Feature accedemos a su tipo mediante el método getType, que nos devuelve un objeto del tipo FeatureType . Y por otro lado, podemos obtener el tipo de los fenómenos de un almacén mediante el método getDefaultFeatureType . Hay que tener cuidado con el uso de este método ya que cuando estemos trabajando con almacenes que contengan Feature de varios tipos, nos dará de forma arbitraria sólo un tipo. Si queremos saber los tipos de fenómenos que contiene un almacén deberemos invocar a getFeatureTypes que nos devolverá una lista de los FeatureType que tiene el almacén.

Puede ser conveniente repasar la referencia sobre:


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: