Los distintos modos en que un usuario puede interactuar con un objeto de tipo MapControl son lo que se denomina behavior.
Así, en un momento dado, puede ser necesario tener que trabajar seleccionando una polilínea, o un punto, o un rectángulo, o arrastrando el ratón sobre un MapControl, o, porqué no, combinando varios de estos comportamientos individuales (por ej. seleccionando un punto del objeto de tipo MapControl y arrastrando (drag & drop) el ratón) ... Cada uno de estos comportamientos posibles con los que trabajar con MapControl es lo que, en la terminología de libFMap se denomina behavior. De hecho, es común utilizar varios de estos comportamientos básicos combinados para definir la interacción con MapControl vía una herramienta determinada.
Ninguno de los behavior se encargará de tratar la información seleccionada por el usuario sobre MapControl, para esto están las ToolListeners. Behavior se encarga de procesar los eventos que se producen en MapControl, preparando eventos genéricos que enviará a la ToolListener actual, con la información necesaria.
De este modo, se consigue simular distintas herramientas con las que un usuario podrá trabajar con un objeto de tipo MapControl, y a la vez facilitar al desarrollador el incorporar nuevas herramientas, basándonos en el principio de divide y vencerás y una variante del patrón software "Chain-of-responsibility pattern".
Actualmente, todos los eventos que puede recibir cualquier tipo de behavior son producidos por el manejo del ratón sobre el objeto MapControl, y estos pueden ser de tipo MouseEvent, o de tipo MouseWheelEvent, según su naturaleza, atenderá a uno u otro tipo de evento. Por otro lado, cualquiera de las excepciones que puede lanzar son de tipo BehaviorException.
El diagrama 1 muestra cómo se integra en MapControl, y la relación con los tipos de eventos que puede recibir, o excepciones que puede lanzar.
El diagrama 2 muestra los tipos de comportamientos básicos implementados en la librería libFMap.
Tipo | Proyecto | Modo de interacción | Eventos hacia la herramienta | Herramienta asociada |
---|---|---|---|---|
CADToolAdapter | extCAD | Por Ratón: Haciendo click con el botón derecho del ratón solicita a la herramienta actual que muestre un popup con las opciones de edición actualmente disponibles. Si es con el botón izquierdo agrega un punto en dicha posición. Soltando cualquier botón solicita a MapControl que se repinte. Mientras que, moviéndolo además actualiza las coordenadas en la barra de estado. En caso que esté habilitado el snap, actualizará el icono del cursor conforme el punto de control más cercano en una pequeña área de tolerancia bajo. Por Teclado: En caso de escribir un comando por teclado, cambia a una herramienta en modo selección, y según el tipo de comando escrito y el estado actual de la herramienta, puede tomar la información como un nuevo punto, una opción o el valor de una opción. Aplicará cambios, o notificará que el comando no es válido, en la misma consola de edición. |
MouseEvent, InputEvent | 1 o más CADTool |
CircleBehavior | libFMap | Pulsando el botón izquierdo del ratón, permite definir un área circular: primero se indica centro, y luego un punto de la circunferencia, mediante el botón izquierdo del ratón. | MeasureEvent | CircleListener |
CompoundBehavior | libFMap | Funciona como una combinación de Behavior de libFMap. El modo de interacción dependerá de los Behavior que lo componen. | MouseEvent, MouseWheelEvent | 1 o más ToolListener |
DraggerBehavior | libFMap | Arrastrando el ratón y manteniendo pulsado cualquiera de sus botones. Irá actualizando MapControl teniendo en cuenta la variación del punto actual respecto al inicial. Una vez soltado el ratón, el cambio será permanente. | MoveEvent | PanListener |
MouseMovementBehavior | libFMap | Notifica eventos de movimiento, o arrastre (mantener pulsado un botón del ratón y a la vez moverlo) del ratón sobre MapControl. | PointEvent | PointListener |
MoveBehavior | libFMap | Igual que DraggedBehavior pero manteniendo pulsado el botón izquierdo del ratón. | MoveEvent | PanListener |
PointBehavior | libFMap | Selección de un punto con un click por medio de cualquier botón del ratón. Pulsando una vez, solicita la cancelación del dibujado actual de MapControl. Siempre notifica el que un botón haya dejado de estar pulsado, y en caso que se hubiese hecho un click doble, notifica el evento al soltar el botón. |
PointEvent | PointListener |
PolylineBehavior | libFMap | Con un click de cualquier botón del ratón, notifica y almacena el punto, iniciándose así el modo de dibujado de polilínea. Todos los eventos de movimiento o arrastre del ratón posteriores son tambien notificados, hasta que con 2 clicks se finaliza el modo, enviando todos los vértices seleccionados por el usuario a la ToolListener asociada. | MeasureEvent | PolylineListener |
PolygonBehavior | libFMap | Igual a PolylineBehavior, pero ahora el primer punto será también el último de la polilínea, y cada nuevo en agregarse será el penúltimo. | MeasureEvent | PolylineListener |
RectangleBehavior | libFMap | Notifica el punto donde se hizo click con el botón izquierdo del ratón, a partir de ese momento según se arrastre el ratón se mostrará un rectángulo tomando como segundo vértice en diagonal la posición actual del cursor. Una vez se suelte el botón, quedará definida el área rectangular. Así pues, durante el arrastre y al soltar el botón, se generarán eventos que notifiquen la posición actual del ratón. Via los puntos inicial y el final se definirá un rectángulo con los lados paralelos a los ejes X e Y de coordenadas. |
RectangleEvent | RectangleListener |
Chain of responsibility Pattern: información acerca de este patrón software, con código fuente de ejemplo.
ToolListeners: herramientas que combinadas con algún comportamiento Behavior permiten trabajar con MapControl.
ToolListener events: eventos que utiliza un Behavior para notificar información a su/s ToolListener asociada.
Según el comportamiento que se quiere simular en MapControl, los eventos de ratón que se produzcan en este, serán base para la creacción de algún tipo de evento que complete la información necesaria para que la ToolListener asociada a dicho comportamiento pueda completar la simulación de la herramienta con la que se interactúa con MapControl.
Existen cuatro tipos de estos eventos, todos ellos producidos como consecuencia de eventos de ratón (MouseEvent) al trabajar el usuario sobre dicha vista, que son los únicos que puede recibir cualquiera de los tool listeners.
Así, una herramienta como el pan, que requiere arrastrar con el ratón, necesitará recibir no sólo la información del evento de ratón nativo de Java (MouseEvent), sino también las posiciones inicial y final de dicho arrastre.
Si en cambio se está seleccionando una herramienta que solo requiere información de un punto dado, necesitaré dicho punto. O si es por área, información de dicha área.
Se buscaba tener el mínimo número de eventos posibles, y que sean lo más genéricos para que sirvan para el máximo número de tipos de tool listeners, y así facilitar la programación. Esto hizo que se crearan los 4 tipos de eventos siguientes, que se crearán como consecuencia de alguna acción del ratón sobre MapControl y cuyo evento (MouseEvent) almacenarán internamente. Decir, por último que ninguno de estos eventos se considera de tipo FMapEvent, dado que estos últimos están más relacionados con el dibujado de las capas.
MeasureEvent: evento asociado a tool listeners que permiten crear o seleccionar polilíneas abiertas, cerradas, que se corten o no.
Información adicional que aporta:
MoveEvent: evento asociado a tool listeners que requieren que el usuario realice un movimiento del ratón sobre pantalla, tipo drag & drop (arrastrar y soltar).
Información adicional que aporta:
PointEvent: evento asociado a tool listeners que requieren que el usuario seleccione un punto.
Información adicional que aporta:
RectangleEvent: evento asociado a tool listeners que requieren que el usuario seleccione un área rectangular.
Información adicional que aporta:
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.
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.
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:
El proceso de pintado de MapControl sigue el siguiente algoritmo:
Notas sobre el proceso de pintado:
MapControl permite registrar listeners, de tipo ExceptionListener, con los que recibir notificación de expcepciones producidas:
El diagrama 1 muestra los principales elementos que intervienen con MapControl.
Pulse aquí para ver el diagrama en grande.
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. Destacar las que proporciona el proyecto appgvSIG, que, en general, solo agregan como funcionalidad el actualizar las herramientas disponibles en la GUI de gvSIG , una ejecutada la lógica de la herramienta de la que hereda (en libFMap).
Existe multitud de herramientas definidas en la librería libFMap, todas implementan la interfaz ToolListener, y se agrupan en 5 tipos, según se puede observar en el diagrama 1: selección por área circular, rectangular, polinínea, por punto, o de tipo drag & drop (arrastrar y soltar).
Todas reciben eventos fruto de acciones realizadas con el ratón sobre la vista (MapControl) .
El diagrama 2 muestra los tool listener definidos en la librería libFMap asociados a una selección por área circular.
Tipo | Proyecto | Descripción | Cancelable | Eventos a los que responde | Icono |
---|---|---|---|---|---|
CircleMeasureListener | appgvSIG | Calcula el radio del círculo y lo muestra en el la barra de estado de gvSIG como distancias parcial y total. | No. | MeasureEvent |
El diagrama 3 muestra los tool listener definidos en la librería libFMap asociados a una selección de tipo drag & drop (arrastrar y soltar).
Tipo | Proyecto | Descripción | Cancelable | Eventos a los que responde | Icono |
---|---|---|---|---|---|
MapOverviewChangeZoomListener | appgvSIG | Actualiza el extent del ViewPort del MapControl con el área rectangular seleccionada, o dibuja en el MapOverview asociado un rectángulo delimitando dicha área. De este segundo modo establece dicha área como el extent a visualizar en el objeto MapControl asociado al MapOverview. |
Sí. | RectangleEvent, MoveEvent | |
MapOverviewPanListener | appgvSIG | Desplaza el área rectangular bajo el cursor del ratón en el objeto MapOverview según se arrastra éste mientras se mantiene pulsado su botón derecho. De este modo se establece dicha área como el extent a visualizar en el objeto MapControl asociado al MapOverview. |
Sí. | MoveEvent | |
PanListenerImpl | libFMap | Actualiza el ViewPort del objeto MapControl asociado con un nuevo extent. | Sí. | MoveEvent | |
PanListener | appgvSIG | Igual a PanListenerImpl, actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | Sí. | MoveEvent |
El diagrama 4 muestra los tool listener definidos en la librería libFMap asociados a una selección por punto.
Pulse aquí para ver el diagrama 4 en tamaño completo.
Tipo | Proyecto | Descripción | Cancelable | Eventos a los que responde | Icono |
---|---|---|---|---|---|
DwgEntityListener | extDWG | Selecciona una geometría que tenga un punto en la posición seleccionada por el cursor, en una capa con información DWG. | No. | PointEvent | Cursor.CROSSHAIR_CURSOR |
InfoListener | appgvSIG | Muestra la información alfanumérica disponible para el punto seleccionado y un área alrededor de 7 unidades equivalentes en coordenadas del mapa, en todas las capas activas del MapControl asociado. Dicha información se muestra en forma de tabla. |
No. | PointEvent | |
LinkListener | appgvSIG | Muestra en un panel información (imagen, texto, etc) asociada a cualquier feature de las capas activas cuya área intersecte con el punto seleccionado. | No. | PointEvent | |
MapOverviewListener | appgvSIG | Crea un rectángulo centrado en el punto seleccionado con el botón izquierdo del ratón sobre el objeto MapOverview asociado. De este modo se establece dicha área como el extent a visualizar en el objeto MapControl asociado al MapOverview. |
Sí. | PointEvent | |
PointSelectionListener | libFMap | Selecciona todas las features de capas vectoriales activas del objeto MapControl asociado cuya área intersecte con el punto seleccionado por un simple click de cualquier botón del ratón. | No. | PointEvent | |
PointSelectListener | appgvSIG | Igual a PointSelectionListener actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | No. | PointEvent | |
SelectImageListenerImpl | libFMap | ToolListener anticuado que permitía tener SelectImageListener en appgvSIG para la selección de capa raster. | Sí. | PointEvent | |
SelectImageListener | appgvSIG | Selecciona la capa raster situada más arriba en el TOC con información en la posición indicada en el objeto MapControl asociado. Dicha capa pasará a estar activa, mientras que el resto estará como no activas. Posteriormente actualiza los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. Esta ToolListener también está anticuada. |
Sí. | PointEvent | |
StatusBarListener | appgvSIG | Muestra en la barra de estado de la interfaz gráfica de gvSIG las coordenadas equivalentes al punto sobre el objeto MapControl asociado, llamémosle m. Sigue las siguientes reglas para obtener las expresiones de las coordenadas: Si m no está proyectado, expresa las coordenadas como distancia en píxeles respecto la esquina superior izquierda (0, 0). Si estando proyectado, las unidades de medida de distancia del ViewPort de m están en grados, calcula las coordenadas geográficas. En cualquier otro caso expresa las coordenadas como números decimales teniendo en cuenta la proyección actual. En caso de no estar proyectado m, expresará las coordenadas como latitud: Lat = y longitud: Long =, sino como X: X = e Y: Y =, como prefijo, mientras que como sufijo: En caso de utilizar números decimales: 8 decimales si la proyección de *m es EPSG: 4230 ó EPSG: 4326, o 2 decimales con cualquier otra proyección. En caso de no utilizar números decimales, seguirá este patrón: S?Gº M' S'', con:
|
No. | PointEvent | No utiliza icono. |
ToolSelectGraphic | extCAD | Selecciona ítems de la capa GraphicLayer de objeto MapControl asociado, cuya área intersecta con el punto indicado. | No. | PointEvent | |
WCSZoomPixelCursorListener | extWCS | Realiza una operación de zoom acercar en el objeto MapControl asociado, tomando como centro el punto seleccionado. El factor de zoom dependerá de la resolución máxima de las capas activas con información WCS |
No. | PointEvent | |
ZoomOutListenerImpl | libFMap | Realiza una operación de zoom alejar sobre el objeto MapControl asociado. Para ello calcula el nuevo extent de su ViewPort según las siguientes ecuaciones: ViewPort vp = mapControl.getMapContext().getViewPort(); Point2D p2 = vp.toMapPoint(event.getPoint()); double factor = 1/MapContext.ZOOMOUTFACTOR; Rectangle2D.Double r = new Rectangle2D.Double(); double nuevoX = p2.getX() - ((vp.getExtent().getWidth() * factor) / 2.0); double nuevoY = p2.getY() - ((vp.getExtent().getHeight() * factor) / 2.0); r.x = nuevoX; r.y = nuevoY; r.width = vp.getExtent().getWidth() * factor; r.height = vp.getExtent().getHeight() * factor; vp.setExtent(r); Hay que contar que el extent calculado no tendrá porqué coincidir con el que en última instancia se visualize, dado que ViewPort se encargará de calcular el extent ajustado a partir de éste. |
Sí. | PointEvent | |
ZoomOutListener | appgvSIG | Igual a ZoomOutListenerImpl actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | Sí. | PointEvent | |
ZoomOutRightButtonListener | libFMap | Funciona igual que ZoomOutListenerImpl, pero sólo como respuesta al pulsado del botón derecho del ratón. | Sí. | PointEvent | |
ZoomPixelCursorListener | appgvSIG | Realiza una operación de zoom acercar calculando el nuevo extent del ViewPort del objeto MapControl asociado centrado en su pixel más al punto seleccionado. | No. | PointEvent |
El diagrama 5 muestra los tool listener definidos en la librería libFMap asociados a una selección por polilínea.
Tipo | Proyecto | Descripción | Cancelable | Eventos a los que responde | Icono |
---|---|---|---|---|---|
AreaListenerImpl | libFMap | Calcula perímetro y área de la selección rectangular definida con dos vértices de una de sus diagonales. Si el objeto MapControl no está proyectado, toma las coordenadas como geográficas (latitud - longitud), sino, las calcula teniendo en cuenta su proyección y unidades de medida. |
No. | MeasureEvent | |
AreaListener | appgvSIG | Igual que AreaListenerImpl actualizando la información de perímetro y área en la barra de estado de la GUI de gvSIG. | No. | MeasureEvent | |
MeasureListenerImpl | libFMap | Calcula y muestra por la salida estándar el valor de la longitud total y la del último segmento de la polilínea definida por una serie de puntos. | No. | MeasureEvent | |
MeasureListener | appgvSIG | Calcula y muestra por en el statusbar de gvSIG el valor de la longitud total y la del último segmento de la polilínea definida por una serie de puntos. | No. | MeasureEvent | |
PolygonSelectionListener | libFMap | Selecciona todas las features de las capas activas y vectoriales que intersecten con el área poligonal definida sobre el objeto MapControl asociado. La selección se producirá una vez se finalice la creacción de la polilínea. |
No. | MeasureEvent | |
PolygonSelectListener | appgvSIG | Igual a PolygonSelectionListener actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | No. | MeasureEvent |
El diagrama 6 muestra los tool listener definidos en la librería libFMap asociados a una selección por área rectangular.
Tipo | Proyecto | Descripción | Cancelable | Eventos a los que responde | Icono |
---|---|---|---|---|---|
MapOverviewChangeZoomListener | appgvSIG | Permite realizar un cambio de extent según un área rectangular definida sobre un objeto de tipo MapOverview. Si la acción es un movimiento, y el objeto asociado es de tipo MapOverview actualiza el extent manteniendo el zoom. Si la acción es la selección de un área rectangular, y supera los 3x3 píxeles, realiza una operación de zoom in adaptando el ViewPort de MapOverview al extent equivalente en coordenadas del mundo al área seleccionada. |
Sí. | MoveEvent, RectangleEvent | |
RectangleSelectionListener | libFMap | De las capas vectoriales que estén activas, selecciona todas las features que intersecten con el área rectangular definida. | No. | RectangleEvent | |
RectangleSelectListener | appgvSIG | Igual a RectangleSelectionListener actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | No. | RectangleEvent | |
SaveRasterListenerImpl | libFMap, extRasterTools | Almacena el área rectangular definida, en coordenadas del mapa y del mundo. | Sí. | RectangleEvent | |
SaveRasterListener | extRasterTools | Permite guardar el área rectangular seleccionada, como un fichero raster, vía un panel cone opciones de salvado. | Sí. | RectangleEvent | |
ExportToGeoRasterToolListener | extRasterTools | El diálogo de recorte raster utiliza esta herramienta para permitir al usuario seleccionar una nueva área y con ello actualizar las coordenadas de recorte. | Sí. | RectangleEvent | |
ZoomInListenerImpl | libFMap | Realiza una operación de zoom acercar sobre el objeto MapControl asociado. Para ello calcula el nuevo extent de su ViewPort según las siguientes ecuaciones: double factor = 1/MapContext.ZOOMINFACTOR; Rectangle2D rect = event.getWorldCoordRect(); Rectangle2D.Double r = new Rectangle2D.Double(); ViewPort vp = mapCtrl.getMapContext().getViewPort()" double nuevoX = rect.getMaxX() - ((vp.getExtent().getWidth() * factor) / 2.0); double nuevoY = rect.getMaxY() - ((vp.getExtent().getHeight() * factor) / 2.0); Rectangle2D.Double r; r.x = nuevoX; r.y = nuevoY; r.width = vp.getExtent().getWidth() * factor; r.height = vp.getExtent().getHeight() * factor; vp.setExtent(r); Hay que contar que el extent calculado no tendrá porqué coincidir con el que en última instancia se visualize, dado que que ViewPort se encargará de calcular el extent ajustado a partir de éste. |
Sí. | RectangleEvent | |
ZoomInListener | appgvSIG | Igual a ZoomInListenerImpl actualizando los controles disponibles de la interfaz gráfica de gvSIG para trabajar con la información actual en el objeto MapControl asociado. | Sí. | RectangleEvent |