Personal tools
gvSIG Desktop
gvSIG Desktop

Cached time 11/21/13 17:34:51 Clear cache and reload

 
.. include-document:: org.gvsig.tools/reference-links
   :rest:

.. include-document::  org.gvsig.fmap.geom/reference-links
   :rest:

.. include-document::  org.gvsig.fmap.dal/reference-links
   :rest:

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_.

.. code-block:: java

  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. 

.. code-block:: java

  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. 

.. code-block:: java

  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.

.. code-block:: java

  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.

.. code-block:: java

  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. 

.. code-block:: java

  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.

.. code-block:: java

  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:

.. code-block:: java
 
  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.

.. code-block:: java

  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:

.. code-block:: java

  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:

.. code-block:: java

  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.

.. code-block:: java

  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.

.. code-block:: java

  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:

* FeatureSet_

* Feature_

* FeatureType_

View source document


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: