Marcas de Google Maps con menu contextual

En un post anterior hablaba de como añadir un tooltip personalizado a nuestras marcas de google maps, que permitia mostrar cualquier objeto DOM como si fuese un tooltip de la marca.

Siguiendo la misma idea, podemos hacer un menu contextual para las marcas, que se active al hacer click sobre ellas, y contenga acciones a ejecutar sobre la marca en cuestion.

Para ello seguiremos los siguientes pasos:

  1. Crear el menu y sus acciones
  2. Asignar el menu a la marca, y una referencia de la marca al menu para poder ejecutar acciones sobre ella
  3. Mostrar el menu con el evento onclick y ocultarlo al seleccionar una opción

ver ejemplo

Leave a Comment

Marcas de Google Maps con tooltip personalizado

Al situar el ratón encima de una imagen o cualquier elemento del DOM, éste es capaz de mostrar en un tooltip el texto de su atributo 'title',

ejemplo

En las marcas de Google Maps este efecto puede conseguirse especificando el atributo 'title' en el constructor de la marca.

var coords = new GLatLng(41.647471, -0.885569);
var mark = new GMarker(coords,  {title:'texto de ejemplo'})

ver ejemplo

El problema es que esta opción es muy limitada, ya que se restringe al uso de texto plano. Lo ideal sería poder mostrar cualquier elemento del DOM que queramos, con su HTML y su CSS.
Para conseguirlo vamos a seguir estos pasos:

  1. Crear el nodo del DOM que queramos mostrar o coger un nodo ya existente y ponerlo como un atributo de la marca
  2. Al escuchar el evento mouseover de la marca, añadir el nodo al div contenedor del mapa en el la posición en la que se encuentra la marca
  3. Al escuchar el evento mouseout de la marca, eliminar el nodo del div contenedor del mapa para ocultarlo.

ver ejemplo

Leave a Comment

Cómo limitar el zoom y el desplazamiento en Google Maps

En ocasiones nos interesa restringir el desplazamiento del usuario sobre el mapa a un área determinada, o limitar los niveles de zoom máximo y mínimo.

En el caso del zoom la solución es bastante sencilla. Solo hay que sobreescribir los métodos getMinimumResolution() y getMaximumResolution() de cada uno de los tipos de mapas GMapType presentes en nuestro mapa.
Para el caso del desplazamiento, la solución es algo más compleja. Consiste en escuchar el evento move, que es lanzado por el objeto GMap cuando hay un desplazamiento, y comprobar las coordenadas del mapa tras el desplazamiento. Si estas coordenadas quedan fuera del área a la que queremos limitar el desplazamiento, usamos el metodo GMap.setCenter para que el mapa quede dentro de nuesta área.

Esta clase implementa la funcionalidad deseada:

/******  TRestricter  **********************************************/

// Constructor
TRestricter = function (map) {
this.map = map;
}
// Función que activa la limitación del desplazamiento entre la esquina inferior izquierda
// y la esquina superior derecha
TRestricter.prototype.restrict = function (sw, ne) {
this.map._allowedBounds = new GLatLngBounds(sw, ne);
GEvent.addListener(this.map, 'move', this.checkBounds);
}
// Función que desactiva la limitación del desplazamiento
TRestricter.prototype.unrestrict = function () {
this.map._allowedBounds = null;
}
// Listener encargado de comprobar el desplazamiento
TRestricter.prototype.checkBounds = function() {
if (!this._allowedBounds || this._allowedBounds.contains(this.getCenter())) return;
var x = Math.min(Math.max(this.getCenter().lng(), this._allowedBounds.getSouthWest().lng()), this._allowedBounds.getNorthEast().lng());
var y = Math.min(Math.max(this.getCenter().lat(), this._allowedBounds.getSouthWest().lat()), this._allowedBounds.getNorthEast().lat());
this.setCenter(new GLatLng(y,x));
}
// Establece los límites de zoom del mapa
TRestricter.prototype.zoomLevels = function (min, max) {
var array = this.map.getMapTypes() || [];
for (var i=0; i
array[i].getMinimumResolution = function () { return min };
array[i].getMaximumResolution = function () { return max };
}
}

Y aquí tenemos un ejemplo de la clase en acción

Leave a Comment

eclipse tips

Para aquellos que, como yo, utilizáis eclipse (o cualquiera de sus variantes) como IDE de desarrollo, aquí os dejo una serie de truquillos, explicaciones y atajos de teclado interesantes. Seguro que ya conocéis la mayoría, pero allá van.

  • Toggle Comment: (ctrl+shift+C) Comenta/Descomenta la linea actual o lineas seleccionadas.
  • Generate Element Comment: (shift+alt+J) Genera las lineas de comentario del elemento sobre el que está el cursor.
  • Organize Imports: (ctrl+shift+O) Importa las clases que necesitas y elimina las que no.
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Correct Indentation: (ctrl+I) corrige la identación (tabulación de lineas) de la linea actual o lineas seleccionadas.
  • Format: (ctrl+shift+F) Esto te deja el código bonito. Deja los espacios correspondientes, saltos de línea, identación... según las convenciones de java.
    Pero lo mejor, es que puedes definir todas esas cosas a tu gusto. Ve al menu de Windows->Preferences->Java->Code Style->Formatter. Seleccionando "Edit" accedes a un menu donde puedes cambiar el aspecto del código sobre los comentarios, saltos de linea, sentencias de control, declaración de variables...
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Clean Up: Esta herramienta es realmente poderosa. Es parecida a "Format". Permite eliminar/añadir el modificador this, paréntesis, llaves, anotaciones automáticas y otras cosillas.
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Generate Getters/Setters: Genera métodos get/set estilo JavaBean para los campos que queramos.
  • Generate Constructor using Fields: Genera el constructor de la clase con los campos definidos, en plan JavaBean.
  • Surround With: (shift+alt+Z) Mete las lineas seleccionadas en un bloque if, while, try-catch, etc.

Leave a Comment

Cómo limitar el numero de caracteres que muestra un h:outputText

En alguna ocasión, trabajando con JSF, he necesitado limitar el número de caracteres que se muestran en un elemento de salida de texto, como un outuputText.Una forma fácil de hacerlo es implementar un Converter personalizado.

En primer lugar necesitas declarar una clase que implemente la interfaz javax.faces.convert.Converter e implementar los métodos getAsObject y getAsString.
Aquí tienes la implementación del Converter: StringLimiterConverter

package es.neodoo.control.jsf;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public class StringLimiterConverter implements Converter {
    private static final String LIMIT_PARAMETER_NAME = "limit";
    private static final int DEFAULT_LIMIT = 5;

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        return limit(value, getLimitAttribute(component));
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (! (value instanceof String))
            return null;
        else {
            return limit(value.toString(), getLimitAttribute(component));
        }
    }

    private int getLimitAttribute(UIComponent component) {
        Object att = component.getAttributes().get(LIMIT_PARAMETER_NAME);
        if (att == null)
            return DEFAULT_LIMIT;
        else
            return Integer.parseInt((String)component.getAttributes().get(LIMIT_PARAMETER_NAME));
    }

    private String limit(String s, int limit) {
        String limited = s;
        if (! (s.length() <= limit))
            limited = s.substring(0, limit);
        return limited;
    }
}

Después registra el converter en el archivo faces-config.xml

<faces -config>
          <converter>
               </converter><converter -id>stringLimiterConverter</converter>
               <converter -class>tes.jsf.converter.StringLimiterConverter</converter>
         
     </faces>

Y ya puedes usarlo a través de la etiqueta de JSF

<h :o utputText value="En un lugar de la Mancha...">
          <f :converter converterId="stringLimiterConverter" />
          <f :attribute name="limit" value="6" />
     </h>

Con el atributo <f:attribute name="limit" value="6" /> podemos indicarle el número de caracteres que queremos que nos muestre

Leave a Comment

Cómo iterar sobre un Set en un dataTable

El otro día iba a crear un h:dataTable con los valores almacenados en un java.util.Set, y me llevé una sopresa al comprobar que no se podía.

La razón es que el componente UIData (que es vital para el funcionamiento de las etiquetas h:dataTable y ui:repeat entre otras ) está basado en índices numéricos, al igual que la interfaz java.util.List.
La interfaz java.util.Set está basada en parejas de elementos clave/valor, por lo que no se lleva bien con el componente UIData.

Una solución para poder usar nuestro Set con dataTables es convertirlo previamenta a un List.
Para ello podemos usar un ELResolver. Ya expliqué cómo hacerlo en otro post

Leave a Comment

Cómo implementar un ELResolver personalizado

El Expression Language de JSF nos permite acceder a propiedades de los objetos siempre que estas esten disponibles mediante getters.

Esto está muy bien, pero en ocasiones lo que queremos obtener es un objeto que solo está accesible a través de un método que no es un getter, como por ejemplo el tamaño de una java.util.Collection disponible a través de su método collection.size(), o los valores de un java.util.Map disponibles a través del método values().

Para ello podemos realizar nuestra propia implementación de la clase javax.el.ELResolver.
Esta clase es la encargada de evaluar las ELs (las expresiones que van entre #{}) y realizar las acciones correspondientes.
Sustiye a los antiguos PropertyResolver y VariableResolver, que están marcados como obsoletos (deprecated) en la especificación 1.2 de JSF (que es la usada en la versión 5 de Java EE).

Para usar nuestro propio ELResolver solo deboemos seguir tres sencillos pasos:

  • 1) Implementar nuestra clase que extienda de javax.el.ELResolver
    Puedes descargarla aquí: MyELResolver
    Necesitarás añadir al classpath de compilación el archivo el-api.jar
  • package tes.jsf;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;

    import javax.el.ELContext;

    public class MyELResolver extends javax.el.ELResolver {

        @Override
        public Class getCommonPropertyType(ELContext context, Object base) {
            return null;
        }

        @Override
        public Iterator getFeatureDescriptors(ELContext context, Object base) {
            return null;
        }

        @Override
        public Class getType(ELContext context, Object base, Object property) {
            return null;
        }

        @Override
        public Object getValue(ELContext context, Object base, Object property) {
            if (base instanceof Collection) {
                return resolveInCollection(context, (Collection)base, property);
            } else if (base instanceof Map) {
                return resolveInMap(context, (Map)base, property);
            } else {
                return null;
            }
        }

        @Override
        public boolean isReadOnly(ELContext arg0, Object arg1, Object arg2) {
            return true;
        }

        @Override
        public void setValue(ELContext arg0, Object arg1, Object arg2, Object arg3) {
        }

        private Object resolveInCollection(ELContext context, Collection base, Object property) {
            if (property.equals("size")) {
                context.setPropertyResolved(true);
                return base.size();
            } else if (property.equals("toMap")) {
                context.setPropertyResolved(true);
                return collectionToMap(base);
            } else if (property.equals("toList")) {
                context.setPropertyResolved(true);
                return new ArrayList(base);
            }else {
                return null;
            }
        }

        private Object resolveInMap(ELContext context, Map base, Object property) {
            if (property.equals("size")) {
                context.setPropertyResolved(true);
                return base.size();
            } else if (property.equals("values")) {
                context.setPropertyResolved(true);
                return base.values();
            } else if (property.equals("keySet")) {
                context.setPropertyResolved(true);
                return base.keySet();
            } else if (property.equals("entrySet")) {
                context.setPropertyResolved(true);
                return base.entrySet();
            } else {
                return null;
            }
        }

        private Map<object> collectionToMap(Collection col) {
            Map</object><object> map = new HashMap</object><object>();
            for (Object obj : col) {
                map.put(obj, obj);
            }
            return map;
        }
    }

  • 2) Declarar el ELResolver en el archivo faces-config.xml
    La declaración debe hacerse dentro de la seccion <application>.
    <el-resolver>tes.jsf.MyELResolver</el-resolver>
  • 3) Usar las nuevas posibilidades del Language Expression
    • El tamaño de mi Collection es: #{myCollection.size}
    • Puedes usarlo para rellenar tus combos:
    •      <h:selectOneMenu>
                <f:selectItems value="#{myCollection.toMap}" />
           <h:selectOneMenu>
    • Para asociar tu java.util.Set con un dataTable
           <h:dataTable value="#{mySet.toList}" var="entry">
                ...
           </h:dataTable>
    • Para iterar sobre las entradas de un Map
    •      <ui:repeat value="#{myMap.entrySet.toList}" var="entry">
                #{entry.key} : #{entry.value} <br />
           </ui:repeat>
    • Y para todo lo que se te ocurra añadir

Comments (3)

Adjuntar el código fuente o javadoc a un .jar en eclipse

En numerosas ocasiones viene bien tener a mano el javadoc de una libería o su código fuente.

¿Cómo evitar que nos salga el Class File Editor con el mensaje "source not found" o "no javadoc could be found"?

Simple. Solo tienes que especificar la ruta hasta el código fuente o el javadoc.

Entra a la configuración del "build path" del proyecto, a la pestaña de las librerías.
Project -> Properties -> Java Build Path -> Pestaña Libraries

Allí te saldrá un listado de todos los .jar que se encuentran en el classpath de tu proyecto.
Cada uno de esos jars lleva asociados cuatro elementos, que se pueden ver y editar usando la flecha situada a la izquierda del icono del jar:
- Source: Permite adjuntar el código fuente de las clases contenidas en el jar. Este código puede encontrarse en otro jar o en una carpeta del sistema de archivos.
- Javadoc: Permite adjuntar el javadoc de las clases contenidas en el jar. La ruta al javadoc puede ser una url de internet o una ruta del sistema de archivos local, directorio o fichero.
- Native libraries: Permite especificar librerías nativas del sistema operativo.
- Access rules: Permite restringir el acceso las clases contenidas en el jar.

Leave a Comment