es.emergya.ui.gis.CustomMapView.java Source code

Java tutorial

Introduction

Here is the source code for es.emergya.ui.gis.CustomMapView.java

Source

/*
 * Copyright (C) 2010, Emergya (http://www.emergya.es)
 *
 * @author <a href="mailto:jlrodriguez@emergya.es">Juan Lus Rodrguez</a>
 * @author <a href="mailto:marias@emergya.es">Mara Arias</a>
 * @author <a href="mailto:fario@emergya.es">Flix del Ro Beningno</a>
 *
 * This file is part of GoFleet
 *
 * This software is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * As a special exception, if you link this library with other files to
 * produce an executable, this library does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * This exception does not however invalidate any other reasons why the
 * executable file might be covered by the GNU General Public License.
 */
/*
 * 01/07/2009
 */
package es.emergya.ui.gis;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.LinkedList;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.SwingWorker;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gofleet.context.GoWired;
import org.gofleet.internacionalization.I18n;
import org.openstreetmap.gui.jmapviewer.JobDispatcher;
import org.openstreetmap.gui.jmapviewer.OsmMercator;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.gpx.WayPoint;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.MainMenu;
import org.openstreetmap.josm.gui.MapMover;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;

import es.emergya.actions.Authentication;
import es.emergya.actions.UsuarioAdmin;
import es.emergya.bbdd.bean.HistoricoGPS;
import es.emergya.bbdd.bean.Incidencia;
import es.emergya.bbdd.bean.Recurso;
import es.emergya.bbdd.bean.Usuario;
import es.emergya.cliente.constants.LogicConstants;
import es.emergya.consultas.IncidenciaConsultas;
import es.emergya.consultas.RecursoConsultas;
import es.emergya.consultas.UsuarioConsultas;
import es.emergya.ui.gis.layers.MapViewerLayer;
import es.emergya.ui.gis.markers.CustomMarker;
import es.emergya.ui.gis.markers.CustomMarker.Type;
import es.emergya.ui.gis.paintingThreads.Updatable;
import es.emergya.ui.plugins.LayerSelectionDialog;
import es.emergya.ui.plugins.ZoomPerformed;

/**
 * Extends the functionality of {@link MapView} Allowing access to some private
 * or protected methods, limiting the zoom scale to osm zoom factor equivalents
 * and enabling rotation operation
 * 
 * @author fario
 */
public class CustomMapView extends MapView
        implements RotatableView, MapMoveListener, ZoomPerformed, Contextful, ICustomMapView {

    // Constantes de utilidad
    private static final double PI2 = Math.PI * 2;
    private static final double PI_MEDIO = Math.PI / 2;
    /** Distancia (EastNorthing) minimo a partir del cual se seguira un marcador */
    private static final double FOLLOW_THRESSHOLD = Double.parseDouble(LogicConstants.get("FOLLOW_THRESSHOLD")); // in
    // eastnorthing
    // units
    private static final Log log = LogFactory.getLog(CustomMapView.class);
    public double fps = 2; // solo util para la obu
    private static final long serialVersionUID = 734833313834536432L;
    /** factor de zoom de mapnik */
    protected int zoomFactor;
    /** limites de zoom la capa raster base */
    protected int minZoom = LogicConstants.getInt("MIN_TILE_ZOOM"),
            maxZoom = LogicConstants.getInt("MAX_TILE_ZOOM"); // por defecto,
    // luego se les
    // da
    // valores
    /** Giro actual */
    protected double angle;
    protected double sin, cos = 1; // es util tenerlos a mano, Math.sin/cos son
    // lentos
    /**
     * Este marcador estara siempre en el centro del mapa y si se mueve el mapa
     * lo seguira
     */
    protected Marker follow;
    /**
     * Hacer zoom a una distancia apropiada para que
     * {@link CustomMapView#follow} se vea
     */
    protected boolean autoZoom = true;
    /** Girar en la direccion en la que {@link CustomMapView#follow} se mueve */
    protected boolean autoTurn = true;
    /** Suavizar el giro si autoTurn esta activado */
    protected boolean smoothTurn = true;
    /** Ultima posicion de {@link CustomMapView#follow} */
    private EastNorth lastFollowPos; // se usa para ver en que direccion se
    // mueve
    /** Ultima direccion en la que se movia {@link CustomMapView#follow} */
    private double lastFollowAngle = 0; // se usa para hacer smoothTurn
    /** La distancia que se movio {@link CustomMapView#follow} la ultima vez */
    private double lastDistance = 0; // se usa para hacer autoZoom
    protected Updater positionUpdater;
    protected JPanel layerControlPanel; // controles para mostrar/ocultar capas
    protected LayerSelectionDialog layerDialog;
    // protected JPanel controlPanel; // Diversos controles
    // protected ZoomControlPanel zoom; // controles para hacer zoom
    // contiene los botones de layerControlPanel, para poder ocultarlos o
    // mostrarlos
    protected JPanel zoom;
    protected List<JToggleButton> layerControls;
    protected ActionListener layerControlListener = new LayerControl();
    // lista de callbacks para movimiento y zoom (se necesitan para implementar
    // los listeners
    protected List<MapMoveListener.MapMovedCallback> movedCallbacks = new ArrayList<MapMovedCallback>();
    protected List<ZoomPerformed.ZoomCallback> zoomedCallbacks = new ArrayList<ZoomCallback>();
    protected JPopupMenu contextMenu;
    // metodos de utilidad de josm
    /**
     * Use this to register shortcuts to
     */
    public final JPanel contentPane = new JPanel(new BorderLayout());
    /**
     * The MOTD Layer.
     */
    // private GettingStarted gettingStarted = new GettingStarted();
    /**
     * The main menu bar at top of screen.
     */
    public final MainMenu menu;

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getMenu()
     */
    @Override
    public MainMenu getMenu() {
        return menu;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getContentPane()
     */
    @Override
    public JPanel getContentPane() {
        return contentPane;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getPanel()
     */
    @Override
    public JPanel getPanel() {
        return panel;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setPanel(javax.swing.JPanel)
     */
    @Override
    public void setPanel(JPanel panel) {
        this.panel = panel;
    }

    public JPanel panel = new JPanel(new BorderLayout());
    /**
     * The toolbar preference control to register new actions.
     */
    public ToolbarPreferences toolbar;

    @GoWired
    public I18n i18n;

    /**
     * @return the i18n
     */
    public I18n getI18n() {
        return i18n;
    }

    /**
     * @param i18n the i18n to set
     */
    public void setI18n(I18n i18n) {
        this.i18n = i18n;
    }

    public CustomMapView() {
        super();
        menu = new MainMenu();
        contentPane.add(panel, BorderLayout.CENTER);

        // iniciar los controles mostrar/ocultar capas
        layerControls = new LinkedList<JToggleButton>();

        JToggleButton botonMostrarOcultarBotones = new JToggleButton(getI18n().getString("map.layers.hideButtons"),
                LogicConstants.getIcon("capas_button_mostrar"), false);
        botonMostrarOcultarBotones.setSelected(true);
        botonMostrarOcultarBotones.setActionCommand("#hide");
        // b.setVerticalTextPosition(SwingConstants.BOTTOM);
        // b.setHorizontalTextPosition(SwingConstants.CENTER);
        botonMostrarOcultarBotones.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JToggleButton b = (JToggleButton) e.getSource();
                if (e.getActionCommand().equals("#hide")) {
                    layerControlPanel.removeAll();
                    layerControlPanel.add(Box.createHorizontalStrut(10));
                    layerControlPanel.add(b);
                    b.setActionCommand("#show");
                    b.setText(getI18n().getString("map.layers.showButtons"));
                } else {
                    layerControlPanel.removeAll();
                    layerControlPanel.add(Box.createHorizontalStrut(10));
                    for (JToggleButton bt : layerControls) {
                        layerControlPanel.add(bt);
                        layerControlPanel.add(Box.createHorizontalGlue());
                    }
                    b.setActionCommand("#hide");
                    b.setText(getI18n().getString("map.layers.hideButtons"));
                }
                layerControlPanel.updateUI();
            }
        });
        layerControls.add(botonMostrarOcultarBotones);

        final JToggleButton botonTodoasLasCapas = new JToggleButton(getI18n().getString("map.layers.allLayers"),
                LogicConstants.getIcon("capas_button_mostrar"), false);
        layerDialog = new LayerSelectionDialog(this);
        layerDialog.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                botonTodoasLasCapas.setSelected(false);
            }
        });
        botonTodoasLasCapas.setSelected(false);
        botonTodoasLasCapas.setActionCommand("#all");
        // all.setVerticalTextPosition(SwingConstants.BOTTOM);
        // all.setHorizontalTextPosition(SwingConstants.CENTER);
        botonTodoasLasCapas.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                layerDialog.setLocationRelativeTo((Component) e.getSource());
                layerDialog.setVisible(!layerDialog.isShowing());
            }
        });
        layerControls.add(botonTodoasLasCapas);

        layerControlPanel = new JPanel();
        layerControlPanel.setLayout(new BoxLayout(layerControlPanel, BoxLayout.X_AXIS));

        Main.main.menu = this.menu;
        toolbar = new ToolbarPreferences();
        toolbar.refreshToolbarControl();
        // toolbar.control.updateUI();
        // contentPane.add(toolbar.control, BorderLayout.NORTH);

        contentPane.updateUI();
        panel.updateUI();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#addLayer(org.openstreetmap.josm.gui.
     * layer.Layer, boolean)
     */
    @Override
    public void addLayer(Layer layer, boolean showOnButtonList) {
        addLayer(layer, showOnButtonList, 0);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#addLayer(org.openstreetmap.josm.gui.
     * layer.Layer, boolean, int)
     */
    @Override
    public void addLayer(Layer layer, boolean showOnButtonList, int pos) {
        if (layer instanceof MapViewerLayer) {
            minZoom = Math.max(((MapViewerLayer) layer).getMinZoomLevel(), getMinZoom());
            maxZoom = Math.min(((MapViewerLayer) layer).getMaxZoomLevel(), getMaxZoom());
            if (zoomFactor > maxZoom || zoomFactor < minZoom) {
                zoomFactor = (maxZoom + minZoom) / 2;
            }
            zoomTo(center, zoom2Scale(zoomFactor));
        }
        if (showOnButtonList) {
            JToggleButton b = new JToggleButton(layer.name,
                    LogicConstants.getIcon("capas_button_" + layer.name.toLowerCase()), layer.visible);
            // b.setVerticalTextPosition(SwingConstants.BOTTOM);
            // b.setHorizontalTextPosition(SwingConstants.CENTER);
            b.setActionCommand(layer.name);
            b.addActionListener(layerControlListener);
            layerControls.add(b);
            layerControlPanel.removeAll();
            layerControlPanel.add(Box.createHorizontalStrut(10));
            for (JToggleButton bt : layerControls) {
                layerControlPanel.add(bt);
                layerControlPanel.add(Box.createHorizontalGlue());
            }
            layerControlPanel.updateUI();
        }

        super.addLayer(layer, pos);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#addLayer(org.openstreetmap.josm.gui.
     * layer.Layer, boolean, java.lang.String)
     */
    @Override
    public void addLayer(Layer layer, boolean showOnButtonList, String icon) {
        if (layer instanceof MapViewerLayer) {
            minZoom = Math.max(((MapViewerLayer) layer).getMinZoomLevel(), getMinZoom());
            maxZoom = Math.min(((MapViewerLayer) layer).getMaxZoomLevel(), getMaxZoom());
            if (zoomFactor > maxZoom || zoomFactor < minZoom) {
                zoomFactor = (maxZoom + minZoom) / 2;
            }
            zoomTo(center, zoom2Scale(zoomFactor));
        }
        if (showOnButtonList) {
            JToggleButton b = new JToggleButton(layer.name, LogicConstants.getIcon(icon), layer.visible);
            // b.setVerticalTextPosition(SwingConstants.BOTTOM);
            // b.setHorizontalTextPosition(SwingConstants.CENTER);
            b.setActionCommand(layer.name);
            b.addActionListener(layerControlListener);
            layerControls.add(b);
            layerControlPanel.removeAll();
            layerControlPanel.add(Box.createHorizontalStrut(10));
            for (JToggleButton bt : layerControls) {
                layerControlPanel.add(bt);
                layerControlPanel.add(Box.createHorizontalGlue());
            }
            layerControlPanel.updateUI();
        }

        super.addLayer(layer);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#addLayer(org.openstreetmap.josm.gui.
     * layer.Layer)
     */
    @Override
    public void addLayer(Layer layer) {
        addLayer(layer, true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#setActiveLayer(org.openstreetmap.josm
     * .gui.layer.Layer)
     */
    @Override
    public void setActiveLayer(Layer layer) { // modificado para que no pete
        if (!getAllLayers().contains(layer)) {
            throw new IllegalArgumentException("Layer must be in layerlist");
        }
        if (layer instanceof OsmDataLayer) {
            editLayer = (OsmDataLayer) layer;
            Main.ds = editLayer.data;
        } else {
            Main.ds.setSelected();
        }
        DataSet.fireSelectionChanged(Main.ds.getSelected());
        Layer old = getActiveLayer();
        setActiveLayerReflect(layer);

        if (old != layer) {
            for (Layer.LayerChangeListener l : Layer.listeners) {
                l.activeLayerChange(old, layer);
            }
        }

        repaint();
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#zoomToEditLayerBoundingBox()
     */
    @Override
    public boolean zoomToEditLayerBoundingBox() {
        return false;
    }

    /**
     * Tries to bypass private field declaration for activeLayer field
     * 
     * @param layer
     */
    private void setActiveLayerReflect(Layer layer) {
        activeLayer = layer;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#zoomTo(org.openstreetmap.josm.data.coor
     * .EastNorth, double)
     */
    @Override
    public void zoomTo(EastNorth newCenter, double scale) {
        int factor = zoomFactor;
        if (this.scale > scale) {
            if (zoomFactor == getMaxZoom()) {
                return;
            }
            factor = Math.min(zoomFactor + 1, getMaxZoom());
        } else if (this.scale < scale) {
            if (zoomFactor == getMinZoom()) {
                return;
            }
            factor = Math.max(zoomFactor - 1, getMinZoom());
        }
        zoomToFactor(newCenter, factor);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setAutoZoom(boolean)
     */
    @Override
    public void setAutoZoom(boolean autoZoom) {
        this.autoZoom = autoZoom;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setAutoTurn(boolean)
     */
    @Override
    public void setAutoTurn(boolean autoTurn) {
        this.autoTurn = autoTurn;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setSmoothTurn(boolean)
     */
    @Override
    public void setSmoothTurn(boolean smoothTurn) {
        this.smoothTurn = smoothTurn;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#setContextMenu(javax.swing.JPopupMenu)
     */
    @Override
    public void setContextMenu(JPopupMenu contextMenu) {
        this.contextMenu = contextMenu;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#showMenu(java.awt.Component, int,
     * int)
     */
    @Override
    public void showMenu(Component parent, int x, int y) {
        if (this.contextMenu != null) { // si hay contextmenu
            this.contextMenu.show(parent, x, y);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#zoomPerformed()
     */
    @Override
    public void zoomPerformed() {
        JobDispatcher.getInstance().cancelOutstandingJobs();
        updateMarkers();
        for (ZoomCallback zc : zoomedCallbacks) {
            zc.action();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#addCallback(es.emergya.ui.plugins.
     * ZoomPerformed.ZoomCallback)
     */
    @Override
    public void addCallback(ZoomCallback zcb) {
        if (!zoomedCallbacks.contains(zcb)) {
            zoomedCallbacks.add(zcb);
        }
    }

    // @Override
    // public void clearCallbacks() { zoomedCallbacks.clear(); }
    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#removeCallback(es.emergya.ui.plugins
     * .ZoomPerformed.ZoomCallback)
     */
    @Override
    public boolean removeCallback(ZoomCallback zcb) {
        return zoomedCallbacks.remove(zcb);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#zoomToFactor(org.openstreetmap.josm.
     * data.coor.EastNorth, int)
     */
    @Override
    public void zoomToFactor(EastNorth newCenter, int zoomFactor) {
        if (zoomFactor < getMinZoom() || zoomFactor > getMaxZoom()) {
            return;
        }
        this.zoomFactor = zoomFactor;
        super.zoomTo(newCenter, zoom2Scale(zoomFactor));
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#zoomToFactor(org.openstreetmap.josm.
     * data.coor.EastNorth, int, double)
     */
    @Override
    public void zoomToFactor(EastNorth newCenter, int zoomFactor, double angle) {
        this.angle = normalizeAngle(angle);
        this.sin = Math.sin(angle);
        this.cos = Math.cos(angle);
        this.zoomToFactor(newCenter, zoomFactor);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#zoomTo(org.openstreetmap.josm.data.coor
     * .EastNorth, double, double)
     */
    @Override
    public void zoomTo(EastNorth newCenter, double scale, double angle) {
        this.angle = normalizeAngle(angle);
        this.sin = Math.sin(angle);
        this.cos = Math.cos(angle);
        this.zoomTo(newCenter, scale);
    }

    /**
     * @param angle
     * @return el equivalente a angle entre 0 y 2*PI
     */
    private double normalizeAngle(double angle) {
        while (angle > PI2) {
            angle -= PI2;
        }
        while (angle < -PI2) {
            angle += PI2;
        }
        return angle;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#paint(java.awt.Graphics)
     */
    @Override
    public void paint(Graphics g) {
        Main.mapView = this;
        if (follow != null) {
            follow();
        }
        try {
            super.paint(g); // Esto ya llama a paintComponents
        } catch (ConcurrentModificationException ex) {
            log.warn("Redibujando, datos aun no cargados...");
            return;
        }

        // ============= DEBUG DEBAJO DE ESTA LINEA ================

        // g.drawString(
        // "zoom: " + zoomFactor + ", angle:" + Math.toDegrees(angle),
        // 300, 15);
        // g.drawString((Runtime.getRuntime().freeMemory() / (1024 * 1024))
        // + "M / " + (Runtime.getRuntime().totalMemory() / (1024 * 1024))
        // + "M", 20, getHeight() - 5);

        // Rectangle r = getBoundingBox();
        // // g.drawRect(r.x + 200, r.y + 200, r.width - 400, r.height - 400);
        // LatLon tl, br;
        // tl = getLatLon(r.x, r.y);
        // br = getLatLon(r.x + r.width, r.y + r.height);
        // UTM u = new UTM();
        // // EastNorth etl = u.latlon2eastNorth(tl);
        // // EastNorth ebr = u.latlon2eastNorth(br);
        // EastNorth etl = Main.proj.latlon2eastNorth(tl);
        // EastNorth ebr = Main.proj.latlon2eastNorth(br);
        // Point pmi, pma;
        // pmi = getPoint(etl);
        // pma = getPoint(ebr);
        // Rectangle draw = new Rectangle(pmi.x, pma.y, pma.x - pmi.x, pma.y -
        // pmi.y);
        // g.setColor(Color.GREEN);
        // g.drawRect(draw.x + 201, draw.y + 201, draw.width - 402, draw.height
        // - 402);
        // g.drawString("+  " + draw, getWidth() / 2, 300);

        // g.setColor(Color.RED);
        // g.drawRect(200, 200, getWidth() - 400, getHeight() - 400);

        // g.setColor(Color.RED);
        // Point p = getPoint(Main.proj.latlon2eastNorth(getLatLon(getWidth() /
        // 2,
        // getHeight() / 2 - 200)));
        // g.fillOval(p.x - 10, p.y - 10, 20, 20);
        // g.setColor(Color.BLACK);
        // g.fillRect(getWidth() / 2 - 5, getHeight() / 2 - 5, 10, 10);

        // EastNorth mi = getEastNorth(0, getHeight() - 1);
        // EastNorth ma = getEastNorth(getWidth() - 1, 0);
        // Point pmi, pma;
        // pmi = getPoint(mi);
        // pma = getPoint(ma);
        // Rectangle r = new Rectangle(pmi.x, pma.y, pma.x - pmi.x, pmi.y -
        // pma.y);
        // g.drawString("+  " + r, getWidth() / 2, 300);
        // g.drawRect(r.x + 200, r.y + 200, r.width - 400, r.height - 400);
        // LatLon ll = getLatLon(getWidth()/2, getHeight()/2);
        // g.drawString("+  " + ll.lat() + " x " + ll.lon(), getWidth()/2,
        // getHeight()/2);
        // drawCompass(g);

    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#updateMousePosition()
     */
    @Override
    public void updateMousePosition() {
    }

    private Thread turner; // se usa para el giro suave

    /**
     * Si algun {@link Marker} esta en {@link CustomMapView#follow} lo sigue,
     * gira y hace zoom si corresponde.
     */
    protected void follow() {
        if (lastFollowPos == null) {
            lastFollowPos = follow.eastNorth;
        } else {
            // distancias
            double dy = follow.eastNorth.getY() - lastFollowPos.getY();
            double dx = follow.eastNorth.getX() - lastFollowPos.getX();
            // si se ha movido lo bastante...
            if (Math.abs(dx) > FOLLOW_THRESSHOLD || Math.abs(dy) > FOLLOW_THRESSHOLD) {
                if (autoTurn) {
                    double na = Math.atan2(dy, dx);
                    na -= lastFollowAngle;
                    // Normalize the angle
                    if (na < -Math.PI) {
                        na += PI2;
                    }
                    if (na > Math.PI) {
                        na -= PI2;
                    }

                    if (smoothTurn) {
                        final double target = na; // para usarlo enel thread
                        if (turner != null && turner.isAlive()) // paramos el
                        // thread si
                        // vamos a girar
                        // otra vez
                        {
                            turner.interrupt();
                        }
                        turner = new Thread(new Runnable() {

                            public void run() {
                                double na = target;
                                while (Math.abs(na) > 0) { // vamos girando 0.5
                                    // de lo que
                                    // queremos
                                    na /= 2;
                                    lastFollowAngle += na;
                                    zoomToFactor(center, zoomFactor, PI_MEDIO - lastFollowAngle);
                                    try {
                                        Thread.sleep(60);
                                    } catch (InterruptedException e) {
                                    }
                                }
                            }
                        });
                        turner.start();
                    } else {
                        lastFollowAngle += na; // giro brusco
                    }

                    // Normalize the angle
                    if (lastFollowAngle < 0) {
                        lastFollowAngle += PI2;
                    }
                    if (lastFollowAngle > PI2) {
                        lastFollowAngle -= PI2;
                    }
                } else {
                    lastFollowAngle = -getAngle() + PI_MEDIO; // deshacemos el
                    // giro si no se
                    // gira
                }
                if (autoZoom) {
                    // Adjusts the current zoom factor to match an appropiate
                    // zoom
                    // level for the speed of the followed object
                    double dist = (lastDistance + Main.proj.eastNorth2latlon(follow.eastNorth)
                            .greatCircleDistance(Main.proj.eastNorth2latlon(lastFollowPos))) / 2;
                    LatLon ll1 = getLatLon(0, 0);
                    LatLon ll2 = getLatLon((int) (40 / fps), 0);
                    double sampledist = ll1.greatCircleDistance(ll2); // get a
                    // sample distance of 20px on the screen
                    if (dist > sampledist * 1.5) { // if we move more than +-50%
                        // of 20px -> zoom out
                        zoomFactor = Math.max(zoomFactor - 1, getMinZoom());
                        ;
                    } else if (dist < sampledist * 0.5) { // if we move less
                        // than +-50% of
                        // 20px -> zoom in
                        zoomFactor = Math.min(zoomFactor + 1, getMaxZoom());
                        ;
                    }
                    lastDistance = dist;
                }

                zoomToFactor(follow.eastNorth, zoomFactor, PI_MEDIO - lastFollowAngle);

                lastFollowPos = new EastNorth(follow.eastNorth.east(), follow.eastNorth.north());

                // zoomTo(follow.eastNorth, getScale(), PI_MEDIO -
                // lastFollowAngle);
            }
        }
    }

    /**
     * Dibuja una brujula apuntando al norte
     * 
     * @param g
     */
    @SuppressWarnings("unused")
    private void drawCompass(Graphics g) {
        int x = 50, y = getHeight() - 50;
        // ((Graphics2D)g).rotate(Math.PI + lastFollowAngle, x, y);
        ((Graphics2D) g).rotate(-PI_MEDIO - getAngle(), x, y);
        g.setColor(Color.LIGHT_GRAY);
        g.fillOval(0, getHeight() - 100, 100, 100);
        g.setColor(Color.BLACK);
        g.fillRect(x - 3, y - 3, 50, 6);
        g.setColor(Color.RED);
        g.fillRect(x + 42, y - 3, 6, 6);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#zoom()
     */
    @Override
    public int zoom() {
        return zoomFactor;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#zoom2Scale(int)
     */
    @Override
    public double zoom2Scale(int zoom) {
        return Math.max(world.east() / OsmMercator.falseEasting(zoom),
                world.north() / OsmMercator.falseNorthing(zoom));
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getMinZoom()
     */
    @Override
    public int getMinZoom() {
        return minZoom;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getMaxZoom()
     */
    @Override
    public int getMaxZoom() {
        return maxZoom;
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setInitAdapter(es.emergya.ui.gis.
     * CustomMapView.InitAdapter)
     */
    public void setInitAdapter(InitAdapter initAdapter) {
        for (ComponentListener l : this.getComponentListeners()) {
            this.removeComponentListener(l);
        }
        this.addComponentListener(initAdapter);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#setDefaultInitAdapter()
     */
    @Override
    public void setDefaultInitAdapter() {
        setInitAdapter(new InitAdapter());
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#rotate(double)
     */
    @Override
    public void rotate(double r) {
        angle += r;
        if (r > Math.PI * 2) {
            angle -= Math.PI * 2;
        }
        if (r < -Math.PI * 2) {
            angle += Math.PI * 2;
        }
        sin = Math.sin(getAngle());
        cos = Math.cos(getAngle());
        repaint();
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getAngle()
     */
    @Override
    public double getAngle() {
        return angle;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#setFollow(org.openstreetmap.josm.gui
     * .layer.markerlayer.Marker)
     */
    @Override
    public void setFollow(Marker m) {
        this.follow = m;
        this.lastFollowPos = m.eastNorth;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * es.emergya.ui.gis.ICustomMapView#getPoint(org.openstreetmap.josm.data
     * .coor.EastNorth)
     */
    @Override
    public Point getPoint(EastNorth p) {
        Point pt = super.getPoint(p);
        return translateR(pt.x, pt.y);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getEastNorth(int, int)
     */
    @Override
    public EastNorth getEastNorth(int x, int y) {
        Point p = translate(x, y);
        return super.getEastNorth(p.x, p.y);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getLatLon(int, int)
     */
    @Override
    public LatLon getLatLon(int x, int y) {
        Point p = translate(x, y);
        return super.getLatLon(p.x, p.y);
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#getBoundingBox()
     */
    @Override
    public Rectangle getBoundingBox() {
        int minx = 0;
        int miny = 0;
        int maxx = getWidth();
        int maxy = getHeight();
        Point p0 = translateR(minx, miny);// tl
        Point p1 = translateR(minx, maxy);// bl
        Point p2 = translateR(maxx, maxy);// br
        Point p3 = translateR(maxx, miny);// tr

        int[] x = { p0.x, p1.x, p2.x, p3.x };
        Arrays.sort(x);

        int[] y = { p0.y, p1.y, p2.y, p3.y };
        Arrays.sort(y);

        return new Rectangle(x[0], y[0], x[x.length - 1] - x[0], y[y.length - 1] - y[0]);
    }

    /**
     * Returns the position of the point rotated the current rotation from the
     * center
     */
    private Point translate(Point p) {
        return translate(p.x, p.y);
    }

    /**
     * Returns the position of the point rotated the oposite to the current
     * rotation from the center
     */
    private Point translateR(int x, int y) {
        double x0 = getWidth() / 2;
        double y0 = getHeight() / 2;
        double nx = x0 + cos * (x - x0) + sin * (y - y0);
        double ny = y0 - sin * (x - x0) + cos * (y - y0);
        return new Point((int) nx, (int) ny);
    }

    /**
     * Returns the position of the point rotated the current rotation from the
     * center
     */
    private Point translate(int x, int y) {
        int x0 = getWidth() >> 1;
        int y0 = getHeight() >> 1;
        double nx = x0 + cos * (x - x0) - sin * (y - y0);
        double ny = y0 + sin * (x - x0) + cos * (y - y0);
        return new Point((int) nx, (int) ny);
    }

    public class InitAdapter extends ComponentAdapter {

        @Override
        public void componentResized(ComponentEvent e) {
            removeComponentListener(this);

            setMouseListeners();

            initControlPanels();

            setResizeListeners();

            // startPositionUpdater();

            zoomPerformed();
        }

        protected void setMouseListeners() {
            MapMover m = new MapMover(CustomMapView.this, CustomMapView.this.contentPane);
            m.setPanButton(MouseEvent.BUTTON1);
        }

        protected void startPositionUpdater() {
            positionUpdater = new Updater();
            new Thread(positionUpdater).start();
        }

        protected void setResizeListeners() {
            CustomMapView.this.addComponentListener(new ComponentAdapter() {

                @Override
                public void componentResized(ComponentEvent e) {
                    super.componentResized(e);
                    int h = e.getComponent().getHeight();
                    int w = e.getComponent().getWidth();
                    layerControlPanel.setBounds(0, h - 100, w, 100);
                    // controlPanel.setBounds(0, 0, w,
                    // controlPanel.getHeight());
                    zoom.setBounds(zoom.getX(), zoom.getY(), zoom.getWidth(), zoom.getHeight());
                    layerControlPanel.updateUI();
                    zoom.updateUI();
                }
            });
        }

        protected void initControlPanels() {
            // zoom = new ZoomControlPanel(CustomMapView.this);
            // add(zoom);
            // zoom.setBounds(0, 0, getWidth(), 100);
            zoom = new JPanel(new BorderLayout());
            zoom.setOpaque(false);
            zoom.setLayout(new BoxLayout(zoom, BoxLayout.Y_AXIS));
            JButton in = new JButton(LogicConstants.getIcon("map_button_zoommas"));
            in.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    zoomToFactor(getCenter(), zoom() + 1);
                }
            });
            zoom.add(in, BorderLayout.NORTH);
            JPanel foo = new JPanel();
            foo.setOpaque(false);
            foo.add(new ZoomMapSlider(CustomMapView.this));
            zoom.add(foo, BorderLayout.CENTER);
            JButton out = new JButton(LogicConstants.getIcon("map_button_zoommenos"));
            out.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    zoomToFactor(getCenter(), zoom() - 1);
                }
            });
            zoom.add(out, BorderLayout.SOUTH);
            add(zoom);
            zoom.setBounds(0, 10, 70, 270);

            // controlPanel = new ControlPanel(CustomMapView.this);
            //
            // add(controlPanel);
            // controlPanel.setBounds(0, 0, getWidth(), 40);

            layerDialog.init();

            layerControlPanel.setOpaque(false);
            layerControlPanel.removeAll();
            layerControlPanel.add(Box.createHorizontalStrut(10));
            for (JToggleButton bt : layerControls) {
                layerControlPanel.add(bt);
                layerControlPanel.add(Box.createHorizontalGlue());
            }
            add(layerControlPanel);
            layerControlPanel.setBounds(0, getHeight() - 100, getWidth(), 100);
        }
    }

    /**
     * Cambia la visibilidad de los botones de
     * {@link CustomMapView#layerControlPanel}
     * 
     * @author fario
     * 
     */
    private class LayerControl implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                boolean status = false;
                final String nombreCapa = e.getActionCommand();
                for (Layer l : getAllLayers()) {
                    if (l.name.indexOf(nombreCapa) == 0) {
                        l.visible = !l.visible;
                        status = l.visible;
                        updateMarkers();
                    }
                }

                // vehiculos_visibles
                // personas_visibles
                // incidencias_visibles
                String capa_incidences = getI18n().getString("Incidences.incidences");
                String capa_vehiculos = getI18n().getString("Resources.resources.vehicles");
                String capa_people = getI18n().getString("Resources.resources.people");
                Usuario u = UsuarioConsultas.find(Authentication.getUsuario().getNombreUsuario());

                if (u != null) {
                    if (nombreCapa.equalsIgnoreCase(capa_incidences))
                        u.setIncidenciasVisibles(status);
                    else if (nombreCapa.equalsIgnoreCase(capa_vehiculos))
                        u.setVehiculosVisibles(status);
                    else if (nombreCapa.equalsIgnoreCase(capa_people))
                        u.setPersonasVisibles(status);

                    UsuarioAdmin.saveOrUpdate(u);
                }

            } catch (Throwable t) {
                log.error("Error actualizando el estado de la capa", t);
            }

        }
    };

    /**
     * Actualiza la capa con marcadores que simulan vehiculos que se mueven
     * 
     * @author fario
     * 
     */
    @Deprecated
    private class Updater implements Runnable {

        private boolean running = true;

        @Override
        public void run() {
            running = true;
            while (running) {
                // if(System.currentTimeMillis() - time > 1000/fps){
                // buscar todos los markers que son updatable en todas las
                // marker layers
                for (Layer l : getAllLayers()) {
                    if (l instanceof MarkerLayer) {
                        for (Marker m : ((MarkerLayer) l).data) {
                            if (m instanceof Updatable) {
                                ((Updatable) m).update();
                            }
                        }
                    }
                }

                repaint();
                // } else {
                try {
                    Thread.sleep((long) (1000 / fps));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // }
            }
        }

        public void stop() {
            this.running = false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#movementEnded()
     */
    @Override
    public void movementEnded() {
        updateMarkers();
        for (MapMovedCallback m : this.movedCallbacks) {
            m.action();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#movementStarted()
     */
    @Override
    public void movementStarted() {
    }

    ;

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#addCallback(es.emergya.ui.gis.
     * MapMoveListener.MapMovedCallback)
     */
    @Override
    public void addCallback(MapMovedCallback mmc) {
        if (!this.movedCallbacks.contains(mmc)) {
            this.movedCallbacks.add(mmc);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#clearCallbacks()
     */
    @Override
    public void clearCallbacks() {
        this.movedCallbacks.clear();
        this.zoomedCallbacks.clear();
    }

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#removeCallback(es.emergya.ui.gis.
     * MapMoveListener.MapMovedCallback)
     */
    @Override
    public boolean removeCallback(MapMovedCallback mmc) {
        return this.movedCallbacks.remove(mmc);
    }

    // Separate threads to reload the markers
    private SwingWorker<Object, Object> mapUpdater;

    /*
     * (non-Javadoc)
     * 
     * @see es.emergya.ui.gis.ICustomMapView#updateMarkers()
     */
    @Override
    public void updateMarkers() {
        Rectangle r = getBoundingBox();
        final LatLon tl, br;
        // tomamos los pois en un area de 3 pantallas cuadradas, de manera que
        // se pueda arrastrar sin notar que no estan cargados los poi de mas
        // all, ya que solo se deberian cargar cuando deja de moverse.
        tl = getLatLon(r.x - r.width, r.y - r.height);
        br = getLatLon(r.x + 2 * r.width, r.y + 2 * r.height);

        if (mapUpdater == null || mapUpdater.isDone()) {
            mapUpdater = new SwingWorker<Object, Object>() {

                @Override
                protected Object doInBackground() throws Exception {
                    try {
                        updateIncidences(tl, br);
                        updateResources(tl, br);
                    } catch (Throwable t) {
                        log.error("Error al actualizar marcadores", t);
                    }
                    return null;
                }

                @Override
                protected void done() {
                    repaint();
                }
            };

            mapUpdater.execute();

        }
    }

    private void updateResources(final LatLon topleft, final LatLon bottomright) {
        MarkerLayer people = null, vehicles = null;
        // tomar las capas vehiculo y persona
        for (Layer l : getAllLayers()) {
            if (l.name.equalsIgnoreCase(getI18n().getString("Resources.resources.people"))) {
                people = (MarkerLayer) l;
            }
            if (l.name.equalsIgnoreCase(getI18n().getString("Resources.resources.vehicles"))) {
                vehicles = (MarkerLayer) l;
            }
            if (people != null && vehicles != null) {
                break;
            }
        }

        if (getParent() instanceof IMapViewer) { // actualizar los recursos
            // disponibles
            ((MapViewer) getParent()).updateControls();
        }

        List<Recurso> allres = RecursoConsultas.getAll(Authentication.getUsuario());

        boolean peopleShowing = people != null && people.visible;
        boolean vehiclesShowing = vehicles != null && vehicles.visible;

        Collection<Marker> peop = new LinkedList<Marker>();
        Collection<Marker> veh = new LinkedList<Marker>();
        for (Recurso r : allres) {
            HistoricoGPS h = r.getHistoricoGps();
            if (h == null) {
                continue;
            }
            WayPoint w = new WayPoint(new LatLon(h.getPosY(), h.getPosX()));
            String name = r.getNombre();

            if (r.getPatrullas() != null)
                name += " (" + r.getPatrullas().getNombre() + ")";

            w.attr.put("name", name);
            w.attr.put("symbol",
                    LogicConstants.get("DIRECTORIO_ICONOS_FLOTAS") + "/" + r.getFlotas().getJuegoIconos());
            // w.attr.put("color", LogicConstants.get("COLOR_ESTADO_REC_"
            // + r.getEstadoEurocop().getId(), "#000000"));
            if (peopleShowing && r.getTipo().equalsIgnoreCase(Recurso.PERSONA)) {
                CustomMarker<String, Recurso> marker = new CustomMarker<String, Recurso>(w, people,
                        r.getIdentificador(), Type.RESOURCE);
                marker.setObject(r);
                peop.add(marker);
            } else if (vehiclesShowing && r.getTipo().equalsIgnoreCase(Recurso.VEHICULO)) {

                CustomMarker<String, Recurso> marker = new CustomMarker<String, Recurso>(w, vehicles,
                        r.getIdentificador(), Type.RESOURCE);
                marker.setObject(r);
                veh.add(marker);
            }
        }
        if (people != null) {
            people.data = peop;
        }
        if (vehicles != null) {
            vehicles.data = veh;
        }
    }

    private void updateIncidences(final LatLon topleft, final LatLon bottomright) {
        MarkerLayer layer = null;
        // tomar las capas vehiculo y persona
        for (Layer l : getAllLayers()) {
            if (l.name.equals(getI18n().getString("Incidences.incidences"))) {
                layer = (MarkerLayer) l;
                break;
            }
        }
        if (layer == null)
            return;

        List<Incidencia> allres = IncidenciaConsultas.getOpened();
        Collection<Marker> nuevoData = new LinkedList<Marker>();

        for (Incidencia i : allres) {
            try {
                if (i.getGeometria() != null) {
                    final com.vividsolutions.jts.geom.Point centroid = i.getGeometria().getCentroid();
                    if (centroid != null) {
                        LatLon latlon = new LatLon(centroid.getCoordinate().y, centroid.getCoordinate().x);
                        WayPoint w = new WayPoint(latlon);
                        w.attr.put("name", i.getTitulo() + " (" + i.getPrioridad() + ")");
                        w.attr.put("symbol",
                                LogicConstants.get("DIRECTORIO_ICONOS_INCIDENCIAS", "incidencia/") + "incidencia_"
                                        + i.getEstado().toString() + "_" + i.getCategoria().toString() + "_"
                                        + i.getPrioridad().toString());
                        w.attr.put("color",
                                LogicConstants.get("COLOR_ESTADO_INC_" + i.getEstado().getId(), "#000000"));

                        CustomMarker<Long, Incidencia> marker = new CustomMarker<Long, Incidencia>(w, layer,
                                i.getId(), Type.INCIDENCE);
                        marker.setObject(i);
                        nuevoData.add(marker);
                    }
                }
            } catch (Throwable t) {
                log.error("Error al intentar pintar una incidencia", t);
            }
        }

        layer.data = nuevoData;
    }
}