//$HeadURL$ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2012 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: info@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.igeo.commands.geoprocessing; import static java.util.Collections.singletonList; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.UUID; import org.deegree.crs.components.Unit; import org.deegree.datatypes.QualifiedName; import org.deegree.datatypes.Types; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.igeo.commands.CommandHelper; import org.deegree.igeo.config.EnvelopeType; import org.deegree.igeo.config.MemoryDatasourceType; import org.deegree.igeo.config.LayerType.MetadataURL; import org.deegree.igeo.dataadapter.DataAccessAdapter; import org.deegree.igeo.dataadapter.FeatureAdapter; import org.deegree.igeo.mapmodel.Datasource; import org.deegree.igeo.mapmodel.Layer; import org.deegree.igeo.mapmodel.MapModel; import org.deegree.igeo.mapmodel.MemoryDatasource; import org.deegree.igeo.modules.DefaultMapModule.SelectedFeaturesVisitor; import org.deegree.igeo.views.swing.geoprocessing.BufferModel; import org.deegree.kernel.AbstractCommand; import org.deegree.kernel.Command; import org.deegree.model.Identifier; import org.deegree.model.feature.Feature; import org.deegree.model.feature.FeatureCollection; import org.deegree.model.feature.FeatureFactory; import org.deegree.model.feature.FeatureProperty; import org.deegree.model.feature.schema.FeatureType; import org.deegree.model.feature.schema.PropertyType; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.Geometry; import org.deegree.model.spatialschema.MultiSurface; import org.deegree.model.spatialschema.Surface; /** * {@link Command} implementation for creating buffers around geometries of a layer. The result will be added as new * layer. * * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ * */ public class BufferCommand extends AbstractCommand { public static enum BUFFERTYPE { inside, outside, both, inside_filled, outside_filled }; public static final QualifiedName name = new QualifiedName( "Create Buffer" ); private static final ILogger LOG = LoggerFactory.getLogger( BufferCommand.class ); private Layer layer; private int segments; private BUFFERTYPE bufferType; private int capStyle; private double[] distances; private boolean mergeIntersectingBuffer = false; private String newLayerName; private QualifiedName geomProperty; private QualifiedName propertyForBufferDistance; private Layer newLayer; private boolean performed; private boolean overlayedBuffers; private Unit bufferUnit; /** * * @param layer * @param bufferModel */ public BufferCommand( Layer layer, BufferModel bufferModel ) { this.layer = layer; this.distances = bufferModel.getDistances(); this.segments = bufferModel.getSegments(); this.capStyle = bufferModel.getCapStyle(); this.newLayerName = bufferModel.getNewLayerName(); this.geomProperty = bufferModel.getGeometryProperty(); this.bufferType = bufferModel.getBufferType(); this.overlayedBuffers = bufferModel.isOverlayedBuffers(); this.mergeIntersectingBuffer = bufferModel.shallMerge(); this.propertyForBufferDistance = bufferModel.getPropertyForBufferDistance(); this.bufferUnit = bufferModel.getBufferUnit(); if ( bufferType == BUFFERTYPE.inside || bufferType == BUFFERTYPE.inside_filled ) { for ( int i = 0; i < distances.length; i++ ) { this.distances[i] = -1 * this.distances[i]; } } } public void execute() throws Exception { performed = false; MapModel mm = layer.getOwner(); SelectedFeaturesVisitor visitor = new SelectedFeaturesVisitor( -1 ); try { mm.walkLayerTree( visitor ); FeatureCollection tmp = visitor.col; FeatureType ft = null; List<Layer> layers = mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION ); if ( layers.size() == 0 ) { throw new Exception( "one layer must be selected" ); } List<DataAccessAdapter> dada = layers.get( 0 ).getDataAccess(); for ( DataAccessAdapter dataAccessAdapter : dada ) { if ( dataAccessAdapter instanceof FeatureAdapter ) { if ( tmp.size() == 0 ) { tmp.addAllUncontained( ( (FeatureAdapter) dataAccessAdapter ).getFeatureCollection() ); } ft = ( (FeatureAdapter) dataAccessAdapter ).getSchema(); } } if ( tmp.size() > 0 ) { if ( processMonitor != null ) { processMonitor.setMaximumValue( visitor.col.size() ); } FeatureCollection fc = FeatureFactory.createFeatureCollection( UUID.randomUUID().toString(), visitor.col.size() ); Iterator<Feature> iterator = visitor.col.iterator(); if ( geomProperty == null ) { geomProperty = CommandHelper.findGeomProperty( visitor.col.getFeature( 0 ) ); } int cnt = 0; FeatureType bufferedFeatureType = createBufferedFeatureType( ft ); while ( iterator.hasNext() ) { processMonitor.updateStatus( cnt++, "" ); Feature feature = (Feature) iterator.next(); if ( this.propertyForBufferDistance != null ) { fillDistancesFromProperty( feature ); } Geometry origGeom = feature.getDefaultGeometryPropertyValue(); Geometry lastGeom = null; if ( overlayedBuffers ) { for ( int i = distances.length - 1; i >= 0; i-- ) { bufferGeometry( fc, bufferedFeatureType, feature, origGeom, lastGeom, distances[i] ); } } else { for ( int i = 0; i < distances.length; i++ ) { lastGeom = bufferGeometry( fc, bufferedFeatureType, feature, origGeom, lastGeom, distances[i] ); } } } if ( mergeIntersectingBuffer ) { fc = mergeIntersectingBuffers( fc ); } Envelope env = fc.getBoundedBy(); MemoryDatasourceType mdst = new MemoryDatasourceType(); EnvelopeType et = new EnvelopeType(); et.setMinx( env.getMin().getX() ); et.setMiny( env.getMin().getY() ); et.setMaxx( env.getMax().getX() ); et.setMaxy( env.getMax().getY() ); et.setCrs( mm.getEnvelope().getCoordinateSystem().getPrefixedName() ); mdst.setExtent( et ); mdst.setMinScaleDenominator( 0d ); mdst.setMaxScaleDenominator( 100000000d ); Datasource ds = new MemoryDatasource( mdst, null, null, fc ); Identifier id = new Identifier( newLayerName ); int i = 0; while ( mm.exists( id ) ) { id = new Identifier( newLayerName + "_" + i++ ); } newLayer = new Layer( mm, id, id.getValue(), newLayerName, singletonList( ds ), Collections.<MetadataURL> emptyList() ); newLayer.setEditable( true ); mm.insert( newLayer, layer.getParent(), layer, false ); } } catch ( Exception e ) { LOG.logError( "Unknown error", e ); throw e; } finally { if ( processMonitor != null ) { processMonitor.cancel(); processMonitor = null; } } performed = true; } private void fillDistancesFromProperty( Feature feature ) { FeatureProperty fp = feature.getDefaultProperty( this.propertyForBufferDistance ); if ( fp != null ) { double d = ( (Number) fp.getValue() ).doubleValue(); for ( int i = 0; i < distances.length; i++ ) { this.distances[i] = d + ( i + 1 ); } if ( bufferType == BUFFERTYPE.inside || bufferType == BUFFERTYPE.inside_filled ) { for ( int i = 0; i < distances.length; i++ ) { this.distances[i] = -1 * this.distances[i]; } } } } private Geometry bufferGeometry( FeatureCollection fc, FeatureType bufferedFeatureType, Feature feature, Geometry origGeom, Geometry lastGeom, double distance ) throws CloneNotSupportedException { if ( "cm".equals( bufferUnit.getSymbol() ) ) { distance /= 100d; } else if ( "km".equals( bufferUnit.getSymbol() ) ) { distance *= 1000d; } if ( distance > 0 || ( distance < 0 && origGeom instanceof Surface ) || ( distance < 0 && origGeom instanceof MultiSurface ) ) { Geometry geom = null; switch ( bufferType ) { case inside: geom = origGeom.getBuffer( distance, segments, capStyle ); geom = origGeom.difference( geom ); break; case outside: if ( origGeom instanceof Surface || origGeom instanceof MultiSurface ) { geom = origGeom.getBuffer( distance, segments, capStyle ); geom = geom.difference( origGeom ); } else { return lastGeom; } break; case both: if ( origGeom instanceof Surface || origGeom instanceof MultiSurface ) { geom = origGeom.getBuffer( distance, segments, capStyle ); Geometry geom2 = origGeom.getBuffer( -1 * distance, segments, capStyle ); geom = geom.difference( geom2 ); } else { return lastGeom; } break; default: geom = origGeom.getBuffer( distance, segments, capStyle ); break; } // clone feature to ensure that the new layer contains its own instances Feature nfeature = createBufferedFeature( feature, bufferedFeatureType, distance ); FeatureProperty fp = nfeature.getDefaultProperty( geomProperty ); if ( lastGeom != null && !overlayedBuffers ) { Geometry tmpG = geom; geom = geom.difference( lastGeom ); lastGeom = tmpG; } else { lastGeom = geom; } fp.setValue( geom ); fc.add( nfeature ); } return lastGeom; } /** * * @param ft * @return feature type for buffered features */ private FeatureType createBufferedFeatureType( FeatureType ft ) { PropertyType[] pt = ft.getProperties(); List<PropertyType> list = new ArrayList<PropertyType>( pt.length + 1 ); for ( PropertyType propertyType : pt ) { list.add( propertyType ); } PropertyType npt = FeatureFactory.createSimplePropertyType( new QualifiedName( "buffer_distance", ft.getNameSpace() ), Types.FLOAT, 0, 1 ); list.add( npt ); pt = list.toArray( new PropertyType[list.size()] ); QualifiedName name = new QualifiedName( "buffered_" + ft.getName().getLocalName(), ft.getNameSpace() ); return FeatureFactory.createFeatureType( name, false, pt ); } /** * * @param feature * @param bufferedFeatureType * @param distance * @return new feature * @throws CloneNotSupportedException */ private Feature createBufferedFeature( Feature feature, FeatureType bufferedFeatureType, double distance ) throws CloneNotSupportedException { FeatureProperty[] properties = feature.getProperties(); FeatureProperty[] fp = new FeatureProperty[properties.length + 1]; for ( int i = 0; i < fp.length - 1; i++ ) { if ( properties[i].getValue() instanceof FeatureCollection ) { Object v = ( (FeatureCollection) properties[i].getValue() ).clone(); fp[i] = FeatureFactory.createFeatureProperty( properties[i].getName(), v ); } else if ( properties[i].getValue() instanceof Feature ) { Object v = ( (Feature) properties[i].getValue() ).clone(); fp[i] = FeatureFactory.createFeatureProperty( properties[i].getName(), v ); } else { fp[i] = FeatureFactory.createFeatureProperty( properties[i].getName(), properties[i].getValue() ); } } QualifiedName name = new QualifiedName( "buffer_distance", bufferedFeatureType.getNameSpace() ); fp[fp.length - 1] = FeatureFactory.createFeatureProperty( name, distance ); return FeatureFactory.createFeature( "UUID_" + UUID.randomUUID().toString(), bufferedFeatureType, fp ); } /** * @param fc * @return */ private FeatureCollection mergeIntersectingBuffers( FeatureCollection fc ) { List<Integer> tmp1 = new ArrayList<Integer>( fc.size() ); Feature[] features = fc.toArray(); boolean merged = false; do { merged = false; for ( int i = 0; i < features.length; i++ ) { for ( int j = i + 1; j < features.length; j++ ) { if ( !tmp1.contains( j ) ) { Geometry g1 = features[i].getDefaultGeometryPropertyValue(); Geometry g2 = features[j].getDefaultGeometryPropertyValue(); if ( g1.intersects( g2 ) ) { g1 = g1.union( g2 ); FeatureProperty fp = features[i].getDefaultProperty( geomProperty ); fp.setValue( g1 ); tmp1.add( j ); merged = true; } } } } } while ( merged ); FeatureCollection mergedFc = FeatureFactory.createFeatureCollection( "UUID_" + UUID.randomUUID().toString(), features.length ); for ( int i = 0; i < features.length; i++ ) { if ( !tmp1.contains( i ) ) { mergedFc.add( features[i] ); } } return mergedFc; } /* * (non-Javadoc) * * @see org.deegree.kernel.Command#getName() */ public QualifiedName getName() { return name; } /* * (non-Javadoc) * * @see org.deegree.kernel.Command#getResult() */ public Object getResult() { return newLayer; } @Override public boolean isUndoSupported() { return true; } @Override public void undo() throws Exception { if ( performed ) { newLayer.getOwner().remove( newLayer ); performed = false; } } }