/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008-2011, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.swing.tool; import java.lang.ref.WeakReference; import java.util.logging.Logger; import org.geotools.geometry.DirectPosition2D; import org.geotools.map.MapContent; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; /** * Abstract base class for helper classes used by {@code InfoTool} to query * {@code Layers}. The primary reason for having this class is to avoid * loading grid coverage classes unless they are really needed, and thus * avoid the need for users to have JAI in the classpath when working with * vector data. * <p> * The type parameter <code><T></code> defines the return type of the * {@linkplain #getInfo} method. * * @see InfoTool * * @author Michael Bedward * @since 2.6 * * * @source $URL$ * @version $URL$ */ public abstract class InfoToolHelper<T> { private static final Logger LOGGER = Logging.getLogger("org.geotools.swing"); private final WeakReference<MapContent> contentRef; private CoordinateReferenceSystem dataCRS; private boolean transformRequired; private boolean transformFailed; private MathTransform transform; /** * Protected constructor. * * @param content the map content * @param dataCRS the coordinate reference system of the feature data that will be queried * by this helper */ protected InfoToolHelper(MapContent content, CoordinateReferenceSystem dataCRS) { this.contentRef = new WeakReference<MapContent>(content); doSetCRS(dataCRS); } /** * Get feature data at the given position. * * @param pos the location to query * * @param params additional parameters as optionally defined by the sub-class * * @return data of type {@code T} as defined by the sub-class * * @see #isValid() */ public abstract T getInfo(DirectPosition2D pos, Object ...params) throws Exception; /** * Query if this helper has a reference to a {@code MapContent} and {@code Layer}. * <p> * Helpers only hold a {@linkplain WeakReference} to the map content and layer * with which they are working to avoid blocking garbage collection when layers are * discarded. If this method returns {@code false} the helper should be re-created. * <pre><code> * // * // Example using a VectorLayerHelper... * // * VectorLayerHelper helper = ... * * if (helper != null && helper.isValid()) { * FeatureCollection coll = helper.getInfo(queryLocation, ...); * // do something useful with results * * } else { * // (Re-)create the helper * // Note: example only; this obviously depends on your use case * helper = new VectorLayerHelper(content, layer); * } * </code></pre> * * @return */ public abstract boolean isValid(); /** * Get the {@code MapContent} associated with this helper. The helper maintains * only a {@linkplain WeakReference} to the content. * * @return the map content or null if it is no longer current. */ public MapContent getMapContent() { return contentRef != null ? contentRef.get() : null; } /** * Check if queries with this helper involve transforming between coordinate * systems. * * @return true if coordinte transformation is required; false otherwise */ protected boolean isTransformRequired() { return transformRequired; } /** * Get the {@code MathTransform} to re-project data from the coordinate system of * the {@code MapContent} to that of the {@code Layer}. * * @return the transform or {@code null} if either the layer's coordinate system is the same * as that of the map content, or either has a {@code null} CRS. */ public MathTransform getTransform() { if (transform == null && !transformFailed && dataCRS != null) { MapContent content = getMapContent(); if (content == null) { throw new IllegalStateException("null map content"); } CoordinateReferenceSystem contentCRS = content.getCoordinateReferenceSystem(); try { transform = CRS.findMathTransform(contentCRS, dataCRS, true); } catch (Exception ex) { LOGGER.warning("Can't transform map content to layer CRS"); transformFailed = true; } } return transform; } /** * Set the coordinate reference system that pertains to the feature data * that will be queried by this helper. * * @param crs data coordinate reference system */ protected void setCRS(CoordinateReferenceSystem crs) { doSetCRS(crs); } private void doSetCRS(CoordinateReferenceSystem crs) { this.dataCRS = crs; MapContent content = getMapContent(); if (content == null) { throw new IllegalStateException("null map content"); } final CoordinateReferenceSystem contentCRS = content.getCoordinateReferenceSystem(); transformRequired = false; if (contentCRS != null && crs != null && !CRS.equalsIgnoreMetadata(contentCRS, dataCRS)) { transformRequired = true; } } }