/* * 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.util.Collection; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import org.geotools.data.FeatureSource; import org.geotools.data.Query; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.jts.Geometries; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.map.FeatureLayer; import org.geotools.map.Layer; import org.opengis.feature.Feature; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.Name; import org.opengis.feature.type.PropertyDescriptor; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Helper class used by {@linkplain InfoTool} to query vector features in a * {@linkplain org.geotools.map.FeatureLayer}. * * @author Michael Bedward * @since 2.6 * * @source $URL$ * @version $URL$ */ public class FeatureLayerHelper extends InfoToolHelper { /** * Default distance fraction used with line and point features. * When the user clicks on the map, this tool searches for features within * a rectangle of width w centred on the mouse location, where w is the * average map side length multiplied by the value of this constant. */ public static final double DEFAULT_DISTANCE_FRACTION = 0.01d; private static final GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); private static final FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2(null); private String attrName; private Geometries geomType; /** * No argument constructor required by the helper lookup system. */ public FeatureLayerHelper() { } /** * {@inheritDoc} * The {@code layer} argument must be an instance of {@linkplain FeatureLayer}. */ @Override public void setLayer(Layer layer) { if (!(layer instanceof FeatureLayer)) { throw new IllegalArgumentException("layer must be an instance of FeatureLayer"); } super.setLayer(layer); GeometryDescriptor geomDesc = layer.getFeatureSource().getSchema().getGeometryDescriptor(); attrName = geomDesc.getLocalName(); Class<? extends Geometry> geomClass = (Class<? extends Geometry>) geomDesc.getType().getBinding(); geomType = Geometries.getForBinding(geomClass); } @Override public boolean isSupportedLayer(Layer layer) { return layer instanceof FeatureLayer; } @Override public InfoToolResult getInfo(DirectPosition2D pos) throws Exception { InfoToolResult result = new InfoToolResult(); if (isValid()) { Filter filter = null; if (geomType == Geometries.POLYGON || geomType == Geometries.MULTIPOLYGON) { Geometry posGeom = createSearchPoint(pos); filter = filterFactory.intersects( filterFactory.property(attrName), filterFactory.literal(posGeom)); } else { ReferencedEnvelope env = createSearchEnv(pos); filter = filterFactory.bbox(filterFactory.property(attrName), env); } Query query = new Query(null, filter); query.setCoordinateSystemReproject(getMapContent().getCoordinateReferenceSystem()); FeatureSource featureSource = getLayer().getFeatureSource(); Collection<PropertyDescriptor> descriptors = featureSource.getSchema().getDescriptors(); FeatureCollection queryResult = featureSource.getFeatures(query); FeatureIterator iter = queryResult.features(); try { while (iter.hasNext()) { Feature f = iter.next(); result.newFeature(f.getIdentifier().getID()); for (PropertyDescriptor desc : descriptors) { Name name = desc.getName(); Object value = f.getProperty(name).getValue(); if (value != null) { if (value instanceof Geometry) { result.setFeatureValue(name, value.getClass().getSimpleName()); } else { result.setFeatureValue(name, value); } } else { result.setFeatureValue(name, "null"); } } } } finally { iter.close(); } } return result; } /** * Converts the query position, in map content coordinates, to a position * in layer coordinates and returns it as a JTS {@code Point}. * * @param pos query position in map content coordaintes * @return point in layer coordinates */ private Geometry createSearchPoint(DirectPosition2D pos) { try { DirectPosition2D trPos = new DirectPosition2D(); getContentToLayerTransform().transform(pos, trPos); Geometry point = geometryFactory.createPoint(new Coordinate(trPos.x, trPos.y)); return point; } catch (Exception ex) { throw new IllegalStateException(ex); } } private ReferencedEnvelope createSearchEnv(DirectPosition2D pos) { ReferencedEnvelope mapBounds = getMapContent().getViewport().getBounds(); if (mapBounds == null || mapBounds.isEmpty()) { // fall back to layer bounds Layer layer = getLayer(); if (layer == null) { // this should never happen throw new IllegalStateException("Target layer has been lost"); } mapBounds = getLayer().getBounds(); } double halfWidth = 0.5 * DEFAULT_DISTANCE_FRACTION * (mapBounds.getWidth() + mapBounds.getHeight()); CoordinateReferenceSystem contentCRS = getMapContent().getCoordinateReferenceSystem(); ReferencedEnvelope env = new ReferencedEnvelope( pos.x - halfWidth, pos.x + halfWidth, pos.y - halfWidth, pos.y + halfWidth, contentCRS); if (isTransformRequired()) { CoordinateReferenceSystem layerCRS = getLayer().getFeatureSource().getSchema().getCoordinateReferenceSystem(); try { env = env.transform(layerCRS, true); } catch (Exception ex) { throw new IllegalStateException(ex); } } return env; } }