Personal tools
You are here: Home Development Documents gvSIG desktop 1.0 / 1.1 FMap MapControl MapControl
Document Actions

MapControl

by Pablo Piqueras last modified 2010-06-01 22:46

Componente diseñado para simular la interacción con las distintas capas con información gráfica, mediante herramientas que el usuario pueda manejar.

Introducción
-------------

MapControl es un componente Java de intefaz gráfica, que pinta un conjunto de capas con información gráfica, vía su objeto MapContext_ que las contiene, y captura los eventos de ratón que se producen en él vía un Behavior_ (que puede ser una composición de varios) que define cómo se comporta actualmente, enriqueciendo dichos eventos con la información necesaria para poder simular la herramienta de interacción actual, vía ToolListener_, que será la que complete la simulación.


Descripción
------------

Según el tipo de capas visibles y activas, gvSIG_ nos proporcionará una serie de herramientas con las que el usuario podrá interaccionar con el objeto MapControl que contiene las capas.

Al seleccionar cualquiera de las herramientas disponibles, lo que se está haciendo es seleccionar un comportamiento para trabajar con MapControl, que puede ser el resultado de múltiples comportamientos denominados cada uno Behavior_, cada uno con una ToolListener_.

La librería *libFMap* define cada uno de estos comportientos básicos, que procesan los eventos de ratón producidos en MapControl, generando otros `eventos `_ con la información necesaria según su naturaleza. Estos eventos serán los que se envíen a la herramienta actualmente seleccionada para interactuar con MapControl.

Dicha herramienta tendrá un icono que verá el usuario en su cursor, y una serie de métodos que serán invocados según el tipo de evento que se produzca.

Las ToolListener incluyen la lógica que complementa a los Behavior para simular una herramienta con la que interactuar con un objeto de tipo MapControl.

En la librería libFMap se definen las ToolListener básicas, pero existen otras muchas que heredan de estas, y están definidas en otros proyectos. 

MapControl utiliza un doble-buffer propio para el dibujado, intentando en la medida que sea posible, dibujar en el buffer, y una vez finalizado, enviar esa información a pantalla.

MapControl crea un objeto compartido de tipo *Cancellable*, con el que notificará a MapContext y a las capas que se estén dibujando el que pueden seguir con el proceso, o deben cancelar el dibujado. (No todas las capas pueden cancelar su dibujado).

Normalmente, cuando se utiliza una instacia de MapControl, se asignan sus posibles comportamientos, identificándolos cada uno con una cadena de texto. Posteriormente, según la herramienta con la que el usuario esté trabajando, se le indicará al objeto MapControl, que utilice como *"herramienta activa"* uno de ellos.


Pintado de MapControl
----------------------

Se busca siempre tener el menor tiempo de respuesta en la interacción con el usuario, por ello en caso que el proceso de pintado sea pesado, se actualiza la pantalla con el valor de pintado del buffer cada *t* milisegundos. 

MapControl se encarga de atender sus peticiones de pintado mediante un objeto de tipo *Drawer2* que notificará a su hilo *trabajador* para que ejecute solo una a la vez, manteniendo otra en espera. Si llegase una petición de pintado, habiendo otra en espera, esta segunda se perdería. Por otro lado, si el hilo *"trabajador"* encargado de pintar, finalizase, quedaría en *espera pasiva*, hasta nueva notificación por parte de *Drawer2*.

Para indicar qué es lo que se repintará, MapControl define 3 estados posibles:

* ACTUALIZADO / UPDATED : refresca la pantalla con el contenido del buffer. Es proceso de pintado rápido.
* DESACTUALIZADO / OUTDATED : actualizar toda la información visual de todas las capas visibles y disponibles. Puede ser el proceso más pesado de pintado.
* ONLY_GRAPHICS : actualizar capa/s de simbología y geometrías.


Proceso de pintado
~~~~~~~~~~~~~~~~~~~

El proceso de pintado de MapControl sigue el siguiente algoritmo:

- Si el *status* es *ACTUALIZADO*:

  - Si hay *doble-buffer*:

    - Si hay un *Behavior* para gestionar la instancia de *MapControl*: delega el proceso de dibujado a dicho *Behavior*, invocando: *behavior_instance.paintComponent(g)*.
    - Sino, repinta rápidamente la información gráfica actual, invocando: *g.drawImage(image,0,0,null)*.

- Si el estado es *OUTDATED* o *ONLY_GRAPHICS*:

  - Repinta rápidamente la información previa invocando *g.drawImage(image,0,0,null)*, y crea una petición de pintado *(PaintingRequest)*, con la que delegará el proceso pesado de pintado al *hilo trabajador* de Drawer2, siguiendo el patrón software *SingleWorketThread*, e iniciando el temporizador con el que actualizará la pantalla cada 360 ms. según se ejecuta el proceso pesado de pintado. Una vez es atendida la petición de dibujado, se invoca a *MapContext* para que pinte las capas: *mapContext.draw(image, g, cancel,mapContext.getScaleView())*.


**Notas sobre el proceso de pintado:**

- Puede ser cancelado en cualquier momento invocando *cancelDrawing()*.
- Será en última instancia la implementación de pintado de cada capa la que la pinte, y realice la cancelación, pudiendo haber quienes no cancelen su proceso de pintado.
- Se puede forzar a pintar todas las capas de MapControl invocando: *drawMap(boolean)*.
- Se puede forzar a repintar solo las capas sucias invocando: *rePaintDirtyLayers()*.
- Se puede forzar a repintar solo la capa *GraphicLayer* invocando: *drawGraphics()*.


Algoritmo de Pintado
~~~~~~~~~~~~~~~~~~~~~

- *MapControl* al iniciarse crea un objeto *Drawer2*. Este a su vez crea un hilo de tipo *Worker*.

- Cada vez que se invoca a *paintComponent(Graphics g)* de *MapControl*, y su *estatus* es *ACTUALIZADO*:

  - Si hay *doble-buffer*:

    - Si hay una herramienta activa: le delega el control para que pinte sobre el *front-buffer*.
    - Sino, refresca el *front-buffer*.

- Cada vez que se invoca a *paintComponent(Graphics g)* de *MapControl*, y su *estatus* es *DESACTUALIZADO*, o *ONLY_GRAPHICS*, creará una nueva petición de tipo *PaintingRequest* que se la asignará al objeto de tipo *Drawer2*, e iniciará el temporizador. *Drawer2* almacenará la petición como *waitingRequest*, y, en caso que el *hilo trabajador* estuviese esperando, le notifica que vuelva a ejecución.

- El *hilo trabajador* de *Drawer2*, *(Worker)*, una vez en ejecución, intenta recoger una petición *(PaintingRequest)*.

- Si no hubiese ninguna, *Drawer2* lo pondría en espera pasiva.

- En caso que hubiese alguna, *Drawer2* le asignaría *waitingRequest*, que pasaría a ser ahora *paintingRequest*.

- Si estando el *hilo trabajador* en ejecución, llegase otra petición, pasaría a ser *waitingRequest*.

- El hilo *Worker*:

  - Si *MapControl* tiene double-buffer donde pintar:

    - Avisa a *MapControl* para que cancele cualquier proceso de dibujado previo, invocando *cancelDrawing()*. 
    - Ejecuta el algoritmo de pintado de la petición: *painting_request_instance.paint()*:

      - Fuerza la cancelación de procesos de dibujado previos, vía el objeto compartido de tipo *CancelDraw*.
      - Si *status* es DESACTUALIZADO:

        - Obtiene el buffer sobre el que pintar: el *back-buffer* del *double-buffer* de *MapControl*: *Graphics2D g*.
        - Actualiza el *back-buffer* con el color de fondo del *ViewPort*, o en blanco, si no hubiese ninguno definido.
        - Pone *status* = ACTUALIZADO.
        - Avisa a *MapContext* para que pinte las capas: *mapContext.draw(double-buffer, front-buffer, canceldraw_compartido, escala_del_ViewPort)*.
      - Si *status* es ONLY_GRAPHICS:

        - Pone *status* = ACTUALIZADO.
        - Avisa a *MapContext* para que pinte *GraphicLayer*: *mapContext.drawGraphics(double-buffer, front-buffer, canceldraw_compartido, escala_del_ViewPort)*.

      - Para el temporizador.
      - Repinta MapControl.

  - Sino, pondrá: *status* = DESACTUALIZADO

- Por su parte, cada vez que se dispare el *temporizador*, mandará refrescar MapControl, de modo que el usuario verá la imagen parcialmente, cada vez más completa.

- Si se produce alguna excepción ejecutando el código de la petición de pintado *(PaintingRequest)*, parará el temporizador, y la notificará a *ExceptionHandlingSupport*, para que la notifique a todos los *ExceptionListener* registrados.


Gestión de Excepciones
-----------------------

MapControl permite registrar listeners, de tipo *ExceptionListener*, con los que recibir notificación de expcepciones producidas:

- Atendiendo una petición de pintado.
- Trabajando con la *herramienta activa*.
- O, ejecutando alguna de las operaciones soportadas por defecto por MapControl: *zoom in*, o *zoom out* .


Diagrama
---------

El *diagrama 1* muestra los principales elementos que intervienen con MapControl.

.. figure:: images/mapcontrol/smalldcmapcontrol.png
   :height: 404
   :width: 920
   :align: center

   *Diagrama 1: diagrama de clases simplificado de MapControl. Versión en tamaño reducido.*


Pulse aquí_ para ver el diagrama en grande.

Descripción del diagrama
~~~~~~~~~~~~~~~~~~~~~~~~~

- *MapControl* es un componente *Swing* que implementa:

  - *ComponentListener* para atender eventos producidos al moverlo, ocultarlo, redimensionarlo, o mostrarlo. En realidad estos métodos no están implementados, sino que se han dejado por si alguna subclase los necesitase.
  - *CommandListener* para atender eventos producidos por la ejecución de operaciones de edición de capas.

- Contiene un *doble-buffer* propio sobre el que realizar el proceso de pintado: se dibuja en el *back-buffer*, y una vez completado, o cada cierto tiempo si el proceso es largo, se actualiza el *front-buffer* con el *back-buffer*.

- Crea un objeto *(CancelDraw)* compartido de tipo *Cancellable* con el que podrá notificar a todas las capas y *MapContext* si pueden continuar el proceso de dibujado, o deben cancelarlo.

- Las distintas capas con información gráfica las almacena en un objeto de tipo *MapContext*, que servirá para gestionarlas, gestionar los eventos *(agrupados en AtomicEvent)* que se producen en ellas mediante un buffer *(EventBuffer)*, proyectarlas según una *proyección* y el puerto de vista *(ViewPort)* disponible, y controlar su dibujado: calidad, ...

- Contiene una serie de identificadores que definen su posible comportamiento, cada identificador asociado a un Behavior o una composición de estos. En un momento dado, tendrá como *currentMapTool*, sólo uno de estos comportamientos posibles.

- Define un listener: *MapToolListener*, que será el encargado de procesar cualquier evento de ratón que se produzca en *MapControl*, y notificarlo a la herramienta activa: *currentMapTool*. Para el caso del evento por rueda del ratón: *MouseWheelEvent*, solo notifica un evento por segundo, para dar tiempo a que se pueda cancelar el proceso anterior.

- La herramienta activa delega el control a la *ToolListener* asociada, enviándole un `evento `__ con la información necesaria para que realice dicho procesamiento. Cualquier excepción durante dicho procesamiento implicará el lanzamiento de alguna *BehaviorException*.

- *BehaviorException* es una simple excepción de Java, que sirve para identificar un problema producido en la ejecución del código de un *Behavior*.

- Toda excepción producida atendiendo una petición de pintado, trabajando con la *herramienta activa*, o, ejecutando alguna de las operaciones soportadas por defecto por MapControl: *zoom in*, o *zoom out*, será lanzada por un objeto de tipo *ExceptionHandlerSupport* a las *ExceptionListener* que estuviesen registradas.

- Todos los eventos *(de tipo AtomicEvent)* que pueda lanzar el objeto interno de tipo *MapContext* serán capturados y procesados por un listener de tipo *MapContextListener* definido en *MapControl*.

- Siempre que es invocado el método *protected void paintComponent(Graphics g)* de *MapControl*, y su *estatus* es *DESACTUALIZADO*, o *ONLY_GRAPHICS*, se creará una nueva petición de pintado: *PaintingRequest*, que se le notificará el *Drawer2*, objeto encargado de gestionarlas.

- El objeto interno de tipo *Drawer2* posee un *hilo trabajador (Worker)* que es el que realiza el pintado implementado en la petición *(PaintingRequest)*. Drawer2 sólo almacena 2 peticiones de pintado, la que está siendo atendida, y una en cola:

  - Si llega una nueva petición, se borra la que estaba en cola.
  - Si el *hilo trabador* acaba, intenta obtener la petición en cola, en caso que no hubiese, pasa a espera pasiva.
  - Estando el *hilo trabajador* en espera pasiva, si llega una petición de pintado, se notifica al hilo para que vuelva a ejecución. 

- Contiene un temporizador *(Timer)* que utilizará para refrescar el *front-buffer* del *double-buffer* cada 360 ms. con la información del *back-buffer*, mientras se está ejecutando un proceso pesado de pintado.


Funcionalidad
--------------

- *Cancelación del pintado*: permite cancelar el proceso actual de pintado en cualquier momento.

- *Componente Java de GUI*: el desarrollador puede incorporar *MapControl* en la interfaz gráfica de su aplicación como otro *JComponent* más.

- *Doble buffer*: obtener el doble-buffer con el que pinta las capas.

- *Herramientas*:

  - Agregar un *Behavior*, o un conjunto (que será un *CompoundBehavior*), identificado por un *String*, como herramienta con las que se podría interactuar con el objeto *MapControl*.
  - Buscar una *herramienta* registrada, vía su identificador.
  - Obtener todas las herramientas registradas, junto con su identificador.
  - Obtener sólo los identificadores de las herramientas registradas.
  - Establacer una de las herramientas registradas, como *activa*, de modo que definirá el comportamiento de *MapControl*.
  - Obtener la herramienta activa, o su identificador.
  - Averiguar si hay una determinada herramienta registrada.
  - Establecer como activa, la que previamente lo estuvo.

- *Listeners*:

  - *ExceptionListener* : agregar, o eliminar este tipo de listener, o notificar a todos estos listeners registrados de alguna excepción producida:

    - Atendiendo una petición de pintado.
    - Trabajando con la *herramienta activa*.
    - O, ejecutando alguna de las operaciones soportadas por defecto por MapControl: *zoom in*, o *zoom out* .

- *MapContext*: establecer u obtener un objeto *MapContext*. Las distintas capas con información gráfica las almacena en un objeto de tipo *MapContext*, que servirá para gestionarlas, gestionar los eventos *(agrupados en AtomicEvent)* que se producen en ellas mediante un buffer *(EventBuffer)*, proyectarlas según una *proyección* y el puerto de vista *(ViewPort)* disponible, y controlar su dibujado: calidad, ...

- *Pintado*: ofrece distintos mecanismos de pintado *( vea `Pintado de MapControl`_ )*:

  - Forzar pintado completo de todas las capas (cancelando el pintado previo que estuviese en marcha):

    - Pintado completo de todas las capas: *drawMap(false)*.
    - Pintado completo de todas las capas, vaciando el *front-buffer* con el color de fondo del puerto de vista, o a blanco, si no estuviese definido: *drawMap(true)*.
  - Pintar sólo la capa de tipo *GraphicLayer* existente: *drawGraphics()*.
  - Repintar solo las capas que no estén actualizadas *(capas sucias)* (cancelando el pintado previo que estuviese en marcha): *rePaintDirtyLayers()*.

- *Proyección*: obtener la proyección actual en que se está mostrando la información gráfica de las capas.

- *Puerto de vista*: obtener el puerto de vista utilizado para ajustar el *extent* seleccionado de las capas, al disponible.

- *Zoom*: *MapControl* implementa por defecto las operaciones de *zoom in*, y *zoom out*.


Enlaces de interés
-------------------

* Behavior_: descripción de los comportamientos básicos.
* Eventos_: tipos de eventos que pueden recibir las ToolListener.
* FLayers_: las distintas capas con información gráfica que puede contener MapContext.
* MapContext_: lógica de gestión y pintado de las capas.
* MapOverview_ : MapControl que se puede utilizar como vista en miniatura de otro.
* ToolListener_: *listeners* que completan la simulación de una herramienta con la que interactuar con MapControl.


.. _aquí: ../images/mapcontrol/dcmapcontrol.png

.. _Behavior: behavior

.. _Eventos: toollistenersevents

.. _FLayers: ../dirlayers/layers

.. _gvSIG: http://www.gvsig.gva.es

.. _libFMap: ../introduccion/introduccion

.. _MapContext: ../dirmapcontext/mapcontext

.. _MapOverview: ../../gvsig/el-proyecto-y-sus-documentos/vista/mapoverview

.. _ToolListener: toollistener

.. _ViewPort: ../dirmapcontext/viewport

View source document


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: