Package org.geotools.swing

Source Code of org.geotools.swing.AbstractMapPane

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2011, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library 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
*    Lesser General Public License for more details.
*/

package org.geotools.swing;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyBoundsAdapter;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;

import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapContent;
import org.geotools.map.Layer;
import org.geotools.map.MapViewport;
import org.geotools.map.event.MapBoundsEvent;
import org.geotools.map.event.MapBoundsListener;
import org.geotools.map.event.MapLayerEvent;
import org.geotools.map.event.MapLayerListEvent;
import org.geotools.map.event.MapLayerListListener;
import org.geotools.renderer.lite.LabelCache;
import org.geotools.swing.event.DefaultMapMouseEventDispatcher;
import org.geotools.swing.event.MapMouseEventDispatcher;
import org.geotools.swing.event.MapMouseListener;
import org.geotools.swing.event.MapPaneEvent;
import org.geotools.swing.event.MapPaneKeyHandler;
import org.geotools.swing.event.MapPaneListener;
import org.geotools.swing.tool.CursorTool;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;


/**
* Base class for Swing map panes. It extends Swing's {@code JPanel} class and
* handles window sizing and repainting as well as redirecting mouse events.
* It also provides basic implementations of all interface methods. Sub-classes
* must implement {@linkplain #drawLayers(boolean)} and override
* {@linkplain JPanel#paintComponent(java.awt.Graphics)}.
*
* @author Michael Bedward
* @since 8.0
*
* @source $URL$
* @version $Id$
*/
public abstract class AbstractMapPane extends JPanel
        implements MapPane, RenderingExecutorListener, MapLayerListListener, MapBoundsListener {
   
    /**
     * Default delay (500 milliseconds) before the map will be redrawn when
     * resizing the pane or moving the displayed image. This avoids flickering
     * and redundant rendering.
     */
    public static final int DEFAULT_PAINT_DELAY = 500;

    /**
     * Default background color (white).
     */
    public static final Color DEFAULT_BACKGROUND_COLOR = Color.WHITE;

    protected final ScheduledExecutorService paneTaskExecutor;
    protected Future<?> resizedFuture;
    protected int paintDelay;
    protected final AtomicBoolean acceptRepaintRequests;

    /* Fields used for map panning */
    protected final AtomicBoolean baseImageMoved;
    protected Future<?> imageMovedFuture;
    protected final Point imageOrigin;
   
    protected final Lock drawingLock;
    protected final ReadWriteLock paramsLock;

    protected final Set<MapPaneListener> listeners = new HashSet<MapPaneListener>();
    protected final MouseDragBox dragBox;


    /*
     * If the user sets the display area before the pane is shown on
     * screen we store the requested envelope with this field and refer
     * to it when the pane is shown.
     */
    protected ReferencedEnvelope pendingDisplayArea;

    /*
     * This field is used to cache the full extent of the combined map
     * layers.
     */
    protected ReferencedEnvelope fullExtent;
   
    protected MapContent mapContent;
    protected RenderingExecutor renderingExecutor;
    protected KeyListener keyHandler;
    protected MapMouseEventDispatcher mouseEventDispatcher;

    protected LabelCache labelCache;
    protected AtomicBoolean clearLabelCache;
   
    protected CursorTool currentCursorTool;
   
   
    public AbstractMapPane(MapContent content, RenderingExecutor executor) {
        setBackground(DEFAULT_BACKGROUND_COLOR);
        setFocusable(true);

        drawingLock = new ReentrantLock();
        paramsLock = new ReentrantReadWriteLock();
       
        paneTaskExecutor = Executors.newSingleThreadScheduledExecutor();
        paintDelay = DEFAULT_PAINT_DELAY;
        acceptRepaintRequests = new AtomicBoolean(true);
        clearLabelCache = new AtomicBoolean(true);
        baseImageMoved = new AtomicBoolean();
        imageOrigin = new Point(0, 0);
       
        dragBox = new MouseDragBox(this);
        mouseEventDispatcher = new DefaultMapMouseEventDispatcher(this);

        addMouseListener(dragBox);
        addMouseMotionListener(dragBox);

        addMouseListener(mouseEventDispatcher);
        addMouseMotionListener(mouseEventDispatcher);
        addMouseWheelListener(mouseEventDispatcher);

        /*
         * Listen for mouse entered events to (re-)set the
         * current tool cursor, otherwise the cursor seems to
         * default to the standard cursor sometimes (at least
         * on OSX)
         */
        addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                super.mouseEntered(e);
                if (currentCursorTool != null) {
                    setCursor(currentCursorTool.getCursor());
                }
            }
        });

        keyHandler = new MapPaneKeyHandler(this);
        addKeyListener(keyHandler);

        /*
         * Note: we listen for both resizing events (with HierarchyBoundsListener)
         * and showing events (with HierarchyListener). Although showing
         * is often accompanied by resizing this is not reliable in Swing.
         */
        addHierarchyListener(new HierarchyListener() {
            @Override
            public void hierarchyChanged(HierarchyEvent he) {
                if ((he.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
                    if (isShowing()) {
                        onShownOrResized();
                    }
                }
            }
        });

        addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
            @Override
            public void ancestorResized(HierarchyEvent he) {
                if (isShowing()) {
                    onShownOrResized();
                }
            }
        });
       
        doSetMapContent(content);
        doSetRenderingExecutor(executor);
    }
   
    /**
     * Draws layers into one or more images which will then be displayed
     * by the map pane.
     *
     * @param recreate
     */
    protected abstract void drawLayers(boolean recreate);
   
    /**
     * Gets the rendering executor, creating a default one if
     * necessary.
     *
     * @return the rendering executor
     */
    public RenderingExecutor getRenderingExecutor() {
        if (renderingExecutor == null) {
            doSetRenderingExecutor( new DefaultRenderingExecutor() );
        }
        return renderingExecutor;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public MapMouseEventDispatcher getMouseEventDispatcher() {
        return mouseEventDispatcher;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public void setMouseEventDispatcher(MapMouseEventDispatcher dispatcher) {
        if (mouseEventDispatcher != null) {
            mouseEventDispatcher.removeAllListeners();
        }
       
        mouseEventDispatcher = dispatcher;
    }
   
    /**
     * Sets the rendering executor. If {@code executor} is {@code null},
     * the default {@linkplain DefaultRenderingExecutor} will be set on
     * the next call to {@linkplain #getRenderingExecutor()}.
     *
     * @param newExecutor the rendering executor
     */
    public void setRenderingExecutor(RenderingExecutor executor) {
        doSetRenderingExecutor(executor);
    }
   
    private void doSetRenderingExecutor(RenderingExecutor newExecutor) {
        if (renderingExecutor != null) {
            renderingExecutor.shutdown();
        }
       
        renderingExecutor = newExecutor;
    }
   
    /**
     * Gets the current handler for keyboard actions.
     *
     * @return current handler (may be {@code null})
     */
    public KeyListener getKeyHandler() {
        return keyHandler;
    }
   
    /**
     * Sets a handler for keyboard actions which control the map pane's
     * display. The default handler is {@linkplain MapPaneKeyHandler} which
     * provides for scrolling and zooming.
     *
     * @param controller the new handler or {@code null} to disable key handling
     */
    public void setKeyHandler(KeyListener controller) {
        if (keyHandler != null) {
            removeKeyListener(keyHandler);
        }
       
        if (controller != null) {
            addKeyListener(controller);
        }
       
        keyHandler = controller;
    }
   
    /**
     * Gets the current paint delay interval in milliseconds. The map pane
     * uses this delay period to avoid flickering and redundant rendering
     * when drag-resizing the pane or panning the map image.
     *
     * @return delay in milliseconds
     */
    public long getPaintDelay() {
        paramsLock.readLock().lock();
        try {
            return paintDelay;
           
        } finally {
            paramsLock.readLock().unlock();
        }
    }

    /**
     * Sets the current paint delay interval in milliseconds. The map pane
     * uses this delay period to avoid flickering and redundant rendering
     * when drag-resizing the pane or panning the map image.
     *
     * @param delay the delay in milliseconds; if {@code <=} 0 the default delay
     *        period will be set
     */
    public void setPaintDelay(int delay) {
        paramsLock.writeLock().lock();
        try {
            if (delay < 0) {
                paintDelay = DEFAULT_PAINT_DELAY;
            } else {
                paintDelay = delay;
            }

        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * Specify whether the map pane should defer its normal
     * repainting behaviour.
     * <p>
     * Typical use:
     * <pre>{@code
     * myMapPane.setRepaint(false);
     *
     * // do various things that would cause time-consuming
     * // re-paints normally
     *
     * myMapPane.setRepaint(true);
     * myMapPane.repaint();
     * }</pre>
     *
     * @param repaint if true, paint requests will be handled normally;
     * if false, paint requests will be deferred.
     *
     * @see #isAcceptingRepaints()
     */
    @Override
    public void setIgnoreRepaint(boolean ignoreRepaint) {
        drawingLock.lock();
        try {
            super.setIgnoreRepaint(ignoreRepaint);
            acceptRepaintRequests.set( !ignoreRepaint );
           
        } finally {
            drawingLock.unlock();
        }
    }
   
    /**
     * Query whether the map pane is currently accepting or ignoring
     * repaint requests from other GUI components and the system.
     *
     * @return true if the pane is currently accepting repaint requests;
     *         false if it is ignoring them
     *
     * @see #setRepaint(boolean)
     */
    public boolean isAcceptingRepaints() {
        return acceptRepaintRequests.get();
    }

    protected void onShownOrResized() {
        if (resizedFuture != null && !resizedFuture.isDone()) {
            resizedFuture.cancel(true);
        }
       
        resizedFuture = paneTaskExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                setForNewSize();
               
                // Call repaint here rather than within setForNewSize so that
                // drawingLock will be available in paintComponent
                repaint();
            }
        }, paintDelay, TimeUnit.MILLISECONDS);
    }
   
    protected void setForNewSize() {
        drawingLock.lock();
        try {
            if (mapContent != null) {

                /*
                 * Compare the new pane screen size to the viewport's screen area
                 * and skip further action if the two rectangles are equal. This
                 * check avoid extra rendering requests when redundant resize events
                 * are received (e.g. on mouse button release after drag resizing).
                 */
                if (mapContent.getViewport().getScreenArea().equals(getVisibleRect())) {
                    return;
                }

                mapContent.getViewport().setScreenArea(getVisibleRect());

                if (pendingDisplayArea != null) {
                    doSetDisplayArea(pendingDisplayArea);
                    pendingDisplayArea = null;

                } else if (mapContent.getViewport().getBounds().isEmpty()) {
                    setFullExtent();
                    doSetDisplayArea(fullExtent);
                }

                publishEvent(new MapPaneEvent(this,
                        MapPaneEvent.Type.DISPLAY_AREA_CHANGED,
                        getDisplayArea()));

                acceptRepaintRequests.set(true);
                drawLayers(true);
            }
           
        } finally {
            drawingLock.unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void moveImage(int dx, int dy) {
        drawingLock.lock();
        try {
            if (isShowing() && !getVisibleRect().isEmpty()) {
                imageOrigin.translate(dx, dy);
                baseImageMoved.set(true);
                repaint();
                onImageMoved();
            }

        } finally {
            drawingLock.unlock();
        }
    }

    protected void onImageMoved() {
        if (imageMovedFuture != null && !imageMovedFuture.isDone()) {
            imageMovedFuture.cancel(true);
        }

        imageMovedFuture = paneTaskExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                afterImageMoved();
                clearLabelCache.set(true);
                drawLayers(false);
                repaint();
            }
        }, paintDelay, TimeUnit.MILLISECONDS);
    }

    /**
     * Called after the base image has been dragged. Sets the new map area and
     * transforms
     */
    protected void afterImageMoved() {
        paramsLock.writeLock().lock();
        try {
            int dx = imageOrigin.x;
            int dy = imageOrigin.y;
            DirectPosition2D newPos = new DirectPosition2D(dx, dy);
            mapContent.getViewport().getScreenToWorld().transform(newPos, newPos);

            ReferencedEnvelope env = new ReferencedEnvelope(mapContent.getViewport().getBounds());
            env.translate(env.getMinimum(0) - newPos.x, env.getMaximum(1) - newPos.y);
            doSetDisplayArea(env);

            imageOrigin.setLocation(0, 0);
            baseImageMoved.set(false);

        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MapContent getMapContent() {
        paramsLock.readLock().lock();
        try {
            return mapContent;
           
        } finally {
            paramsLock.readLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setMapContent(MapContent content) {
        paramsLock.writeLock().lock();
        try {
            doSetMapContent(content);
           
        } finally {
            paramsLock.writeLock().unlock();
        }
    }
   
    private void doSetMapContent(MapContent newMapContent) {
        if (mapContent != newMapContent) {

            if (mapContent != null) {
                mapContent.removeMapLayerListListener(this);
                for( Layer layer : mapContent.layers() ){
                    if( layer instanceof ComponentListener){
                        removeComponentListener( (ComponentListener) layer );
                    }
                }
            }

            mapContent = newMapContent;

            if (mapContent != null) {
                MapViewport viewport = mapContent.getViewport();
                viewport.setMatchingAspectRatio(true);
                Rectangle rect = getVisibleRect();
                if (!rect.isEmpty()) {
                    viewport.setScreenArea(rect);
                }
               
                mapContent.addMapLayerListListener(this);
                mapContent.addMapBoundsListener(this);

                if (!mapContent.layers().isEmpty()) {
                    // set all layers as selected by default for the info tool
                    for (Layer layer : mapContent.layers()) {
                        layer.setSelected(true);

                        if (layer instanceof ComponentListener) {
                            addComponentListener((ComponentListener) layer);
                        }
                    }

                    setFullExtent();
                    doSetDisplayArea(mapContent.getViewport().getBounds());
                }
            }

            MapPaneEvent event = new MapPaneEvent(
                    this, MapPaneEvent.Type.NEW_MAPCONTENT, mapContent);
            publishEvent(event);
           
            drawLayers(false);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ReferencedEnvelope getDisplayArea() {
        paramsLock.readLock().lock();
        try {
            if (mapContent != null) {
                return mapContent.getViewport().getBounds();
            } else if (pendingDisplayArea != null) {
                return new ReferencedEnvelope(pendingDisplayArea);
            } else {
                return new ReferencedEnvelope();
            }

        } finally {
            paramsLock.readLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setDisplayArea(Envelope envelope) {
        paramsLock.writeLock().lock();
        try {
            if (envelope == null) {
                throw new IllegalArgumentException("envelope must not be null");
            }

            doSetDisplayArea(envelope);
            if (mapContent != null) {
                clearLabelCache.set(true);
                drawLayers(false);
            }

        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * Helper method for {@linkplain #setDisplayArea} which is also called by
     * other methods that want to set the display area without provoking
     * repainting of the display
     *
     * @param envelope requested display area
     */
    protected void doSetDisplayArea(Envelope envelope) {
        if (mapContent != null) {
            CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
            if (crs == null) {
                // assume that it is the current CRS
                crs = mapContent.getCoordinateReferenceSystem();
            }

            ReferencedEnvelope refEnv = new ReferencedEnvelope(
                    envelope.getMinimum(0), envelope.getMaximum(0),
                    envelope.getMinimum(1), envelope.getMaximum(1),
                    crs);

            mapContent.getViewport().setBounds(refEnv);

        } else {
            pendingDisplayArea = new ReferencedEnvelope(envelope);
        }

        // Publish the resulting display area with the event
        publishEvent( new MapPaneEvent(this,
                MapPaneEvent.Type.DISPLAY_AREA_CHANGED,
                getDisplayArea()) );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void reset() {
        paramsLock.writeLock().lock();
        try {
            if (fullExtent != null) {
                setDisplayArea(fullExtent);
            }
           
        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AffineTransform getScreenToWorldTransform() {
        paramsLock.readLock().lock();
        try {
            if (mapContent != null) {
                return mapContent.getViewport().getScreenToWorld();
            } else {
                return null;
            }

        } finally {
            paramsLock.readLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AffineTransform getWorldToScreenTransform() {
        paramsLock.readLock().lock();
        try {
            if (mapContent != null) {
                return mapContent.getViewport().getWorldToScreen();
            } else {
                return null;
            }

        } finally {
            paramsLock.readLock().unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addMapPaneListener(MapPaneListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        listeners.add(listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeMapPaneListener(MapPaneListener listener) {
        if (listener != null) {
            listeners.remove(listener);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addMouseListener(MapMouseListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        mouseEventDispatcher.addMouseListener(listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeMouseListener(MapMouseListener listener) {
        if (listener != null) {
            mouseEventDispatcher.removeMouseListener(listener);
        }
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public CursorTool getCursorTool() {
        return currentCursorTool;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public void setCursorTool(CursorTool tool) {
        paramsLock.writeLock().lock();
        try {
            if (currentCursorTool != null) {
                mouseEventDispatcher.removeMouseListener(currentCursorTool);
            }

            currentCursorTool = tool;

            if (currentCursorTool == null) {
                setCursor(Cursor.getDefaultCursor());
                dragBox.setEnabled(false);

            } else {
                setCursor(currentCursorTool.getCursor());
                dragBox.setEnabled(currentCursorTool.drawDragBox());
                currentCursorTool.setMapPane(this);
                mouseEventDispatcher.addMouseListener(currentCursorTool);
            }

        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * Called when a new map layer has been added. Sets the layer
     * as selected (for queries) and, if the layer table is being
     * used, adds the new layer to the table.
     */
    @Override
    public void layerAdded(MapLayerListEvent event) {
        paramsLock.writeLock().lock();
        try {
            Layer layer = event.getElement();

            if (layer instanceof ComponentListener) {
                addComponentListener((ComponentListener) layer);
            }

            setFullExtent();
            MapViewport viewport = mapContent.getViewport();
            if (viewport.getBounds().isEmpty()) {
                viewport.setBounds(fullExtent);
            }
           
        } finally {
            paramsLock.writeLock().unlock();
        }
       
        drawLayers(false);
        repaint();
    }

    /**
     * Called when a map layer has been removed
     */
    @Override
    public void layerRemoved(MapLayerListEvent event) {
        paramsLock.writeLock().lock();
        try {
            Layer layer = event.getElement();

            if (layer instanceof ComponentListener) {
                removeComponentListener((ComponentListener) layer);
            }

            if (mapContent.layers().isEmpty()) {
                fullExtent = null;
            } else {
                setFullExtent();
            }
           
        } finally {
            paramsLock.writeLock().unlock();
        }

        drawLayers(false);
        repaint();
    }

    /**
     * Called when a map layer has changed, e.g. features added
     * to a displayed feature collection
     */
    @Override
    public void layerChanged(MapLayerListEvent event) {
        paramsLock.writeLock().lock();
        try {
            int reason = event.getMapLayerEvent().getReason();

            if (reason == MapLayerEvent.DATA_CHANGED) {
                setFullExtent();
            }

            if (reason != MapLayerEvent.SELECTION_CHANGED) {
                clearLabelCache.set(true);
                drawLayers(false);
            }
           
        } finally {
            paramsLock.writeLock().unlock();
        }

        repaint();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void layerMoved(MapLayerListEvent event) {
        drawLayers(false);
        repaint();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void layerPreDispose(MapLayerListEvent event) {
        getRenderingExecutor().cancelAll();
    }

    /**
     * Called by the map content's viewport when its bounds have changed. Used
     * here to watch for a changed CRS, in which case the map is re-displayed
     * at full extent.
     */
    @Override
    public void mapBoundsChanged(MapBoundsEvent event) {
        paramsLock.writeLock().lock();
        try {
            int type = event.getType();
            if ((type & MapBoundsEvent.COORDINATE_SYSTEM_MASK) != 0) {
                /*
                 * The coordinate reference system has changed. Set the map
                 * to display the full extent of layer bounds to avoid the
                 * effect of a shrinking map
                 */
                setFullExtent();
                reset();
            }
           
        } finally {
            paramsLock.writeLock().unlock();
        }
    }

    /**
     * Publish a MapPaneEvent to registered listeners
     *
     * @param ev the event to publish
     * @see MapPaneListener
     */
    protected void publishEvent(MapPaneEvent ev) {
        for (MapPaneListener listener : listeners) {
            switch (ev.getType()) {
                case NEW_MAPCONTENT:
                    listener.onNewMapContent(ev);
                    break;

                case DISPLAY_AREA_CHANGED:
                    listener.onDisplayAreaChanged(ev);
                    break;

                case RENDERING_STARTED:
                    listener.onRenderingStarted(ev);
                    break;

                case RENDERING_STOPPED:
                    listener.onRenderingStopped(ev);
                    break;
            }
        }
    }

    /**
     * Determines the full extent of of
     *
     * @return {@code true} if full extent was set successfully
     */
    protected boolean setFullExtent() {
        if (mapContent != null && !mapContent.layers().isEmpty()) {
            try {
                fullExtent = mapContent.getMaxBounds();

                /*
                 * Guard against degenerate envelopes (e.g. empty
                 * map layer or single point feature)
                 */
                if (fullExtent == null ) {
                    // set arbitrary bounds centred on 0,0
                    fullExtent = new ReferencedEnvelope(-1, 1, -1, 1, mapContent.getCoordinateReferenceSystem());

                } else {
                    double w = fullExtent.getWidth();
                    double h = fullExtent.getHeight();
                    double x = fullExtent.getMinimum(0);
                    double y = fullExtent.getMinimum(1);

                    double xmin = x;
                    double xmax = x + w;
                    if (w <= 0.0) {
                        xmin = x - 1.0;
                        xmax = x + 1.0;
                    }

                    double ymin = y;
                    double ymax = y + h;
                    if (h <= 0.0) {
                        ymin = y - 1.0;
                        ymax = y + 1.0;
                    }

                    fullExtent = new ReferencedEnvelope(xmin, xmax, ymin, ymax, mapContent.getCoordinateReferenceSystem());
                }

            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        } else {
            fullExtent = null;
        }
       
        return fullExtent != null;
    }

    /**
     * {@inheritDoc}
     * Publishes a {@linkplain MapPaneEvent} of type
     * {@code MapPaneEvent.Type.RENDERING_STARTED} to listeners.
     */
    @Override
    public void onRenderingStarted(RenderingExecutorEvent ev) {
        publishEvent(new MapPaneEvent(this, MapPaneEvent.Type.RENDERING_STARTED));
    }
   

    /**
     * {@inheritDoc}
     * Publishes a {@linkplain MapPaneEvent} of type
     * {@code MapPaneEvent.Type.RENDERING_STOPPED} to listeners.
     */
    @Override
    public void onRenderingCompleted(RenderingExecutorEvent event) {
        if (clearLabelCache.get()) {
            labelCache.clear();
        }

        clearLabelCache.set(false);
        repaint();
        publishEvent(new MapPaneEvent(this, MapPaneEvent.Type.RENDERING_STOPPED));
    }

    /**
     * {@inheritDoc}
     * Publishes a {@linkplain MapPaneEvent} of type
     * {@code MapPaneEvent.Type.RENDERING_STOPPED} to listeners.
     */
    @Override
    public void onRenderingFailed(RenderingExecutorEvent ev) {
        publishEvent(new MapPaneEvent(this, MapPaneEvent.Type.RENDERING_STOPPED));
    }

}
TOP

Related Classes of org.geotools.swing.AbstractMapPane

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.