1. Introducción
  2. Capas
      2.1 VectorialData
  3. ViewPort
  4. Eventos
      4.1 AtomicEvent's
  5. Drivers
      5.1 VectorialFileDriver
  6. Interfaz de usuario
      6.1 MapControl
      6.2 MapBehavior
          6.2.1 Creación de una herramienta en base a un behavior existente
        6.2.2 Creación de una herramienta con un comportamiento no implementado
      6.3 Composición de herramientas

1. Introducción

    En el paquete FMap, un mapa está compuesto por:

    Para obtener una imagen de un mapa hay que:

	LayerFactory.setDriversPath("C:\\drivers");
	ViewPort vp = new ViewPort(ProjectionPool.get("ed50utm30"));
vp.setImageSize(new Dimension(100, 100));
FMap mapa = new FMap(vp);
	l = LayerFactory.createLayer("Vias", "DemoSHPDriver", new File("c:\\vias.shp"), ProjectionPool.get("ed50utm30"));
mapa.getLayers().addLayer(l);
	BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
mapa.draw(img, img.createGraphics());

El ejemplo concreto que se acaba de mostrar se corresponde al ImageFrame del ejemplo "com.iver.cit.gvsig.fmap.DrawImage". Para un ejemplo de uso de FMap desde un elemento complejo de interfaz de usuario ver Interfaz de usuario

2. Capas

    Cada mapa visualiza en una imagen cartografía cuyo origen puede ser muy diverso: ficheros, servidores WMS, ... Para añadir un origen de datos cartográficos a un mapa aparece el concepto de Capa. Las capas representan un origen de datos cartográficos independientemente de su ubicación y naturaleza. Fmap tiene un método getLayers() el cual devuelve una capa especial, consistente en un conjunto de capas inicialmente vacío que son utilizadas para realizar las operaciones de dibujado, impresión, ...
    En FMap, una capa viene definida por la interfaz FLayer de modo que toda clase que implemente la interfaz FLayer es una capa. Además de esta interfaz, hay un conjunto de interfaces que definen las características de una capa. Estas están en el paquete "com.iver.cit.gvsig.fmap.layer.layerOperations" y permiten crear capas con distintas capacidades a medida de la necesidad del programador, usuario, estandar, ...
    Por otro lado, la creación de las capas que inicialmente parten con gvSIG está centralizada en FLayers teniendo ésta métodos estáticos para crear cualquiera de estas capas fácilmente.
    Una vez se obtiene una referencia a FLayer, si se quiere realizar una operación concreta, se debe de comprobar si dicha capa implementa la interfaz del paquete "com.iver.cit.gvsig.fmap.layer.layerOperations" que da soporte a dicha operación, teniendo que hacer un casting para realizar la operación. Por ejemplo, el siguiente código borraría la selección de todas las capas activas con soporte de selección de un array layers :

		for (Iterator iter = layers.iterator(); iter.hasNext();) {
FLayer layer = (FLayer) iter.next();

if (layer.isActive()) {
if (layer instanceof Selectable) {
((Selectable) layer).clearSelection();
}
}
}

    Para entender de forma más completa las operaciones que se pueden hacer con las capas se puede leer la documentación a nivel de API de las interfaces del paquete mencionado anteriormente.
    (TODO: Poner la descripción de las interfaces aquí)

2.1 VectorialData

    Mención a parte merece la interfaz VectorialData por su sofistificación. Las capas vectoriales en FMap pueden tener una fuente de datos secuencial o aleatoria, en función del driver utilizado, por lo que una selección por rectángulo debería de implementarse dos veces, una para cada tipo de driver. Para evitar esto hemos empleado un mecanismo mediante el cual, el programador debe implementar unas clases que derivan de com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor. En esta interfaz hay 3 métodos: start, visit y stop. Estos métodos están documentados en la API. Pongamos un sencillo ejemplo: para realizar una selección por rectángulo tendra que

	public void setRect(Rectangle2D r) {
rect = r;
}
	public boolean start(FLayer layer) {
return layer instanceof Selectable;
}
	public void visit(IGeometry g, int index) {
if (g.intersects(rect)) {
bitset.set(index, true);
} else {
bitset.set(index, false);
}
}
		((Selectable) layer).setSelection(bitset);
		((VectorialData) layer).process(new SelectionByRectVisitor(r));

    Como se puede comprobar, en ningún momento hemos necesitado saber si el origen de la capa es secuencial o aleatorio. En el paquete "com.iver.cit.gvsig.fmap.operations.strategies" hay muchos más ejemplos de FeatureVisitors.

3. ViewPort

    La clase ViewPort guarda la información relativa a las transformaciónes de coordenadas y datos acerca de la proyección actual. Para ello, almacena el tamaño de la imagen sobre la que se dibuja, el rectángulo de visualización, el rectángulo ajustado al marco de visualización, etc. También se ocupa de gestionar los "listeners" que escuchan los eventos de cambio de "extent", y de realizar los cálculos de área, perímetro y distancia.

4. Eventos

    FMap pone a disposición del programador todo un mecanismo para que se pueda saber "lo que está pasando por dentro del mapa". Cada elemento de FMap tiene un método de la forma addXXXListener, mediente el cual el programador puede registrarse como observador de los eventos que ocurren en el objeto en cuestión. Por ejemplo, el ViewPort tiene un addViewPortListener que recibe una interfaz ViewPortListener. La clase que implemente esta interfaz y sea registrada mediante el método addViewPortListener será notificada de los eventos de cambio de extent y cambio de color de fondo en el ViewPort mediante invocaciones a los métodos de la interfaz que implementa. Esto presenta un problema y para mostrarlo vamos a suponer que tenemos un control de interfaz de usuario que escucha eventos del ViewPort y de la colección de capas, de manera que cuando se añade una capa o se modifica el extent se redibuja la imagen que muestra. Resulta que cuando se añade la primera capa se modifica también el extent por lo que en el caso del control del ejemplo se refrescará la imagen dos veces de manera innecesaria. La solución son los AtomicEvent's.

    4.1 AtomicEvent's

    Para solucionar el problema anterior FMap incorpora dos métodos beginAtomicEvent y endAtomicEvent. Estos métodos no afectan a la gestión de eventos de los elementos individuales de FMap, afecta a la gestión de eventos desde FMap. Al igual que otros elementos del paquete FMap contiene un método addAtomicEventListener con la funcionalidad análoga a los addXXXListener comentados antes. Una vez registrado, el listener será notificado de cualquier evento que suceda por dentro de esa instancia de FMap (en las capas, viewport, leyenda, ...) con la única diferencia que podrá ser notificado de varios eventos al mismo tiempo. Si un trozo de código se encuentra entre las instrucciones beginAtomicEvent y endAtomicEvent, los objetos individuales (ViewPort, Layers, ...) dispararán eventos de la misma manera, pero la instancia de FMap acumulará los eventos desde que se ejecuta beginAtomicEvent hasta que se ejecuta endAtomicEvent, momento en el cual se disparará un AtomicEvent con los eventos acumulados embebidos en el anterior. En caso de que no se use beginAtomicEvent y endAtomicEvent FMap no acumulará eventos, pero seguirá disparándolos a medida que le van llegando. Como ejemplo de listener de AtomicEvent tenemos la clase NewMapControl, la cual escucha atomic events en la clase interna MapContextListener. Como ejemplo de código que usa beginAtomicEvent y endAtomicEvent tenemos el método execute de la extensión com.iver.cit.gvsig.Abrir en gvSIG

5 Drivers

    FMap lee las fuentes de datos mediante el uso de drivers, lo cual permite a cualquiera implementar un driver determinado para cualquier formato existente. Para ello hay que configurar un directorio en el que se colocan los drivers cada uno dentro de su directorio de la siguiente manera:



    Cada tipo de driver (vectorial, raster, ...) debe ser implementado mediante una interfaz distinta (VectorialFileDriver, WMSDriver, ...) y además de implementar esta interfaz, se pueden implementar otras interfaces que añaden un valor añadido al driver.

5.1 VectorialFileDriver

    Para crear un driver de un fichero de tipo vectorial hay que implementar la interfaz VectorialFileDriver cuyos métodos están documentados en el JavaDoc. Mediante esta interfaz el driver obtiene el acceso a los datos geográficos de los ficheros vectoriales. Además de esta interfaz hay que implementar una interfaz para el acceso a los datos alfanuméricos. Dependiendo de cómo estén organizados estos datos se puede implementar com.iver.cit.gvsig.fmap.drivers.ExternalData, que es una interfaz útil para cuando los datos alfanuméricos se encuentran en un fichero distinto al fichero de datos geográficos (caso del shapefile), o se puede implementar com.hardcode.gdbms.engine.data.FileDriver que es adecuado para los casos en los que la tabla de datos alfanuméricos se encuentra en el mismo soporte que los datos geográficos (caso del DGN).

    Una vez implementadas estas dos interfaces, se pueden implementar otras para darle un valor añadido a los drivers:


6 Interfaz de usuario

    Con el paquete FMap se proporciona un control de interfaz de usuario junto con una serie de herramientas diseñadas para este control. MapControl es la interfaz de usuario sobre FMap que se proporciona con gvSIG y automatiza gran parte de la programación del interfaz gráfico de un mapa, lanzando el dibujado en un segundo plano, redibujando automáticamente cuando el FMap que tiene por debajo queda invalidado, ... Además se proporcionan una serie de herramientas preparadas para su uso y extensibles de manera que la incorporación de nuevas herramientas por parte del usuario sea un proceso trivial siempre que el comportamiento de la herramienta ya esté programado. En las secciones posteriores se verá esto en más detalle.

6.1 MapControl

    MapControl es un control de usuario que tiene como modelo una instancia de FMap a la que se puede acceder mediante el método getMapContext(). A continuación presentamos los pasos básicos para mostrar un Frame con un MapControl sin herramientas. En la sección siguiente se añadirán herramientas con comportamientos ya existentes y cómo añadir herramientas con un comportamiento no programado todavía.

            LayerFactory.setDriversPath(
            "C:\\eclipse3\\workspace\\Andami\\gvSIG\\extensiones\\com.iver.cit.gvsig\\drivers");
                FLayer l = LayerFactory.createLayer("Vias", "gvSIG shp driver",
                    new File("C:/Documents and Settings/fernando/Mis documentos/vias.shp"),
                    ProjectionPool.get("EPSG:23030"));
                newMapControl.getMapContext().getLayers().addLayer(l);
		newMapControl.addMapTool("zoom", new RectangleBehavior(new ZoomInListenerImpl(newMapControl)));
newMapControl.setTool("zoom");

6.2 MapBehaviors

    Las herramientas que se proporcionan en FMap son en realidad comportamientos. Se define un comportamiento del ratón tal como hacer un rectángulo (clase RectangleBehavior), dibujar una polilinea (PolylineBehavior), etc. y estos comportamientos disparan eventos relacionados con su propio comportamiento, por ejemplo, la herramienta de hacer rectángulo cuando el usuario termina de dibujar el rectángulo se lanza un evento rectangle definido en la interfaz com.iver.cit.gvsig.fmap.tools.RectangleListener. De esta manera, para implementar las herramientas que tengan el comportamiento de dibujado de rectángulo (zoom in, selección por rectángulo, ...) sólo tienen que implementarse los listeners de los eventos. En el ejemplo anterior hemos visto como se añadía una herramienta al MapControl mediante el behavior RectangleMapBehavior el cual toma en su constructor la acción que se realiza con el rectángulo (acercar la imagen).

6.2.1 Creación de una herramienta en base a un behavior existente

    Para crear una herramienta con un comportamiento ya implementado, hay que implementar la interfaz que dicho behavior espera. Para averiguar qué interfaz es ésta hay que leer la documentación de cada behavior. En el paquete "com.iver.cit.gvsig.fmap.tools" hay múltiples ejemplos sobre como implementar las interfaces de los distintos behaviors.
  
6.2.2 Creación de una herramienta con un comportamiento no implementado

    Para realizar una herramienta para la cual no haya un behavior definido se pueden realizar dos aproximaciones:


6.3 Composición de herramientas

    El modelo de herramientas sigue un patrón composite. Esto quiere decir que existe una herramienta especial que consiste realmente en un conjunto de herramientas. Teoría a parte, esto quiere decir que podemos tener varias herramientas seleccionadas simultáneamente como una sóla. Por ejemplo, podemos tener una herramienta que haga zoom in, a la vez que podemos tener la herramienta que haga zoomout con el botón derecho del ratón y a la vez que tenemos una maptool que muestra la coordenada de la posición actual del ratón. Tenemos un ejemplo de esto en la clase View de gvSIG:

        m_MapControl.addMapTool("zoomIn", new CompoundBehavior(new RectangleBehavior(zil),
new PointBehavior(zoil), new MouseMovementBehavior(sbl)));

    Que añade la herramienta compuesta por las 3 herramientas simples deseadas. También existe un método de conveniencia que acepta un array de Behaviour's y crea internamente el CompoundBehavior.