de.fhg.igd.mapviewer.BasicMapKit.java Source code

Java tutorial

Introduction

Here is the source code for de.fhg.igd.mapviewer.BasicMapKit.java

Source

/*
 * Copyright (c) 2016 Fraunhofer IGD
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Fraunhofer IGD <http://www.igd.fraunhofer.de/>
 */
package de.fhg.igd.mapviewer;

import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.mapviewer.DefaultTileCache;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.jdesktop.swingx.mapviewer.GeotoolsConverter;
import org.jdesktop.swingx.mapviewer.IllegalGeoPositionException;
import org.jdesktop.swingx.mapviewer.JXMapKit;
import org.jdesktop.swingx.mapviewer.JXMapViewer;
import org.jdesktop.swingx.mapviewer.TileCache;
import org.jdesktop.swingx.mapviewer.TileOverlayPainter;
import org.jdesktop.swingx.painter.CompoundPainter;
import org.jdesktop.swingx.painter.Painter;
import org.springframework.util.ClassUtils;

import de.fhg.igd.mapviewer.concurrency.Concurrency;
import de.fhg.igd.mapviewer.concurrency.IJob;
import de.fhg.igd.mapviewer.concurrency.Job;
import de.fhg.igd.mapviewer.concurrency.Progress;
import de.fhg.igd.mapviewer.concurrency.SwingCallback;
import de.fhg.igd.mapviewer.server.MapServer;

/**
 * Basic MapKit
 *
 * @author <a href="mailto:simon.templer@igd.fhg.de">Simon Templer</a>
 */
public class BasicMapKit extends JXMapKit {

    private static final Log log = LogFactory.getLog(BasicMapKit.class);

    private static final long serialVersionUID = -708805464636342709L;

    /**
     * The painter for the current map tool
     */
    private final MapToolPainter toolPainter;

    /**
     * The current map server
     */
    private MapServer server;

    private TileCache cache;

    /**
     * Defines whether this is synced with the 3D camera
     */
    private boolean synced;

    /**
     * The painter for all overlays
     */
    private final CompoundPainter<JXMapViewer> painter;

    /**
     * The custom painters
     */
    private final CompoundPainter<JXMapViewer> customPainter;

    /**
     * The painters associated directly with the map
     * 
     * @see MapServer#getMapOverlay()
     */
    private final CompoundPainter<JXMapViewer> mapPainter;

    private List<MapPainter> customPainters = new ArrayList<MapPainter>();

    private Lock customPaintersLock = new ReentrantLock();

    /**
     * Creates a basic map kit
     */
    public BasicMapKit() {
        this(new DefaultTileCache());
    }

    /**
     * Creates a basic map kit
     * 
     * @param cache the tile cache to use
     */
    public BasicMapKit(TileCache cache) {
        super();

        this.cache = cache;

        getMiniMap().setPanEnabled(false);
        getMiniMap().setCursor(Cursor.getDefaultCursor());

        getZoomSlider().setCursor(Cursor.getDefaultCursor());
        getZoomInButton().setCursor(Cursor.getDefaultCursor());
        getZoomOutButton().setCursor(Cursor.getDefaultCursor());

        getMiniMap().addMouseListener(new MouseAdapter() {

            /**
             * @see MouseAdapter#mouseClicked(MouseEvent)
             */
            @Override
            public void mouseClicked(MouseEvent me) {
                getMainMap().setCenterPosition(getMiniMap().convertPointToGeoPosition(me.getPoint()));
            }

        });

        // create painter for map tools
        toolPainter = new MapToolPainter(getMainMap());

        customPainter = new CompoundPainter<JXMapViewer>();
        customPainter.setCacheable(false);

        mapPainter = new CompoundPainter<JXMapViewer>();
        mapPainter.setCacheable(false);

        painter = new CompoundPainter<JXMapViewer>();
        painter.setPainters(customPainter, toolPainter, mapPainter);
        painter.setCacheable(false);

        updatePainters();

        // register as state provider
        // GuiState.getInstance().registerStateProvider(this);
    }

    /**
     * Update the custom painters
     */
    private void updatePainters() {
        customPaintersLock.lock();
        try {
            for (MapPainter painter : customPainters) {
                painter.setMapKit(this);
            }

            MapPainter[] painters = new MapPainter[customPainters.size()];
            customPainters.toArray(painters);
            customPainter.setPainters(painters);
        } finally {
            customPaintersLock.unlock();
        }
    }

    /**
     * Get all custom painters of a given type
     * 
     * @param type the painter type
     * @param <T> the painter type
     * @return the list of custom painters (not backed by the map kit)
     */
    @SuppressWarnings("unchecked")
    public <T extends MapPainter> List<T> getCustomPainters(Class<T> type) {
        List<T> results = new ArrayList<T>();

        customPaintersLock.lock();
        try {
            for (MapPainter painter : customPainters) {
                if (ClassUtils.isAssignable(type, painter.getClass())) {
                    results.add((T) painter);
                }
            }
        } finally {
            customPaintersLock.unlock();
        }

        return results;
    }

    /**
     * @return the list of custom painters (not backed by the map kit)
     */
    public List<MapPainter> getCustomPainters() {
        List<MapPainter> results;

        customPaintersLock.lock();
        try {
            results = new ArrayList<MapPainter>(customPainters);
        } finally {
            customPaintersLock.unlock();
        }

        return results;
    }

    /**
     * Sets the custom painters
     * 
     * @param customPainters the custom painters
     */
    public void setCustomPainters(List<MapPainter> customPainters) {
        customPaintersLock.lock();
        try {
            this.customPainters = new ArrayList<MapPainter>(customPainters);
        } finally {
            customPaintersLock.unlock();
        }

        updatePainters();
    }

    /**
     * Adds a custom map painter
     * 
     * @param painter the map painter
     */
    public void addCustomPainter(MapPainter painter) {
        synchronized (customPainters) {
            customPainters.add(painter);
        }

        updatePainters();
    }

    /**
     * Removes a custom map painter
     * 
     * @param painter the map painter
     */
    public void removeCustomPainter(MapPainter painter) {
        synchronized (customPainters) {
            customPainters.remove(painter);
        }

        updatePainters();
    }

    /**
     * Get all tile overlay painters of a certain type
     * 
     * @param <T> the painter type
     * @param type the painter type
     * @return the list of tile overlay painters
     */
    @SuppressWarnings("unchecked")
    public <T extends TileOverlayPainter> List<T> getTilePainters(Class<T> type) {
        List<T> results = new ArrayList<T>();

        for (TileOverlayPainter painter : getMainMap().getTileOverlays()) {
            if (ClassUtils.isAssignable(type, painter.getClass())) {
                results.add((T) painter);
            }
        }

        return results;
    }

    /**
     * Creates all map painters save the tool painter
     * 
     * @param mapViewer the main map viewer
     * 
     * @return a list of painters
     */
    protected List<Painter<JXMapViewer>> createPainters(JXMapViewer mapViewer) {
        return new ArrayList<Painter<JXMapViewer>>();
    }

    /**
     * Set the current map tool
     * 
     * @param tool the {@link MapTool} to use
     */
    public void setMapTool(MapTool tool) {
        if (toolPainter.getMapTool() != null)
            toolPainter.getMapTool().setActive(false);
        toolPainter.setMapTool(tool);
        tool.setActive(true);
        getMainMap().setPanEnabled(tool.isPanEnabled());
        getMainMap().setCursor(tool.getCursor());
        getMainMap().repaint();
    }

    /**
     * Get the current map tool
     * 
     * @return the current map tool
     */
    public MapTool getMapTool() {
        return toolPainter.getMapTool();
    }

    /**
     * Set the map kit's map server
     * 
     * @param server the map server
     * @param skipZoom if zooming to the area visible in the last map shall be
     *            skipped (makes sense when instead loading the state from file)
     */
    public void setServer(final MapServer server, final boolean skipZoom) {
        this.server = server;

        // remember map area
        final Set<GeoPosition> gps = new HashSet<GeoPosition>();
        gps.add(getMainMap().convertPointToGeoPosition(new Point((int) Math.round(getMainMap().getWidth() * 0.1),
                (int) Math.round(getMainMap().getHeight() * 0.1))));
        gps.add(getMainMap().convertPointToGeoPosition(new Point((int) Math.round(getMainMap().getWidth() * 0.9),
                (int) Math.round(getMainMap().getHeight() * 0.9))));

        onChangingServer(server);

        IJob<Void> job = new Job<Void>(Messages.BasicMapKit_0, new SwingCallback<Void>() {

            @Override
            protected void finished(Void result) {
                // dispose old map overlay
                Painter<?>[] oldOverlays = mapPainter.getPainters();
                if (oldOverlays != null) {
                    for (Painter<?> oldOverlay : oldOverlays) {
                        if (oldOverlay instanceof MapPainter) {
                            ((MapPainter) oldOverlay).dispose();
                        }
                    }
                }

                // set the new map overlay
                MapPainter mapOverlay = server.getMapOverlay();
                if (mapOverlay != null) {
                    mapPainter.setPainters(mapOverlay);
                    mapOverlay.setMapKit(BasicMapKit.this);
                } else {
                    mapPainter.setPainters();
                }
                getMainMap().setOverlayPainter(painter);

                if (!skipZoom) {
                    // done in the step below - setCenterPosition(pos);
                    zoomToPositions(gps);
                }

                revalidate();
            }

            @Override
            protected void error(Throwable e) {
                log.error("Error configuring map", e); //$NON-NLS-1$
            }

        }) {

            @Override
            public Void work(Progress progress) throws Exception {
                // configure the map kit
                BasicMapKit.this.setTileFactory(server.getTileFactory(cache));

                return null;
            }

        };
        Concurrency.startJob(job);
    }

    /**
     * Set the tile cache
     * 
     * @param cache the tile cache to use
     */
    public void setTileCache(TileCache cache) {
        this.cache = cache;

        // re-set current map
        MapServer server = getServer();
        setServer(server, false);
    }

    /**
     * Called just before the current map server is set to the given server
     * 
     * @param server the new map server
     */
    protected void onChangingServer(MapServer server) {
        // override me
    }

    /**
     * @see JXMapKit#setZoom(int)
     */
    @Override
    public void setZoom(int zoom) {
        zoom = Math.min(zoom, getMainMap().getTileFactory().getTileProvider().getMaximumZoom());
        super.setZoom(zoom);

        if (zoom + 4 > getMainMap().getTileFactory().getTileProvider().getMaximumZoom())
            setMiniMapVisible(false);
        else
            setMiniMapVisible(true);
    }

    /**
     * Refresh the map
     */
    public void refresh() {
        getMainMap().repaint();
    }

    /**
     * Zoom in and center on the given {@link GeoPosition}
     * 
     * @param pos the {@link GeoPosition}
     */
    public void zoomInToPosition(GeoPosition pos) {
        setZoom(getMainMap().getTileFactory().getTileProvider().getMinimumZoom());
        setCenterPosition(pos);
    }

    private static Set<GeoPosition> equalizeEpsg(Collection<GeoPosition> positions) {
        if (positions.isEmpty())
            return new HashSet<GeoPosition>(positions);

        int epsg = -1;

        Set<GeoPosition> result = new HashSet<GeoPosition>();

        for (GeoPosition pos : positions) {
            if (epsg == -1) {
                epsg = pos.getEpsgCode();
                result.add(pos);
            } else if (epsg != pos.getEpsgCode()) {
                GeoPosition altPos;
                try {
                    altPos = GeotoolsConverter.getInstance().convert(pos, epsg);
                    result.add(altPos);
                } catch (IllegalGeoPositionException e) {
                    log.warn("Error converting GeoPosition, ignoring this position"); //$NON-NLS-1$
                }
            } else {
                result.add(pos);
            }
        }

        return result;
    }

    /**
     * Center on the given {@link GeoPosition}s
     * 
     * @param positions the {@link GeoPosition}s
     */
    public void centerOnPositions(Set<GeoPosition> positions) {
        if (positions.size() == 0)
            return;

        positions = equalizeEpsg(positions);

        double minX = 0, maxX = 0, minY = 0, maxY = 0;

        boolean init = false;
        int epsg = positions.iterator().next().getEpsgCode();

        for (GeoPosition pos : positions) {
            if (!init) {
                // first pos
                minY = maxY = pos.getY();
                minX = maxX = pos.getX();

                init = true;
            } else {
                if (pos.getY() < minY)
                    minY = pos.getY();
                else if (pos.getY() > maxY)
                    maxY = pos.getY();

                if (pos.getX() < minX)
                    minX = pos.getX();
                else if (pos.getX() > maxX)
                    maxX = pos.getX();
            }
        }

        setCenterPosition(new GeoPosition((minX + maxX) / 2.0, (minY + maxY) / 2.0, epsg));
    }

    private Rectangle2D generateBoundingRect(double minX, double minY, double maxX, double maxY, int epsg, int zoom)
            throws IllegalGeoPositionException {
        java.awt.geom.Point2D p1 = getMainMap().getTileFactory().getTileProvider().getConverter()
                .geoToPixel(new GeoPosition(minX, minY, epsg), zoom);
        java.awt.geom.Point2D p2 = getMainMap().getTileFactory().getTileProvider().getConverter()
                .geoToPixel(new GeoPosition(maxX, maxY, epsg), zoom);

        return new Rectangle2D.Double((p1.getX() < p2.getX()) ? (p1.getX()) : (p2.getX()),
                (p1.getY() < p2.getY()) ? (p1.getY()) : (p2.getY()), Math.abs(p2.getX() - p1.getX()),
                Math.abs(p2.getY() - p1.getY()));
    }

    /**
     * Zoom in and center on the given {@link GeoPosition}s
     * 
     * @param positions the {@link GeoPosition}s
     */
    public void zoomToPositions(Set<GeoPosition> positions) {
        if (positions.size() == 0)
            return;

        positions = equalizeEpsg(positions);
        int epsg = positions.iterator().next().getEpsgCode();

        double minX = 0, maxX = 0, minY = 0, maxY = 0;

        boolean init = false;

        for (GeoPosition pos : positions) {
            if (!init) {
                // first pos
                minY = maxY = pos.getY();
                minX = maxX = pos.getX();

                init = true;
            } else {
                if (pos.getY() < minY)
                    minY = pos.getY();
                else if (pos.getY() > maxY)
                    maxY = pos.getY();

                if (pos.getX() < minX)
                    minX = pos.getX();
                else if (pos.getX() > maxX)
                    maxX = pos.getX();
            }
        }

        // center on positions
        setCenterPosition(new GeoPosition((minX + maxX) / 2.0, (minY + maxY) / 2.0, epsg));

        // initial zoom
        int zoom = getMainMap().getTileFactory().getTileProvider().getMinimumZoom();

        try {
            if (positions.size() >= 2) {
                int viewWidth = (int) getMainMap().getViewportBounds().getWidth();
                int viewHeight = (int) getMainMap().getViewportBounds().getHeight();

                Rectangle2D rect = generateBoundingRect(minX, minY, maxX, maxY, epsg, zoom);

                while ((viewWidth < rect.getWidth() || viewHeight < rect.getHeight())
                        && zoom < getMainMap().getTileFactory().getTileProvider().getMaximumZoom()) {
                    zoom++;
                    rect = generateBoundingRect(minX, minY, maxX, maxY, epsg, zoom);
                }
            }

            setZoom(zoom);
        } catch (IllegalGeoPositionException e) {
            log.warn("Error zooming to positions");// , e); //$NON-NLS-1$
        }
    }

    /**
     * @return the server
     */
    public MapServer getServer() {
        return server;
    }

    /**
     * @param synced true if synced with the 3D map camera, false otherwise
     */
    public void setSynced(boolean synced) {
        this.synced = synced;
    }

    /**
     * @return true if synced with the 3D map camera, false otherwise
     */
    public boolean isSynced() {
        return this.synced;
    }

    /*
     * protected void loadState(GuiState state) { int oldEpsg =
     * state.getInteger(BasicMapKit.class, LAST_EPSG, DEF_EPSG);
     * 
     * GeoPosition p1 = new GeoPosition(state.getDouble(BasicMapKit.class,
     * SHOW_MIN_X, DEF_MIN_X), state.getDouble(BasicMapKit.class, SHOW_MIN_Y,
     * DEF_MIN_Y), oldEpsg); GeoPosition p2 = new
     * GeoPosition(state.getDouble(BasicMapKit.class, SHOW_MAX_X, DEF_MAX_X),
     * state.getDouble(BasicMapKit.class, SHOW_MAX_Y, DEF_MAX_Y), oldEpsg);
     * 
     * Set<GeoPosition> positions = new HashSet<GeoPosition>();
     * positions.add(p1); positions.add(p2); zoomToPositions(positions); }
     */

    /*
     * (non-Javadoc)
     * 
     * @see
     * de.fhg.igd.mutable.gui.StateProvider#onStateSave(de.fhg.igd.mutable.gui.
     * GuiState)
     */
    /*
     * @Override public void onStateSave(GuiState state) { GeoPosition min =
     * getMainMap().convertPointToGeoPosition(new Point((int)
     * Math.round(getMainMap().getWidth() * 0.1), (int)
     * Math.round(getMainMap().getHeight() * 0.1))); GeoPosition max =
     * getMainMap().convertPointToGeoPosition(new Point((int)
     * Math.round(getMainMap().getWidth() * 0.9), (int)
     * Math.round(getMainMap().getHeight() * 0.9)));
     * 
     * //GeoPosition min = getMainMap().convertPointToGeoPosition(new Point(0,
     * 0)); //GeoPosition max = getMainMap().convertPointToGeoPosition(new
     * Point(getMainMap().getWidth(), getMainMap().getHeight()));
     * 
     * int epsg = min.getEpsgCode();
     * 
     * try { if (epsg != max.getEpsgCode()) max =
     * EpsgGeoConverter.INSTANCE.convert(max, epsg);
     * 
     * state.setDouble(BasicMapKit.class, SHOW_MIN_Y, min.getY());
     * state.setDouble(BasicMapKit.class, SHOW_MIN_X, min.getX());
     * state.setDouble(BasicMapKit.class, SHOW_MAX_Y, max.getY());
     * state.setDouble(BasicMapKit.class, SHOW_MAX_X, max.getX());
     * 
     * state.setInteger(BasicMapKit.class, LAST_EPSG, epsg); } catch
     * (IllegalGeoPositionException e) { log.error(
     * "Error saving map state: Could not convert GeoPosition", e); } }
     */

    /*
     * @Override public void guiClosing() { // do nothing }
     */

    /*
     * @Override public void guiStarted() { loadState(GuiState.getInstance()); }
     */

}