[Schmitzm-commits] r531 - in branches/1.0-gt2-2.6/src: gtmig/org/geotools/swing schmitzm/geotools/gui schmitzm/geotools/map/event schmitzm/geotools/styling skrueger/geotools

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Nov 19 10:43:12 CET 2009


Author: alfonx
Date: 2009-11-19 10:42:59 +0100 (Thu, 19 Nov 2009)
New Revision: 531

Added:
   branches/1.0-gt2-2.6/src/skrueger/geotools/GeomFilterGenerator.java
   branches/1.0-gt2-2.6/src/skrueger/geotools/MouseSelectionTracker.java
   branches/1.0-gt2-2.6/src/skrueger/geotools/SelectXMapPaneMouseListener.java
   branches/1.0-gt2-2.6/src/skrueger/geotools/XMapPane.java
   branches/1.0-gt2-2.6/src/skrueger/geotools/ZoomXMapPaneMouseListener.java
Removed:
   branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java
   branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java
   branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java
   branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java
   branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java
Modified:
   branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/FeatureTablePane.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/MouseSelectionTracker.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/SelectableXMapPane.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/FeatureSelectedEvent.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GeneralSelectionEvent.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GridCoverageSelectedEvent.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/MapPaneEvent.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/ObjectSelectionEvent.java
   branches/1.0-gt2-2.6/src/schmitzm/geotools/styling/StylingUtil.java
   branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
Log:
Moved XMapPane from gtmig to skrueger.geotools

Deleted: branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,249 +0,0 @@
-package gtmig.org.geotools.swing;
-
-import java.awt.geom.Point2D;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.geotools.data.FeatureSource;
-import org.geotools.filter.AbstractFilter;
-import org.geotools.filter.GeometryFilterImpl;
-import org.geotools.filter.spatial.DWithinImpl;
-import org.geotools.referencing.CRS;
-import org.geotools.resources.CRSUtilities;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.feature.type.GeometryDescriptor;
-import org.opengis.filter.expression.Expression;
-import org.opengis.filter.spatial.BinarySpatialOperator;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-
-import schmitzm.geotools.FilterUtil;
-import schmitzm.geotools.JTSUtil;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-/**
- * The {@link GeomFilterGenerator} prepares a {@link BinarySpatialOperator}
- * filter for multiple use. Only the "right" argument is prepared. The
- * "left" argument (the geometry attribute of the {@link FeatureSource} to
- * filter) is first set on calling {@link #adaptFilter(FeatureSource)} for a
- * specific {@link FeatureSource}. This method also takes care to recreate
- * the filter (or its "right" argument) if the given {@link FeatureSource}
- * has another {@link CoordinateReferenceSystem} than the base constraint.<br>
- * The type of filter (e.g. distance or bounding box) is specified by the
- * subclass implemenation of
- * {@link #prepareFilter(CoordinateReferenceSystem)} .
- * 
- * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
- */
- public abstract class GeomFilterGenerator {
-	/**
-	 * Holds the {@link CoordinateReferenceSystem} the filter constraint
-	 * bases on.
-	 */
-	protected CoordinateReferenceSystem baseCRS = null;
-	/**
-	 * Caches the base filter constraint for several
-	 * {@link CoordinateReferenceSystem CoordinateReferenceSystems}.
-	 */
-	protected Map<CoordinateReferenceSystem, GeometryFilterImpl> filterCache = new HashMap<CoordinateReferenceSystem, GeometryFilterImpl>();
-
-	/**
-	 * Creates a new filter generator
-	 * 
-	 * @param crs
-	 *            {@link CoordinateReferenceSystem} the base constraint
-	 *            ("right" filter argument is relative to)
-	 */
-	public GeomFilterGenerator(CoordinateReferenceSystem crs) {
-		this.baseCRS = crs;
-	}
-
-	/**
-	 * Creates a filter containing the base constraint ("right" argument)
-	 * transformed to the given {@link CoordinateReferenceSystem}.
-	 * 
-	 * @param crs
-	 *            the {@link CoordinateReferenceSystem} the base constraint
-	 *            is transformed to
-	 */
-	protected abstract GeometryFilterImpl prepareFilter(
-			CoordinateReferenceSystem crs);
-
-	/**
-	 * Completes the filter with its "left" argument for a concrete
-	 * {@link FeatureSource}. If the {@link FeatureSource FeatureSource's}
-	 * CRS differs from the CRS the base constraint is specified in, first a
-	 * new filter is created by calling
-	 * {@link #prepareFilter(CoordinateReferenceSystem)}.
-	 * 
-	 * @param fs
-	 *            {@link FeatureSource} the filter is adaped to
-	 * @return
-	 */
-	public GeometryFilterImpl adaptFilter(
-			FeatureSource<SimpleFeatureType, SimpleFeature> fs) {
-		GeometryDescriptor geomDescr = fs.getSchema()
-				.getGeometryDescriptor();
-		CoordinateReferenceSystem fsCRS = geomDescr
-				.getCoordinateReferenceSystem();
-		GeometryFilterImpl filter = filterCache.get(fsCRS);
-		if (filter == null) {
-			filter = prepareFilter(fsCRS);
-			filterCache.put(fsCRS, filter);
-		}
-		Expression geometry = FilterUtil.FILTER_FAC2.property(geomDescr.getLocalName());
-		filter.setExpression1(geometry);
-		return filter;
-	}
-
-	/**
-	 * {@link GeomFilterGenerator} for a bounding box constraint.
-	 * 
-	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
-	 */
-	public static class BoundingBoxFilterGenerator extends GeomFilterGenerator {
-		/**
-		 * Holds the base constraint (bounding box {@link Envelope}) relative to
-		 * the {@linkplain GeomFilterGenerator#baseCRS base CRS}.
-		 */
-		protected Envelope baseEnv = null;
-
-		/**
-		 * Creates a new filter generator.
-		 * 
-		 * @param baseEnv
-		 *            defines the bounding box
-		 * @param baseCrs
-		 *            defines the CRS of the bounding box
-		 */
-		public BoundingBoxFilterGenerator(Envelope baseEnv,
-				CoordinateReferenceSystem baseCrs) {
-			super(baseCrs);
-			this.baseEnv = baseEnv;
-		}
-
-		/**
-		 * Prepares a filter with the bounding box transformed to the given
-		 * {@link CoordinateReferenceSystem} as the "right" argument.
-		 * 
-		 * @param crs
-		 *            the {@link CoordinateReferenceSystem} the bounding box is
-		 *            transformed to
-		 */
-		protected GeometryFilterImpl prepareFilter(CoordinateReferenceSystem crs) {
-			Envelope bbEnv = baseEnv;
-			if (!CRS.equalsIgnoreMetadata(baseCRS,crs))
-				bbEnv = JTSUtil.transformEnvelope(baseEnv, baseCRS, crs);
-			// Filter fuer Envelope zusammenstellen
-			Expression bbox = FilterUtil.FILTER_FAC.createBBoxExpression(bbEnv);
-			GeometryFilterImpl bboxFilter = (GeometryFilterImpl) FilterUtil.FILTER_FAC
-					.createGeometryFilter(AbstractFilter.GEOMETRY_BBOX);
-			// GeometryFilterImpl bboxFilter =
-			// (GeometryFilterImpl)ff.createGeometryFilter(AbstractFilter.
-			// GEOMETRY_WITHIN);
-			bboxFilter.setExpression2(bbox);
-			return bboxFilter;
-		}
-	}
-
-	/**
-	 * {@link GeomFilterGenerator} for a "near distance" constraint.
-	 * 
-	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
-	 */
-	public static class NearPointFilterGenerator extends GeomFilterGenerator {
-		/**
-		 * Holds the base constraint (coordinate) relative to the
-		 * {@linkplain GeomFilterGenerator#baseCRS base CRS}.
-		 */
-		protected Coordinate basePoint = null;
-		/**
-		 * Holds the distance "around" the base point relative to the
-		 * {@linkplain GeomFilterGenerator#baseCRS base CRS}.
-		 */
-		protected double baseDist = 0.0;
-		/**
-		 * Holds a point which is in distance {@link #baseDist} to the
-		 * {@link #basePoint}.
-		 */
-		protected Coordinate basePoint2 = null;
-
-		/**
-		 * Creates a new filter generator.
-		 * 
-		 * @param basePoint
-		 *            defNearPointFilterGeneratorines the point for the "near point" constraint
-		 * @param dist
-		 *            defines the distance around the base point
-		 * @param crs
-		 *            defines the CRS of base point
-		 */
-		public NearPointFilterGenerator(Coordinate basePoint, double dist,
-				CoordinateReferenceSystem crs) {
-			super(crs);
-			this.basePoint = basePoint;
-			this.baseDist = dist;
-			// Create a point which is in distance "dist" to the base point
-			this.basePoint2 = new Coordinate(basePoint.x + dist, basePoint.y);
-		}
-
-		/**
-		 * Creates a new filter generator.
-		 * 
-		 * @param basePoint
-		 *            defines the point for the "near point" constraint
-		 * @param dist
-		 *            defines the distance around the base point
-		 * @param crs
-		 *            defines the CRS of base point
-		 */
-		public NearPointFilterGenerator(Point2D basePoint, double dist,
-				CoordinateReferenceSystem crs) {
-			this(new Coordinate(basePoint.getX(), basePoint.getY()), dist, crs);
-		}
-
-		/**
-		 * Prepares a filter with the base point and distance transformed to the
-		 * given {@link CoordinateReferenceSystem} as the "right" argument.
-		 * 
-		 * @param crs
-		 *            the {@link CoordinateReferenceSystem} the point and
-		 *            distance is transformed to
-		 */
-		protected GeometryFilterImpl prepareFilter(CoordinateReferenceSystem crs) {
-			Coordinate nearPoint = basePoint;
-			double nearDist = baseDist;
-			if (!baseCRS.equals(crs)) {
-				nearPoint = JTSUtil
-						.transformCoordinate(basePoint, baseCRS, crs);
-				// Transform the distance (maybe "dirty")
-				// --> transform the point2 and calculate the
-				// distance to the tranformed base point
-				Coordinate nearPoint2 = JTSUtil.transformCoordinate(basePoint2,
-						baseCRS, crs);
-
-				if (nearPoint == null || nearPoint2 == null)
-					throw new RuntimeException("Unable to transform CRS from "
-							+ baseCRS + " to " + crs);
-
-				nearDist = Math.abs(nearPoint.x - nearPoint2.x);
-			}
-			// Filter fuer Point zusammenstellen
-			final Geometry geometry = FilterUtil.GEOMETRY_FAC
-					.createPoint(nearPoint);
-
-			final DWithinImpl dwithinFilter = new DWithinImpl(
-					FilterUtil.FILTER_FAC2, null, null);
-			dwithinFilter.setDistance(nearDist);
-			dwithinFilter.setExpression2(FilterUtil.FILTER_FAC2
-					.literal(geometry));
-
-			return dwithinFilter;
-		}
-
-	}
-
-}
\ No newline at end of file

Deleted: branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,377 +0,0 @@
-// Migration process to Geotools 2.6:
-//    Because not included in gt2-2.6-M2 this class is taken 1:1
-//    from Geotools 2.4.5 and made public.
-//    TODO: It should be removed or included in SCHMITZM.
-
-/*
- *    GeoTools - OpenSource mapping toolkit
- *    http://geotools.org
- *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
- *    (C) 2001, Institut de Recherche pour le D�veloppement
- *
- *    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; either
- *    version 2.1 of the License, or (at your option) any later version.
- *
- *    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 gtmig.org.geotools.swing;
-
-// Geometry
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.event.MouseEvent;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.Line2D;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RectangularShape;
-import java.awt.geom.RoundRectangle2D;
-
-import javax.swing.event.MouseInputAdapter;
-
-
-/**
- * Controller which allows the user to select a region of a component. The user must click on a
- * point in the component, then drag the mouse pointer whilst keeping the button pressed. During
- * the dragging, the shape which is drawn will normally be a rectangle.  Other shapes could always
- * be used such as, for example, an ellipse. To use this class, it is necessary to create a derived
- * class which defines the following methods:
- *
- * <ul>
- *   <li>{@link #selectionPerformed} (obligatory)</li>
- *   <li>{@link #getModel} (optional)</li>
- * </ul>
- *
- * This controller should then be registered with one, and only one, component
- * using the following syntax:
- *
- * <blockquote><pre>
- * {@link Component} component=...
- * MouseSelectionTracker control=...
- * component.addMouseListener(control);
- * </pre></blockquote>
- *
- * @since 2.0
- * @source $URL: http://svn.geotools.org/tags/2.4.5/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/MouseSelectionTracker.java $
- * @version $Id: MouseSelectionTracker.java 22482 2006-10-31 02:58:00Z desruisseaux $
- * @author Martin Desruisseaux
- */
-public abstract class MouseSelectionTracker extends MouseInputAdapter {
-    /**
-     * Stippled rectangle representing the region which the user is currently
-     * selecting.  This rectangle can be empty.  These coordinates are only
-     * significant in the period between the user pressing the mouse button
-     * and then releasing it to outline a region. Conventionally, the
-     * {@code null} value indicates that a line should be used instead of
-     * a rectangular shape.  The coordinates are always expressed in pixels.
-     */
-    private transient RectangularShape mouseSelectedArea;
-
-    /**
-     * Colour to replace during XOR drawings on a graphic.
-     * This colour is specified in {@link Graphics2D#setColor}.
-     */
-    private Color backXORColor = Color.white;
-
-    /**
-     * Colour to replace with during the XOR drawings on a graphic.
-     * This colour is specified in {@link Graphics2D#setXORMode}.
-     */
-    private Color lineXORColor = Color.black;
-
-    /**
-     * <var>x</var> coordinate of the mouse when the button is pressed.
-     */
-    private transient int ox;
-
-    /**
-     * <var>y</var> coordinate of the mouse when the button is pressed.
-     */
-    private transient int oy;
-
-    /**
-     * <var>x</var> coordinate of the mouse during the last drag.
-     */
-    private transient int px;
-
-    /**
-     * <var>y</var> coordinate of the mouse during the last drag.
-     */
-    private transient int py;
-
-    /**
-     * Indicates whether a selection is underway.
-     */
-    private transient boolean isDragging;
-
-    /**
-     * Constructs an object which will allow rectangular regions to be selected using the mouse.
-     */
-    public MouseSelectionTracker() {
-    }
-
-    /**
-     * Specifies the colours to be used for drawing the outline of a box when
-     * the user selects a region.  All {@code a} colours will be replaced
-     * by {@code b} colours and vice versa.
-     */
-    public void setXORColors(final Color a, final Color b) {
-        backXORColor = a;
-        lineXORColor = b;
-    }
-
-    /**
-     * Returns the geometric shape to use for marking the boundaries of a region.  This shape is
-     * normally a rectangle but could also be an ellipse, an arrow or even other shapes. The
-     * coordinates of the returned shape will not be taken into account. In fact, these coordinates
-     * will regularly be discarded. Only the class of the returned shape will count (for example,
-     * {@link java.awt.geom.Ellipse2D} vs {@link java.awt.geom.Rectangle2D}) and their parameters
-     * which are not linked to their position (for example, the rounding of a rectangle's
-     * corners).
-     * <p>
-     * The shape returned will normally be from a class derived from {@link RectangularShape},
-     * but could also be from the {@link Line2D} class. <strong>Any other class risks throwing a
-     * {@link ClassCastException} when executed</strong>.
-     *
-     * The default implementation always returns an object {@link Rectangle}.
-     *
-     * @param  event Mouse coordinate when the button is pressed.  This information can be used by
-     *         the derived classes which like to be informed of the position of the mouse before
-     *         chosing a geometric shape.
-     * @return Shape from the class {link RectangularShape} or {link Line2D}, or {@code null}
-     *         to indicate that we do not want to make a selection.
-     */
-    protected Shape getModel(final MouseEvent event) {
-        return new Rectangle();
-    }
-
-    /**
-     * Method which is automatically called after the user selects a region with the mouse.
-     * All coordinates passed in as parameters are expressed in pixels.
-     *
-     * @param ox <var>x</var> coordinate of the mouse when the user pressed the mouse button.
-     * @param oy <var>y</var> coordinate of the mouse when the user pressed the mouse button.
-     * @param px <var>x</var> coordinate of the mouse when the user released the mouse button.
-     * @param py <var>y</var> coordinate of the mouse when the user released the mouse button.
-     */
-    protected abstract void selectionPerformed(int ox, int oy, int px, int py);
-
-    /**
-     * Returns the geometric shape surrounding the last region to be selected by the user. An
-     * optional affine transform can be specified to convert the region selected by the user
-     * into logical coordinates. The class of the shape returned depends on the model returned by
-     * {@link #getModel}:
-     *
-     * <ul>
-     *   <li>If the model is null (which means that this {@code MouseSelectionTracker} object only
-     *       draws a line between points), the object returned will belong to the {@link Line2D}
-     *       class.</li>
-     *   <li>If the model is not null, the object returned can be from the same class (most often
-     *       {@link java.awt.geom.Rectangle2D}). There could always be situations where the object
-     *       returned is from another class, for example if the affine transform carries out a
-     *       rotation.</li>
-     * </ul>
-     *
-     * @param  transform Affine transform which converts logical coordinates into pixel coordinates.
-     *         It is usually an affine transform which is used in a {@code paint(...)} method to
-     *         draw shapes expressed in logical coordinates.
-     * @return A geometric shape enclosing the last region to be selected by the user, or
-     *         {@code null} if no selection has yet been made.
-     * @throws NoninvertibleTransformException If the affine transform can't be inverted.
-     */
-    public Shape getSelectedArea(final AffineTransform transform) throws NoninvertibleTransformException {
-        if (ox == px && oy == py) {
-            return null;
-        }
-        RectangularShape shape = mouseSelectedArea;
-        if (transform != null && !transform.isIdentity()) {
-            if (shape == null) {
-                final Point2D.Float po = new Point2D.Float(ox, oy);
-                final Point2D.Float pp = new Point2D.Float(px, py);
-                transform.inverseTransform(po, po);
-                transform.inverseTransform(pp, pp);
-                return new Line2D.Float(po, pp);
-            } else {
-                if (canReshape(shape, transform)) {
-                    final Point2D.Double point = new Point2D.Double();
-                    double xmin = Double.POSITIVE_INFINITY;
-                    double ymin = Double.POSITIVE_INFINITY;
-                    double xmax = Double.NEGATIVE_INFINITY;
-                    double ymax = Double.NEGATIVE_INFINITY;
-                    for (int i = 0; i < 4; i++) {
-                        point.x = (i&1) == 0 ? shape.getMinX() : shape.getMaxX();
-                        point.y = (i&2) == 0 ? shape.getMinY() : shape.getMaxY();
-                        transform.inverseTransform(point, point);
-                        if (point.x < xmin) xmin = point.x;
-                        if (point.x > xmax) xmax = point.x;
-                        if (point.y < ymin) ymin = point.y;
-                        if (point.y > ymax) ymax = point.y;
-                    }
-                    if (shape instanceof Rectangle) {
-                        return new Rectangle2D.Float((float) xmin,
-                                                     (float) ymin,
-                                                     (float) (xmax - xmin),
-                                                     (float) (ymax - ymin));
-                    } else {
-                        shape = (RectangularShape) shape.clone();
-                        shape.setFrame(xmin, ymin, xmax - xmin, ymax - ymin);
-                        return shape;
-                    }
-                }
-                else {
-                    return transform.createInverse().createTransformedShape(shape);
-                }
-            }
-        }
-        else {
-            return (shape != null) ? (Shape) shape.clone() : new Line2D.Float(ox, oy, px, py);
-        }
-    }
-
-    /**
-     * Indicates whether we can transform {@code shape} simply by calling its
-     * {@code shape.setFrame(...)} method rather than by using the heavy artillery
-     * that is the {@code transform.createTransformedShape(shape)} method.
-     */
-    private static boolean canReshape(final RectangularShape shape,
-                                      final AffineTransform transform) {
-        final int type=transform.getType();
-        if ((type & AffineTransform.TYPE_GENERAL_TRANSFORM) != 0) return false;
-        if ((type & AffineTransform.TYPE_MASK_ROTATION)     != 0) return false;
-        if ((type & AffineTransform.TYPE_FLIP)              != 0) {
-            if (shape instanceof Rectangle2D)      return true;
-            if (shape instanceof Ellipse2D)        return true;
-            if (shape instanceof RoundRectangle2D) return true;
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a {@link Graphics2D} object to be used for drawing in the specified component. We
-     * must not forget to call {@link Graphics2D#dispose} when the graphics object is no longer
-     * needed.
-     */
-    private Graphics2D getGraphics(final Component c) {
-        final Graphics2D graphics = (Graphics2D) c.getGraphics();
-        graphics.setXORMode(lineXORColor);
-        graphics.setColor  (backXORColor);
-        return graphics;
-    }
-
-    /**
-     * Informs this controller that the mouse button has been pressed.
-     * The default implementation retains the mouse coordinate (which will
-     * become one of the corners of the future rectangle to be drawn) 
-     * and prepares {@code this} to observe the mouse movements.
-     *
-     * @throws ClassCastException if {@link #getModel} doesn't return a shape
-     *         from the class {link RectangularShape} or {link Line2D}.
-     */
-    public void mousePressed(final MouseEvent event) throws ClassCastException {
-        if (!event.isConsumed() && (event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
-            final Component source = event.getComponent();
-            if (source != null) {
-                Shape model = getModel(event);
-                if (model != null) {
-                    isDragging = true;
-                    ox = px = event.getX();
-                    oy = py = event.getY();
-                    if (model instanceof Line2D) {
-                        model = null;
-                    }
-                    mouseSelectedArea = (RectangularShape) model;
-                    if (mouseSelectedArea != null) {
-                        mouseSelectedArea.setFrame(ox, oy, 0, 0);
-                    }
-                    source.addMouseMotionListener(this);
-                }
-                source.requestFocus();
-                event.consume();
-            }
-        }
-    }
-
-    /**
-     * Informs this controller that the mouse has been dragged.  The default
-     * implementation uses this to move a corner of the rectangle used to
-     * select the region. The other corner remains fixed at the point
-     * where the mouse was at the moment its button was pressed..
-     */
-    public void mouseDragged(final MouseEvent event) {
-        if (isDragging) {
-            final Graphics2D graphics = getGraphics(event.getComponent());
-            if (mouseSelectedArea == null) {
-                graphics.drawLine(ox, oy, px, py);
-                px = event.getX();
-                py = event.getY();
-                graphics.drawLine(ox, oy, px, py);
-            } else {
-                graphics.draw(mouseSelectedArea);
-                int xmin = this.ox;
-                int ymin = this.oy;
-                int xmax = px = event.getX();
-                int ymax = py = event.getY();
-                if (xmin > xmax) {
-                    final int xtmp = xmin;
-                    xmin = xmax; xmax = xtmp;
-                }
-                if (ymin > ymax) {
-                    final int ytmp = ymin;
-                    ymin = ymax; ymax = ytmp;
-                }
-                mouseSelectedArea.setFrame(xmin, ymin, xmax - xmin, ymax - ymin);
-                graphics.draw(mouseSelectedArea);
-            }
-            graphics.dispose();
-            event.consume();
-        }
-    }
-
-    /**
-     * Informs this controller that the mouse button has been released.
-     * The default implementation calls {@link #selectionPerformed} with
-     * the bounds of the selected region as parameters.
-     */
-    public void mouseReleased(final MouseEvent event) {
-        if (isDragging && (event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
-            isDragging = false;
-            final Component component = event.getComponent();
-            component.removeMouseMotionListener(this);
-
-            final Graphics2D graphics = getGraphics(event.getComponent());
-            if (mouseSelectedArea == null) {
-                graphics.drawLine(ox, oy, px, py);
-            } else {
-                graphics.draw(mouseSelectedArea);
-            }
-            graphics.dispose();
-            px = event.getX();
-            py = event.getY();
-            selectionPerformed(ox, oy, px, py);
-            event.consume();
-        }
-    }
-
-    /**
-     * Informs this controller that the mouse has been moved but not as a
-     * result of the user selecting a region.  The default implementation
-     * signals to the source component that {@code this} is no longer
-     * interested in being informed about mouse movements.
-     */
-    public void mouseMoved(final MouseEvent event) {
-        // Normally not necessary, but it seems that this "listener"
-        // sometimes stays in place when it shouldn't.
-        event.getComponent().removeMouseMotionListener(this);
-    }
-}

Deleted: branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,268 +0,0 @@
-package gtmig.org.geotools.swing;
-
-import gtmig.org.geotools.swing.GeomFilterGenerator.BoundingBoxFilterGenerator;
-
-import java.awt.Cursor;
-import java.awt.Point;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-import java.awt.geom.Point2D;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-
-import org.apache.log4j.Logger;
-import org.geotools.data.memory.MemoryFeatureCollection;
-import org.geotools.feature.FeatureCollection;
-import org.geotools.map.MapLayer;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-
-import schmitzm.geotools.gui.SelectableXMapPane;
-import schmitzm.geotools.map.event.FeatureSelectedEvent;
-import sun.misc.PerformanceLogger;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-import com.vividsolutions.jts.geom.GeometryFactory;
-
-public class SelectXMapPaneMouseListener extends MouseAdapter {
-
-	private final SelectableXMapPane xMapPane;
-	private boolean enabled;
-
-	public boolean isEnabled() {
-		if (!xMapPane.isWellDefined())
-			return false;
-		if ((xMapPane.getState() == XMapPane.ZOOM_IN
-				|| xMapPane.getState() == xMapPane.ZOOM_OUT || xMapPane
-				.getState() == xMapPane.PAN))
-			return false;
-		if (xMapPane.getState() == XMapPane.NONE)
-			;
-		return enabled;
-	}
-
-	/**
-	 * Reagiert auf Linksklick mit der ueber {@link #setState(int)}eingestellten
-	 * Aktion und auf Rechtsklick mit Zoom-Out (sofern {@link #ZOOM_IN}-State
-	 * fuer Linksklick eingestellt). Alle anderen Klicks werden ignoriert.
-	 * 
-	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
-	 *         (University of Bonn/Germany)
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public void mouseClicked(MouseEvent e) {
-		if (!isEnabled())
-			return;
-		
-		xMapPane.setCursor(XMapPane.WAIT_CURSOR);
-		//		
-		// int clickX = e.getX();
-		// int clickY = e.getY();
-		// selectionPerformed(clickX, clickY, clickX, clickY);}
-		//		
-		int state = xMapPane.getState();
-
-		/**
-		 * BEGIN StefanChange Dieser Block findet sichtbare Features im Umkreis
-		 * des Mausklicks und kümmert sich selbst um das verschicken der Events.
-		 * Dabei wird z.Zt. immer eine FeaterCollection mit nur einem
-		 * SimpleFeature verschickt. Und zwar jenes SimpleFeature, welches am
-		 * nächsten liegt.
-		 */
-
-		// Fenster-Koordinate in Geo-Koordinate umwandelt
-		Point2D geoCoord = xMapPane.getScreenToWorld().transform(e.getPoint(),
-				null);
-		
-		com.vividsolutions.jts.geom.Point mousePoint = new GeometryFactory()
-				.createPoint(new Coordinate(geoCoord.getX(), geoCoord.getY()));
-
-		// Rechnet 10 Pixel in die Einheit der Karte um.
-		final Point pAtDistance = new Point((int) e.getPoint().getX() + 10,
-				(int) e.getPoint().getY());
-		final Point2D geoCoordAtDistance = xMapPane.getScreenToWorld()
-				.transform(pAtDistance, null);
-		final Double dist = Math.abs(geoCoordAtDistance.getX()
-				- geoCoord.getX()) / 2;
-
-//		final Envelope envelope = new Envelope(geoCoord.getX(),
-//				geoCoord.getY(), geoCoord.getX(), geoCoord.getY());
-
-		//
-		// This is a tweak. The NearPointFilterGenerator doesn't work
-		// any more in GT2.6... So we use a small BBOX.
-		// Well, it's not the
-		// NearPointFilterGenerator, but GeoTools that fails. I already
-		// invested 3h in this... I guess the bug is in
-		// ExtractBoundsFilterVisitor.visit(ProperyName) retuning null.
-		// It should return the BBOX of value the named property.
-
-		// Hashtable<MapLayer, FeatureCollection<SimpleFeatureType,
-		// SimpleFeature>> result = findVisibleFeatures(
-		// new NearPointFilterGenerator(geoCoord, dist,
-		// getContext().getCoordinateReferenceSystem()),
-		// state, envelope);
-		
-//		envelope.expandBy(dist);
-//
-		Envelope envelope = new Envelope(geoCoord.getX() - dist, geoCoord
-				.getY()
-				- dist, geoCoord.getX() + dist, geoCoord.getY() + dist);
-
-		Hashtable<MapLayer, FeatureCollection<SimpleFeatureType, SimpleFeature>> result = xMapPane
-				.findVisibleFeatures(new BoundingBoxFilterGenerator(  envelope,
-						xMapPane.getContext().getCoordinateReferenceSystem()),
-						state, envelope);
-
-		// Ein Event auslösen für das jeweils nächste Feature pro Layer
-		for (Enumeration<MapLayer> element = result.keys(); element
-				.hasMoreElements();) {
-
-			MapLayer layer = element.nextElement();
-			FeatureCollection<SimpleFeatureType, SimpleFeature> fc = result
-					.get(layer);
-			FeatureCollection<SimpleFeatureType, SimpleFeature> fcOne;
-
-			if (fc != null && !fc.isEmpty()) {
-
-				if (fc.size() > 1) {
-					// Hier werden alle Features weggeschmissen, bis auf
-					// das raeumlich naechste.
-
-					SimpleFeature nearestFeature = null;
-					Double nearestDist = 0.0;
-
-					Iterator<SimpleFeature> fcIt = fc.iterator();
-					try {
-
-						while (fcIt.hasNext()) {
-							SimpleFeature f = fcIt.next();
-							Object obj = f.getAttribute(0);
-
-							if (obj instanceof Geometry) {
-								// Bei Punkten ja noch ganz einfach:
-								Geometry featureGeometry = (Geometry) obj;
-								double distance = featureGeometry
-										.distance(mousePoint);
-
-								if ((nearestFeature == null)
-										|| (distance < nearestDist)) {
-									nearestFeature = f;
-									nearestDist = distance;
-								}
-							} else {
-								LOGGER
-										.info("!obj instanceof Geometry      obj = "
-												+ obj.getClass()
-														.getSimpleName());
-							}
-
-						}
-
-					} finally {
-						fc.close(fcIt);
-					}
-
-					fcOne = new MemoryFeatureCollection(fc.getSchema());
-					fc.clear();
-					fcOne.add(nearestFeature);
-				} else {
-					fcOne = fc;
-				}
-				xMapPane.fireMapPaneEvent(new FeatureSelectedEvent(xMapPane,
-						layer, envelope, fcOne));
-			}
-		}
-		
-		xMapPane.updateCursor();
-
-	}
-
-	/**
-	 * Verarbeitet die Mausrad-Aktion, indem gezoomed wird.
-	 * 
-	 * @param wheelEvt
-	 *            Mausrad-Event
-	 */
-	@Override
-	public void mouseWheelMoved(MouseWheelEvent wheelEvt) {
-		if (!isEnabled())
-			return;
-
-		final int units = wheelEvt.getUnitsToScroll();
-		if (units > 0)
-			xMapPane.zoomTo(wheelEvt.getPoint(), 1. + .11 * units);
-		else
-			xMapPane.zoomTo(wheelEvt.getPoint(), 2. / -units);
-	}
-
-	@Override
-	public void mouseDragged(final MouseEvent event) {
-		if (!isEnabled())
-			return;
-
-		xMapPane.mouseDragged(startPos, lastPos, event);
-
-		lastPos = event.getPoint();
-	}
-
-	/**
-	 * Stores beginning of a drag event in window coordinates
-	 */
-	protected Point startPos;
-
-	/**
-	 * Stores last position of a drag event in window coordinates
-	 */
-	protected Point lastPos;
-
-	public void mouseReleased(final MouseEvent event) {
-		if (!isEnabled())
-			return;
-
-		if (xMapPane.getState() == XMapPane.PAN
-				|| event.getButton() == MouseEvent.BUTTON3) {
-			xMapPane.performPan();
-		} else if (xMapPane.getState() == XMapPane.SELECT_ALL
-				|| xMapPane.getState() == XMapPane.SELECT_TOP) {
-			xMapPane.drawRectangle(xMapPane.getGraphics(), startPos, event
-					.getPoint());
-
-			Point mp = event.getPoint();
-
-			// keine wirkliche Selektion, sondern nur ein Klick
-			if (startPos.x == mp.x || startPos.y == mp.y) {
-				mouseClicked(event);
-				return;
-			}
-
-			// SELECTION!
-			xMapPane.performSelectionEvent(startPos.x, startPos.y, mp.x, mp.y);
-		}
-
-	}
-
-	public void mousePressed(final MouseEvent e) {
-		if (!isEnabled())
-			return;
-		startPos = new Point(e.getPoint());
-		lastPos = new Point(e.getPoint());
-	}
-
-	public SelectXMapPaneMouseListener(SelectableXMapPane xMapPane) {
-		this.xMapPane = xMapPane;
-	}
-
-	public void setEnabled(boolean enabled) {
-		this.enabled = enabled;
-	}
-
-	private static Logger LOGGER = Logger
-			.getLogger(SelectXMapPaneMouseListener.class);
-
-}

Deleted: branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,2267 +0,0 @@
-package gtmig.org.geotools.swing;
-
-import java.awt.Color;
-import java.awt.Cursor;
-import java.awt.Font;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Point2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import javax.swing.Timer;
-
-import org.apache.log4j.Logger;
-import org.geotools.feature.FeatureCollection;
-import org.geotools.geometry.jts.JTS;
-import org.geotools.geometry.jts.ReferencedEnvelope;
-import org.geotools.map.DefaultMapContext;
-import org.geotools.map.MapContext;
-import org.geotools.map.MapLayer;
-import org.geotools.map.event.MapLayerEvent;
-import org.geotools.map.event.MapLayerListEvent;
-import org.geotools.map.event.MapLayerListListener;
-import org.geotools.map.event.MapLayerListener;
-import org.geotools.referencing.CRS;
-import org.geotools.renderer.GTRenderer;
-import org.geotools.renderer.label.LabelCacheImpl;
-import org.geotools.renderer.lite.LabelCache;
-import org.geotools.renderer.lite.StreamingRenderer;
-import org.geotools.swing.JMapPane;
-import org.geotools.swing.event.MapMouseEvent;
-import org.geotools.swing.event.MapPaneEvent;
-import org.geotools.swing.event.MapPaneListener;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.referencing.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
-
-import schmitzm.geotools.GTUtil;
-import schmitzm.geotools.JTSUtil;
-import schmitzm.geotools.gui.SelectableXMapPane;
-import schmitzm.geotools.map.event.JMapPaneListener;
-import schmitzm.geotools.map.event.MapLayerAdapter;
-import schmitzm.lang.LangUtil;
-import schmitzm.swing.JPanel;
-import schmitzm.swing.SwingUtil;
-import skrueger.geotools.RenderingExecutor;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
-/**
- * The {@link XMapPane} class uses a Geotools {@link GTRenderer} to paint up to
- * two {@link MapContext}s: a "local" {@link MapContext} and a "background"
- * {@link MapContext}. The idea is, that rendering a background layer made up of
- * e.g. OSM data, may take much longer than rendering local data.<br>
- * Every {@link MapContext} is rendered on a {@link Thread} of it's own.
- * Starting/ cancelling these threads is done by the {@link RenderingExecutor}.<br>
- * <br>
- * While the renderers are rending the map, a <br>
- * The {@link XMapPane} is based on schmitzm {@link JPanel}, so
- * {@link #print(Graphics)} will automatically set the background of components
- * to pure white.
- * 
- * The XMapPane has a {@link MouseListener} that manages zooming.<br>
- * A logo/icon to float in the lower left corner may be set with
- * {@link #setMapImage(BufferedImage)}<br>
- * 
- * @see SelectableXMapPane - an extension of {@link XMapPane} that supports
- *      selecting features.
- * 
- * @author stefan
- * 
- */
-public class XMapPane extends JPanel {
-
-	private static final int IMAGETYPE = BufferedImage.TYPE_INT_RGB;
-	private static final int IMAGETYPE_withAlpha = BufferedImage.TYPE_INT_ARGB;
-
-	private static Logger LOGGER = Logger.getLogger(XMapPane.class);
-
-	private boolean acceptsRepaintCalls = true;
-
-	/**
-	 * Main {@link MapContext} that holds all layers that are rendered into the
-	 * {@link #localImage} by the {@link #localRenderer}
-	 */
-	MapContext localContext;
-
-	/**
-	 * {@link MapContext} holding the background layers. Use it for layers that
-	 * CAN take very long for rendering, like layer from the Internet: WMS, WFS,
-	 * OSM...<br>
-	 * <code>null</code> by default.
-	 * 
-	 * @see #setBgContext(MapContext)
-	 * @see #getBgContext()
-	 */
-	MapContext bgContext;
-
-	/**
-	 * While threads are working, calls {@link XMapPane#updateFinalImage()}
-	 * regularly and {@link #repaint()}. This {@link Timer} is stopped when all
-	 * renderers have finished.
-	 * 
-	 * @see #INITIAL_REPAINT_DELAYAL
-	 * @see #REPEATING_REPAINT_DELAY
-	 */
-	final private Timer repaintTimer;
-
-	/**
-	 * The initial delay in milliseconds until the {@link #finalImage} is
-	 * updated the first time.
-	 */
-	public static final int INITIAL_REPAINT_DELAY = 1000;
-
-	/**
-	 * While the {@link #bgExecuter} and {@link #localExecuter} are rendering,
-	 * the {@link #repaintTimer} is regularly updating the {@link #finalImage}
-	 * with previews.
-	 */
-	public static final long REPEATING_REPAINT_DELAY = 500;
-
-	/**
-	 * Default delay (milliseconds) before the map will be redrawn when resizing
-	 * the pane. This is to avoid flickering while drag-resizing.
-	 */
-	public static final int DEFAULT_RESIZING_PAINT_DELAY = 500;
-
-	public static final int NONE = -123;
-
-	/**
-	 * Flag fuer Modus "Kartenausschnitt bewegen". Nicht fuer Window-Auswahl
-	 * moeglich!
-	 * 
-	 * @see #setState(int)
-	 */
-	public static final int PAN = 1;
-
-	/**
-	 * Flag fuer Modus "SimpleFeature-Auswahl auf allen (sichtbaren) Layern".
-	 * 
-	 * @see #setState(int)
-	 * @see #setState(int)
-	 */
-	public static final int SELECT_ALL = 103;
-	/**
-	 * Flag fuer Modus
-	 * "Auswahl nur eines Features, das erste sichtbare von Oben".
-	 * 
-	 * @see #setState(int)
-	 * @see #setState(int)
-	 */
-	public static final int SELECT_ONE_FROM_TOP = 104;
-	/**
-	 * Flag fuer Modus
-	 * "SimpleFeature-Auswahl auf dem obersten (sichtbaren) Layer".
-	 * 
-	 * @see #setState(int)
-	 * @see #setState(int)
-	 */
-	public static final int SELECT_TOP = 4;
-
-	public static final Cursor WAIT_CURSOR = Cursor
-			.getPredefinedCursor(Cursor.WAIT_CURSOR);
-
-	final static Font waitFont = new Font("Arial", Font.BOLD, 30);
-
-	/**
-	 * Flag fuer Modus "Heran zoomen".
-	 * 
-	 * @see #setState(int)
-	 * @see #setState(int)
-	 */
-	public static final int ZOOM_IN = 2;
-
-	/**
-	 * Flag fuer Modus "Heraus zoomen". Nicht fuer Window-Auswahl moeglich!
-	 * 
-	 * @see #setState(int)
-	 */
-	public static final int ZOOM_OUT = 3;
-
-	/**
-	 * Konvertiert die Maus-Koordinaten (relativ zum <code>JMapPane</code>) in
-	 * Karten-Koordinaten.
-	 * 
-	 * @param e
-	 *            Maus-Ereignis
-	 */
-	public static Point2D getMapCoordinatesFromEvent(final MouseEvent e) {
-		// aktuelle Geo-Position aus GeoMouseEvent ermitteln
-		if (e != null && e instanceof MapMouseEvent)
-			try {
-				return ((MapMouseEvent) e).getMapPosition().toPoint2D();
-			} catch (final Exception err) {
-				LOGGER
-						.error(
-								"return ((GeoMouseEvent) e).getMapCoordinate(null).toPoint2D();",
-								err);
-			}
-
-		// aktuelle Geo-Position ueber Transformation des JMapPane berechnen
-		if (e != null && e.getSource() instanceof XMapPane) {
-
-			final XMapPane xMapPane = (XMapPane) e.getSource();
-
-			if (!xMapPane.isWellDefined())
-				return null;
-
-			final AffineTransform at = xMapPane.getScreenToWorld();
-			if (at != null)
-				return at.transform(e.getPoint(), null);
-			return null;
-		}
-		throw new IllegalArgumentException(
-				"MouseEvent has to be of instance MapMouseEvent or come from an XMapPane");
-	}
-
-	/**
-	 * Listens to changes of the "background" {@link MapContext} and triggers
-	 * repaints where needed.
-	 */
-	private final MapLayerListListener bgContextListener = new MapLayerListListener() {
-
-		@Override
-		public void layerAdded(final MapLayerListEvent event) {
-			event.getLayer().addMapLayerListener(bgMapLayerListener);
-
-			if (localContext.getLayers().length == 0
-					&& bgContext.getLayers().length == 1) { // the first one and
-				// localContext is
-				// empty
-				if (!setMapArea(localContext.getAreaOfInterest()))
-					requestStartRendering();
-				return;
-			}
-
-			// We need to redraw, even in case that the mapArea didn't change
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-
-		}
-
-		@Override
-		public void layerChanged(final MapLayerListEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerMoved(final MapLayerListEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerRemoved(final MapLayerListEvent event) {
-			if (event.getLayer() != null)
-				event.getLayer().removeMapLayerListener(bgMapLayerListener);
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-	};
-
-	/**
-	 * compass and icon are rendered into this image
-	 */
-	// protected BufferedImage gadgetsImage;
-
-	protected RenderingExecutor bgExecuter;
-
-	/**
-	 * The Renderer for the LocalLayers uses this Image. When set to null,
-	 * please dispose this {@link Graphics2D}
-	 */
-	private BufferedImage localImage;
-
-	private BufferedImage finalImage;
-	/**
-	 * If # {@link #bgExecuter} is using {@link #bgRenderer} for the Background
-	 * uses this Image. When set to null, please dispose the {@link Graphics2D}
-	 */
-	private BufferedImage bgImage;
-
-	/**
-	 * Optionally a transparent image to paint over the map in the lower right
-	 * corner.
-	 * 
-	 * @see #addGadgets(Graphics2D)
-	 * @see #setMapImage(BufferedImage)
-	 **/
-	private BufferedImage mapImage = null;
-
-	/**
-	 * Listens to each layer in the local {@link MapContext} for changes and
-	 * triggers repaints.
-	 */
-	protected MapLayerListener bgMapLayerListener = new MapLayerAdapter() {
-
-		@Override
-		public void layerChanged(final MapLayerEvent event) {
-			// Change of SLD for example
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerHidden(final MapLayerEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerShown(final MapLayerEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-	};
-
-	/**
-	 * A flag indicating if dispose() was already called. If true, then further
-	 * use of this {@link SelectableXMapPane} is undefined.
-	 */
-	private boolean disposed = false;
-
-	/**
-	 * While dragging, the {@link #updateFinalImage()} method is translating the
-	 * cached images while setting it together.
-	 **/
-	Point imageOrigin = new Point(0, 0);
-	/**
-	 * For every rendering thread started,
-	 * {@link GTUtil#createGTRenderer(MapContext)} is called to create a new
-	 * renderer. These Java2D rendering hints are passed to the
-	 * {@link GTRenderer}. The java2dHints are the same for all renderers (bg
-	 * and local).
-	 */
-	private RenderingHints java2dHints;
-
-	protected LabelCache labelCache = new LabelCacheImpl();
-
-	/**
-	 * Listens to changes of the "local" {@link MapContext} and triggers
-	 * repaints where needed.
-	 */
-	private final MapLayerListListener localContextListener = new MapLayerListListener() {
-
-		@Override
-		public void layerAdded(final MapLayerListEvent event) {
-			event.getLayer().addMapLayerListener(localMapLayerListener);
-
-			localRenderer.setContext(getContext());
-
-			if (localContext.getLayers().length == 1) { // the first one
-				// if the Area of Interest is unset, the LayerBounds are used
-				if (!setMapArea(localContext.getAreaOfInterest()))
-					repaint();
-
-				return;
-			}
-
-			// We need to redraw, even in case that the mapArea didn't change
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-
-		}
-
-		@Override
-		public void layerChanged(final MapLayerListEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			localRenderer.setContext(getContext());
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerMoved(final MapLayerListEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			localRenderer.setContext(getContext());
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerRemoved(final MapLayerListEvent event) {
-			if (event.getLayer() != null)
-				event.getLayer().removeMapLayerListener(localMapLayerListener);
-			// mapImageInvalid = true;
-			// repaint();
-			localRenderer.setContext(getContext());
-			requestStartRendering();
-		}
-	};
-
-	private final RenderingExecutor localExecuter = new RenderingExecutor(this);
-
-	/**
-	 * Listens to each layer in the local {@link MapContext} for changes and
-	 * triggers repaints.
-	 */
-	protected MapLayerListener localMapLayerListener = new MapLayerAdapter() {
-
-		@Override
-		public void layerChanged(final MapLayerEvent event) {
-			localRenderer.setContext(getContext()); // betters for SLD changes?!
-			// Change of SLD for example
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerHidden(final MapLayerEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-
-		@Override
-		public void layerShown(final MapLayerEvent event) {
-			// mapImageInvalid = true;
-			// repaint();
-			requestStartRendering();
-		}
-	};
-
-	private final GTRenderer localRenderer = GTUtil.createGTRenderer();
-	
-	private final GTRenderer bgRenderer = GTUtil.createGTRenderer();
-
-	/**
-	 * the area of the map to draw
-	 */
-	protected Envelope mapArea = null;
-
-	/**
-	 * A flag set it {@link #setMapArea(Envelope)} to indicated the
-	 * {@link #paintComponent(Graphics)} method, that the image on-screen is
-	 * associated with {@link #oldMapArea} and may hence be scaled for a quick
-	 * preview.<br>
-	 * The flag is reset
-	 **/
-	private boolean mapAreaChanged = false;
-
-	/**
-	 * This color is used as the default background color when painting a map.
-	 */
-	private Color mapBackgroundColor = Color.WHITE;
-
-	/**
-	 * A flag indicating that the shown image is invalid and needs to be
-	 * re-rendered.
-	 */
-	protected boolean mapImageInvalid = true;
-
-	/**
-	 * Holds a flag for each layer, whether it is regarded or ignored on
-	 * {@link #SELECT_TOP}, {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP}
-	 * actions.
-	 */
-	final protected HashMap<MapLayer, Boolean> mapLayerSelectable = new HashMap<MapLayer, Boolean>();
-
-	/**
-	 * List of listeners of this {@link XMapPane}
-	 */
-	protected Vector<JMapPaneListener> mapPaneListeners = new Vector<JMapPaneListener>();
-	/**
-	 * If not <code>null</code>, the {@link XMapPane} will not allow to zoom/pan
-	 * out of that area
-	 **/
-	private Envelope maxExtend = null;
-	private Double maxZoomScale = Double.MIN_VALUE;
-
-	private Double minZoomScale = Double.MAX_VALUE;
-
-	/**
-	 * We store the old mapArea for a moment to use it for the
-	 * "quick scaled preview" in case of ZoomOut
-	 **/
-	protected Envelope oldMapArea = null;
-
-	/**
-	 * We store the old transform for a moment to use it for the
-	 * "quick scaled preview" in case of ZoomIn
-	 **/
-	protected AffineTransform oldScreenToWorld = null;
-
-	/**
-	 * A flag indicating, that the image size has changed and the buffered
-	 * images are not big enough any more
-	 **/
-	protected boolean paneResized = false;
-
-	private BufferedImage preFinalImage;
-
-	// if null, no quick preview will be shown
-	private int quickPreviewHint = 0;
-
-	private Map<Object, Object> rendererHints;
-
-	private volatile Boolean requestStartRendering = false;
-
-	private final Timer resizeTimer;
-
-	private final int resizingPaintDelay;
-
-	/**
-	 * Transformation zwischen Fenster-Koordinaten und Karten-Koordinaten
-	 * (lat/lon)
-	 */
-	protected AffineTransform screenToWorld = null;
-
-	/**
-	 * The flag {@link #requestStartRendering} can be set to true by events.
-	 * This {@link Timer} checks the flag regularly and starts one renderer
-	 * thread.
-	 */
-	final private Timer startRenderThreadsTimer;
-
-	/**
-	 * The default state is ZOOM_IN, hence by default the
-	 * {@link #zoomMapPaneMouseListener} is also enabled.
-	 **/
-	private int state = ZOOM_IN;
-
-	/**
-	 * Manuell gesetzter statischer Cursor, unabhaengig von der aktuellen
-	 * MapPane-Funktion
-	 */
-	protected Cursor staticCursor = null;
-
-	private AffineTransform worldToScreen;
-
-	/**
-	 * This {@link MouseListener} is managing all zoom related tasks
-	 */
-	public final ZoomXMapPaneMouseListener zoomMapPaneMouseListener = new ZoomXMapPaneMouseListener(
-			this);
-
-	// TODO doku
-	public XMapPane() {
-		this(null, null);
-	}
-
-	/**
-	 * full constructor extending JPanel
-	 * 
-	 * @param rendererHints
-	 * 
-	 * @param layout
-	 *            - layout (probably shouldn't be set)
-	 * @param isDoubleBuffered
-	 *            - a Swing thing I don't really understand
-	 * @param render
-	 *            - what to draw the map with
-	 * @param localContext
-	 *            - {@link MapContext} of layer to render.
-	 */
-	public XMapPane(final MapContext localContext_,
-			final Map<Object, Object> rendererHints) {
-		super(true);
-
-		setRendererHints(rendererHints);
-
-		setOpaque(true);
-
-		localRenderer.setJava2DHints(getJava2dHints());
-
-		if (localContext_ != null)
-			setLocalContext(localContext_);
-
-		/**
-		 * Adding the #zoomMapPaneMouseListener
-		 */
-		this.addMouseListener(zoomMapPaneMouseListener);
-		this.addMouseMotionListener(zoomMapPaneMouseListener);
-		this.addMouseWheelListener(zoomMapPaneMouseListener);
-
-		/*
-		 * We use a Timer object to avoid rendering delays and flickering when
-		 * the user is drag-resizing the parent container of this map pane.
-		 * 
-		 * Using a ComponentListener doesn't work because, unlike a JFrame, the
-		 * pane receives a stream of events during drag-resizing.
-		 */
-		resizingPaintDelay = DEFAULT_RESIZING_PAINT_DELAY;
-		resizeTimer = new Timer(resizingPaintDelay, new ActionListener() {
-
-			public void actionPerformed(final ActionEvent e) {
-				paneResized = true;
-
-				if (!isWellDefined())
-					return;
-
-				final Rectangle bounds = getVisibleRect();
-
-				final Envelope geoMapArea = tranformWindowToGeo(bounds.x,
-						bounds.y, bounds.x + bounds.width, bounds.y
-								+ bounds.height);
-
-				setMapArea(bestAllowedMapArea(geoMapArea));
-			}
-		});
-		resizeTimer.setRepeats(false);
-		this.addComponentListener(new ComponentAdapter() {
-
-			@Override
-			public void componentResized(final ComponentEvent e) {
-				if (bgExecuter != null)
-					bgExecuter.cancelTask();
-				if (localExecuter != null)
-					localExecuter.cancelTask();
-				resizeTimer.restart();
-			}
-
-		});
-
-		/*
-		 * Setting up the repaintTimer. Not started automatically.
-		 */
-		repaintTimer = new Timer((int) REPEATING_REPAINT_DELAY,
-				new ActionListener() {
-
-					@Override
-					public void actionPerformed(final ActionEvent e) {
-						updateFinalImage();
-						XMapPane.this.repaint();
-					}
-				});
-		repaintTimer.setInitialDelay(INITIAL_REPAINT_DELAY);
-		repaintTimer.setRepeats(true);
-
-		/*
-		 * Setting up the startRenderThreadsTimer. This Timer starts
-		 * automatically.
-		 */
-		startRenderThreadsTimer = new Timer(100, new ActionListener() {
-
-			@Override
-			public void actionPerformed(final ActionEvent e) {
-				synchronized (requestStartRendering) {
-					if (requestStartRendering && isWellDefined()) {
-
-						if (localExecuter.isRunning()) {
-							localExecuter.cancelTask();
-						} else {
-							requestStartRendering = false;
-							startRendering();
-						}
-					}
-				}
-			}
-		});
-		startRenderThreadsTimer.start();
-
-	}
-
-	/**
-	 * Fuegt der Map einen Listener hinzu.
-	 * 
-	 * @param l
-	 *            neuer Listener
-	 */
-	public void addMapPaneListener(final JMapPaneListener l) {
-		mapPaneListeners.add(l);
-	}
-
-	/**
-	 * Korrigiert den {@link Envelope} aka {@code mapArea} auf die beste
-	 * erlaubte Flaeche damit die Massstabsbeschaenkungen noch eingehalten
-	 * werden, FALLS der uebergeben Envelope nicht schon gueltig sein sollte.<br>
-	 * Since 21. April 09: Before thecalculation starts, the aspect ratio is
-	 * corrected. This change implies, that setMapArea() will most of the time
-	 * not allow setting to a wrong aspectRatio.
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public Envelope bestAllowedMapArea(Envelope env) {
-		if (getWidth() == 0)
-			return env;
-		if (env == null)
-			return null;
-
-		Envelope newArea = null;
-
-		/**
-		 * Correct the aspect Ratio before we check the rest. Otherwise we might
-		 * easily fail. We allow to grow here, because we don't check against
-		 * the maxExtend
-		 */
-		final Rectangle curPaintArea = getVisibleRect();
-
-		env = JTSUtil.fixAspectRatio(curPaintArea, env, true);
-
-		final double scale = env.getWidth() / getWidth();
-		final double centerX = env.getMinX() + env.getWidth() / 2.;
-		final double centerY = env.getMinY() + env.getHeight() / 2.;
-		double newWidth2 = 0;
-		double newHeight2 = 0;
-		if (scale < getMaxZoomScale()) {
-			// ****************************************************************************
-			// Wir zoomen weiter rein als erlaubt => Anpassen des envelope
-			// ****************************************************************************
-			newWidth2 = getMaxZoomScale() * getWidth() / 2.;
-			newHeight2 = getMaxZoomScale() * getHeight() / 2.;
-		} else if (scale > getMinZoomScale()) {
-			// ****************************************************************************
-			// Wir zoomen weiter raus als erlaubt => Anpassen des envelope
-			// ****************************************************************************
-			newWidth2 = getMinZoomScale() * getWidth() / 2.;
-			newHeight2 = getMinZoomScale() * getHeight() / 2.;
-		} else {
-			// ****************************************************************************
-			// Die mapArea / der Envelope ist ist gueltig! Keine Aenderungen
-			// ****************************************************************************
-			newArea = env;
-		}
-
-		if (newArea == null) {
-
-			final Coordinate ll = new Coordinate(centerX - newWidth2, centerY
-					- newHeight2);
-			final Coordinate ur = new Coordinate(centerX + newWidth2, centerY
-					+ newHeight2);
-
-			newArea = new Envelope(ll, ur);
-		}
-
-		final Envelope maxAllowedExtend = getMaxExtend();
-		while (maxAllowedExtend != null && !maxAllowedExtend.contains(newArea)
-				&& newArea != null && !newArea.isNull()
-				&& !Double.isNaN(newArea.getMinX())
-				&& !Double.isNaN(newArea.getMaxX())
-				&& !Double.isNaN(newArea.getMinY())
-				&& !Double.isNaN(newArea.getMaxY())) {
-			/*
-			 * If a maxExtend is set, we have to honour that...
-			 */
-
-			// Exceeds top? Move down and maybe cut
-			if (newArea.getMaxY() > maxAllowedExtend.getMaxY()) {
-				final double divY = newArea.getMaxY()
-						- maxAllowedExtend.getMaxY();
-				// LOGGER.debug("Moving area down by " + divY);
-
-				newArea = new Envelope(new Coordinate(newArea.getMinX(),
-						newArea.getMinY() - divY), new Coordinate(newArea
-						.getMaxX(), newArea.getMaxY() - divY));
-
-				if (newArea.getMinY() < maxAllowedExtend.getMinY()) {
-					// LOGGER.debug("Now it exeeds the bottom border.. cut!");
-					// And cut the bottom if it moved out of the area
-					newArea = new Envelope(new Coordinate(newArea.getMinX(),
-							maxAllowedExtend.getMinY()), new Coordinate(newArea
-							.getMaxX(), newArea.getMaxY()));
-
-					// LOGGER.debug("and fix aspect ratio");
-
-					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
-							false);
-				}
-			}
-
-			// Exceeds bottom? Move up and maybe cut
-			if (newArea.getMinY() < maxAllowedExtend.getMinY()) {
-				final double divY = newArea.getMinY()
-						- maxAllowedExtend.getMinY();
-				// LOGGER.debug("Moving area up by " + divY);
-
-				newArea = new Envelope(new Coordinate(newArea.getMinX(),
-						newArea.getMinY() - divY), new Coordinate(newArea
-						.getMaxX(), newArea.getMaxY() - divY));
-
-				if (newArea.getMaxY() > maxAllowedExtend.getMaxY()) {
-					// LOGGER.debug("Now it exeeds the top border.. cut!");
-					// And cut the bottom if it moved out of the area
-					newArea = new Envelope(new Coordinate(newArea.getMinX(),
-							newArea.getMinY()), new Coordinate(newArea
-							.getMaxX(), maxAllowedExtend.getMaxY()));
-
-					// LOGGER.debug("and fix aspect ratio");
-
-					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
-							false);
-				}
-			}
-
-			// Exceeds to the right? move and maybe cut
-			if (newArea.getMaxX() > maxAllowedExtend.getMaxX()) {
-
-				// Move left..
-				final double divX = newArea.getMaxX()
-						- maxAllowedExtend.getMaxX();
-				// LOGGER.debug("Moving area left by " + divX);
-
-				newArea = new Envelope(new Coordinate(newArea.getMinX() - divX,
-						newArea.getMinY()), new Coordinate(newArea.getMaxX()
-						- divX, newArea.getMaxY()));
-
-				if (newArea.getMinX() < maxAllowedExtend.getMinX()) {
-					// LOGGER.debug("Now it exeeds the left border.. cut!");
-					// And cut the left if it moved out of the area
-					newArea = new Envelope(new Coordinate(maxAllowedExtend
-							.getMinX(), newArea.getMinY()), new Coordinate(
-							newArea.getMaxX(), newArea.getMaxY()));
-
-					// LOGGER.debug("and fix aspect ratio");
-
-					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
-							false);
-				}
-			}
-
-			// Exceeds to the left? move and maybe cut
-			if (newArea.getMinX() < maxAllowedExtend.getMinX()) {
-
-				// Move right..
-				final double divX = newArea.getMinX()
-						- maxAllowedExtend.getMinX();
-				// LOGGER.debug("Moving area right by " + divX);
-
-				newArea = new Envelope(new Coordinate(newArea.getMinX() - divX,
-						newArea.getMinY()), new Coordinate(newArea.getMaxX()
-						- divX, newArea.getMaxY()));
-
-				if (newArea.getMaxX() > maxAllowedExtend.getMaxX()) {
-					// LOGGER.debug("Now it exeeds the right border.. cut!");
-					// And cut the left if it moved out of the area
-					newArea = new Envelope(new Coordinate(newArea.getMinX(),
-							newArea.getMinY()), new Coordinate(maxAllowedExtend
-							.getMaxX(), newArea.getMaxY()));
-
-					// LOGGER.debug("and fix aspect ratio");
-
-					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
-							false);
-				}
-			}
-		}
-
-		return newArea;
-	}
-
-	/**
-	 * Should be called when the {@link JMapPane} is not needed no more to help
-	 * the GarbageCollector
-	 * 
-	 * Removes all {@link JMapPaneListener}s that are registered
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public void dispose() {
-		if (isDisposed())
-			return;
-
-		setPainting(false);
-
-		resizeTimer.stop();
-		startRenderThreadsTimer.stop();
-
-		disposed = true;
-
-		if (bgExecuter != null) {
-			bgExecuter.cancelTask();
-			bgExecuter.dispose();
-		}
-
-		if (localExecuter != null) {
-			int i = 0;
-			localExecuter.cancelTask();
-			while (i++ < 10 && localExecuter.isRunning()) {
-				try {
-					Thread.sleep(200);
-				} catch (final InterruptedException e) {
-					// TODO Auto-generated catch block
-					e.printStackTrace();
-				}
-			}
-			if (localExecuter.isRunning()) {
-				System.out
-						.println("BAD BAD BAD... still running the thread....");
-			}
-			localExecuter.dispose();
-		}
-		//
-		// if (bgImage != null) {
-		// bgImage = dispose(bgImage);
-		// bgImage = null;
-		// // LangUtil.gcTotal();
-		// }
-		//
-		// if (localImage != null) {
-		// localImage = dispose(localImage);
-		// localImage = null;
-		// // LangUtil.gcTotal();
-		// }
-		//
-		// if (finalImage != null) {
-		// finalImage = dispose(finalImage);
-		// finalImage = null;
-		// // LangUtil.gcTotal();
-		// }
-		//
-		// if (preFinalImage != null) {
-		// preFinalImage = dispose(preFinalImage);
-		// preFinalImage = null;
-		// }
-		disposeImages();
-
-		// LangUtil.gcTotal();
-
-		// Alle mapPaneListener entfernen
-		mapPaneListeners.clear();
-
-		removeMouseMotionListener(zoomMapPaneMouseListener);
-		removeMouseListener(zoomMapPaneMouseListener);
-
-		if (localContext != null)
-			getContext().clearLayerList();
-		if (bgContext != null)
-			getBgContext().clearLayerList();
-
-		removeAll();
-	}
-
-	/**
-	 * Draws a rectangle in XOR mode from the origin at {@link #startPos} to the
-	 * given point. All in screen coordinates.
-	 */
-	protected void drawRectangle(final Graphics graphics, final Point startPos,
-			final Point e) {
-
-		if (!isWellDefined())
-			return;
-
-		// undraw last box/draw new box
-		final int left = Math.min(startPos.x, e.x);
-		final int right = Math.max(startPos.x, e.x);
-		final int top = Math.max(startPos.y, e.y);
-		final int bottom = Math.min(startPos.y, e.y);
-		final int width = right - left;
-		final int height = top - bottom;
-
-		if (width == 0 && height == 0)
-			return;
-
-		graphics.setXORMode(Color.WHITE);
-		graphics.drawRect(left, bottom, width, height);
-	}
-
-	/**
-	 * Diretly paints scaled preview into the {@link SelectableXMapPane}. Used
-	 * to give the user something to look at while we are rendering. Method
-	 * should be called after {@link #setMapArea(Envelope)} has been set to the
-	 * new mapArea and transform has been reset.<br>
-	 * 
-	 * @param g
-	 *            Graphics2D to paint the preview into
-	 * 
-	 * @param state
-	 *            Max be {@link #ZOOM_IN} or {@link #ZOOM_OUT}
-	 */
-	protected boolean drawScaledPreviewImage_Zoom(final Graphics2D graphics) {
-
-		if (quickPreviewHint == 0)
-			return false;
-
-		graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-				RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
-		graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-				RenderingHints.VALUE_ANTIALIAS_OFF);
-		graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
-				RenderingHints.VALUE_RENDER_SPEED);
-
-		if (oldMapArea == null)
-			return false;
-
-		final Rectangle visibleArea = getVisibleRect();
-
-		// Calculate the oldMapArea in the current WindowCoordinates:
-		final Envelope oldMapWindow = tranformGeoToWindow(oldMapArea.getMinX(),
-				oldMapArea.getMinY(), oldMapArea.getMaxX(), oldMapArea
-						.getMaxY(), null);
-
-		final int xx1 = (int) Math.round(oldMapWindow.getMinX());
-		final int yy1 = (int) Math.round(oldMapWindow.getMinY());
-		final int xx2 = (int) Math.round(oldMapWindow.getMaxX());
-		final int yy2 = (int) Math.round(oldMapWindow.getMaxY());
-
-		graphics.drawImage(getPreFinalImage(), xx1, yy1, xx2, yy2,
-				(int) visibleArea.getMinX(), (int) visibleArea.getMinY(),
-				(int) visibleArea.getMaxX(), (int) visibleArea.getMaxY(),
-				getMapBackgroundColor(), null);
-
-		final Rectangle painedArea = new Rectangle(xx1, yy1, xx2 - xx1, yy2
-				- yy1);
-
-		SwingUtil.clearAround(graphics, painedArea, visibleArea);
-
-		addGadgets(graphics);
-
-		quickPreviewHint = 0;
-
-		graphics.dispose();
-
-		// Something has been drawn
-		return true;
-	}
-
-	public MapContext getBgContext() {
-		return bgContext;
-	}
-
-	/**
-	 * Lazyly initializes a {@link BufferedImage} for the background renderer.
-	 */
-	private Image getBgImage() {
-		//
-		// if (bgImage == null) {
-		// bgImage = createImage(getBounds().width, getBounds().height);
-		// }
-
-		return bgImage;
-	}
-
-	public MapContext getContext() {
-		if (localContext == null) {
-			setLocalContext(new DefaultMapContext());
-		}
-		return localContext;
-	}
-
-	private BufferedImage getFinalImage() {
-		//
-		if (finalImage == null) {
-			// Rectangle curPaintArea = getVisibleRect();
-			finalImage = new BufferedImage(getBounds().width,
-					getBounds().height, IMAGETYPE);
-
-			requestStartRendering();
-		}
-		return finalImage;
-	}
-
-	public RenderingHints getJava2dHints() {
-		return java2dHints;
-	}
-
-	/**
-	 * Lazyly initializes a {@link BufferedImage} for the background renderer.
-	 */
-	private BufferedImage getLocalImage() {
-
-		if (localImage == null) {
-			localImage = new BufferedImage(getBounds().width,
-					getBounds().height, IMAGETYPE_withAlpha);
-		}
-
-		return localImage;
-	}
-
-	/**
-	 * Returns a copy of the mapArea
-	 * 
-	 * @return
-	 */
-	public Envelope getMapArea() {
-		if (mapArea == null) {
-			ReferencedEnvelope mapArea_ = null;
-			try {
-				mapArea_ = localContext.getLayerBounds();
-			} catch (final IOException e) {
-				LOGGER.warn("context.getLayerBounds()", e);
-			}
-
-			if (mapArea_ != null) {
-				mapImageInvalid = true; /* note we need to redraw */
-//				setMapArea(mapArea_); // results in a loop
-				mapArea = bestAllowedMapArea(mapArea_);
-			}
-		}
-
-		if (mapArea == null)
-			return null;
-
-		return new Envelope(mapArea);
-	}
-
-	/**
-	 * Returns the background {@link Color} of the map pane. Default is white.
-	 **/
-	public Color getMapBackgroundColor() {
-		return mapBackgroundColor;
-	}
-
-	/**
-	 * Get the BufferedImage to use as a flaoting icon in the lower right
-	 * corner.
-	 * 
-	 * @return <code>null</code> if the feature is deactivated.
-	 */
-	public BufferedImage getMapImage() {
-		return mapImage;
-	}
-
-	/**
-	 * Returns the evelope of the viewable area. The JMapPane will never show
-	 * anything outside of this extend. If this has been set to
-	 * <code>null</code> via {@link #setMaxExtend(Envelope)}, it tries to return
-	 * quickly the context's bounds. It it takes to long to determine the
-	 * context bounds, <code>null</code> is returned.
-	 * 
-	 * @param maxExtend
-	 *            <code>null</code> to not have this restriction.
-	 */
-
-	public Envelope getMaxExtend() {
-		if (maxExtend == null) {
-			final ReferencedEnvelope layerBounds = GTUtil
-					.getVisibleLayoutBounds(localContext);
-			if (layerBounds == null) {
-				// TODO Last fallback could be the CRS valid area
-				return null;
-			}
-
-			// Kartenbereich um 10% vergroessern
-			return JTSUtil.fixAspectRatio(this.getBounds(), JTSUtil
-					.expandEnvelope(layerBounds, 0.1), true);
-		}
-		return maxExtend;
-	}
-
-	/**
-	 * Retuns the maximum allowed zoom scale. This is the smaller number value
-	 * of the two. Defaults to {@link Double}.MIN_VALUE
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public Double getMaxZoomScale() {
-		return maxZoomScale;
-	}
-
-	/**
-	 * Retuns the minimum allowed zoom scale. This is the bigger number value of
-	 * the two. Defaults to {@link Double}.MAX_VALUE
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public Double getMinZoomScale() {
-		return minZoomScale;
-	}
-
-	private Image getPreFinalImage() {
-		// if (preFinalImage == null) {
-		//			
-		// // Rectangle curPaintArea = getVisibleRect();
-		// // preFinalImage = new BufferedImage(curPaintArea.width,
-		// // curPaintArea.height, BufferedImage.TYPE_INT_RGB);
-		//			
-		// preFinalImage = createImage(getBounds().width, getBounds().height);
-		//
-		// requestStartRendering();
-		// }
-		return preFinalImage;
-	}
-
-	public Map<Object, Object> getRendererHints() {
-		return rendererHints;
-	}
-
-	/**
-	 * Liefert eine affine Transformation, um von den Fenster-Koordinaten in die
-	 * Karten-Koordinaten (Lat/Lon) umzurechnen.
-	 * 
-	 * @return eine Kopie der aktuellen Transformation; <code>null</code> wenn
-	 *         noch keine Karte angezeigt wird
-	 */
-	public AffineTransform getScreenToWorld() {
-		if (screenToWorld == null)
-			resetTransforms();
-		// nur Kopie der Transformation zurueckgeben!
-		if (screenToWorld == null)
-			return null;
-		return new AffineTransform(screenToWorld);
-	}
-
-	public int getState() {
-		return state;
-	}
-
-	/**
-	 * Liefert den statisch eingestellten Cursor, der unabhaengig von der
-	 * eingestellten MapPane-Aktion (Zoom, Auswahl, ...) verwendet wird.
-	 * 
-	 * @return {@code null}, wenn kein statischer Cursor verwendet, sondern der
-	 *         Cursor automatisch je nach MapPane-Aktion eingestellt wird.
-	 */
-	public Cursor getStaticCursor() {
-		return this.staticCursor;
-	}
-
-	public AffineTransform getWorldToScreenTransform() {
-		if (worldToScreen == null) {
-			resetTransforms();
-		}
-		// nur Kopie der Transformation zurueckgeben!
-		return new AffineTransform(worldToScreen);
-	}
-
-	/**
-	 * A flag indicating if dispose() has already been called. If true, then
-	 * further use of this {@link SelectableXMapPane} is undefined.
-	 */
-	private boolean isDisposed() {
-		return disposed;
-	}
-
-	/**
-	 * Returns whether a layer is regarded or ignored on {@link #SELECT_TOP},
-	 * {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP} actions. Returns
-	 * <code>true</code> if the selectability has not been defined.
-	 * 
-	 * @param layer
-	 *            a layer
-	 */
-	public boolean isMapLayerSelectable(final MapLayer layer) {
-		final Boolean selectable = mapLayerSelectable.get(layer);
-		return selectable == null ? true : selectable;
-	}
-
-	/**
-	 * Return <code>true</code> if a CRS and a {@link #mapArea} are set and the
-	 * {@link XMapPane} is visible and has bounds set.
-	 */
-	public boolean isWellDefined() {
-
-		try {
-
-			if (getContext() == null)
-				return false;
-			if (getContext().getLayerCount() <= 0)
-				return false;
-			if (getMapArea() == null)
-				return false;
-			if (getBounds().getWidth() == 0)
-				return false;
-			if (getBounds().getHeight() == 0)
-				return false;
-		} catch (final Exception e) {
-			return false;
-		}
-		return true;
-	}
-
-	public void mouseDragged(final Point startPos, final Point lastPos,
-			final MouseEvent event) {
-
-		if ((getState() == XMapPane.PAN)
-				|| ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)) {
-
-			if (getCursor() != SwingUtil.PANNING_CURSOR) {
-				setCursor(SwingUtil.PANNING_CURSOR);
-
-				// While panning, we deactivate the rendering. So the tasts are
-				// ready to start when the panning os done.
-				if (bgExecuter != null)
-					bgExecuter.cancelTask();
-				if (localExecuter != null)
-					localExecuter.cancelTask();
-			}
-
-			if (lastPos.x > 0 && lastPos.y > 0) {
-				final int dx = event.getX() - lastPos.x;
-				final int dy = event.getY() - lastPos.y;
-
-				// TODO Stop dragging when the drag would not be valid...
-				// boolean dragValid = true;
-				// // check if this panning results in a valid mapArea
-				// {
-				// Rectangle winBounds = xMapPane.getBounds();
-				// winBounds.translate(xMapPane.imageOrigin.x,
-				// -xMapPane.imageOrigin.y);
-				// Envelope newMapAreaBefore = xMapPane.tranformWindowToGeo(
-				// winBounds.x, winBounds.y, winBounds.x
-				// + winBounds.width, winBounds.y
-				// + winBounds.height);
-				//					
-				//
-				// winBounds = xMapPane.getBounds();
-				// Point testIng = new Point(xMapPane.imageOrigin);
-				// testIng.translate(dx, dy);
-				// winBounds.translate(testIng.x, -testIng.y);
-				// Envelope newMapAreaAfter = xMapPane.tranformWindowToGeo(
-				// winBounds.x, winBounds.y, winBounds.x
-				// + winBounds.width, winBounds.y
-				// + winBounds.height);
-				//
-				// // If the last drag doesn't change the MapArea anymore cancel
-				// it.
-				// if (xMapPane.bestAllowedMapArea(newMapAreaAfter).equals(
-				// xMapPane.bestAllowedMapArea(newMapAreaBefore))){
-				// dragValid = false;
-				// return;
-				// }
-				// }
-
-				imageOrigin.translate(dx, dy);
-				updateFinalImage();
-				repaint();
-			}
-
-		} else if ((getState() == XMapPane.ZOOM_IN)
-				|| (getState() == XMapPane.ZOOM_OUT)
-				|| (getState() == XMapPane.SELECT_ALL)
-				|| (getState() == XMapPane.SELECT_TOP)
-		// || (getState() == XMapPane.SELECT_ONE_FROM_TOP)
-		) {
-			final Graphics graphics = getGraphics();
-
-			drawRectangle(graphics, startPos, event.getPoint());
-
-			if ((lastPos.x > 0) && (lastPos.y > 0)) {
-				drawRectangle(graphics, startPos, lastPos);
-			}
-
-			graphics.dispose();
-
-		}
-
-	}
-
-	/**
-	 * Called by the {@link RenderingExecutor} when rendering was cancelled.
-	 */
-	public void onRenderingCancelled() {
-		repaintTimer.stop();
-		LOGGER.debug("Rendering cancelled");
-	}
-
-	/**
-	 * Called by the {@link RenderingExecutor} when rendering has been
-	 * completed.
-	 */
-	public void onRenderingCompleted() {
-		repaintTimer.stop();
-		updateFinalImage();
-		repaint();
-	}
-
-	/**
-	 * Called by the {@linkplain XMapPane.RenderingTask} when rendering failed.
-	 * Publishes a {@linkplain MapPaneEvent} of type {@code
-	 * MapPaneEvent.Type.RENDERING_STOPPED} to listeners.
-	 * 
-	 * @param renderingError
-	 *            The error that occured during rendering
-	 * 
-	 * @see MapPaneListener#onRenderingStopped(org.geotools.swing.event.MapPaneEvent)
-	 */
-	public void onRenderingFailed(final Exception renderingError) {
-		repaintTimer.stop();
-		LOGGER.warn("Rendering failed", renderingError);
-		updateFinalImage();
-		repaint();
-
-	}
-
-	public void onRenderingPending() {
-		// LOGGER.debug("Pending rendering updates the preview...");
-		updateFinalImage();
-		repaint();
-	}
-
-	@Override
-	protected void paintComponent(final Graphics g) {
-		if (!acceptsRepaintCalls)
-			return;
-
-		// Maybe update the cursor and maybe stop the repainting timer
-		updateCursor();
-
-		super.paintComponent(g); // candidate for removal
-
-		boolean paintedSomething = false;
-
-		if (mapImageInvalid) { /* if the map changed then redraw */
-
-			mapImageInvalid = false; // Reset for next round
-
-			// If the new mapArea and the oldMapArea intersect, we can draw some
-			// quick scaled preview to make the user feel that something is
-			// happening.
-			if (mapAreaChanged && oldMapArea != null
-					&& getMapArea().intersects(oldMapArea)
-					& !getMapArea().equals(oldMapArea)) {
-
-				mapAreaChanged = false;
-
-				if (getMapArea().covers(oldMapArea)) {
-					setQuickPreviewHint(ZOOM_OUT);
-					paintedSomething = drawScaledPreviewImage_Zoom((Graphics2D) g);
-				} else if (oldMapArea.covers(getMapArea())) {
-					setQuickPreviewHint(ZOOM_IN);
-					paintedSomething = drawScaledPreviewImage_Zoom((Graphics2D) g);
-				}
-
-			}
-
-			if (paneResized) {
-				paneResized = false;
-				disposeImages();
-			}
-
-			// Start the Threads and Timers to render the image
-			requestStartRendering();
-
-		}
-
-		if (!paintedSomething) {
-
-			g.drawImage(getFinalImage(), 0, 0, null);
-
-			g.dispose(); // cand. for removal
-
-			paintedSomething = true; // cand. for removal
-		}
-
-	}
-
-	/**
-	 * Heavily works on releasing all resources related to the four
-	 * {@link Image}s used to cache the results of the different renderers.<br>
-	 * In November 2009 i had some memory leaking problems with {@link XMapPane}
-	 * . The resources of the buffered images were never released. It seem to be
-	 * important to call the GC right after flushing the images.<br>
-	 * Hence this method may take a while, because it calls the GC up to four
-	 * times.
-	 */
-	private void disposeImages() {
-
-		// System.out.println("vorher = "
-		// + new MbDecimalFormatter().format(LangUtil.gcTotal()));
-		// bi.flush();
-		// return bi = null;
-		// System.out.println("nacher = "
-		// + new MbDecimalFormatter().format(LangUtil.gcTotal()));
-		//
-		// System.out.println("\n");
-
-		if (preFinalImage != null) {
-			preFinalImage.flush();
-			preFinalImage = null;
-			LangUtil.gc();
-		}
-		if (finalImage != null) {
-			finalImage.flush();
-			finalImage = null;
-			LangUtil.gc();
-		}
-		if (localImage != null) {
-			localImage.flush();
-			localImage = null;
-			LangUtil.gc();
-		}
-		if (bgImage != null) {
-			bgImage.flush();
-			bgImage = null;
-			LangUtil.gc();
-		}
-
-	}
-
-	/**
-	 * Performs a {@value #PAN} action. During panning, the displacement is
-	 * stored in {@link #imageOrigin} object. Calling {@link #performPan()} will
-	 * reset the offset and call {@link #setMapArea(Envelope)}.
-	 */
-	public void performPan() {
-
-		final Rectangle winBounds = getBounds();
-		winBounds.translate(-imageOrigin.x, -imageOrigin.y);
-		final Envelope newMapArea = tranformWindowToGeo(winBounds.x,
-				winBounds.y, winBounds.x + winBounds.width, winBounds.y
-						+ winBounds.height);
-
-		imageOrigin.x = 0;
-		imageOrigin.y = 0;
-
-		if (!setMapArea(newMapArea)) {
-			/**
-			 * If setMapArea returns true, the finalImage is updated anyways.
-			 * This if-case exists to ensure that we repaint a correct image
-			 * even if the new panning area has been denied.
-			 */
-			updateFinalImage();
-			repaint();
-		}
-
-		if (getCursor() == SwingUtil.PANNING_CURSOR)
-			setCursor(SwingUtil.PAN_CURSOR);
-	}
-
-	//
-	// /**
-	// * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein Screenshot
-	// gemacht
-	// * wird) wird. Dann werden wird der Hintergrund auf WEISS gesetzt.
-	// *
-	// * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	// * Kr&uuml;ger</a>
-	// */
-	// @Override
-	// public void print(final Graphics g) {
-	// final Color orig = getBackground();
-	// setBackground(Color.WHITE);
-	//
-	// // wrap in try/finally so that we always restore the state
-	// try {
-	// super.print(g);
-	// } finally {
-	// setBackground(orig);
-	// }
-	// }
-
-	/**
-	 * Entfernt einen Listener von der Map.
-	 * 
-	 * @param l
-	 *            zu entfernender Listener
-	 */
-	public void removeMapPaneListener(final JMapPaneListener l) {
-		mapPaneListeners.remove(l);
-	}
-
-	/**
-	 * Cancels all running renderers and sets the flag to start new ones. <br>
-	 * 
-	 * @see #startRenderThreadsTimer
-	 */
-	private void requestStartRendering() {
-		if (bgExecuter != null)
-			bgExecuter.cancelTask();
-		if (localExecuter != null)
-			localExecuter.cancelTask();
-		requestStartRendering = true;
-
-	}
-
-	/**
-	 * Berechnet die Transformation zwischen Fenster- und Karten-Koordinaten
-	 * neu.
-	 */
-	protected void resetTransforms() {
-		if (getMapArea() == null || getWidth() == 0 || getHeight() == 0)
-			return;
-
-		// We store the last Transform
-		oldScreenToWorld = screenToWorld;
-
-		this.screenToWorld = new AffineTransform(
-		// Genauso wie die Fenster-Koordinaten, werden die Longitude-Koordinaten
-				// nach rechts (Osten) hin groesser
-				// --> positive Verschiebung
-				getMapArea().getWidth() / getWidth(),
-				// keine Verzerrung
-				0.0, 0.0,
-				// Waehrend die Fenster-Koordinaten nach unten hin groesser
-				// werden,
-				// werden Latitude-Koordinaten nach Sueden hin keiner
-				// --> negative Verschiebung
-				-getMapArea().getHeight() / getHeight(),
-				// Die Longitude-Koordinaten werden nach Osten hin groesser
-				// --> obere linke Ecke des Fensters hat also den Minimalwert
-				getMapArea().getMinX(),
-				// Die Latitude-Koordinaten werden nach Norden hin groesser
-				// --> obere linke Ecke des Fensters hat also den Maximalwert
-				getMapArea().getMaxY());
-
-		try {
-			this.worldToScreen = screenToWorld.createInverse();
-		} catch (final NoninvertibleTransformException e) {
-			LOGGER.error(e);
-		}
-	}
-
-	public void setBgContext(final MapContext context) {
-
-		// Remove the default listener from the old context
-		if (this.bgContext != null) {
-			this.bgContext.removeMapLayerListListener(bgContextListener);
-
-			// adding listener to all layers
-			for (final MapLayer mapLayer : bgContext.getLayers()) {
-				mapLayer.removeMapLayerListener(bgMapLayerListener);
-			}
-		}
-
-		this.bgContext = context;
-
-		if (context != null) {
-			setMapArea(bgContext.getAreaOfInterest());
-
-			this.bgContext.addMapLayerListListener(bgContextListener);
-
-			// adding listener to all layers
-			for (final MapLayer mapLayer : bgContext.getLayers()) {
-				mapLayer.addMapLayerListener(bgMapLayerListener);
-			}
-		}
-		mapImageInvalid = true;
-		repaint();
-	}
-
-	public void setJava2dHints(final RenderingHints java2dHints) {
-		this.java2dHints = java2dHints;
-	}
-
-	/**
-	 * 
-	 * @param context
-	 */
-	public void setLocalContext(final MapContext context) {
-		// Remove the default listener from the old context
-		if (this.localContext != null) {
-			this.localContext.removeMapLayerListListener(localContextListener);
-
-			// adding listener to all layers
-			for (final MapLayer mapLayer : localContext.getLayers()) {
-				mapLayer.removeMapLayerListener(localMapLayerListener);
-			}
-		}
-
-		this.localContext = context;
-
-		if (context != null) {
-
-			setMapArea(localContext.getAreaOfInterest());
-
-			localRenderer.setContext(localContext);
-
-			this.localContext.addMapLayerListListener(localContextListener);
-
-			// adding listener to all layers
-			for (final MapLayer mapLayer : localContext.getLayers()) {
-				mapLayer.addMapLayerListener(localMapLayerListener);
-			}
-		}
-
-		mapImageInvalid = true;
-		repaint();
-	}
-
-	/**
-	 * @param newMapArea
-	 * @return <code>true</code> if the mapArea has been changed and a repaint
-	 *         has been triggered.
-	 */
-	public boolean setMapArea(final Envelope newMapArea) {
-
-		if (newMapArea == null
-				|| bestAllowedMapArea(newMapArea).equals(mapArea)) {
-			// No change.. no need to repaint
-			return false;
-		}
-
-		// Testing, whether NaN or Infinity are used in the newMapArea
-		if (newMapArea.isNull() || Double.isInfinite(newMapArea.getMaxX())
-				|| Double.isInfinite(newMapArea.getMaxY())
-				|| Double.isInfinite(newMapArea.getMinX())
-				|| Double.isInfinite(newMapArea.getMinY())) {
-			// No change.. ugly new values
-			LOGGER.warn("setMapArea has been called with newArea = "
-					+ newMapArea);
-			return false;
-		}
-
-		// Testing, whether the difference if just minimal
-		if (mapArea != null) {
-			final Envelope candNew = bestAllowedMapArea(newMapArea);
-			final double tolX = mapArea.getWidth() / 1000.;
-			final double tolY = mapArea.getHeight() / 1000.;
-			if ((candNew.getMinX() - tolX < mapArea.getMinX())
-					&& (mapArea.getMinX() < candNew.getMinX() + tolX)
-					&& (candNew.getMaxX() - tolX < mapArea.getMaxX())
-					&& (mapArea.getMaxX() < candNew.getMaxX() + tolX)
-
-					&& (candNew.getMinY() - tolY < mapArea.getMinY())
-					&& (mapArea.getMinY() < candNew.getMinY() + tolY)
-					&& (candNew.getMaxY() - tolY < mapArea.getMaxY())
-					&& (mapArea.getMaxY() < candNew.getMaxY() + tolY)
-
-			) {
-				// The two mapAreas only differ my 1/1000th.. ignore
-
-				return false;
-			}
-		}
-
-		oldMapArea = mapArea;
-
-		this.mapArea = bestAllowedMapArea(newMapArea);
-
-		if (localContext != null) {
-			localContext.setAreaOfInterest(mapArea, localContext
-					.getCoordinateReferenceSystem());
-		}
-		if (bgContext != null) {
-			bgContext.setAreaOfInterest(mapArea, localContext
-					.getCoordinateReferenceSystem());
-		}
-		resetTransforms();
-		mapImageInvalid = true;
-		mapAreaChanged = true;
-		repaint();
-
-		// LOGGER.debug("New maparea = " + mapArea);
-		return true;
-	}
-
-	/**
-	 * Set the background color of the map.
-	 * 
-	 * @param if <code>null</code>, white is used.
-	 */
-	public void setMapBackgroundColor(Color bgColor) {
-		if (bgColor == null)
-			bgColor = Color.WHITE;
-		this.mapBackgroundColor = bgColor;
-	}
-
-	/**
-	 * Set the BufferedImage to use as a flaoting icon in the lower right corner
-	 * 
-	 * @param mapImageIcon
-	 *            <code>null</code> is allowed and deactivates this icon.
-	 */
-	public void setMapImage(final BufferedImage mapImage) {
-		this.mapImage = mapImage;
-	}
-
-	/**
-	 * Sets whether a layer is regarded or ignored on {@link #SELECT_TOP},
-	 * {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP} actions.
-	 * 
-	 * @param layer
-	 *            a layer
-	 * @param selectable
-	 *            if {@code false} the layer is ignored during the upper
-	 *            mentioned actions. If <code>null</code>, the default (true)
-	 *            will be used.
-	 */
-	public void setMapLayerSelectable(final MapLayer layer,
-			final Boolean selectable) {
-		if (selectable == null)
-			mapLayerSelectable.remove(layer);
-		else
-			mapLayerSelectable.put(layer, selectable);
-	}
-
-	/**
-	 * Defines an evelope of the viwable area. The JMapPane will never show
-	 * anything outside of this extend.
-	 * 
-	 * @param maxExtend
-	 *            <code>null</code> to not have this restriction.
-	 */
-	public void setMaxExtend(final Envelope maxExtend) {
-		this.maxExtend = maxExtend;
-	}
-
-	/**
-	 * Set the maximum allowed zoom scale. This is the smaller number value of
-	 * the two. If <code>null</code> is passed, Double.MINVALUE are used which
-	 * mean there is no restriction.
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public void setMaxZoomScale(final Double maxZoomScale) {
-		this.maxZoomScale = maxZoomScale == null ? Double.MIN_VALUE
-				: maxZoomScale;
-	}
-
-	// /** Stored the time used for the last real rendering in ms. **/
-	// private long lastRenderingDuration = Long.MAX_VALUE;
-
-	/**
-	 * Set the minimum (nearest) allowed zoom scale. This is the bigger number
-	 * value of the two. If <code>null</code> is passed, Double.MAXVALUE are
-	 * used which mean there is no restriction.
-	 * 
-	 * 
-	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
-	 *         Kr&uuml;ger</a>
-	 */
-	public void setMinZoomScale(final Double minZoomScale) {
-		this.minZoomScale = minZoomScale == null ? Double.MAX_VALUE
-				: minZoomScale;
-	}
-
-	/**
-	 * 
-	 * @param b
-	 */
-	public void setPainting(final boolean b) {
-		acceptsRepaintCalls = b;
-	}
-
-	// /**
-	// * Returns in milli seconds the time the last rending of the
-	// * {@link SelectableXMapPane} took. #Long.MAX_VALUE if the JMapPane has
-	// not
-	// * been rendered yet.
-	// */
-	// public long getLastRenderingDuration() {
-	// return lastRenderingDuration;
-	// }
-
-	public void setQuickPreviewHint(final int quickPreviewHint) {
-		this.quickPreviewHint = quickPreviewHint;
-
-	}
-
-	private void setRendererHints(final Map<Object, Object> rendererHints) {
-		this.rendererHints = rendererHints;
-	}
-
-	/**
-	 * Enables/Disables the ZOOM Mouse Listener. Upates the Cursor and stops the
-	 * repaint Timer if
-	 * 
-	 * @param state
-	 */
-	public void setState(final int state) {
-		this.state = state;
-
-		zoomMapPaneMouseListener.setEnabled((state == ZOOM_IN
-				|| state == ZOOM_OUT || state == PAN));
-
-		// Je nach Aktion den Cursor umsetzen
-		updateCursor();
-	}
-
-	/**
-	 * Standardmaessig wird der Cursor automatisch je nach MapPane-Aktion (Zoom,
-	 * Auswahl, ...) gesetzt. Mit dieser Methode kann ein statischer Cursor
-	 * gesetzt werden, der unabhaengig von der aktuellen MapPanes-Aktion
-	 * beibehalten wird. Um diesen statischen Cursor wieder zu entfernen, kann
-	 * {@code null} als Parameter uebergeben werden
-	 * 
-	 * @param cursor
-	 *            Cursor
-	 */
-	public void setStaticCursor(final Cursor cursor) {
-		this.staticCursor = cursor;
-		if (cursor != null)
-			super.setCursor(cursor);
-	}
-
-	/**
-	 * Starts rendering on one or two threads
-	 */
-	private void startRendering() {
-
-		if (!isWellDefined())
-			return;
-
-		if (bgExecuter != null) {
-			// Stop all renderers
-			bgExecuter.cancelTask();
-		}
-
-		if (localExecuter != null) {
-			localExecuter.cancelTask();
-		}
-
-		final Rectangle curPaintArea = getVisibleRect();
-
-		/**
-		 * We have to set new renderer
-		 */
-
-		if (getBgContext() != null) {
-			// bgExecuter = new RenderingExecutor();
-			// LOGGER.debug("starting bg renderer:");
-			// // /* System.out.println("rendering"); */
-			// final GTRenderer createGTRenderer = GTUtil.createGTRenderer(
-			// bgContext, getRendererHints());
-			// createGTRenderer.setJava2DHints(getJava2dHints());
-			// bgExecuter.submit(getBgContext().getAreaOfInterest(),
-			// curPaintArea,
-			// (Graphics2D) getBgImage().getGraphics(), createGTRenderer);
-		}
-
-		if (getContext() != null) {
-			// localExecuter = new RenderingExecutor(this, 150l);
-			// LOGGER.debug("starting local renderer:");
-
-			// Clear label cache
-			labelCache.clear();
-			final Map<Object, Object> rh = localRenderer.getRendererHints();
-			rh.put(StreamingRenderer.LABEL_CACHE_KEY, labelCache);
-			localRenderer.setRendererHints(rh);
-
-			final boolean submitted = localExecuter.submit(getContext()
-					.getAreaOfInterest(), curPaintArea,
-					(Graphics2D) getLocalImage().getGraphics(), localRenderer,
-					getWorldToScreenTransform());
-			if (submitted)
-				repaintTimer.restart();
-			else
-				requestStartRendering = true; // Try to start rendering again in
-			// a moment
-		}
-
-		updateCursor();
-	}
-
-	/**
-	 * Transformiert einen Geo-Koordinaten-Bereich in Fenster-Koordinaten.
-	 * 
-	 * @param ox
-	 *            X-Koordinate der VON-Position
-	 * @param oy
-	 *            Y-Koordinate der VON-Position
-	 * @param px
-	 *            X-Koordinate der BIS-Position
-	 * @param py
-	 *            Y-Koordinate der BIS-Position
-	 * @param winToGeotransform
-	 *            Eine Window to Geo transform. If <code>null</code>,
-	 *            {@link #getScreenToWorld()} is used.
-	 */
-	public Envelope tranformGeoToWindow(final double ox, final double oy,
-			final double px, final double py,
-			final AffineTransform winToGeotransform) {
-		final AffineTransform at = winToGeotransform == null ? getScreenToWorld()
-				: winToGeotransform;
-		Point2D geoO;
-		try {
-			geoO = at.inverseTransform(new Point2D.Double(ox, oy), null);
-			final Point2D geoP = at.inverseTransform(
-					new Point2D.Double(px, py), null);
-			return new Envelope(geoO.getX(), geoP.getX(), geoO.getY(), geoP
-					.getY());
-		} catch (final NoninvertibleTransformException e) {
-			LOGGER.error(e);
-			return new Envelope(ox, oy, px, py);
-		}
-	}
-
-	/**
-	 * Transformiert einen Fenster-Koordinaten-Bereich in Geo-Koordinaten.
-	 * 
-	 * @param ox
-	 *            X-Koordinate der VON-Position
-	 * @param oy
-	 *            Y-Koordinate der VON-Position
-	 * @param px
-	 *            X-Koordinate der BIS-Position
-	 * @param py
-	 *            Y-Koordinate der BIS-Position
-	 */
-	public Envelope tranformWindowToGeo(final int ox, final int oy,
-			final int px, final int py) {
-		final AffineTransform at = getScreenToWorld();
-		final Point2D geoO = at.transform(new Point2D.Double(ox, oy), null);
-		final Point2D geoP = at.transform(new Point2D.Double(px, py), null);
-		return new Envelope(geoO.getX(), geoP.getX(), geoO.getY(), geoP.getY());
-	}
-
-	/**
-	 * Will update the cursor. If all rendering is finished also stops the
-	 * {@link #repaintTimer}
-	 */
-	public void updateCursor() {
-
-		// if the renderers have stopped, also stop the timer that is updating
-		// the final image
-		if (bgExecuter != null && bgExecuter.isRunning()
-				|| localExecuter != null && localExecuter.isRunning()) {
-			setCursor(WAIT_CURSOR);
-			return;
-		} else {
-			// Allow one last rendering
-			if (repaintTimer.isRunning()) {
-				repaintTimer.stop();
-				updateFinalImage();
-				repaint();
-			}
-		}
-
-		// wenn manueller Cursor gesetzt ist, dann diesen verwenden (unabhaengig
-		// von der aktuellen Aktion
-		if (this.staticCursor != null) {
-			setCursor(staticCursor);
-			return;
-		}
-		if (getCursor() == SwingUtil.PANNING_CURSOR) {
-			// This cursor will reset itself
-			return;
-		}
-
-		// Set the cursor depending on what tool is in use...
-		switch (state) {
-		case SELECT_TOP:
-		case SELECT_ONE_FROM_TOP:
-		case SELECT_ALL:
-			setCursor(SwingUtil.CROSSHAIR_CURSOR);
-			break;
-		case ZOOM_IN:
-			setCursor(SwingUtil.ZOOMIN_CURSOR);
-			break;
-		case ZOOM_OUT:
-			setCursor(SwingUtil.ZOOMOUT_CURSOR);
-			break;
-		case PAN:
-			setCursor(SwingUtil.PAN_CURSOR);
-			break;
-		default:
-			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
-			break;
-		}
-	}
-
-	/**
-	 * The renderers are all rendering into their own {@link Image}s. This
-	 * method combines all images to one {@link #finalImage}. The
-	 * {@link #repaintTimer} is calling this method regularely to update the
-	 * {@link #finalImage} even if the renderers are still working.
-	 */
-	synchronized protected Image updateFinalImage() {
-
-		// Render the two map images first, into the preFinalImage
-		if (bgExecuter != null) {
-			final Graphics2D preFinalG = (Graphics2D) getPreFinalImage()
-					.getGraphics();
-			preFinalG.setBackground(getMapBackgroundColor());
-
-			preFinalG.drawImage(getBgImage(), 0, 0, getMapBackgroundColor(),
-					null);
-
-			// // Draw the local layers image
-			preFinalG.drawImage(getLocalImage(), 0, 0, null);
-			preFinalG.dispose();
-
-		} else {
-			preFinalImage = getLocalImage();
-		}
-
-		final Graphics2D finalG = getFinalImage().createGraphics();
-		finalG.setBackground(getMapBackgroundColor());
-		finalG.drawImage(getPreFinalImage(), imageOrigin.x, imageOrigin.y,
-				getMapBackgroundColor(), null);
-
-		final int finalImageHeight = getFinalImage().getHeight(null);
-		final int finalImageWidth = getFinalImage().getWidth(null);
-
-		final Rectangle painedArea = new Rectangle(imageOrigin.x,
-				imageOrigin.y, finalImageWidth, finalImageHeight);
-		SwingUtil.clearAround(finalG, painedArea, getVisibleRect());
-
-		addGadgets(finalG);
-
-		finalG.dispose();
-
-		return finalImage;
-	}
-
-	/**
-	 * Paints some optional stuff into the given {@link Graphics2D}. Usually
-	 * called as the last layer when {@link #updateFinalImage()}
-	 */
-	private void addGadgets(final Graphics2D graphics) {
-
-		// Paint a logo to the bottom right if available
-		if (mapImage != null) {
-			graphics.drawImage(mapImage, getBounds().width
-					- mapImage.getWidth() - 10, getBounds().height
-					- mapImage.getHeight() - 10, null);
-		}
-
-		// If the rendering process is still running, indicate this is the image
-		if (bgExecuter != null && bgExecuter.isRunning()
-				|| localExecuter != null && localExecuter.isRunning()) {
-
-			final Color c = graphics.getColor();
-			graphics.setFont(waitFont);
-			String waitStr = "Wait..."; //i8n
-			
-			graphics.setColor(Color.WHITE);
-			graphics.drawString(waitStr, 39, 69); 
-			graphics.setColor(Color.BLACK);
-			graphics.drawString(waitStr, 40, 70); 
-
-			graphics.setColor(c);
-		}
-
-	}
-
-	/**
-	 * Sets the {@link #mapArea} to best possibly present the given features. If only one
-	 * single point is given, the window is moved over the point.
-	 * 
-	 * @param features
-	 *            if <code>null</code> or size==0, the function doesn nothing.
-	 */
-	public void zoomTo(
-			final FeatureCollection<SimpleFeatureType, SimpleFeature> features) {
-
-		final CoordinateReferenceSystem mapCRS = getContext()
-				.getCoordinateReferenceSystem();
-		final CoordinateReferenceSystem fCRS = features.getSchema()
-				.getGeometryDescriptor().getCoordinateReferenceSystem();
-
-		double width = mapArea.getWidth();
-		double height = mapArea.getHeight();
-		final double ratio = height / width;
-
-		if (features == null || features.size() == 0) {
-			// feature count == 0 Zoom to the full extend
-			return;
-		} else if (features.size() == 1) {
-
-			// feature count == 1 Just move the window to the point and zoom 'a
-			// bit'
-			final SimpleFeature singleFeature = features.iterator().next();
-
-			if (((Geometry) singleFeature.getDefaultGeometry())
-					.getCoordinates().length > 1) {
-				// System.out.println("Zoomed to only pne poylgon");
-				// Poly
-				// TODO max width vs. height
-				width = features.getBounds().getWidth() * 3;
-				height = ratio * width;
-			} else {
-				// System.out.println("Zoomed in a bit becasue only one point");
-				// width *= .9;
-				// height *= .9;
-			}
-
-			Coordinate centre = features.getBounds().centre();
-			if (!mapCRS.equals(fCRS)) {
-				// only to calculations if the CRS differ
-				try {
-					MathTransform fToMap;
-					fToMap = CRS.findMathTransform(fCRS, mapCRS);
-					// centre is transformed to the mapCRS
-					centre = JTS.transform(centre, null, fToMap);
-				} catch (final FactoryException e) {
-					LOGGER.error("Looking for a Math transform", e);
-				} catch (final TransformException e) {
-					LOGGER.error("Looking for a Math transform", e);
-				}
-			}
-
-			final Coordinate newLeftBottom = new Coordinate(centre.x - width
-					/ 2., centre.y - height / 2.);
-			final Coordinate newTopRight = new Coordinate(
-					centre.x + width / 2., centre.y + height / 2.);
-
-			final Envelope newMapArea = new Envelope(newLeftBottom, newTopRight);
-
-			setMapArea(newMapArea);
-
-		} else {
-			final ReferencedEnvelope fBounds = features.getBounds();
-
-			Envelope bounds;
-			if (!mapCRS.equals(fCRS)) {
-				bounds = JTSUtil.transformEnvelope(fBounds, fCRS, mapCRS);
-			} else {
-				bounds = fBounds;
-			}
-			// BB umrechnen von Layer-CRS in Map-CRS
-
-			// Expand a bit
-			bounds.expandBy(bounds.getWidth() / 6., bounds.getHeight() / 6.);
-
-			setMapArea(bounds);
-		}
-	}
-
-	/**
-	 * Zooms towards a point.
-	 * 
-	 * @param center
-	 *            position in window coordinates
-	 * @param zoomFactor
-	 *            > 1 for zoom in, < 1 for zoom out. Default is 1.33
-	 */
-	public void zoomTo(final Point center) {
-		zoomTo(center, null);
-	}
-
-	/**
-	 * Zooms towards a point.
-	 * 
-	 * @param center
-	 *            position in window coordinates
-	 * @param zoomFaktor
-	 *            > 1 for zoom in, < 1 for zoom out. Default is 1.33.
-	 */
-	public void zoomTo(Point center, Double zoomFaktor) {
-		if (zoomFaktor == null || zoomFaktor == 0.)
-			zoomFaktor = 2.;
-
-		final Point2D gcenter = getScreenToWorld().transform(center, null);
-		center = null;
-
-		if (Double.isNaN(gcenter.getX()) || Double.isNaN(gcenter.getY())
-				|| Double.isInfinite(gcenter.getX())
-				|| Double.isInfinite(gcenter.getY())
-
-		) {
-			// Not inside valid CRS area! cancel
-			return;
-		}
-
-		final Envelope mapArea = getMapArea();
-
-		final Envelope newMapArea = new Envelope(mapArea);
-		newMapArea.expandBy((mapArea.getWidth() * zoomFaktor - mapArea
-				.getWidth()) / 2., (mapArea.getHeight() * zoomFaktor - mapArea
-				.getHeight()) / 2.);
-
-		// Move the newMapArea above the new center
-		newMapArea.translate(gcenter.getX() - mapArea.centre().x, gcenter
-				.getY()
-				- mapArea.centre().y);
-
-		setMapArea(newMapArea);
-	}
-
-}

Deleted: branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,251 +0,0 @@
-package gtmig.org.geotools.swing;
-
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.event.InputEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-
-import org.apache.log4j.Logger;
-
-import schmitzm.swing.SwingUtil;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geom.Envelope;
-
-public class ZoomXMapPaneMouseListener extends MouseAdapter {
-
-	private static Logger LOGGER = Logger
-			.getLogger(ZoomXMapPaneMouseListener.class);
-
-
-	protected final XMapPane xMapPane;
-
-	protected boolean enabled = true;
-
-	public ZoomXMapPaneMouseListener(XMapPane xMapPane) {
-		if (xMapPane == null) throw new IllegalArgumentException("xMapPane may not be null");
-		this.xMapPane = xMapPane;
-	}
-
-	/**
-	 * Stores beginning of a drag event in window coordinates
-	 */
-	protected Point startPos;
-	
-	/**
-	 * Stores last position of a drag event in window coordinates
-	 */
-	protected  Point lastPos;
-
-	/**
-	 * Draws a rectangle in XOR mode from the origin at {@link #startPos} to the
-	 * given point. All in screen coordinates.
-	 */
-	protected void drawRectangle(final Graphics graphics, Point e) {
-		// undraw last box/draw new box
-		final int left = Math.min(startPos.x, e.x);
-		final int right = Math.max(startPos.x, e.x);
-		final int top = Math.max(startPos.y, e.y);
-		final int bottom = Math.min(startPos.y, e.y);
-		final int width = right - left;
-		final int height = top - bottom;
-
-		if (width == 0 && height == 0)
-			return;
-
-		graphics.setXORMode(Color.WHITE);
-		graphics.drawRect(left, bottom, width, height);
-	}
-
-	public void mousePressed(final MouseEvent e) {
-		if (!isEnabled())
-			return;
-		startPos = new Point(e.getPoint());
-		lastPos = new Point(e.getPoint());
-	}
-
-	public void mouseReleased(final MouseEvent e) {
-		if (!isEnabled())
-			return;
-
-		final Rectangle bounds = xMapPane.getBounds();
-
-		Envelope mapArea = xMapPane.getMapArea();
-
-		// Replace with transform and translate
-		final double mapWidth = mapArea.getWidth();
-		final double mapHeight = mapArea.getHeight();
-
-		final double startX = ((startPos.x * mapWidth) / (double) bounds.width)
-				+ mapArea.getMinX();
-		final double startY = (((bounds.getHeight() - startPos.y) * mapHeight) / (double) bounds.height)
-				+ mapArea.getMinY();
-		final double endX = ((e.getPoint().x * mapWidth) / (double) bounds.width)
-				+ mapArea.getMinX();
-		final double endY = (((bounds.getHeight() - e.getPoint().y) * mapHeight) / (double) bounds.height)
-				+ mapArea.getMinY();
-
-		if ((xMapPane.getState() == XMapPane.PAN)
-				|| (e.getButton() == MouseEvent.BUTTON3)) {
-			xMapPane.performPan();
-
-		} else if (xMapPane.getState() == XMapPane.ZOOM_IN) {
-
-			drawRectangle(xMapPane.getGraphics(), e.getPoint());
-
-
-			// Don't zoom too small areas. Handle it like a click then
-			if ((Math.abs(startPos.x - e.getPoint().x) * Math.abs(e.getPoint().y - startPos.y)) < 160) {
-				xMapPane.zoomTo(e.getPoint(), .5);
-				return;
-			}
-
-			final double left = Math.min(startX, endX);
-			final double right = Math.max(startX, endX);
-			final double bottom = Math.min(startY, endY);
-			final double top = Math.max(startY, endY);
-			final Coordinate ll = new Coordinate(left, bottom);
-			final Coordinate ur = new Coordinate(right, top);
-
-			xMapPane.setMapArea(new Envelope(ll, ur));
-
-		} else if (xMapPane.getState() == XMapPane.ZOOM_OUT) {
-
-			// Remove the rectangle
-			drawRectangle(xMapPane.getGraphics(), e.getPoint());
-			
-			
-
-			// Don't zoom too small areas
-			if ((Math.abs(startPos.x - e.getPoint().x) * Math.abs(e.getPoint().y - startPos.y)) < 100) {
-				xMapPane.zoomTo(e.getPoint(), 2.);
-				return;
-			}
-
-			// make the dragged rectangle in screen coords the new map size?
-			final double left = Math.min(startX, endX);
-			final double right = Math.max(startX, endX);
-			final double bottom = Math.min(startY, endY);
-			final double top = Math.max(startY, endY);
-			final double nWidth = (mapWidth * mapWidth) / (right - left);
-			final double nHeight = (mapHeight * mapHeight) / (top - bottom);
-			final double deltaX1 = left - mapArea.getMinX();
-			final double nDeltaX1 = (deltaX1 * nWidth) / mapWidth;
-			final double deltaY1 = bottom - mapArea.getMinY();
-			final double nDeltaY1 = (deltaY1 * nHeight) / mapHeight;
-			final Coordinate ll = new Coordinate(mapArea.getMinX() - nDeltaX1,
-					mapArea.getMinY() - nDeltaY1);
-			final double deltaX2 = mapArea.getMaxX() - right;
-			final double nDeltaX2 = (deltaX2 * nWidth) / mapWidth;
-			final double deltaY2 = mapArea.getMaxY() - top;
-			final double nDeltaY2 = (deltaY2 * nHeight) / mapHeight;
-			final Coordinate ur = new Coordinate(mapArea.getMaxX() + nDeltaX2,
-					mapArea.getMaxY() + nDeltaY2);
-
-			xMapPane.setMapArea(new Envelope(ll, ur));
-		}
-
-
-	}
-
-
-	public void setEnabled(boolean enabled) {
-		this.enabled = enabled;
-	}
-
-	/**
-	 * Returns <code>true</code> if the xMapPane is well defined and the
-	 * listener is enabled.
-	 */
-	public boolean isEnabled() {
-		return enabled && xMapPane.isWellDefined();
-	}
-	
-	/**
-	 * Verarbeitet die Mausrad-Aktion, indem gezoomed wird.
-	 * 
-	 * @param wheelEvt
-	 *            Mausrad-Event
-	 */
-	public void mouseWheelMoved(MouseWheelEvent wheelEvt) {
-		if (!isEnabled()) return;
-
-		final int units = wheelEvt.getUnitsToScroll();
-		if (units > 0)
-			xMapPane.zoomTo(wheelEvt.getPoint(), 1. + .11 * units);
-		else
-			xMapPane.zoomTo(wheelEvt.getPoint(), 2. / -units);
-	}
-
-	@Override
-	public void mouseClicked(final MouseEvent e) {
-
-		if (!isEnabled())
-			return;
-
-		int state = xMapPane.getState();
-
-		// Nothing to do for panning
-		if (state == XMapPane.PAN)
-			return;
-
-		// double zoomFactor = 1.;
-		//		
-		// final Envelope mapArea = xMapPane.getMapArea();
-		// if (mapArea == null)
-		// return;
-		//
-		// final Rectangle bounds = xMapPane.getVisibleRect();
-		//
-		// final double x = (double) (e.getX());
-		// final double y = (double) (e.getY());
-		// final double width = mapArea.getWidth();
-		// final double height = mapArea.getHeight();
-		// final double width2 = width / 2.0;
-		// final double height2 = height / 2.0;
-		// final double mapX = ((x * width) / (double) bounds.width)
-		// + mapArea.getMinX();
-		// final double mapY = (((bounds.getHeight() - y) * height) / (double)
-		// bounds.height)
-		// + mapArea.getMinY();
-		// double zlevel = 1.0;
-		//		
-		if (state == XMapPane.ZOOM_IN && e.getButton() == MouseEvent.BUTTON1) {
-			xMapPane.zoomTo(e.getPoint(), 1 / 2.);
-		} else if (state == XMapPane.ZOOM_IN
-				&& e.getButton() == MouseEvent.BUTTON3
-				|| state == XMapPane.ZOOM_OUT) {
-			xMapPane.zoomTo(e.getPoint(), 2.);
-		}
-		//
-		// final Coordinate ll = new Coordinate(mapX - (width2 / zlevel), mapY
-		// - (height2 / zlevel));
-		// final Coordinate ur = new Coordinate(mapX + (width2 / zlevel), mapY
-		// + (height2 / zlevel));
-		// final Envelope newMapArea = new Envelope(ll, ur);
-		//
-		// xMapPane.setMapArea(newMapArea);
-
-		// xMapPane.repaint();
-
-	}
-
-	/**
-	 * Constantly fired while the mouse is dragging
-	 */
-	public void mouseDragged(final MouseEvent event) {
-		if (!isEnabled())
-			return;
-		
-		xMapPane.mouseDragged(startPos, lastPos, event);
-
-
-		lastPos = event.getPoint();
-	}
-
-
-}

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/FeatureTablePane.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/FeatureTablePane.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/FeatureTablePane.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,8 +29,6 @@
  ******************************************************************************/
 package schmitzm.geotools.gui;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -58,6 +56,7 @@
 import schmitzm.swing.JPanel;
 import schmitzm.swing.SortableJTable;
 import schmitzm.swing.SwingUtil;
+import skrueger.geotools.XMapPane;
 
 /**
  * Diese Komponente stellt eine Tabelle dar, in der die Attribute einer

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/MouseSelectionTracker.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/MouseSelectionTracker.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/MouseSelectionTracker.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -38,7 +38,7 @@
  * @see #setSelectionMode(SelectionMode)
  * @author <a href="mailto:Martin.Schmitz at koeln.de">Martin Schmitz</a>
  */
-public abstract class MouseSelectionTracker extends gtmig.org.geotools.swing.MouseSelectionTracker {
+public abstract class MouseSelectionTracker extends skrueger.geotools.MouseSelectionTracker {
   /** Selection events the tracker reacts on. */
   public enum SelectionMode {
     /** Tracker reacts on single clicks as well as on

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/SelectableXMapPane.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/SelectableXMapPane.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/gui/SelectableXMapPane.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,11 +29,6 @@
  ******************************************************************************/
 package schmitzm.geotools.gui;
 
-import gtmig.org.geotools.swing.GeomFilterGenerator;
-import gtmig.org.geotools.swing.SelectXMapPaneMouseListener;
-import gtmig.org.geotools.swing.XMapPane;
-import gtmig.org.geotools.swing.GeomFilterGenerator.BoundingBoxFilterGenerator;
-
 import java.awt.BorderLayout;
 import java.awt.LayoutManager;
 import java.awt.Point;
@@ -91,6 +86,10 @@
 import schmitzm.geotools.map.event.MapPaneEvent;
 import schmitzm.geotools.map.event.ScaleChangedEvent;
 import schmitzm.geotools.styling.StylingUtil;
+import skrueger.geotools.GeomFilterGenerator;
+import skrueger.geotools.SelectXMapPaneMouseListener;
+import skrueger.geotools.XMapPane;
+import skrueger.geotools.GeomFilterGenerator.BoundingBoxFilterGenerator;
 
 import com.vividsolutions.jts.geom.Envelope;
 
@@ -137,7 +136,7 @@
  *         (University of Bonn/Germany)
  * @version 1.0
  */
-public class SelectableXMapPane extends gtmig.org.geotools.swing.XMapPane {
+public class SelectableXMapPane extends XMapPane {
 
 	/** Logger for debug messages. */
 	protected static final Logger LOGGER = Logger

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/FeatureSelectedEvent.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/FeatureSelectedEvent.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/FeatureSelectedEvent.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,12 +29,11 @@
  ******************************************************************************/
 package schmitzm.geotools.map.event;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import org.geotools.feature.FeatureCollection;
 import org.geotools.map.MapLayer;
 
 import schmitzm.geotools.gui.SelectableXMapPane;
+import skrueger.geotools.XMapPane;
 
 import com.vividsolutions.jts.geom.Envelope;
 

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GeneralSelectionEvent.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GeneralSelectionEvent.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GeneralSelectionEvent.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,7 +29,6 @@
  ******************************************************************************/
 package schmitzm.geotools.map.event;
 
-import gtmig.org.geotools.swing.XMapPane;
 import schmitzm.geotools.gui.SelectableXMapPane;
 
 import com.vividsolutions.jts.geom.Envelope;

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GridCoverageSelectedEvent.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GridCoverageSelectedEvent.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/GridCoverageSelectedEvent.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,12 +29,11 @@
  ******************************************************************************/
 package schmitzm.geotools.map.event;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import org.geotools.coverage.grid.GridCoverage2D;
 import org.geotools.map.MapLayer;
 
 import schmitzm.geotools.gui.SelectableXMapPane;
+import skrueger.geotools.XMapPane;
 
 import com.vividsolutions.jts.geom.Envelope;
 

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/MapPaneEvent.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/MapPaneEvent.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/MapPaneEvent.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,8 +29,8 @@
  ******************************************************************************/
 package schmitzm.geotools.map.event;
 
-import gtmig.org.geotools.swing.XMapPane;
 import schmitzm.geotools.gui.SelectableXMapPane;
+import skrueger.geotools.XMapPane;
 
 /**
  * Diese Klasse stellt ein Ereignis dar, das von einem {@link SelectableXMapPane}

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/ObjectSelectionEvent.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/ObjectSelectionEvent.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/map/event/ObjectSelectionEvent.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,11 +29,10 @@
  ******************************************************************************/
 package schmitzm.geotools.map.event;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import org.geotools.map.MapLayer;
 
 import schmitzm.geotools.gui.SelectableXMapPane;
+import skrueger.geotools.XMapPane;
 
 import com.vividsolutions.jts.geom.Envelope;
 

Modified: branches/1.0-gt2-2.6/src/schmitzm/geotools/styling/StylingUtil.java
===================================================================
--- branches/1.0-gt2-2.6/src/schmitzm/geotools/styling/StylingUtil.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/schmitzm/geotools/styling/StylingUtil.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -29,10 +29,7 @@
  ******************************************************************************/
 package schmitzm.geotools.styling;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import java.awt.Color;
-import java.awt.Component;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -107,8 +104,6 @@
 import org.opengis.coverage.grid.GridCoverage;
 import org.opengis.feature.simple.SimpleFeature;
 import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.feature.type.AttributeDescriptor;
-import org.opengis.feature.type.Name;
 import org.opengis.filter.And;
 import org.opengis.filter.Filter;
 import org.opengis.filter.FilterFactory;
@@ -124,6 +119,7 @@
 import schmitzm.lang.LangUtil;
 import skrueger.geotools.StyledFeaturesInterface;
 import skrueger.geotools.StyledRasterInterface;
+import skrueger.geotools.XMapPane;
 import skrueger.geotools.selection.FeatureMapLayerSelectionSynchronizer;
 
 import com.vividsolutions.jts.geom.Geometry;

Copied: branches/1.0-gt2-2.6/src/skrueger/geotools/GeomFilterGenerator.java (from rev 512, branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java)
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/GeomFilterGenerator.java	2009-11-06 15:00:17 UTC (rev 512)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/GeomFilterGenerator.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -0,0 +1,249 @@
+package skrueger.geotools;
+
+import java.awt.geom.Point2D;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.geotools.data.FeatureSource;
+import org.geotools.filter.AbstractFilter;
+import org.geotools.filter.GeometryFilterImpl;
+import org.geotools.filter.spatial.DWithinImpl;
+import org.geotools.referencing.CRS;
+import org.geotools.resources.CRSUtilities;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.feature.type.GeometryDescriptor;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.spatial.BinarySpatialOperator;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+
+import schmitzm.geotools.FilterUtil;
+import schmitzm.geotools.JTSUtil;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+/**
+ * The {@link GeomFilterGenerator} prepares a {@link BinarySpatialOperator}
+ * filter for multiple use. Only the "right" argument is prepared. The
+ * "left" argument (the geometry attribute of the {@link FeatureSource} to
+ * filter) is first set on calling {@link #adaptFilter(FeatureSource)} for a
+ * specific {@link FeatureSource}. This method also takes care to recreate
+ * the filter (or its "right" argument) if the given {@link FeatureSource}
+ * has another {@link CoordinateReferenceSystem} than the base constraint.<br>
+ * The type of filter (e.g. distance or bounding box) is specified by the
+ * subclass implemenation of
+ * {@link #prepareFilter(CoordinateReferenceSystem)} .
+ * 
+ * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+ */
+ public abstract class GeomFilterGenerator {
+	/**
+	 * Holds the {@link CoordinateReferenceSystem} the filter constraint
+	 * bases on.
+	 */
+	protected CoordinateReferenceSystem baseCRS = null;
+	/**
+	 * Caches the base filter constraint for several
+	 * {@link CoordinateReferenceSystem CoordinateReferenceSystems}.
+	 */
+	protected Map<CoordinateReferenceSystem, GeometryFilterImpl> filterCache = new HashMap<CoordinateReferenceSystem, GeometryFilterImpl>();
+
+	/**
+	 * Creates a new filter generator
+	 * 
+	 * @param crs
+	 *            {@link CoordinateReferenceSystem} the base constraint
+	 *            ("right" filter argument is relative to)
+	 */
+	public GeomFilterGenerator(CoordinateReferenceSystem crs) {
+		this.baseCRS = crs;
+	}
+
+	/**
+	 * Creates a filter containing the base constraint ("right" argument)
+	 * transformed to the given {@link CoordinateReferenceSystem}.
+	 * 
+	 * @param crs
+	 *            the {@link CoordinateReferenceSystem} the base constraint
+	 *            is transformed to
+	 */
+	protected abstract GeometryFilterImpl prepareFilter(
+			CoordinateReferenceSystem crs);
+
+	/**
+	 * Completes the filter with its "left" argument for a concrete
+	 * {@link FeatureSource}. If the {@link FeatureSource FeatureSource's}
+	 * CRS differs from the CRS the base constraint is specified in, first a
+	 * new filter is created by calling
+	 * {@link #prepareFilter(CoordinateReferenceSystem)}.
+	 * 
+	 * @param fs
+	 *            {@link FeatureSource} the filter is adaped to
+	 * @return
+	 */
+	public GeometryFilterImpl adaptFilter(
+			FeatureSource<SimpleFeatureType, SimpleFeature> fs) {
+		GeometryDescriptor geomDescr = fs.getSchema()
+				.getGeometryDescriptor();
+		CoordinateReferenceSystem fsCRS = geomDescr
+				.getCoordinateReferenceSystem();
+		GeometryFilterImpl filter = filterCache.get(fsCRS);
+		if (filter == null) {
+			filter = prepareFilter(fsCRS);
+			filterCache.put(fsCRS, filter);
+		}
+		Expression geometry = FilterUtil.FILTER_FAC2.property(geomDescr.getLocalName());
+		filter.setExpression1(geometry);
+		return filter;
+	}
+
+	/**
+	 * {@link GeomFilterGenerator} for a bounding box constraint.
+	 * 
+	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+	 */
+	public static class BoundingBoxFilterGenerator extends GeomFilterGenerator {
+		/**
+		 * Holds the base constraint (bounding box {@link Envelope}) relative to
+		 * the {@linkplain GeomFilterGenerator#baseCRS base CRS}.
+		 */
+		protected Envelope baseEnv = null;
+
+		/**
+		 * Creates a new filter generator.
+		 * 
+		 * @param baseEnv
+		 *            defines the bounding box
+		 * @param baseCrs
+		 *            defines the CRS of the bounding box
+		 */
+		public BoundingBoxFilterGenerator(Envelope baseEnv,
+				CoordinateReferenceSystem baseCrs) {
+			super(baseCrs);
+			this.baseEnv = baseEnv;
+		}
+
+		/**
+		 * Prepares a filter with the bounding box transformed to the given
+		 * {@link CoordinateReferenceSystem} as the "right" argument.
+		 * 
+		 * @param crs
+		 *            the {@link CoordinateReferenceSystem} the bounding box is
+		 *            transformed to
+		 */
+		protected GeometryFilterImpl prepareFilter(CoordinateReferenceSystem crs) {
+			Envelope bbEnv = baseEnv;
+			if (!CRS.equalsIgnoreMetadata(baseCRS,crs))
+				bbEnv = JTSUtil.transformEnvelope(baseEnv, baseCRS, crs);
+			// Filter fuer Envelope zusammenstellen
+			Expression bbox = FilterUtil.FILTER_FAC.createBBoxExpression(bbEnv);
+			GeometryFilterImpl bboxFilter = (GeometryFilterImpl) FilterUtil.FILTER_FAC
+					.createGeometryFilter(AbstractFilter.GEOMETRY_BBOX);
+			// GeometryFilterImpl bboxFilter =
+			// (GeometryFilterImpl)ff.createGeometryFilter(AbstractFilter.
+			// GEOMETRY_WITHIN);
+			bboxFilter.setExpression2(bbox);
+			return bboxFilter;
+		}
+	}
+
+	/**
+	 * {@link GeomFilterGenerator} for a "near distance" constraint.
+	 * 
+	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+	 */
+	public static class NearPointFilterGenerator extends GeomFilterGenerator {
+		/**
+		 * Holds the base constraint (coordinate) relative to the
+		 * {@linkplain GeomFilterGenerator#baseCRS base CRS}.
+		 */
+		protected Coordinate basePoint = null;
+		/**
+		 * Holds the distance "around" the base point relative to the
+		 * {@linkplain GeomFilterGenerator#baseCRS base CRS}.
+		 */
+		protected double baseDist = 0.0;
+		/**
+		 * Holds a point which is in distance {@link #baseDist} to the
+		 * {@link #basePoint}.
+		 */
+		protected Coordinate basePoint2 = null;
+
+		/**
+		 * Creates a new filter generator.
+		 * 
+		 * @param basePoint
+		 *            defNearPointFilterGeneratorines the point for the "near point" constraint
+		 * @param dist
+		 *            defines the distance around the base point
+		 * @param crs
+		 *            defines the CRS of base point
+		 */
+		public NearPointFilterGenerator(Coordinate basePoint, double dist,
+				CoordinateReferenceSystem crs) {
+			super(crs);
+			this.basePoint = basePoint;
+			this.baseDist = dist;
+			// Create a point which is in distance "dist" to the base point
+			this.basePoint2 = new Coordinate(basePoint.x + dist, basePoint.y);
+		}
+
+		/**
+		 * Creates a new filter generator.
+		 * 
+		 * @param basePoint
+		 *            defines the point for the "near point" constraint
+		 * @param dist
+		 *            defines the distance around the base point
+		 * @param crs
+		 *            defines the CRS of base point
+		 */
+		public NearPointFilterGenerator(Point2D basePoint, double dist,
+				CoordinateReferenceSystem crs) {
+			this(new Coordinate(basePoint.getX(), basePoint.getY()), dist, crs);
+		}
+
+		/**
+		 * Prepares a filter with the base point and distance transformed to the
+		 * given {@link CoordinateReferenceSystem} as the "right" argument.
+		 * 
+		 * @param crs
+		 *            the {@link CoordinateReferenceSystem} the point and
+		 *            distance is transformed to
+		 */
+		protected GeometryFilterImpl prepareFilter(CoordinateReferenceSystem crs) {
+			Coordinate nearPoint = basePoint;
+			double nearDist = baseDist;
+			if (!baseCRS.equals(crs)) {
+				nearPoint = JTSUtil
+						.transformCoordinate(basePoint, baseCRS, crs);
+				// Transform the distance (maybe "dirty")
+				// --> transform the point2 and calculate the
+				// distance to the tranformed base point
+				Coordinate nearPoint2 = JTSUtil.transformCoordinate(basePoint2,
+						baseCRS, crs);
+
+				if (nearPoint == null || nearPoint2 == null)
+					throw new RuntimeException("Unable to transform CRS from "
+							+ baseCRS + " to " + crs);
+
+				nearDist = Math.abs(nearPoint.x - nearPoint2.x);
+			}
+			// Filter fuer Point zusammenstellen
+			final Geometry geometry = FilterUtil.GEOMETRY_FAC
+					.createPoint(nearPoint);
+
+			final DWithinImpl dwithinFilter = new DWithinImpl(
+					FilterUtil.FILTER_FAC2, null, null);
+			dwithinFilter.setDistance(nearDist);
+			dwithinFilter.setExpression2(FilterUtil.FILTER_FAC2
+					.literal(geometry));
+
+			return dwithinFilter;
+		}
+
+	}
+
+}
\ No newline at end of file

Copied: branches/1.0-gt2-2.6/src/skrueger/geotools/MouseSelectionTracker.java (from rev 512, branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java)
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/MouseSelectionTracker.java	2009-11-06 15:00:17 UTC (rev 512)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/MouseSelectionTracker.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -0,0 +1,377 @@
+// Migration process to Geotools 2.6:
+//    Because not included in gt2-2.6-M2 this class is taken 1:1
+//    from Geotools 2.4.5 and made public.
+//    TODO: It should be removed or included in SCHMITZM.
+
+/*
+ *    GeoTools - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
+ *    (C) 2001, Institut de Recherche pour le D�veloppement
+ *
+ *    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; either
+ *    version 2.1 of the License, or (at your option) any later version.
+ *
+ *    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 skrueger.geotools;
+
+// Geometry
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RectangularShape;
+import java.awt.geom.RoundRectangle2D;
+
+import javax.swing.event.MouseInputAdapter;
+
+
+/**
+ * Controller which allows the user to select a region of a component. The user must click on a
+ * point in the component, then drag the mouse pointer whilst keeping the button pressed. During
+ * the dragging, the shape which is drawn will normally be a rectangle.  Other shapes could always
+ * be used such as, for example, an ellipse. To use this class, it is necessary to create a derived
+ * class which defines the following methods:
+ *
+ * <ul>
+ *   <li>{@link #selectionPerformed} (obligatory)</li>
+ *   <li>{@link #getModel} (optional)</li>
+ * </ul>
+ *
+ * This controller should then be registered with one, and only one, component
+ * using the following syntax:
+ *
+ * <blockquote><pre>
+ * {@link Component} component=...
+ * MouseSelectionTracker control=...
+ * component.addMouseListener(control);
+ * </pre></blockquote>
+ *
+ * @since 2.0
+ * @source $URL: http://svn.geotools.org/tags/2.4.5/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/MouseSelectionTracker.java $
+ * @version $Id: MouseSelectionTracker.java 22482 2006-10-31 02:58:00Z desruisseaux $
+ * @author Martin Desruisseaux
+ */
+public abstract class MouseSelectionTracker extends MouseInputAdapter {
+    /**
+     * Stippled rectangle representing the region which the user is currently
+     * selecting.  This rectangle can be empty.  These coordinates are only
+     * significant in the period between the user pressing the mouse button
+     * and then releasing it to outline a region. Conventionally, the
+     * {@code null} value indicates that a line should be used instead of
+     * a rectangular shape.  The coordinates are always expressed in pixels.
+     */
+    private transient RectangularShape mouseSelectedArea;
+
+    /**
+     * Colour to replace during XOR drawings on a graphic.
+     * This colour is specified in {@link Graphics2D#setColor}.
+     */
+    private Color backXORColor = Color.white;
+
+    /**
+     * Colour to replace with during the XOR drawings on a graphic.
+     * This colour is specified in {@link Graphics2D#setXORMode}.
+     */
+    private Color lineXORColor = Color.black;
+
+    /**
+     * <var>x</var> coordinate of the mouse when the button is pressed.
+     */
+    private transient int ox;
+
+    /**
+     * <var>y</var> coordinate of the mouse when the button is pressed.
+     */
+    private transient int oy;
+
+    /**
+     * <var>x</var> coordinate of the mouse during the last drag.
+     */
+    private transient int px;
+
+    /**
+     * <var>y</var> coordinate of the mouse during the last drag.
+     */
+    private transient int py;
+
+    /**
+     * Indicates whether a selection is underway.
+     */
+    private transient boolean isDragging;
+
+    /**
+     * Constructs an object which will allow rectangular regions to be selected using the mouse.
+     */
+    public MouseSelectionTracker() {
+    }
+
+    /**
+     * Specifies the colours to be used for drawing the outline of a box when
+     * the user selects a region.  All {@code a} colours will be replaced
+     * by {@code b} colours and vice versa.
+     */
+    public void setXORColors(final Color a, final Color b) {
+        backXORColor = a;
+        lineXORColor = b;
+    }
+
+    /**
+     * Returns the geometric shape to use for marking the boundaries of a region.  This shape is
+     * normally a rectangle but could also be an ellipse, an arrow or even other shapes. The
+     * coordinates of the returned shape will not be taken into account. In fact, these coordinates
+     * will regularly be discarded. Only the class of the returned shape will count (for example,
+     * {@link java.awt.geom.Ellipse2D} vs {@link java.awt.geom.Rectangle2D}) and their parameters
+     * which are not linked to their position (for example, the rounding of a rectangle's
+     * corners).
+     * <p>
+     * The shape returned will normally be from a class derived from {@link RectangularShape},
+     * but could also be from the {@link Line2D} class. <strong>Any other class risks throwing a
+     * {@link ClassCastException} when executed</strong>.
+     *
+     * The default implementation always returns an object {@link Rectangle}.
+     *
+     * @param  event Mouse coordinate when the button is pressed.  This information can be used by
+     *         the derived classes which like to be informed of the position of the mouse before
+     *         chosing a geometric shape.
+     * @return Shape from the class {link RectangularShape} or {link Line2D}, or {@code null}
+     *         to indicate that we do not want to make a selection.
+     */
+    protected Shape getModel(final MouseEvent event) {
+        return new Rectangle();
+    }
+
+    /**
+     * Method which is automatically called after the user selects a region with the mouse.
+     * All coordinates passed in as parameters are expressed in pixels.
+     *
+     * @param ox <var>x</var> coordinate of the mouse when the user pressed the mouse button.
+     * @param oy <var>y</var> coordinate of the mouse when the user pressed the mouse button.
+     * @param px <var>x</var> coordinate of the mouse when the user released the mouse button.
+     * @param py <var>y</var> coordinate of the mouse when the user released the mouse button.
+     */
+    protected abstract void selectionPerformed(int ox, int oy, int px, int py);
+
+    /**
+     * Returns the geometric shape surrounding the last region to be selected by the user. An
+     * optional affine transform can be specified to convert the region selected by the user
+     * into logical coordinates. The class of the shape returned depends on the model returned by
+     * {@link #getModel}:
+     *
+     * <ul>
+     *   <li>If the model is null (which means that this {@code MouseSelectionTracker} object only
+     *       draws a line between points), the object returned will belong to the {@link Line2D}
+     *       class.</li>
+     *   <li>If the model is not null, the object returned can be from the same class (most often
+     *       {@link java.awt.geom.Rectangle2D}). There could always be situations where the object
+     *       returned is from another class, for example if the affine transform carries out a
+     *       rotation.</li>
+     * </ul>
+     *
+     * @param  transform Affine transform which converts logical coordinates into pixel coordinates.
+     *         It is usually an affine transform which is used in a {@code paint(...)} method to
+     *         draw shapes expressed in logical coordinates.
+     * @return A geometric shape enclosing the last region to be selected by the user, or
+     *         {@code null} if no selection has yet been made.
+     * @throws NoninvertibleTransformException If the affine transform can't be inverted.
+     */
+    public Shape getSelectedArea(final AffineTransform transform) throws NoninvertibleTransformException {
+        if (ox == px && oy == py) {
+            return null;
+        }
+        RectangularShape shape = mouseSelectedArea;
+        if (transform != null && !transform.isIdentity()) {
+            if (shape == null) {
+                final Point2D.Float po = new Point2D.Float(ox, oy);
+                final Point2D.Float pp = new Point2D.Float(px, py);
+                transform.inverseTransform(po, po);
+                transform.inverseTransform(pp, pp);
+                return new Line2D.Float(po, pp);
+            } else {
+                if (canReshape(shape, transform)) {
+                    final Point2D.Double point = new Point2D.Double();
+                    double xmin = Double.POSITIVE_INFINITY;
+                    double ymin = Double.POSITIVE_INFINITY;
+                    double xmax = Double.NEGATIVE_INFINITY;
+                    double ymax = Double.NEGATIVE_INFINITY;
+                    for (int i = 0; i < 4; i++) {
+                        point.x = (i&1) == 0 ? shape.getMinX() : shape.getMaxX();
+                        point.y = (i&2) == 0 ? shape.getMinY() : shape.getMaxY();
+                        transform.inverseTransform(point, point);
+                        if (point.x < xmin) xmin = point.x;
+                        if (point.x > xmax) xmax = point.x;
+                        if (point.y < ymin) ymin = point.y;
+                        if (point.y > ymax) ymax = point.y;
+                    }
+                    if (shape instanceof Rectangle) {
+                        return new Rectangle2D.Float((float) xmin,
+                                                     (float) ymin,
+                                                     (float) (xmax - xmin),
+                                                     (float) (ymax - ymin));
+                    } else {
+                        shape = (RectangularShape) shape.clone();
+                        shape.setFrame(xmin, ymin, xmax - xmin, ymax - ymin);
+                        return shape;
+                    }
+                }
+                else {
+                    return transform.createInverse().createTransformedShape(shape);
+                }
+            }
+        }
+        else {
+            return (shape != null) ? (Shape) shape.clone() : new Line2D.Float(ox, oy, px, py);
+        }
+    }
+
+    /**
+     * Indicates whether we can transform {@code shape} simply by calling its
+     * {@code shape.setFrame(...)} method rather than by using the heavy artillery
+     * that is the {@code transform.createTransformedShape(shape)} method.
+     */
+    private static boolean canReshape(final RectangularShape shape,
+                                      final AffineTransform transform) {
+        final int type=transform.getType();
+        if ((type & AffineTransform.TYPE_GENERAL_TRANSFORM) != 0) return false;
+        if ((type & AffineTransform.TYPE_MASK_ROTATION)     != 0) return false;
+        if ((type & AffineTransform.TYPE_FLIP)              != 0) {
+            if (shape instanceof Rectangle2D)      return true;
+            if (shape instanceof Ellipse2D)        return true;
+            if (shape instanceof RoundRectangle2D) return true;
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns a {@link Graphics2D} object to be used for drawing in the specified component. We
+     * must not forget to call {@link Graphics2D#dispose} when the graphics object is no longer
+     * needed.
+     */
+    private Graphics2D getGraphics(final Component c) {
+        final Graphics2D graphics = (Graphics2D) c.getGraphics();
+        graphics.setXORMode(lineXORColor);
+        graphics.setColor  (backXORColor);
+        return graphics;
+    }
+
+    /**
+     * Informs this controller that the mouse button has been pressed.
+     * The default implementation retains the mouse coordinate (which will
+     * become one of the corners of the future rectangle to be drawn) 
+     * and prepares {@code this} to observe the mouse movements.
+     *
+     * @throws ClassCastException if {@link #getModel} doesn't return a shape
+     *         from the class {link RectangularShape} or {link Line2D}.
+     */
+    public void mousePressed(final MouseEvent event) throws ClassCastException {
+        if (!event.isConsumed() && (event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
+            final Component source = event.getComponent();
+            if (source != null) {
+                Shape model = getModel(event);
+                if (model != null) {
+                    isDragging = true;
+                    ox = px = event.getX();
+                    oy = py = event.getY();
+                    if (model instanceof Line2D) {
+                        model = null;
+                    }
+                    mouseSelectedArea = (RectangularShape) model;
+                    if (mouseSelectedArea != null) {
+                        mouseSelectedArea.setFrame(ox, oy, 0, 0);
+                    }
+                    source.addMouseMotionListener(this);
+                }
+                source.requestFocus();
+                event.consume();
+            }
+        }
+    }
+
+    /**
+     * Informs this controller that the mouse has been dragged.  The default
+     * implementation uses this to move a corner of the rectangle used to
+     * select the region. The other corner remains fixed at the point
+     * where the mouse was at the moment its button was pressed..
+     */
+    public void mouseDragged(final MouseEvent event) {
+        if (isDragging) {
+            final Graphics2D graphics = getGraphics(event.getComponent());
+            if (mouseSelectedArea == null) {
+                graphics.drawLine(ox, oy, px, py);
+                px = event.getX();
+                py = event.getY();
+                graphics.drawLine(ox, oy, px, py);
+            } else {
+                graphics.draw(mouseSelectedArea);
+                int xmin = this.ox;
+                int ymin = this.oy;
+                int xmax = px = event.getX();
+                int ymax = py = event.getY();
+                if (xmin > xmax) {
+                    final int xtmp = xmin;
+                    xmin = xmax; xmax = xtmp;
+                }
+                if (ymin > ymax) {
+                    final int ytmp = ymin;
+                    ymin = ymax; ymax = ytmp;
+                }
+                mouseSelectedArea.setFrame(xmin, ymin, xmax - xmin, ymax - ymin);
+                graphics.draw(mouseSelectedArea);
+            }
+            graphics.dispose();
+            event.consume();
+        }
+    }
+
+    /**
+     * Informs this controller that the mouse button has been released.
+     * The default implementation calls {@link #selectionPerformed} with
+     * the bounds of the selected region as parameters.
+     */
+    public void mouseReleased(final MouseEvent event) {
+        if (isDragging && (event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
+            isDragging = false;
+            final Component component = event.getComponent();
+            component.removeMouseMotionListener(this);
+
+            final Graphics2D graphics = getGraphics(event.getComponent());
+            if (mouseSelectedArea == null) {
+                graphics.drawLine(ox, oy, px, py);
+            } else {
+                graphics.draw(mouseSelectedArea);
+            }
+            graphics.dispose();
+            px = event.getX();
+            py = event.getY();
+            selectionPerformed(ox, oy, px, py);
+            event.consume();
+        }
+    }
+
+    /**
+     * Informs this controller that the mouse has been moved but not as a
+     * result of the user selecting a region.  The default implementation
+     * signals to the source component that {@code this} is no longer
+     * interested in being informed about mouse movements.
+     */
+    public void mouseMoved(final MouseEvent event) {
+        // Normally not necessary, but it seems that this "listener"
+        // sometimes stays in place when it shouldn't.
+        event.getComponent().removeMouseMotionListener(this);
+    }
+}

Modified: branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
===================================================================
--- branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -1,7 +1,5 @@
 package skrueger.geotools;
 
-import gtmig.org.geotools.swing.XMapPane;
-
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;

Copied: branches/1.0-gt2-2.6/src/skrueger/geotools/SelectXMapPaneMouseListener.java (from rev 514, branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java)
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/SelectXMapPaneMouseListener.java	2009-11-09 15:05:41 UTC (rev 514)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/SelectXMapPaneMouseListener.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -0,0 +1,265 @@
+package skrueger.geotools;
+
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+import java.awt.geom.Point2D;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+import org.geotools.data.memory.MemoryFeatureCollection;
+import org.geotools.feature.FeatureCollection;
+import org.geotools.map.MapLayer;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+import schmitzm.geotools.gui.SelectableXMapPane;
+import schmitzm.geotools.map.event.FeatureSelectedEvent;
+import skrueger.geotools.GeomFilterGenerator.BoundingBoxFilterGenerator;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+public class SelectXMapPaneMouseListener extends MouseAdapter {
+
+	private final SelectableXMapPane xMapPane;
+	private boolean enabled;
+
+	public boolean isEnabled() {
+		if (!xMapPane.isWellDefined())
+			return false;
+		if ((xMapPane.getState() == XMapPane.ZOOM_IN
+				|| xMapPane.getState() == xMapPane.ZOOM_OUT || xMapPane
+				.getState() == xMapPane.PAN))
+			return false;
+		if (xMapPane.getState() == XMapPane.NONE)
+			;
+		return enabled;
+	}
+
+	/**
+	 * Reagiert auf Linksklick mit der ueber {@link #setState(int)}eingestellten
+	 * Aktion und auf Rechtsklick mit Zoom-Out (sofern {@link #ZOOM_IN}-State
+	 * fuer Linksklick eingestellt). Alle anderen Klicks werden ignoriert.
+	 * 
+	 * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+	 *         (University of Bonn/Germany)
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public void mouseClicked(MouseEvent e) {
+		if (!isEnabled())
+			return;
+		
+		xMapPane.setCursor(XMapPane.WAIT_CURSOR);
+		//		
+		// int clickX = e.getX();
+		// int clickY = e.getY();
+		// selectionPerformed(clickX, clickY, clickX, clickY);}
+		//		
+		int state = xMapPane.getState();
+
+		/**
+		 * BEGIN StefanChange Dieser Block findet sichtbare Features im Umkreis
+		 * des Mausklicks und kümmert sich selbst um das verschicken der Events.
+		 * Dabei wird z.Zt. immer eine FeaterCollection mit nur einem
+		 * SimpleFeature verschickt. Und zwar jenes SimpleFeature, welches am
+		 * nächsten liegt.
+		 */
+
+		// Fenster-Koordinate in Geo-Koordinate umwandelt
+		Point2D geoCoord = xMapPane.getScreenToWorld().transform(e.getPoint(),
+				null);
+		
+		com.vividsolutions.jts.geom.Point mousePoint = new GeometryFactory()
+				.createPoint(new Coordinate(geoCoord.getX(), geoCoord.getY()));
+
+		// Rechnet 10 Pixel in die Einheit der Karte um.
+		final Point pAtDistance = new Point((int) e.getPoint().getX() + 10,
+				(int) e.getPoint().getY());
+		final Point2D geoCoordAtDistance = xMapPane.getScreenToWorld()
+				.transform(pAtDistance, null);
+		final Double dist = Math.abs(geoCoordAtDistance.getX()
+				- geoCoord.getX()) / 2;
+
+//		final Envelope envelope = new Envelope(geoCoord.getX(),
+//				geoCoord.getY(), geoCoord.getX(), geoCoord.getY());
+
+		//
+		// This is a tweak. The NearPointFilterGenerator doesn't work
+		// any more in GT2.6... So we use a small BBOX.
+		// Well, it's not the
+		// NearPointFilterGenerator, but GeoTools that fails. I already
+		// invested 3h in this... I guess the bug is in
+		// ExtractBoundsFilterVisitor.visit(ProperyName) retuning null.
+		// It should return the BBOX of value the named property.
+
+		// Hashtable<MapLayer, FeatureCollection<SimpleFeatureType,
+		// SimpleFeature>> result = findVisibleFeatures(
+		// new NearPointFilterGenerator(geoCoord, dist,
+		// getContext().getCoordinateReferenceSystem()),
+		// state, envelope);
+		
+//		envelope.expandBy(dist);
+//
+		Envelope envelope = new Envelope(geoCoord.getX() - dist, geoCoord
+				.getY()
+				- dist, geoCoord.getX() + dist, geoCoord.getY() + dist);
+
+		Hashtable<MapLayer, FeatureCollection<SimpleFeatureType, SimpleFeature>> result = xMapPane
+				.findVisibleFeatures(new BoundingBoxFilterGenerator(  envelope,
+						xMapPane.getContext().getCoordinateReferenceSystem()),
+						state, envelope);
+
+		// Ein Event auslösen für das jeweils nächste Feature pro Layer
+		for (Enumeration<MapLayer> element = result.keys(); element
+				.hasMoreElements();) {
+
+			MapLayer layer = element.nextElement();
+			FeatureCollection<SimpleFeatureType, SimpleFeature> fc = result
+					.get(layer);
+			FeatureCollection<SimpleFeatureType, SimpleFeature> fcOne;
+
+			if (fc != null && !fc.isEmpty()) {
+
+				if (fc.size() > 1) {
+					// Hier werden alle Features weggeschmissen, bis auf
+					// das raeumlich naechste.
+
+					SimpleFeature nearestFeature = null;
+					Double nearestDist = 0.0;
+
+					Iterator<SimpleFeature> fcIt = fc.iterator();
+					try {
+
+						while (fcIt.hasNext()) {
+							SimpleFeature f = fcIt.next();
+							Object obj = f.getAttribute(0);
+
+							if (obj instanceof Geometry) {
+								// Bei Punkten ja noch ganz einfach:
+								Geometry featureGeometry = (Geometry) obj;
+								double distance = featureGeometry
+										.distance(mousePoint);
+
+								if ((nearestFeature == null)
+										|| (distance < nearestDist)) {
+									nearestFeature = f;
+									nearestDist = distance;
+								}
+							} else {
+								LOGGER
+										.info("!obj instanceof Geometry      obj = "
+												+ obj.getClass()
+														.getSimpleName());
+							}
+
+						}
+
+					} finally {
+						fc.close(fcIt);
+					}
+
+					fcOne = new MemoryFeatureCollection(fc.getSchema());
+					fc.clear();
+					fcOne.add(nearestFeature);
+				} else {
+					fcOne = fc;
+				}
+				xMapPane.fireMapPaneEvent(new FeatureSelectedEvent(xMapPane,
+						layer, envelope, fcOne));
+			}
+		}
+		
+		xMapPane.updateCursor();
+
+	}
+
+	/**
+	 * Verarbeitet die Mausrad-Aktion, indem gezoomed wird.
+	 * 
+	 * @param wheelEvt
+	 *            Mausrad-Event
+	 */
+	@Override
+	public void mouseWheelMoved(MouseWheelEvent wheelEvt) {
+		if (!isEnabled())
+			return;
+
+		final int units = wheelEvt.getUnitsToScroll();
+		if (units > 0)
+			xMapPane.zoomTo(wheelEvt.getPoint(), 1. + .11 * units);
+		else
+			xMapPane.zoomTo(wheelEvt.getPoint(), 2. / -units);
+	}
+
+	@Override
+	public void mouseDragged(final MouseEvent event) {
+		if (!isEnabled())
+			return;
+
+		xMapPane.mouseDragged(startPos, lastPos, event);
+
+		lastPos = event.getPoint();
+	}
+
+	/**
+	 * Stores beginning of a drag event in window coordinates
+	 */
+	protected Point startPos;
+
+	/**
+	 * Stores last position of a drag event in window coordinates
+	 */
+	protected Point lastPos;
+
+	public void mouseReleased(final MouseEvent event) {
+		if (!isEnabled())
+			return;
+
+		if (xMapPane.getState() == XMapPane.PAN
+				|| event.getButton() == MouseEvent.BUTTON3) {
+			xMapPane.performPan();
+		} else if (xMapPane.getState() == XMapPane.SELECT_ALL
+				|| xMapPane.getState() == XMapPane.SELECT_TOP) {
+			xMapPane.drawRectangle(xMapPane.getGraphics(), startPos, event
+					.getPoint());
+
+			Point mp = event.getPoint();
+
+			// keine wirkliche Selektion, sondern nur ein Klick
+			if (startPos.x == mp.x || startPos.y == mp.y) {
+				mouseClicked(event);
+				return;
+			}
+
+			// SELECTION!
+			xMapPane.performSelectionEvent(startPos.x, startPos.y, mp.x, mp.y);
+		}
+
+	}
+
+	public void mousePressed(final MouseEvent e) {
+		if (!isEnabled())
+			return;
+		startPos = new Point(e.getPoint());
+		lastPos = new Point(e.getPoint());
+	}
+
+	public SelectXMapPaneMouseListener(SelectableXMapPane xMapPane) {
+		this.xMapPane = xMapPane;
+	}
+
+	public void setEnabled(boolean enabled) {
+		this.enabled = enabled;
+	}
+
+	private static Logger LOGGER = Logger
+			.getLogger(SelectXMapPaneMouseListener.class);
+
+}

Copied: branches/1.0-gt2-2.6/src/skrueger/geotools/XMapPane.java (from rev 530, branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java)
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/XMapPane.java	2009-11-19 09:31:14 UTC (rev 530)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/XMapPane.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -0,0 +1,2266 @@
+package skrueger.geotools;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.Timer;
+
+import org.apache.log4j.Logger;
+import org.geotools.feature.FeatureCollection;
+import org.geotools.geometry.jts.JTS;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.map.DefaultMapContext;
+import org.geotools.map.MapContext;
+import org.geotools.map.MapLayer;
+import org.geotools.map.event.MapLayerEvent;
+import org.geotools.map.event.MapLayerListEvent;
+import org.geotools.map.event.MapLayerListListener;
+import org.geotools.map.event.MapLayerListener;
+import org.geotools.referencing.CRS;
+import org.geotools.renderer.GTRenderer;
+import org.geotools.renderer.label.LabelCacheImpl;
+import org.geotools.renderer.lite.LabelCache;
+import org.geotools.renderer.lite.StreamingRenderer;
+import org.geotools.swing.JMapPane;
+import org.geotools.swing.event.MapMouseEvent;
+import org.geotools.swing.event.MapPaneEvent;
+import org.geotools.swing.event.MapPaneListener;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+
+import schmitzm.geotools.GTUtil;
+import schmitzm.geotools.JTSUtil;
+import schmitzm.geotools.gui.SelectableXMapPane;
+import schmitzm.geotools.map.event.JMapPaneListener;
+import schmitzm.geotools.map.event.MapLayerAdapter;
+import schmitzm.lang.LangUtil;
+import schmitzm.swing.JPanel;
+import schmitzm.swing.SwingUtil;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+
+/**
+ * The {@link XMapPane} class uses a Geotools {@link GTRenderer} to paint up to
+ * two {@link MapContext}s: a "local" {@link MapContext} and a "background"
+ * {@link MapContext}. The idea is, that rendering a background layer made up of
+ * e.g. OSM data, may take much longer than rendering local data.<br>
+ * Every {@link MapContext} is rendered on a {@link Thread} of it's own.
+ * Starting/ cancelling these threads is done by the {@link RenderingExecutor}.<br>
+ * <br>
+ * While the renderers are rending the map, a <br>
+ * The {@link XMapPane} is based on schmitzm {@link JPanel}, so
+ * {@link #print(Graphics)} will automatically set the background of components
+ * to pure white.
+ * 
+ * The XMapPane has a {@link MouseListener} that manages zooming.<br>
+ * A logo/icon to float in the lower left corner may be set with
+ * {@link #setMapImage(BufferedImage)}<br>
+ * 
+ * @see SelectableXMapPane - an extension of {@link XMapPane} that supports
+ *      selecting features.
+ * 
+ * @author stefan
+ * 
+ */
+public class XMapPane extends JPanel {
+
+	private static final int IMAGETYPE = BufferedImage.TYPE_INT_RGB;
+	private static final int IMAGETYPE_withAlpha = BufferedImage.TYPE_INT_ARGB;
+
+	private static Logger LOGGER = Logger.getLogger(XMapPane.class);
+
+	private boolean acceptsRepaintCalls = true;
+
+	/**
+	 * Main {@link MapContext} that holds all layers that are rendered into the
+	 * {@link #localImage} by the {@link #localRenderer}
+	 */
+	MapContext localContext;
+
+	/**
+	 * {@link MapContext} holding the background layers. Use it for layers that
+	 * CAN take very long for rendering, like layer from the Internet: WMS, WFS,
+	 * OSM...<br>
+	 * <code>null</code> by default.
+	 * 
+	 * @see #setBgContext(MapContext)
+	 * @see #getBgContext()
+	 */
+	MapContext bgContext;
+
+	/**
+	 * While threads are working, calls {@link XMapPane#updateFinalImage()}
+	 * regularly and {@link #repaint()}. This {@link Timer} is stopped when all
+	 * renderers have finished.
+	 * 
+	 * @see #INITIAL_REPAINT_DELAYAL
+	 * @see #REPEATING_REPAINT_DELAY
+	 */
+	final private Timer repaintTimer;
+
+	/**
+	 * The initial delay in milliseconds until the {@link #finalImage} is
+	 * updated the first time.
+	 */
+	public static final int INITIAL_REPAINT_DELAY = 1000;
+
+	/**
+	 * While the {@link #bgExecuter} and {@link #localExecuter} are rendering,
+	 * the {@link #repaintTimer} is regularly updating the {@link #finalImage}
+	 * with previews.
+	 */
+	public static final long REPEATING_REPAINT_DELAY = 500;
+
+	/**
+	 * Default delay (milliseconds) before the map will be redrawn when resizing
+	 * the pane. This is to avoid flickering while drag-resizing.
+	 */
+	public static final int DEFAULT_RESIZING_PAINT_DELAY = 500;
+
+	public static final int NONE = -123;
+
+	/**
+	 * Flag fuer Modus "Kartenausschnitt bewegen". Nicht fuer Window-Auswahl
+	 * moeglich!
+	 * 
+	 * @see #setState(int)
+	 */
+	public static final int PAN = 1;
+
+	/**
+	 * Flag fuer Modus "SimpleFeature-Auswahl auf allen (sichtbaren) Layern".
+	 * 
+	 * @see #setState(int)
+	 * @see #setState(int)
+	 */
+	public static final int SELECT_ALL = 103;
+	/**
+	 * Flag fuer Modus
+	 * "Auswahl nur eines Features, das erste sichtbare von Oben".
+	 * 
+	 * @see #setState(int)
+	 * @see #setState(int)
+	 */
+	public static final int SELECT_ONE_FROM_TOP = 104;
+	/**
+	 * Flag fuer Modus
+	 * "SimpleFeature-Auswahl auf dem obersten (sichtbaren) Layer".
+	 * 
+	 * @see #setState(int)
+	 * @see #setState(int)
+	 */
+	public static final int SELECT_TOP = 4;
+
+	public static final Cursor WAIT_CURSOR = Cursor
+			.getPredefinedCursor(Cursor.WAIT_CURSOR);
+
+	final static Font waitFont = new Font("Arial", Font.BOLD, 30);
+
+	/**
+	 * Flag fuer Modus "Heran zoomen".
+	 * 
+	 * @see #setState(int)
+	 * @see #setState(int)
+	 */
+	public static final int ZOOM_IN = 2;
+
+	/**
+	 * Flag fuer Modus "Heraus zoomen". Nicht fuer Window-Auswahl moeglich!
+	 * 
+	 * @see #setState(int)
+	 */
+	public static final int ZOOM_OUT = 3;
+
+	/**
+	 * Konvertiert die Maus-Koordinaten (relativ zum <code>JMapPane</code>) in
+	 * Karten-Koordinaten.
+	 * 
+	 * @param e
+	 *            Maus-Ereignis
+	 */
+	public static Point2D getMapCoordinatesFromEvent(final MouseEvent e) {
+		// aktuelle Geo-Position aus GeoMouseEvent ermitteln
+		if (e != null && e instanceof MapMouseEvent)
+			try {
+				return ((MapMouseEvent) e).getMapPosition().toPoint2D();
+			} catch (final Exception err) {
+				LOGGER
+						.error(
+								"return ((GeoMouseEvent) e).getMapCoordinate(null).toPoint2D();",
+								err);
+			}
+
+		// aktuelle Geo-Position ueber Transformation des JMapPane berechnen
+		if (e != null && e.getSource() instanceof XMapPane) {
+
+			final XMapPane xMapPane = (XMapPane) e.getSource();
+
+			if (!xMapPane.isWellDefined())
+				return null;
+
+			final AffineTransform at = xMapPane.getScreenToWorld();
+			if (at != null)
+				return at.transform(e.getPoint(), null);
+			return null;
+		}
+		throw new IllegalArgumentException(
+				"MouseEvent has to be of instance MapMouseEvent or come from an XMapPane");
+	}
+
+	/**
+	 * Listens to changes of the "background" {@link MapContext} and triggers
+	 * repaints where needed.
+	 */
+	private final MapLayerListListener bgContextListener = new MapLayerListListener() {
+
+		@Override
+		public void layerAdded(final MapLayerListEvent event) {
+			event.getLayer().addMapLayerListener(bgMapLayerListener);
+
+			if (localContext.getLayers().length == 0
+					&& bgContext.getLayers().length == 1) { // the first one and
+				// localContext is
+				// empty
+				if (!setMapArea(localContext.getAreaOfInterest()))
+					requestStartRendering();
+				return;
+			}
+
+			// We need to redraw, even in case that the mapArea didn't change
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+
+		}
+
+		@Override
+		public void layerChanged(final MapLayerListEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerMoved(final MapLayerListEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerRemoved(final MapLayerListEvent event) {
+			if (event.getLayer() != null)
+				event.getLayer().removeMapLayerListener(bgMapLayerListener);
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+	};
+
+	/**
+	 * compass and icon are rendered into this image
+	 */
+	// protected BufferedImage gadgetsImage;
+
+	protected RenderingExecutor bgExecuter;
+
+	/**
+	 * The Renderer for the LocalLayers uses this Image. When set to null,
+	 * please dispose this {@link Graphics2D}
+	 */
+	private BufferedImage localImage;
+
+	private BufferedImage finalImage;
+	/**
+	 * If # {@link #bgExecuter} is using {@link #bgRenderer} for the Background
+	 * uses this Image. When set to null, please dispose the {@link Graphics2D}
+	 */
+	private BufferedImage bgImage;
+
+	/**
+	 * Optionally a transparent image to paint over the map in the lower right
+	 * corner.
+	 * 
+	 * @see #addGadgets(Graphics2D)
+	 * @see #setMapImage(BufferedImage)
+	 **/
+	private BufferedImage mapImage = null;
+
+	/**
+	 * Listens to each layer in the local {@link MapContext} for changes and
+	 * triggers repaints.
+	 */
+	protected MapLayerListener bgMapLayerListener = new MapLayerAdapter() {
+
+		@Override
+		public void layerChanged(final MapLayerEvent event) {
+			// Change of SLD for example
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerHidden(final MapLayerEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerShown(final MapLayerEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+	};
+
+	/**
+	 * A flag indicating if dispose() was already called. If true, then further
+	 * use of this {@link SelectableXMapPane} is undefined.
+	 */
+	private boolean disposed = false;
+
+	/**
+	 * While dragging, the {@link #updateFinalImage()} method is translating the
+	 * cached images while setting it together.
+	 **/
+	Point imageOrigin = new Point(0, 0);
+	/**
+	 * For every rendering thread started,
+	 * {@link GTUtil#createGTRenderer(MapContext)} is called to create a new
+	 * renderer. These Java2D rendering hints are passed to the
+	 * {@link GTRenderer}. The java2dHints are the same for all renderers (bg
+	 * and local).
+	 */
+	private RenderingHints java2dHints;
+
+	protected LabelCache labelCache = new LabelCacheImpl();
+
+	/**
+	 * Listens to changes of the "local" {@link MapContext} and triggers
+	 * repaints where needed.
+	 */
+	private final MapLayerListListener localContextListener = new MapLayerListListener() {
+
+		@Override
+		public void layerAdded(final MapLayerListEvent event) {
+			event.getLayer().addMapLayerListener(localMapLayerListener);
+
+			localRenderer.setContext(getContext());
+
+			if (localContext.getLayers().length == 1) { // the first one
+				// if the Area of Interest is unset, the LayerBounds are used
+				if (!setMapArea(localContext.getAreaOfInterest()))
+					repaint();
+
+				return;
+			}
+
+			// We need to redraw, even in case that the mapArea didn't change
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+
+		}
+
+		@Override
+		public void layerChanged(final MapLayerListEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			localRenderer.setContext(getContext());
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerMoved(final MapLayerListEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			localRenderer.setContext(getContext());
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerRemoved(final MapLayerListEvent event) {
+			if (event.getLayer() != null)
+				event.getLayer().removeMapLayerListener(localMapLayerListener);
+			// mapImageInvalid = true;
+			// repaint();
+			localRenderer.setContext(getContext());
+			requestStartRendering();
+		}
+	};
+
+	private final RenderingExecutor localExecuter = new RenderingExecutor(this);
+
+	/**
+	 * Listens to each layer in the local {@link MapContext} for changes and
+	 * triggers repaints.
+	 */
+	protected MapLayerListener localMapLayerListener = new MapLayerAdapter() {
+
+		@Override
+		public void layerChanged(final MapLayerEvent event) {
+			localRenderer.setContext(getContext()); // betters for SLD changes?!
+			// Change of SLD for example
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerHidden(final MapLayerEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+
+		@Override
+		public void layerShown(final MapLayerEvent event) {
+			// mapImageInvalid = true;
+			// repaint();
+			requestStartRendering();
+		}
+	};
+
+	private final GTRenderer localRenderer = GTUtil.createGTRenderer();
+	
+	private final GTRenderer bgRenderer = GTUtil.createGTRenderer();
+
+	/**
+	 * the area of the map to draw
+	 */
+	protected Envelope mapArea = null;
+
+	/**
+	 * A flag set it {@link #setMapArea(Envelope)} to indicated the
+	 * {@link #paintComponent(Graphics)} method, that the image on-screen is
+	 * associated with {@link #oldMapArea} and may hence be scaled for a quick
+	 * preview.<br>
+	 * The flag is reset
+	 **/
+	private boolean mapAreaChanged = false;
+
+	/**
+	 * This color is used as the default background color when painting a map.
+	 */
+	private Color mapBackgroundColor = Color.WHITE;
+
+	/**
+	 * A flag indicating that the shown image is invalid and needs to be
+	 * re-rendered.
+	 */
+	protected boolean mapImageInvalid = true;
+
+	/**
+	 * Holds a flag for each layer, whether it is regarded or ignored on
+	 * {@link #SELECT_TOP}, {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP}
+	 * actions.
+	 */
+	final protected HashMap<MapLayer, Boolean> mapLayerSelectable = new HashMap<MapLayer, Boolean>();
+
+	/**
+	 * List of listeners of this {@link XMapPane}
+	 */
+	protected Vector<JMapPaneListener> mapPaneListeners = new Vector<JMapPaneListener>();
+	/**
+	 * If not <code>null</code>, the {@link XMapPane} will not allow to zoom/pan
+	 * out of that area
+	 **/
+	private Envelope maxExtend = null;
+	private Double maxZoomScale = Double.MIN_VALUE;
+
+	private Double minZoomScale = Double.MAX_VALUE;
+
+	/**
+	 * We store the old mapArea for a moment to use it for the
+	 * "quick scaled preview" in case of ZoomOut
+	 **/
+	protected Envelope oldMapArea = null;
+
+	/**
+	 * We store the old transform for a moment to use it for the
+	 * "quick scaled preview" in case of ZoomIn
+	 **/
+	protected AffineTransform oldScreenToWorld = null;
+
+	/**
+	 * A flag indicating, that the image size has changed and the buffered
+	 * images are not big enough any more
+	 **/
+	protected boolean paneResized = false;
+
+	private BufferedImage preFinalImage;
+
+	// if null, no quick preview will be shown
+	private int quickPreviewHint = 0;
+
+	private Map<Object, Object> rendererHints;
+
+	private volatile Boolean requestStartRendering = false;
+
+	private final Timer resizeTimer;
+
+	private final int resizingPaintDelay;
+
+	/**
+	 * Transformation zwischen Fenster-Koordinaten und Karten-Koordinaten
+	 * (lat/lon)
+	 */
+	protected AffineTransform screenToWorld = null;
+
+	/**
+	 * The flag {@link #requestStartRendering} can be set to true by events.
+	 * This {@link Timer} checks the flag regularly and starts one renderer
+	 * thread.
+	 */
+	final private Timer startRenderThreadsTimer;
+
+	/**
+	 * The default state is ZOOM_IN, hence by default the
+	 * {@link #zoomMapPaneMouseListener} is also enabled.
+	 **/
+	private int state = ZOOM_IN;
+
+	/**
+	 * Manuell gesetzter statischer Cursor, unabhaengig von der aktuellen
+	 * MapPane-Funktion
+	 */
+	protected Cursor staticCursor = null;
+
+	private AffineTransform worldToScreen;
+
+	/**
+	 * This {@link MouseListener} is managing all zoom related tasks
+	 */
+	public final ZoomXMapPaneMouseListener zoomMapPaneMouseListener = new ZoomXMapPaneMouseListener(
+			this);
+
+	// TODO doku
+	public XMapPane() {
+		this(null, null);
+	}
+
+	/**
+	 * full constructor extending JPanel
+	 * 
+	 * @param rendererHints
+	 * 
+	 * @param layout
+	 *            - layout (probably shouldn't be set)
+	 * @param isDoubleBuffered
+	 *            - a Swing thing I don't really understand
+	 * @param render
+	 *            - what to draw the map with
+	 * @param localContext
+	 *            - {@link MapContext} of layer to render.
+	 */
+	public XMapPane(final MapContext localContext_,
+			final Map<Object, Object> rendererHints) {
+		super(true);
+
+		setRendererHints(rendererHints);
+
+		setOpaque(true);
+
+		localRenderer.setJava2DHints(getJava2dHints());
+
+		if (localContext_ != null)
+			setLocalContext(localContext_);
+
+		/**
+		 * Adding the #zoomMapPaneMouseListener
+		 */
+		this.addMouseListener(zoomMapPaneMouseListener);
+		this.addMouseMotionListener(zoomMapPaneMouseListener);
+		this.addMouseWheelListener(zoomMapPaneMouseListener);
+
+		/*
+		 * We use a Timer object to avoid rendering delays and flickering when
+		 * the user is drag-resizing the parent container of this map pane.
+		 * 
+		 * Using a ComponentListener doesn't work because, unlike a JFrame, the
+		 * pane receives a stream of events during drag-resizing.
+		 */
+		resizingPaintDelay = DEFAULT_RESIZING_PAINT_DELAY;
+		resizeTimer = new Timer(resizingPaintDelay, new ActionListener() {
+
+			public void actionPerformed(final ActionEvent e) {
+				paneResized = true;
+
+				if (!isWellDefined())
+					return;
+
+				final Rectangle bounds = getVisibleRect();
+
+				final Envelope geoMapArea = tranformWindowToGeo(bounds.x,
+						bounds.y, bounds.x + bounds.width, bounds.y
+								+ bounds.height);
+
+				setMapArea(bestAllowedMapArea(geoMapArea));
+			}
+		});
+		resizeTimer.setRepeats(false);
+		this.addComponentListener(new ComponentAdapter() {
+
+			@Override
+			public void componentResized(final ComponentEvent e) {
+				if (bgExecuter != null)
+					bgExecuter.cancelTask();
+				if (localExecuter != null)
+					localExecuter.cancelTask();
+				resizeTimer.restart();
+			}
+
+		});
+
+		/*
+		 * Setting up the repaintTimer. Not started automatically.
+		 */
+		repaintTimer = new Timer((int) REPEATING_REPAINT_DELAY,
+				new ActionListener() {
+
+					@Override
+					public void actionPerformed(final ActionEvent e) {
+						updateFinalImage();
+						XMapPane.this.repaint();
+					}
+				});
+		repaintTimer.setInitialDelay(INITIAL_REPAINT_DELAY);
+		repaintTimer.setRepeats(true);
+
+		/*
+		 * Setting up the startRenderThreadsTimer. This Timer starts
+		 * automatically.
+		 */
+		startRenderThreadsTimer = new Timer(100, new ActionListener() {
+
+			@Override
+			public void actionPerformed(final ActionEvent e) {
+				synchronized (requestStartRendering) {
+					if (requestStartRendering && isWellDefined()) {
+
+						if (localExecuter.isRunning()) {
+							localExecuter.cancelTask();
+						} else {
+							requestStartRendering = false;
+							startRendering();
+						}
+					}
+				}
+			}
+		});
+		startRenderThreadsTimer.start();
+
+	}
+
+	/**
+	 * Fuegt der Map einen Listener hinzu.
+	 * 
+	 * @param l
+	 *            neuer Listener
+	 */
+	public void addMapPaneListener(final JMapPaneListener l) {
+		mapPaneListeners.add(l);
+	}
+
+	/**
+	 * Korrigiert den {@link Envelope} aka {@code mapArea} auf die beste
+	 * erlaubte Flaeche damit die Massstabsbeschaenkungen noch eingehalten
+	 * werden, FALLS der uebergeben Envelope nicht schon gueltig sein sollte.<br>
+	 * Since 21. April 09: Before thecalculation starts, the aspect ratio is
+	 * corrected. This change implies, that setMapArea() will most of the time
+	 * not allow setting to a wrong aspectRatio.
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public Envelope bestAllowedMapArea(Envelope env) {
+		if (getWidth() == 0)
+			return env;
+		if (env == null)
+			return null;
+
+		Envelope newArea = null;
+
+		/**
+		 * Correct the aspect Ratio before we check the rest. Otherwise we might
+		 * easily fail. We allow to grow here, because we don't check against
+		 * the maxExtend
+		 */
+		final Rectangle curPaintArea = getVisibleRect();
+
+		env = JTSUtil.fixAspectRatio(curPaintArea, env, true);
+
+		final double scale = env.getWidth() / getWidth();
+		final double centerX = env.getMinX() + env.getWidth() / 2.;
+		final double centerY = env.getMinY() + env.getHeight() / 2.;
+		double newWidth2 = 0;
+		double newHeight2 = 0;
+		if (scale < getMaxZoomScale()) {
+			// ****************************************************************************
+			// Wir zoomen weiter rein als erlaubt => Anpassen des envelope
+			// ****************************************************************************
+			newWidth2 = getMaxZoomScale() * getWidth() / 2.;
+			newHeight2 = getMaxZoomScale() * getHeight() / 2.;
+		} else if (scale > getMinZoomScale()) {
+			// ****************************************************************************
+			// Wir zoomen weiter raus als erlaubt => Anpassen des envelope
+			// ****************************************************************************
+			newWidth2 = getMinZoomScale() * getWidth() / 2.;
+			newHeight2 = getMinZoomScale() * getHeight() / 2.;
+		} else {
+			// ****************************************************************************
+			// Die mapArea / der Envelope ist ist gueltig! Keine Aenderungen
+			// ****************************************************************************
+			newArea = env;
+		}
+
+		if (newArea == null) {
+
+			final Coordinate ll = new Coordinate(centerX - newWidth2, centerY
+					- newHeight2);
+			final Coordinate ur = new Coordinate(centerX + newWidth2, centerY
+					+ newHeight2);
+
+			newArea = new Envelope(ll, ur);
+		}
+
+		final Envelope maxAllowedExtend = getMaxExtend();
+		while (maxAllowedExtend != null && !maxAllowedExtend.contains(newArea)
+				&& newArea != null && !newArea.isNull()
+				&& !Double.isNaN(newArea.getMinX())
+				&& !Double.isNaN(newArea.getMaxX())
+				&& !Double.isNaN(newArea.getMinY())
+				&& !Double.isNaN(newArea.getMaxY())) {
+			/*
+			 * If a maxExtend is set, we have to honour that...
+			 */
+
+			// Exceeds top? Move down and maybe cut
+			if (newArea.getMaxY() > maxAllowedExtend.getMaxY()) {
+				final double divY = newArea.getMaxY()
+						- maxAllowedExtend.getMaxY();
+				// LOGGER.debug("Moving area down by " + divY);
+
+				newArea = new Envelope(new Coordinate(newArea.getMinX(),
+						newArea.getMinY() - divY), new Coordinate(newArea
+						.getMaxX(), newArea.getMaxY() - divY));
+
+				if (newArea.getMinY() < maxAllowedExtend.getMinY()) {
+					// LOGGER.debug("Now it exeeds the bottom border.. cut!");
+					// And cut the bottom if it moved out of the area
+					newArea = new Envelope(new Coordinate(newArea.getMinX(),
+							maxAllowedExtend.getMinY()), new Coordinate(newArea
+							.getMaxX(), newArea.getMaxY()));
+
+					// LOGGER.debug("and fix aspect ratio");
+
+					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
+							false);
+				}
+			}
+
+			// Exceeds bottom? Move up and maybe cut
+			if (newArea.getMinY() < maxAllowedExtend.getMinY()) {
+				final double divY = newArea.getMinY()
+						- maxAllowedExtend.getMinY();
+				// LOGGER.debug("Moving area up by " + divY);
+
+				newArea = new Envelope(new Coordinate(newArea.getMinX(),
+						newArea.getMinY() - divY), new Coordinate(newArea
+						.getMaxX(), newArea.getMaxY() - divY));
+
+				if (newArea.getMaxY() > maxAllowedExtend.getMaxY()) {
+					// LOGGER.debug("Now it exeeds the top border.. cut!");
+					// And cut the bottom if it moved out of the area
+					newArea = new Envelope(new Coordinate(newArea.getMinX(),
+							newArea.getMinY()), new Coordinate(newArea
+							.getMaxX(), maxAllowedExtend.getMaxY()));
+
+					// LOGGER.debug("and fix aspect ratio");
+
+					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
+							false);
+				}
+			}
+
+			// Exceeds to the right? move and maybe cut
+			if (newArea.getMaxX() > maxAllowedExtend.getMaxX()) {
+
+				// Move left..
+				final double divX = newArea.getMaxX()
+						- maxAllowedExtend.getMaxX();
+				// LOGGER.debug("Moving area left by " + divX);
+
+				newArea = new Envelope(new Coordinate(newArea.getMinX() - divX,
+						newArea.getMinY()), new Coordinate(newArea.getMaxX()
+						- divX, newArea.getMaxY()));
+
+				if (newArea.getMinX() < maxAllowedExtend.getMinX()) {
+					// LOGGER.debug("Now it exeeds the left border.. cut!");
+					// And cut the left if it moved out of the area
+					newArea = new Envelope(new Coordinate(maxAllowedExtend
+							.getMinX(), newArea.getMinY()), new Coordinate(
+							newArea.getMaxX(), newArea.getMaxY()));
+
+					// LOGGER.debug("and fix aspect ratio");
+
+					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
+							false);
+				}
+			}
+
+			// Exceeds to the left? move and maybe cut
+			if (newArea.getMinX() < maxAllowedExtend.getMinX()) {
+
+				// Move right..
+				final double divX = newArea.getMinX()
+						- maxAllowedExtend.getMinX();
+				// LOGGER.debug("Moving area right by " + divX);
+
+				newArea = new Envelope(new Coordinate(newArea.getMinX() - divX,
+						newArea.getMinY()), new Coordinate(newArea.getMaxX()
+						- divX, newArea.getMaxY()));
+
+				if (newArea.getMaxX() > maxAllowedExtend.getMaxX()) {
+					// LOGGER.debug("Now it exeeds the right border.. cut!");
+					// And cut the left if it moved out of the area
+					newArea = new Envelope(new Coordinate(newArea.getMinX(),
+							newArea.getMinY()), new Coordinate(maxAllowedExtend
+							.getMaxX(), newArea.getMaxY()));
+
+					// LOGGER.debug("and fix aspect ratio");
+
+					newArea = JTSUtil.fixAspectRatio(this.getBounds(), newArea,
+							false);
+				}
+			}
+		}
+
+		return newArea;
+	}
+
+	/**
+	 * Should be called when the {@link JMapPane} is not needed no more to help
+	 * the GarbageCollector
+	 * 
+	 * Removes all {@link JMapPaneListener}s that are registered
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public void dispose() {
+		if (isDisposed())
+			return;
+
+		setPainting(false);
+
+		resizeTimer.stop();
+		startRenderThreadsTimer.stop();
+
+		disposed = true;
+
+		if (bgExecuter != null) {
+			bgExecuter.cancelTask();
+			bgExecuter.dispose();
+		}
+
+		if (localExecuter != null) {
+			int i = 0;
+			localExecuter.cancelTask();
+			while (i++ < 10 && localExecuter.isRunning()) {
+				try {
+					Thread.sleep(200);
+				} catch (final InterruptedException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+			if (localExecuter.isRunning()) {
+				System.out
+						.println("BAD BAD BAD... still running the thread....");
+			}
+			localExecuter.dispose();
+		}
+		//
+		// if (bgImage != null) {
+		// bgImage = dispose(bgImage);
+		// bgImage = null;
+		// // LangUtil.gcTotal();
+		// }
+		//
+		// if (localImage != null) {
+		// localImage = dispose(localImage);
+		// localImage = null;
+		// // LangUtil.gcTotal();
+		// }
+		//
+		// if (finalImage != null) {
+		// finalImage = dispose(finalImage);
+		// finalImage = null;
+		// // LangUtil.gcTotal();
+		// }
+		//
+		// if (preFinalImage != null) {
+		// preFinalImage = dispose(preFinalImage);
+		// preFinalImage = null;
+		// }
+		disposeImages();
+
+		// LangUtil.gcTotal();
+
+		// Alle mapPaneListener entfernen
+		mapPaneListeners.clear();
+
+		removeMouseMotionListener(zoomMapPaneMouseListener);
+		removeMouseListener(zoomMapPaneMouseListener);
+
+		if (localContext != null)
+			getContext().clearLayerList();
+		if (bgContext != null)
+			getBgContext().clearLayerList();
+
+		removeAll();
+	}
+
+	/**
+	 * Draws a rectangle in XOR mode from the origin at {@link #startPos} to the
+	 * given point. All in screen coordinates.
+	 */
+	protected void drawRectangle(final Graphics graphics, final Point startPos,
+			final Point e) {
+
+		if (!isWellDefined())
+			return;
+
+		// undraw last box/draw new box
+		final int left = Math.min(startPos.x, e.x);
+		final int right = Math.max(startPos.x, e.x);
+		final int top = Math.max(startPos.y, e.y);
+		final int bottom = Math.min(startPos.y, e.y);
+		final int width = right - left;
+		final int height = top - bottom;
+
+		if (width == 0 && height == 0)
+			return;
+
+		graphics.setXORMode(Color.WHITE);
+		graphics.drawRect(left, bottom, width, height);
+	}
+
+	/**
+	 * Diretly paints scaled preview into the {@link SelectableXMapPane}. Used
+	 * to give the user something to look at while we are rendering. Method
+	 * should be called after {@link #setMapArea(Envelope)} has been set to the
+	 * new mapArea and transform has been reset.<br>
+	 * 
+	 * @param g
+	 *            Graphics2D to paint the preview into
+	 * 
+	 * @param state
+	 *            Max be {@link #ZOOM_IN} or {@link #ZOOM_OUT}
+	 */
+	protected boolean drawScaledPreviewImage_Zoom(final Graphics2D graphics) {
+
+		if (quickPreviewHint == 0)
+			return false;
+
+		graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+				RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+		graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+				RenderingHints.VALUE_ANTIALIAS_OFF);
+		graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
+				RenderingHints.VALUE_RENDER_SPEED);
+
+		if (oldMapArea == null)
+			return false;
+
+		final Rectangle visibleArea = getVisibleRect();
+
+		// Calculate the oldMapArea in the current WindowCoordinates:
+		final Envelope oldMapWindow = tranformGeoToWindow(oldMapArea.getMinX(),
+				oldMapArea.getMinY(), oldMapArea.getMaxX(), oldMapArea
+						.getMaxY(), null);
+
+		final int xx1 = (int) Math.round(oldMapWindow.getMinX());
+		final int yy1 = (int) Math.round(oldMapWindow.getMinY());
+		final int xx2 = (int) Math.round(oldMapWindow.getMaxX());
+		final int yy2 = (int) Math.round(oldMapWindow.getMaxY());
+
+		graphics.drawImage(getPreFinalImage(), xx1, yy1, xx2, yy2,
+				(int) visibleArea.getMinX(), (int) visibleArea.getMinY(),
+				(int) visibleArea.getMaxX(), (int) visibleArea.getMaxY(),
+				getMapBackgroundColor(), null);
+
+		final Rectangle painedArea = new Rectangle(xx1, yy1, xx2 - xx1, yy2
+				- yy1);
+
+		SwingUtil.clearAround(graphics, painedArea, visibleArea);
+
+		addGadgets(graphics);
+
+		quickPreviewHint = 0;
+
+		graphics.dispose();
+
+		// Something has been drawn
+		return true;
+	}
+
+	public MapContext getBgContext() {
+		return bgContext;
+	}
+
+	/**
+	 * Lazyly initializes a {@link BufferedImage} for the background renderer.
+	 */
+	private Image getBgImage() {
+		//
+		// if (bgImage == null) {
+		// bgImage = createImage(getBounds().width, getBounds().height);
+		// }
+
+		return bgImage;
+	}
+
+	public MapContext getContext() {
+		if (localContext == null) {
+			setLocalContext(new DefaultMapContext());
+		}
+		return localContext;
+	}
+
+	private BufferedImage getFinalImage() {
+		//
+		if (finalImage == null) {
+			// Rectangle curPaintArea = getVisibleRect();
+			finalImage = new BufferedImage(getBounds().width,
+					getBounds().height, IMAGETYPE);
+
+			requestStartRendering();
+		}
+		return finalImage;
+	}
+
+	public RenderingHints getJava2dHints() {
+		return java2dHints;
+	}
+
+	/**
+	 * Lazyly initializes a {@link BufferedImage} for the background renderer.
+	 */
+	private BufferedImage getLocalImage() {
+
+		if (localImage == null) {
+			localImage = new BufferedImage(getBounds().width,
+					getBounds().height, IMAGETYPE_withAlpha);
+		}
+
+		return localImage;
+	}
+
+	/**
+	 * Returns a copy of the mapArea
+	 * 
+	 * @return
+	 */
+	public Envelope getMapArea() {
+		if (mapArea == null) {
+			ReferencedEnvelope mapArea_ = null;
+			try {
+				mapArea_ = localContext.getLayerBounds();
+			} catch (final IOException e) {
+				LOGGER.warn("context.getLayerBounds()", e);
+			}
+
+			if (mapArea_ != null) {
+				mapImageInvalid = true; /* note we need to redraw */
+//				setMapArea(mapArea_); // results in a loop
+				mapArea = bestAllowedMapArea(mapArea_);
+			}
+		}
+
+		if (mapArea == null)
+			return null;
+
+		return new Envelope(mapArea);
+	}
+
+	/**
+	 * Returns the background {@link Color} of the map pane. Default is white.
+	 **/
+	public Color getMapBackgroundColor() {
+		return mapBackgroundColor;
+	}
+
+	/**
+	 * Get the BufferedImage to use as a flaoting icon in the lower right
+	 * corner.
+	 * 
+	 * @return <code>null</code> if the feature is deactivated.
+	 */
+	public BufferedImage getMapImage() {
+		return mapImage;
+	}
+
+	/**
+	 * Returns the evelope of the viewable area. The JMapPane will never show
+	 * anything outside of this extend. If this has been set to
+	 * <code>null</code> via {@link #setMaxExtend(Envelope)}, it tries to return
+	 * quickly the context's bounds. It it takes to long to determine the
+	 * context bounds, <code>null</code> is returned.
+	 * 
+	 * @param maxExtend
+	 *            <code>null</code> to not have this restriction.
+	 */
+
+	public Envelope getMaxExtend() {
+		if (maxExtend == null) {
+			final ReferencedEnvelope layerBounds = GTUtil
+					.getVisibleLayoutBounds(localContext);
+			if (layerBounds == null) {
+				// TODO Last fallback could be the CRS valid area
+				return null;
+			}
+
+			// Kartenbereich um 10% vergroessern
+			return JTSUtil.fixAspectRatio(this.getBounds(), JTSUtil
+					.expandEnvelope(layerBounds, 0.1), true);
+		}
+		return maxExtend;
+	}
+
+	/**
+	 * Retuns the maximum allowed zoom scale. This is the smaller number value
+	 * of the two. Defaults to {@link Double}.MIN_VALUE
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public Double getMaxZoomScale() {
+		return maxZoomScale;
+	}
+
+	/**
+	 * Retuns the minimum allowed zoom scale. This is the bigger number value of
+	 * the two. Defaults to {@link Double}.MAX_VALUE
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public Double getMinZoomScale() {
+		return minZoomScale;
+	}
+
+	private Image getPreFinalImage() {
+		// if (preFinalImage == null) {
+		//			
+		// // Rectangle curPaintArea = getVisibleRect();
+		// // preFinalImage = new BufferedImage(curPaintArea.width,
+		// // curPaintArea.height, BufferedImage.TYPE_INT_RGB);
+		//			
+		// preFinalImage = createImage(getBounds().width, getBounds().height);
+		//
+		// requestStartRendering();
+		// }
+		return preFinalImage;
+	}
+
+	public Map<Object, Object> getRendererHints() {
+		return rendererHints;
+	}
+
+	/**
+	 * Liefert eine affine Transformation, um von den Fenster-Koordinaten in die
+	 * Karten-Koordinaten (Lat/Lon) umzurechnen.
+	 * 
+	 * @return eine Kopie der aktuellen Transformation; <code>null</code> wenn
+	 *         noch keine Karte angezeigt wird
+	 */
+	public AffineTransform getScreenToWorld() {
+		if (screenToWorld == null)
+			resetTransforms();
+		// nur Kopie der Transformation zurueckgeben!
+		if (screenToWorld == null)
+			return null;
+		return new AffineTransform(screenToWorld);
+	}
+
+	public int getState() {
+		return state;
+	}
+
+	/**
+	 * Liefert den statisch eingestellten Cursor, der unabhaengig von der
+	 * eingestellten MapPane-Aktion (Zoom, Auswahl, ...) verwendet wird.
+	 * 
+	 * @return {@code null}, wenn kein statischer Cursor verwendet, sondern der
+	 *         Cursor automatisch je nach MapPane-Aktion eingestellt wird.
+	 */
+	public Cursor getStaticCursor() {
+		return this.staticCursor;
+	}
+
+	public AffineTransform getWorldToScreenTransform() {
+		if (worldToScreen == null) {
+			resetTransforms();
+		}
+		// nur Kopie der Transformation zurueckgeben!
+		return new AffineTransform(worldToScreen);
+	}
+
+	/**
+	 * A flag indicating if dispose() has already been called. If true, then
+	 * further use of this {@link SelectableXMapPane} is undefined.
+	 */
+	private boolean isDisposed() {
+		return disposed;
+	}
+
+	/**
+	 * Returns whether a layer is regarded or ignored on {@link #SELECT_TOP},
+	 * {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP} actions. Returns
+	 * <code>true</code> if the selectability has not been defined.
+	 * 
+	 * @param layer
+	 *            a layer
+	 */
+	public boolean isMapLayerSelectable(final MapLayer layer) {
+		final Boolean selectable = mapLayerSelectable.get(layer);
+		return selectable == null ? true : selectable;
+	}
+
+	/**
+	 * Return <code>true</code> if a CRS and a {@link #mapArea} are set and the
+	 * {@link XMapPane} is visible and has bounds set.
+	 */
+	public boolean isWellDefined() {
+
+		try {
+
+			if (getContext() == null)
+				return false;
+			if (getContext().getLayerCount() <= 0)
+				return false;
+			if (getMapArea() == null)
+				return false;
+			if (getBounds().getWidth() == 0)
+				return false;
+			if (getBounds().getHeight() == 0)
+				return false;
+		} catch (final Exception e) {
+			return false;
+		}
+		return true;
+	}
+
+	public void mouseDragged(final Point startPos, final Point lastPos,
+			final MouseEvent event) {
+
+		if ((getState() == XMapPane.PAN)
+				|| ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)) {
+
+			if (getCursor() != SwingUtil.PANNING_CURSOR) {
+				setCursor(SwingUtil.PANNING_CURSOR);
+
+				// While panning, we deactivate the rendering. So the tasts are
+				// ready to start when the panning os done.
+				if (bgExecuter != null)
+					bgExecuter.cancelTask();
+				if (localExecuter != null)
+					localExecuter.cancelTask();
+			}
+
+			if (lastPos.x > 0 && lastPos.y > 0) {
+				final int dx = event.getX() - lastPos.x;
+				final int dy = event.getY() - lastPos.y;
+
+				// TODO Stop dragging when the drag would not be valid...
+				// boolean dragValid = true;
+				// // check if this panning results in a valid mapArea
+				// {
+				// Rectangle winBounds = xMapPane.getBounds();
+				// winBounds.translate(xMapPane.imageOrigin.x,
+				// -xMapPane.imageOrigin.y);
+				// Envelope newMapAreaBefore = xMapPane.tranformWindowToGeo(
+				// winBounds.x, winBounds.y, winBounds.x
+				// + winBounds.width, winBounds.y
+				// + winBounds.height);
+				//					
+				//
+				// winBounds = xMapPane.getBounds();
+				// Point testIng = new Point(xMapPane.imageOrigin);
+				// testIng.translate(dx, dy);
+				// winBounds.translate(testIng.x, -testIng.y);
+				// Envelope newMapAreaAfter = xMapPane.tranformWindowToGeo(
+				// winBounds.x, winBounds.y, winBounds.x
+				// + winBounds.width, winBounds.y
+				// + winBounds.height);
+				//
+				// // If the last drag doesn't change the MapArea anymore cancel
+				// it.
+				// if (xMapPane.bestAllowedMapArea(newMapAreaAfter).equals(
+				// xMapPane.bestAllowedMapArea(newMapAreaBefore))){
+				// dragValid = false;
+				// return;
+				// }
+				// }
+
+				imageOrigin.translate(dx, dy);
+				updateFinalImage();
+				repaint();
+			}
+
+		} else if ((getState() == XMapPane.ZOOM_IN)
+				|| (getState() == XMapPane.ZOOM_OUT)
+				|| (getState() == XMapPane.SELECT_ALL)
+				|| (getState() == XMapPane.SELECT_TOP)
+		// || (getState() == XMapPane.SELECT_ONE_FROM_TOP)
+		) {
+			final Graphics graphics = getGraphics();
+
+			drawRectangle(graphics, startPos, event.getPoint());
+
+			if ((lastPos.x > 0) && (lastPos.y > 0)) {
+				drawRectangle(graphics, startPos, lastPos);
+			}
+
+			graphics.dispose();
+
+		}
+
+	}
+
+	/**
+	 * Called by the {@link RenderingExecutor} when rendering was cancelled.
+	 */
+	public void onRenderingCancelled() {
+		repaintTimer.stop();
+		LOGGER.debug("Rendering cancelled");
+	}
+
+	/**
+	 * Called by the {@link RenderingExecutor} when rendering has been
+	 * completed.
+	 */
+	public void onRenderingCompleted() {
+		repaintTimer.stop();
+		updateFinalImage();
+		repaint();
+	}
+
+	/**
+	 * Called by the {@linkplain XMapPane.RenderingTask} when rendering failed.
+	 * Publishes a {@linkplain MapPaneEvent} of type {@code
+	 * MapPaneEvent.Type.RENDERING_STOPPED} to listeners.
+	 * 
+	 * @param renderingError
+	 *            The error that occured during rendering
+	 * 
+	 * @see MapPaneListener#onRenderingStopped(org.geotools.swing.event.MapPaneEvent)
+	 */
+	public void onRenderingFailed(final Exception renderingError) {
+		repaintTimer.stop();
+		LOGGER.warn("Rendering failed", renderingError);
+		updateFinalImage();
+		repaint();
+
+	}
+
+	public void onRenderingPending() {
+		// LOGGER.debug("Pending rendering updates the preview...");
+		updateFinalImage();
+		repaint();
+	}
+
+	@Override
+	protected void paintComponent(final Graphics g) {
+		if (!acceptsRepaintCalls)
+			return;
+
+		// Maybe update the cursor and maybe stop the repainting timer
+		updateCursor();
+
+		super.paintComponent(g); // candidate for removal
+
+		boolean paintedSomething = false;
+
+		if (mapImageInvalid) { /* if the map changed then redraw */
+
+			mapImageInvalid = false; // Reset for next round
+
+			// If the new mapArea and the oldMapArea intersect, we can draw some
+			// quick scaled preview to make the user feel that something is
+			// happening.
+			if (mapAreaChanged && oldMapArea != null
+					&& getMapArea().intersects(oldMapArea)
+					& !getMapArea().equals(oldMapArea)) {
+
+				mapAreaChanged = false;
+
+				if (getMapArea().covers(oldMapArea)) {
+					setQuickPreviewHint(ZOOM_OUT);
+					paintedSomething = drawScaledPreviewImage_Zoom((Graphics2D) g);
+				} else if (oldMapArea.covers(getMapArea())) {
+					setQuickPreviewHint(ZOOM_IN);
+					paintedSomething = drawScaledPreviewImage_Zoom((Graphics2D) g);
+				}
+
+			}
+
+			if (paneResized) {
+				paneResized = false;
+				disposeImages();
+			}
+
+			// Start the Threads and Timers to render the image
+			requestStartRendering();
+
+		}
+
+		if (!paintedSomething) {
+
+			g.drawImage(getFinalImage(), 0, 0, null);
+
+			g.dispose(); // cand. for removal
+
+			paintedSomething = true; // cand. for removal
+		}
+
+	}
+
+	/**
+	 * Heavily works on releasing all resources related to the four
+	 * {@link Image}s used to cache the results of the different renderers.<br>
+	 * In November 2009 i had some memory leaking problems with {@link XMapPane}
+	 * . The resources of the buffered images were never released. It seem to be
+	 * important to call the GC right after flushing the images.<br>
+	 * Hence this method may take a while, because it calls the GC up to four
+	 * times.
+	 */
+	private void disposeImages() {
+
+		// System.out.println("vorher = "
+		// + new MbDecimalFormatter().format(LangUtil.gcTotal()));
+		// bi.flush();
+		// return bi = null;
+		// System.out.println("nacher = "
+		// + new MbDecimalFormatter().format(LangUtil.gcTotal()));
+		//
+		// System.out.println("\n");
+
+		if (preFinalImage != null) {
+			preFinalImage.flush();
+			preFinalImage = null;
+			LangUtil.gc();
+		}
+		if (finalImage != null) {
+			finalImage.flush();
+			finalImage = null;
+			LangUtil.gc();
+		}
+		if (localImage != null) {
+			localImage.flush();
+			localImage = null;
+			LangUtil.gc();
+		}
+		if (bgImage != null) {
+			bgImage.flush();
+			bgImage = null;
+			LangUtil.gc();
+		}
+
+	}
+
+	/**
+	 * Performs a {@value #PAN} action. During panning, the displacement is
+	 * stored in {@link #imageOrigin} object. Calling {@link #performPan()} will
+	 * reset the offset and call {@link #setMapArea(Envelope)}.
+	 */
+	public void performPan() {
+
+		final Rectangle winBounds = getBounds();
+		winBounds.translate(-imageOrigin.x, -imageOrigin.y);
+		final Envelope newMapArea = tranformWindowToGeo(winBounds.x,
+				winBounds.y, winBounds.x + winBounds.width, winBounds.y
+						+ winBounds.height);
+
+		imageOrigin.x = 0;
+		imageOrigin.y = 0;
+
+		if (!setMapArea(newMapArea)) {
+			/**
+			 * If setMapArea returns true, the finalImage is updated anyways.
+			 * This if-case exists to ensure that we repaint a correct image
+			 * even if the new panning area has been denied.
+			 */
+			updateFinalImage();
+			repaint();
+		}
+
+		if (getCursor() == SwingUtil.PANNING_CURSOR)
+			setCursor(SwingUtil.PAN_CURSOR);
+	}
+
+	//
+	// /**
+	// * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein Screenshot
+	// gemacht
+	// * wird) wird. Dann werden wird der Hintergrund auf WEISS gesetzt.
+	// *
+	// * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	// * Kr&uuml;ger</a>
+	// */
+	// @Override
+	// public void print(final Graphics g) {
+	// final Color orig = getBackground();
+	// setBackground(Color.WHITE);
+	//
+	// // wrap in try/finally so that we always restore the state
+	// try {
+	// super.print(g);
+	// } finally {
+	// setBackground(orig);
+	// }
+	// }
+
+	/**
+	 * Entfernt einen Listener von der Map.
+	 * 
+	 * @param l
+	 *            zu entfernender Listener
+	 */
+	public void removeMapPaneListener(final JMapPaneListener l) {
+		mapPaneListeners.remove(l);
+	}
+
+	/**
+	 * Cancels all running renderers and sets the flag to start new ones. <br>
+	 * 
+	 * @see #startRenderThreadsTimer
+	 */
+	private void requestStartRendering() {
+		if (bgExecuter != null)
+			bgExecuter.cancelTask();
+		if (localExecuter != null)
+			localExecuter.cancelTask();
+		requestStartRendering = true;
+
+	}
+
+	/**
+	 * Berechnet die Transformation zwischen Fenster- und Karten-Koordinaten
+	 * neu.
+	 */
+	protected void resetTransforms() {
+		if (getMapArea() == null || getWidth() == 0 || getHeight() == 0)
+			return;
+
+		// We store the last Transform
+		oldScreenToWorld = screenToWorld;
+
+		this.screenToWorld = new AffineTransform(
+		// Genauso wie die Fenster-Koordinaten, werden die Longitude-Koordinaten
+				// nach rechts (Osten) hin groesser
+				// --> positive Verschiebung
+				getMapArea().getWidth() / getWidth(),
+				// keine Verzerrung
+				0.0, 0.0,
+				// Waehrend die Fenster-Koordinaten nach unten hin groesser
+				// werden,
+				// werden Latitude-Koordinaten nach Sueden hin keiner
+				// --> negative Verschiebung
+				-getMapArea().getHeight() / getHeight(),
+				// Die Longitude-Koordinaten werden nach Osten hin groesser
+				// --> obere linke Ecke des Fensters hat also den Minimalwert
+				getMapArea().getMinX(),
+				// Die Latitude-Koordinaten werden nach Norden hin groesser
+				// --> obere linke Ecke des Fensters hat also den Maximalwert
+				getMapArea().getMaxY());
+
+		try {
+			this.worldToScreen = screenToWorld.createInverse();
+		} catch (final NoninvertibleTransformException e) {
+			LOGGER.error(e);
+		}
+	}
+
+	public void setBgContext(final MapContext context) {
+
+		// Remove the default listener from the old context
+		if (this.bgContext != null) {
+			this.bgContext.removeMapLayerListListener(bgContextListener);
+
+			// adding listener to all layers
+			for (final MapLayer mapLayer : bgContext.getLayers()) {
+				mapLayer.removeMapLayerListener(bgMapLayerListener);
+			}
+		}
+
+		this.bgContext = context;
+
+		if (context != null) {
+			setMapArea(bgContext.getAreaOfInterest());
+
+			this.bgContext.addMapLayerListListener(bgContextListener);
+
+			// adding listener to all layers
+			for (final MapLayer mapLayer : bgContext.getLayers()) {
+				mapLayer.addMapLayerListener(bgMapLayerListener);
+			}
+		}
+		mapImageInvalid = true;
+		repaint();
+	}
+
+	public void setJava2dHints(final RenderingHints java2dHints) {
+		this.java2dHints = java2dHints;
+	}
+
+	/**
+	 * 
+	 * @param context
+	 */
+	public void setLocalContext(final MapContext context) {
+		// Remove the default listener from the old context
+		if (this.localContext != null) {
+			this.localContext.removeMapLayerListListener(localContextListener);
+
+			// adding listener to all layers
+			for (final MapLayer mapLayer : localContext.getLayers()) {
+				mapLayer.removeMapLayerListener(localMapLayerListener);
+			}
+		}
+
+		this.localContext = context;
+
+		if (context != null) {
+
+			setMapArea(localContext.getAreaOfInterest());
+
+			localRenderer.setContext(localContext);
+
+			this.localContext.addMapLayerListListener(localContextListener);
+
+			// adding listener to all layers
+			for (final MapLayer mapLayer : localContext.getLayers()) {
+				mapLayer.addMapLayerListener(localMapLayerListener);
+			}
+		}
+
+		mapImageInvalid = true;
+		repaint();
+	}
+
+	/**
+	 * @param newMapArea
+	 * @return <code>true</code> if the mapArea has been changed and a repaint
+	 *         has been triggered.
+	 */
+	public boolean setMapArea(final Envelope newMapArea) {
+
+		if (newMapArea == null
+				|| bestAllowedMapArea(newMapArea).equals(mapArea)) {
+			// No change.. no need to repaint
+			return false;
+		}
+
+		// Testing, whether NaN or Infinity are used in the newMapArea
+		if (newMapArea.isNull() || Double.isInfinite(newMapArea.getMaxX())
+				|| Double.isInfinite(newMapArea.getMaxY())
+				|| Double.isInfinite(newMapArea.getMinX())
+				|| Double.isInfinite(newMapArea.getMinY())) {
+			// No change.. ugly new values
+			LOGGER.warn("setMapArea has been called with newArea = "
+					+ newMapArea);
+			return false;
+		}
+
+		// Testing, whether the difference if just minimal
+		if (mapArea != null) {
+			final Envelope candNew = bestAllowedMapArea(newMapArea);
+			final double tolX = mapArea.getWidth() / 1000.;
+			final double tolY = mapArea.getHeight() / 1000.;
+			if ((candNew.getMinX() - tolX < mapArea.getMinX())
+					&& (mapArea.getMinX() < candNew.getMinX() + tolX)
+					&& (candNew.getMaxX() - tolX < mapArea.getMaxX())
+					&& (mapArea.getMaxX() < candNew.getMaxX() + tolX)
+
+					&& (candNew.getMinY() - tolY < mapArea.getMinY())
+					&& (mapArea.getMinY() < candNew.getMinY() + tolY)
+					&& (candNew.getMaxY() - tolY < mapArea.getMaxY())
+					&& (mapArea.getMaxY() < candNew.getMaxY() + tolY)
+
+			) {
+				// The two mapAreas only differ my 1/1000th.. ignore
+
+				return false;
+			}
+		}
+
+		oldMapArea = mapArea;
+
+		this.mapArea = bestAllowedMapArea(newMapArea);
+
+		if (localContext != null) {
+			localContext.setAreaOfInterest(mapArea, localContext
+					.getCoordinateReferenceSystem());
+		}
+		if (bgContext != null) {
+			bgContext.setAreaOfInterest(mapArea, localContext
+					.getCoordinateReferenceSystem());
+		}
+		resetTransforms();
+		mapImageInvalid = true;
+		mapAreaChanged = true;
+		repaint();
+
+		// LOGGER.debug("New maparea = " + mapArea);
+		return true;
+	}
+
+	/**
+	 * Set the background color of the map.
+	 * 
+	 * @param if <code>null</code>, white is used.
+	 */
+	public void setMapBackgroundColor(Color bgColor) {
+		if (bgColor == null)
+			bgColor = Color.WHITE;
+		this.mapBackgroundColor = bgColor;
+	}
+
+	/**
+	 * Set the BufferedImage to use as a flaoting icon in the lower right corner
+	 * 
+	 * @param mapImageIcon
+	 *            <code>null</code> is allowed and deactivates this icon.
+	 */
+	public void setMapImage(final BufferedImage mapImage) {
+		this.mapImage = mapImage;
+	}
+
+	/**
+	 * Sets whether a layer is regarded or ignored on {@link #SELECT_TOP},
+	 * {@link #SELECT_ALL} and {@link #SELECT_ONE_FROM_TOP} actions.
+	 * 
+	 * @param layer
+	 *            a layer
+	 * @param selectable
+	 *            if {@code false} the layer is ignored during the upper
+	 *            mentioned actions. If <code>null</code>, the default (true)
+	 *            will be used.
+	 */
+	public void setMapLayerSelectable(final MapLayer layer,
+			final Boolean selectable) {
+		if (selectable == null)
+			mapLayerSelectable.remove(layer);
+		else
+			mapLayerSelectable.put(layer, selectable);
+	}
+
+	/**
+	 * Defines an evelope of the viwable area. The JMapPane will never show
+	 * anything outside of this extend.
+	 * 
+	 * @param maxExtend
+	 *            <code>null</code> to not have this restriction.
+	 */
+	public void setMaxExtend(final Envelope maxExtend) {
+		this.maxExtend = maxExtend;
+	}
+
+	/**
+	 * Set the maximum allowed zoom scale. This is the smaller number value of
+	 * the two. If <code>null</code> is passed, Double.MINVALUE are used which
+	 * mean there is no restriction.
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public void setMaxZoomScale(final Double maxZoomScale) {
+		this.maxZoomScale = maxZoomScale == null ? Double.MIN_VALUE
+				: maxZoomScale;
+	}
+
+	// /** Stored the time used for the last real rendering in ms. **/
+	// private long lastRenderingDuration = Long.MAX_VALUE;
+
+	/**
+	 * Set the minimum (nearest) allowed zoom scale. This is the bigger number
+	 * value of the two. If <code>null</code> is passed, Double.MAXVALUE are
+	 * used which mean there is no restriction.
+	 * 
+	 * 
+	 * @author <a href="mailto:skpublic at wikisquare.de">Stefan Alfons
+	 *         Kr&uuml;ger</a>
+	 */
+	public void setMinZoomScale(final Double minZoomScale) {
+		this.minZoomScale = minZoomScale == null ? Double.MAX_VALUE
+				: minZoomScale;
+	}
+
+	/**
+	 * 
+	 * @param b
+	 */
+	public void setPainting(final boolean b) {
+		acceptsRepaintCalls = b;
+	}
+
+	// /**
+	// * Returns in milli seconds the time the last rending of the
+	// * {@link SelectableXMapPane} took. #Long.MAX_VALUE if the JMapPane has
+	// not
+	// * been rendered yet.
+	// */
+	// public long getLastRenderingDuration() {
+	// return lastRenderingDuration;
+	// }
+
+	public void setQuickPreviewHint(final int quickPreviewHint) {
+		this.quickPreviewHint = quickPreviewHint;
+
+	}
+
+	private void setRendererHints(final Map<Object, Object> rendererHints) {
+		this.rendererHints = rendererHints;
+	}
+
+	/**
+	 * Enables/Disables the ZOOM Mouse Listener. Upates the Cursor and stops the
+	 * repaint Timer if
+	 * 
+	 * @param state
+	 */
+	public void setState(final int state) {
+		this.state = state;
+
+		zoomMapPaneMouseListener.setEnabled((state == ZOOM_IN
+				|| state == ZOOM_OUT || state == PAN));
+
+		// Je nach Aktion den Cursor umsetzen
+		updateCursor();
+	}
+
+	/**
+	 * Standardmaessig wird der Cursor automatisch je nach MapPane-Aktion (Zoom,
+	 * Auswahl, ...) gesetzt. Mit dieser Methode kann ein statischer Cursor
+	 * gesetzt werden, der unabhaengig von der aktuellen MapPanes-Aktion
+	 * beibehalten wird. Um diesen statischen Cursor wieder zu entfernen, kann
+	 * {@code null} als Parameter uebergeben werden
+	 * 
+	 * @param cursor
+	 *            Cursor
+	 */
+	public void setStaticCursor(final Cursor cursor) {
+		this.staticCursor = cursor;
+		if (cursor != null)
+			super.setCursor(cursor);
+	}
+
+	/**
+	 * Starts rendering on one or two threads
+	 */
+	private void startRendering() {
+
+		if (!isWellDefined())
+			return;
+
+		if (bgExecuter != null) {
+			// Stop all renderers
+			bgExecuter.cancelTask();
+		}
+
+		if (localExecuter != null) {
+			localExecuter.cancelTask();
+		}
+
+		final Rectangle curPaintArea = getVisibleRect();
+
+		/**
+		 * We have to set new renderer
+		 */
+
+		if (getBgContext() != null) {
+			// bgExecuter = new RenderingExecutor();
+			// LOGGER.debug("starting bg renderer:");
+			// // /* System.out.println("rendering"); */
+			// final GTRenderer createGTRenderer = GTUtil.createGTRenderer(
+			// bgContext, getRendererHints());
+			// createGTRenderer.setJava2DHints(getJava2dHints());
+			// bgExecuter.submit(getBgContext().getAreaOfInterest(),
+			// curPaintArea,
+			// (Graphics2D) getBgImage().getGraphics(), createGTRenderer);
+		}
+
+		if (getContext() != null) {
+			// localExecuter = new RenderingExecutor(this, 150l);
+			// LOGGER.debug("starting local renderer:");
+
+			// Clear label cache
+			labelCache.clear();
+			final Map<Object, Object> rh = localRenderer.getRendererHints();
+			rh.put(StreamingRenderer.LABEL_CACHE_KEY, labelCache);
+			localRenderer.setRendererHints(rh);
+
+			final boolean submitted = localExecuter.submit(getContext()
+					.getAreaOfInterest(), curPaintArea,
+					(Graphics2D) getLocalImage().getGraphics(), localRenderer,
+					getWorldToScreenTransform());
+			if (submitted)
+				repaintTimer.restart();
+			else
+				requestStartRendering = true; // Try to start rendering again in
+			// a moment
+		}
+
+		updateCursor();
+	}
+
+	/**
+	 * Transformiert einen Geo-Koordinaten-Bereich in Fenster-Koordinaten.
+	 * 
+	 * @param ox
+	 *            X-Koordinate der VON-Position
+	 * @param oy
+	 *            Y-Koordinate der VON-Position
+	 * @param px
+	 *            X-Koordinate der BIS-Position
+	 * @param py
+	 *            Y-Koordinate der BIS-Position
+	 * @param winToGeotransform
+	 *            Eine Window to Geo transform. If <code>null</code>,
+	 *            {@link #getScreenToWorld()} is used.
+	 */
+	public Envelope tranformGeoToWindow(final double ox, final double oy,
+			final double px, final double py,
+			final AffineTransform winToGeotransform) {
+		final AffineTransform at = winToGeotransform == null ? getScreenToWorld()
+				: winToGeotransform;
+		Point2D geoO;
+		try {
+			geoO = at.inverseTransform(new Point2D.Double(ox, oy), null);
+			final Point2D geoP = at.inverseTransform(
+					new Point2D.Double(px, py), null);
+			return new Envelope(geoO.getX(), geoP.getX(), geoO.getY(), geoP
+					.getY());
+		} catch (final NoninvertibleTransformException e) {
+			LOGGER.error(e);
+			return new Envelope(ox, oy, px, py);
+		}
+	}
+
+	/**
+	 * Transformiert einen Fenster-Koordinaten-Bereich in Geo-Koordinaten.
+	 * 
+	 * @param ox
+	 *            X-Koordinate der VON-Position
+	 * @param oy
+	 *            Y-Koordinate der VON-Position
+	 * @param px
+	 *            X-Koordinate der BIS-Position
+	 * @param py
+	 *            Y-Koordinate der BIS-Position
+	 */
+	public Envelope tranformWindowToGeo(final int ox, final int oy,
+			final int px, final int py) {
+		final AffineTransform at = getScreenToWorld();
+		final Point2D geoO = at.transform(new Point2D.Double(ox, oy), null);
+		final Point2D geoP = at.transform(new Point2D.Double(px, py), null);
+		return new Envelope(geoO.getX(), geoP.getX(), geoO.getY(), geoP.getY());
+	}
+
+	/**
+	 * Will update the cursor. If all rendering is finished also stops the
+	 * {@link #repaintTimer}
+	 */
+	public void updateCursor() {
+
+		// if the renderers have stopped, also stop the timer that is updating
+		// the final image
+		if (bgExecuter != null && bgExecuter.isRunning()
+				|| localExecuter != null && localExecuter.isRunning()) {
+			setCursor(WAIT_CURSOR);
+			return;
+		} else {
+			// Allow one last rendering
+			if (repaintTimer.isRunning()) {
+				repaintTimer.stop();
+				updateFinalImage();
+				repaint();
+			}
+		}
+
+		// wenn manueller Cursor gesetzt ist, dann diesen verwenden (unabhaengig
+		// von der aktuellen Aktion
+		if (this.staticCursor != null) {
+			setCursor(staticCursor);
+			return;
+		}
+		if (getCursor() == SwingUtil.PANNING_CURSOR) {
+			// This cursor will reset itself
+			return;
+		}
+
+		// Set the cursor depending on what tool is in use...
+		switch (state) {
+		case SELECT_TOP:
+		case SELECT_ONE_FROM_TOP:
+		case SELECT_ALL:
+			setCursor(SwingUtil.CROSSHAIR_CURSOR);
+			break;
+		case ZOOM_IN:
+			setCursor(SwingUtil.ZOOMIN_CURSOR);
+			break;
+		case ZOOM_OUT:
+			setCursor(SwingUtil.ZOOMOUT_CURSOR);
+			break;
+		case PAN:
+			setCursor(SwingUtil.PAN_CURSOR);
+			break;
+		default:
+			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+			break;
+		}
+	}
+
+	/**
+	 * The renderers are all rendering into their own {@link Image}s. This
+	 * method combines all images to one {@link #finalImage}. The
+	 * {@link #repaintTimer} is calling this method regularely to update the
+	 * {@link #finalImage} even if the renderers are still working.
+	 */
+	synchronized protected Image updateFinalImage() {
+
+		// Render the two map images first, into the preFinalImage
+		if (bgExecuter != null) {
+			final Graphics2D preFinalG = (Graphics2D) getPreFinalImage()
+					.getGraphics();
+			preFinalG.setBackground(getMapBackgroundColor());
+
+			preFinalG.drawImage(getBgImage(), 0, 0, getMapBackgroundColor(),
+					null);
+
+			// // Draw the local layers image
+			preFinalG.drawImage(getLocalImage(), 0, 0, null);
+			preFinalG.dispose();
+
+		} else {
+			preFinalImage = getLocalImage();
+		}
+
+		final Graphics2D finalG = getFinalImage().createGraphics();
+		finalG.setBackground(getMapBackgroundColor());
+		finalG.drawImage(getPreFinalImage(), imageOrigin.x, imageOrigin.y,
+				getMapBackgroundColor(), null);
+
+		final int finalImageHeight = getFinalImage().getHeight(null);
+		final int finalImageWidth = getFinalImage().getWidth(null);
+
+		final Rectangle painedArea = new Rectangle(imageOrigin.x,
+				imageOrigin.y, finalImageWidth, finalImageHeight);
+		SwingUtil.clearAround(finalG, painedArea, getVisibleRect());
+
+		addGadgets(finalG);
+
+		finalG.dispose();
+
+		return finalImage;
+	}
+
+	/**
+	 * Paints some optional stuff into the given {@link Graphics2D}. Usually
+	 * called as the last layer when {@link #updateFinalImage()}
+	 */
+	private void addGadgets(final Graphics2D graphics) {
+
+		// Paint a logo to the bottom right if available
+		if (mapImage != null) {
+			graphics.drawImage(mapImage, getBounds().width
+					- mapImage.getWidth() - 10, getBounds().height
+					- mapImage.getHeight() - 10, null);
+		}
+
+		// If the rendering process is still running, indicate this is the image
+		if (bgExecuter != null && bgExecuter.isRunning()
+				|| localExecuter != null && localExecuter.isRunning()) {
+
+			final Color c = graphics.getColor();
+			graphics.setFont(waitFont);
+			String waitStr = "Wait..."; //i8n
+			
+			graphics.setColor(Color.WHITE);
+			graphics.drawString(waitStr, 39, 69); 
+			graphics.setColor(Color.BLACK);
+			graphics.drawString(waitStr, 40, 70); 
+
+			graphics.setColor(c);
+		}
+
+	}
+
+	/**
+	 * Sets the {@link #mapArea} to best possibly present the given features. If only one
+	 * single point is given, the window is moved over the point.
+	 * 
+	 * @param features
+	 *            if <code>null</code> or size==0, the function doesn nothing.
+	 */
+	public void zoomTo(
+			final FeatureCollection<SimpleFeatureType, SimpleFeature> features) {
+
+		final CoordinateReferenceSystem mapCRS = getContext()
+				.getCoordinateReferenceSystem();
+		final CoordinateReferenceSystem fCRS = features.getSchema()
+				.getGeometryDescriptor().getCoordinateReferenceSystem();
+
+		double width = mapArea.getWidth();
+		double height = mapArea.getHeight();
+		final double ratio = height / width;
+
+		if (features == null || features.size() == 0) {
+			// feature count == 0 Zoom to the full extend
+			return;
+		} else if (features.size() == 1) {
+
+			// feature count == 1 Just move the window to the point and zoom 'a
+			// bit'
+			final SimpleFeature singleFeature = features.iterator().next();
+
+			if (((Geometry) singleFeature.getDefaultGeometry())
+					.getCoordinates().length > 1) {
+				// System.out.println("Zoomed to only pne poylgon");
+				// Poly
+				// TODO max width vs. height
+				width = features.getBounds().getWidth() * 3;
+				height = ratio * width;
+			} else {
+				// System.out.println("Zoomed in a bit becasue only one point");
+				// width *= .9;
+				// height *= .9;
+			}
+
+			Coordinate centre = features.getBounds().centre();
+			if (!mapCRS.equals(fCRS)) {
+				// only to calculations if the CRS differ
+				try {
+					MathTransform fToMap;
+					fToMap = CRS.findMathTransform(fCRS, mapCRS);
+					// centre is transformed to the mapCRS
+					centre = JTS.transform(centre, null, fToMap);
+				} catch (final FactoryException e) {
+					LOGGER.error("Looking for a Math transform", e);
+				} catch (final TransformException e) {
+					LOGGER.error("Looking for a Math transform", e);
+				}
+			}
+
+			final Coordinate newLeftBottom = new Coordinate(centre.x - width
+					/ 2., centre.y - height / 2.);
+			final Coordinate newTopRight = new Coordinate(
+					centre.x + width / 2., centre.y + height / 2.);
+
+			final Envelope newMapArea = new Envelope(newLeftBottom, newTopRight);
+
+			setMapArea(newMapArea);
+
+		} else {
+			final ReferencedEnvelope fBounds = features.getBounds();
+
+			Envelope bounds;
+			if (!mapCRS.equals(fCRS)) {
+				bounds = JTSUtil.transformEnvelope(fBounds, fCRS, mapCRS);
+			} else {
+				bounds = fBounds;
+			}
+			// BB umrechnen von Layer-CRS in Map-CRS
+
+			// Expand a bit
+			bounds.expandBy(bounds.getWidth() / 6., bounds.getHeight() / 6.);
+
+			setMapArea(bounds);
+		}
+	}
+
+	/**
+	 * Zooms towards a point.
+	 * 
+	 * @param center
+	 *            position in window coordinates
+	 * @param zoomFactor
+	 *            > 1 for zoom in, < 1 for zoom out. Default is 1.33
+	 */
+	public void zoomTo(final Point center) {
+		zoomTo(center, null);
+	}
+
+	/**
+	 * Zooms towards a point.
+	 * 
+	 * @param center
+	 *            position in window coordinates
+	 * @param zoomFaktor
+	 *            > 1 for zoom in, < 1 for zoom out. Default is 1.33.
+	 */
+	public void zoomTo(Point center, Double zoomFaktor) {
+		if (zoomFaktor == null || zoomFaktor == 0.)
+			zoomFaktor = 2.;
+
+		final Point2D gcenter = getScreenToWorld().transform(center, null);
+		center = null;
+
+		if (Double.isNaN(gcenter.getX()) || Double.isNaN(gcenter.getY())
+				|| Double.isInfinite(gcenter.getX())
+				|| Double.isInfinite(gcenter.getY())
+
+		) {
+			// Not inside valid CRS area! cancel
+			return;
+		}
+
+		final Envelope mapArea = getMapArea();
+
+		final Envelope newMapArea = new Envelope(mapArea);
+		newMapArea.expandBy((mapArea.getWidth() * zoomFaktor - mapArea
+				.getWidth()) / 2., (mapArea.getHeight() * zoomFaktor - mapArea
+				.getHeight()) / 2.);
+
+		// Move the newMapArea above the new center
+		newMapArea.translate(gcenter.getX() - mapArea.centre().x, gcenter
+				.getY()
+				- mapArea.centre().y);
+
+		setMapArea(newMapArea);
+	}
+
+}

Copied: branches/1.0-gt2-2.6/src/skrueger/geotools/ZoomXMapPaneMouseListener.java (from rev 513, branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java)
===================================================================
--- branches/1.0-gt2-2.6/src/gtmig/org/geotools/swing/ZoomXMapPaneMouseListener.java	2009-11-09 11:17:34 UTC (rev 513)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/ZoomXMapPaneMouseListener.java	2009-11-19 09:42:59 UTC (rev 531)
@@ -0,0 +1,251 @@
+package skrueger.geotools;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+
+import org.apache.log4j.Logger;
+
+import schmitzm.swing.SwingUtil;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+
+public class ZoomXMapPaneMouseListener extends MouseAdapter {
+
+	private static Logger LOGGER = Logger
+			.getLogger(ZoomXMapPaneMouseListener.class);
+
+
+	protected final XMapPane xMapPane;
+
+	protected boolean enabled = true;
+
+	public ZoomXMapPaneMouseListener(XMapPane xMapPane) {
+		if (xMapPane == null) throw new IllegalArgumentException("xMapPane may not be null");
+		this.xMapPane = xMapPane;
+	}
+
+	/**
+	 * Stores beginning of a drag event in window coordinates
+	 */
+	protected Point startPos;
+	
+	/**
+	 * Stores last position of a drag event in window coordinates
+	 */
+	protected  Point lastPos;
+
+	/**
+	 * Draws a rectangle in XOR mode from the origin at {@link #startPos} to the
+	 * given point. All in screen coordinates.
+	 */
+	protected void drawRectangle(final Graphics graphics, Point e) {
+		// undraw last box/draw new box
+		final int left = Math.min(startPos.x, e.x);
+		final int right = Math.max(startPos.x, e.x);
+		final int top = Math.max(startPos.y, e.y);
+		final int bottom = Math.min(startPos.y, e.y);
+		final int width = right - left;
+		final int height = top - bottom;
+
+		if (width == 0 && height == 0)
+			return;
+
+		graphics.setXORMode(Color.WHITE);
+		graphics.drawRect(left, bottom, width, height);
+	}
+
+	public void mousePressed(final MouseEvent e) {
+		if (!isEnabled())
+			return;
+		startPos = new Point(e.getPoint());
+		lastPos = new Point(e.getPoint());
+	}
+
+	public void mouseReleased(final MouseEvent e) {
+		if (!isEnabled())
+			return;
+
+		final Rectangle bounds = xMapPane.getBounds();
+
+		Envelope mapArea = xMapPane.getMapArea();
+
+		// Replace with transform and translate
+		final double mapWidth = mapArea.getWidth();
+		final double mapHeight = mapArea.getHeight();
+
+		final double startX = ((startPos.x * mapWidth) / (double) bounds.width)
+				+ mapArea.getMinX();
+		final double startY = (((bounds.getHeight() - startPos.y) * mapHeight) / (double) bounds.height)
+				+ mapArea.getMinY();
+		final double endX = ((e.getPoint().x * mapWidth) / (double) bounds.width)
+				+ mapArea.getMinX();
+		final double endY = (((bounds.getHeight() - e.getPoint().y) * mapHeight) / (double) bounds.height)
+				+ mapArea.getMinY();
+
+		if ((xMapPane.getState() == XMapPane.PAN)
+				|| (e.getButton() == MouseEvent.BUTTON3)) {
+			xMapPane.performPan();
+
+		} else if (xMapPane.getState() == XMapPane.ZOOM_IN) {
+
+			drawRectangle(xMapPane.getGraphics(), e.getPoint());
+
+
+			// Don't zoom too small areas. Handle it like a click then
+			if ((Math.abs(startPos.x - e.getPoint().x) * Math.abs(e.getPoint().y - startPos.y)) < 160) {
+				xMapPane.zoomTo(e.getPoint(), .5);
+				return;
+			}
+
+			final double left = Math.min(startX, endX);
+			final double right = Math.max(startX, endX);
+			final double bottom = Math.min(startY, endY);
+			final double top = Math.max(startY, endY);
+			final Coordinate ll = new Coordinate(left, bottom);
+			final Coordinate ur = new Coordinate(right, top);
+
+			xMapPane.setMapArea(new Envelope(ll, ur));
+
+		} else if (xMapPane.getState() == XMapPane.ZOOM_OUT) {
+
+			// Remove the rectangle
+			drawRectangle(xMapPane.getGraphics(), e.getPoint());
+			
+			
+
+			// Don't zoom too small areas
+			if ((Math.abs(startPos.x - e.getPoint().x) * Math.abs(e.getPoint().y - startPos.y)) < 100) {
+				xMapPane.zoomTo(e.getPoint(), 2.);
+				return;
+			}
+
+			// make the dragged rectangle in screen coords the new map size?
+			final double left = Math.min(startX, endX);
+			final double right = Math.max(startX, endX);
+			final double bottom = Math.min(startY, endY);
+			final double top = Math.max(startY, endY);
+			final double nWidth = (mapWidth * mapWidth) / (right - left);
+			final double nHeight = (mapHeight * mapHeight) / (top - bottom);
+			final double deltaX1 = left - mapArea.getMinX();
+			final double nDeltaX1 = (deltaX1 * nWidth) / mapWidth;
+			final double deltaY1 = bottom - mapArea.getMinY();
+			final double nDeltaY1 = (deltaY1 * nHeight) / mapHeight;
+			final Coordinate ll = new Coordinate(mapArea.getMinX() - nDeltaX1,
+					mapArea.getMinY() - nDeltaY1);
+			final double deltaX2 = mapArea.getMaxX() - right;
+			final double nDeltaX2 = (deltaX2 * nWidth) / mapWidth;
+			final double deltaY2 = mapArea.getMaxY() - top;
+			final double nDeltaY2 = (deltaY2 * nHeight) / mapHeight;
+			final Coordinate ur = new Coordinate(mapArea.getMaxX() + nDeltaX2,
+					mapArea.getMaxY() + nDeltaY2);
+
+			xMapPane.setMapArea(new Envelope(ll, ur));
+		}
+
+
+	}
+
+
+	public void setEnabled(boolean enabled) {
+		this.enabled = enabled;
+	}
+
+	/**
+	 * Returns <code>true</code> if the xMapPane is well defined and the
+	 * listener is enabled.
+	 */
+	public boolean isEnabled() {
+		return enabled && xMapPane.isWellDefined();
+	}
+	
+	/**
+	 * Verarbeitet die Mausrad-Aktion, indem gezoomed wird.
+	 * 
+	 * @param wheelEvt
+	 *            Mausrad-Event
+	 */
+	public void mouseWheelMoved(MouseWheelEvent wheelEvt) {
+		if (!isEnabled()) return;
+
+		final int units = wheelEvt.getUnitsToScroll();
+		if (units > 0)
+			xMapPane.zoomTo(wheelEvt.getPoint(), 1. + .11 * units);
+		else
+			xMapPane.zoomTo(wheelEvt.getPoint(), 2. / -units);
+	}
+
+	@Override
+	public void mouseClicked(final MouseEvent e) {
+
+		if (!isEnabled())
+			return;
+
+		int state = xMapPane.getState();
+
+		// Nothing to do for panning
+		if (state == XMapPane.PAN)
+			return;
+
+		// double zoomFactor = 1.;
+		//		
+		// final Envelope mapArea = xMapPane.getMapArea();
+		// if (mapArea == null)
+		// return;
+		//
+		// final Rectangle bounds = xMapPane.getVisibleRect();
+		//
+		// final double x = (double) (e.getX());
+		// final double y = (double) (e.getY());
+		// final double width = mapArea.getWidth();
+		// final double height = mapArea.getHeight();
+		// final double width2 = width / 2.0;
+		// final double height2 = height / 2.0;
+		// final double mapX = ((x * width) / (double) bounds.width)
+		// + mapArea.getMinX();
+		// final double mapY = (((bounds.getHeight() - y) * height) / (double)
+		// bounds.height)
+		// + mapArea.getMinY();
+		// double zlevel = 1.0;
+		//		
+		if (state == XMapPane.ZOOM_IN && e.getButton() == MouseEvent.BUTTON1) {
+			xMapPane.zoomTo(e.getPoint(), 1 / 2.);
+		} else if (state == XMapPane.ZOOM_IN
+				&& e.getButton() == MouseEvent.BUTTON3
+				|| state == XMapPane.ZOOM_OUT) {
+			xMapPane.zoomTo(e.getPoint(), 2.);
+		}
+		//
+		// final Coordinate ll = new Coordinate(mapX - (width2 / zlevel), mapY
+		// - (height2 / zlevel));
+		// final Coordinate ur = new Coordinate(mapX + (width2 / zlevel), mapY
+		// + (height2 / zlevel));
+		// final Envelope newMapArea = new Envelope(ll, ur);
+		//
+		// xMapPane.setMapArea(newMapArea);
+
+		// xMapPane.repaint();
+
+	}
+
+	/**
+	 * Constantly fired while the mouse is dragging
+	 */
+	public void mouseDragged(final MouseEvent event) {
+		if (!isEnabled())
+			return;
+		
+		xMapPane.mouseDragged(startPos, lastPos, event);
+
+
+		lastPos = event.getPoint();
+	}
+
+
+}



More information about the Schmitzm-commits mailing list