//$HeadURL$
/*----------------------------------------------------------------------------
This file is part of deegree, http://deegree.org/
Copyright (C) 2001-2012 by:
- Department of Geography, University of Bonn -
and
- lat/lon GmbH -
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 information:
lat/lon GmbH
Aennchenstr. 19, 53177 Bonn
Germany
http://lat-lon.de/
Department of Geography, University of Bonn
Prof. Dr. Klaus Greve
Postfach 1147, 53001 Bonn
Germany
http://www.geographie.uni-bonn.de/deegree/
e-mail: info@deegree.org
----------------------------------------------------------------------------*/
package org.deegree.igeo.commands.digitize;
import java.util.ArrayList;
import java.util.List;
import org.deegree.datatypes.QualifiedName;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.GeometryUtils;
import org.deegree.framework.util.Pair;
import org.deegree.framework.utils.LineUtils;
import org.deegree.igeo.dataadapter.FeatureAdapter;
import org.deegree.kernel.AbstractCommand;
import org.deegree.kernel.Command;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.model.feature.FeatureProperty;
import org.deegree.model.spatialschema.Curve;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryException;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.model.spatialschema.Point;
import org.deegree.model.spatialschema.Position;
/**
* {@link Command} implementation for joining two {@link Curve}s. Two modes are available:
* <ul>
* <li>1. the result will be one curve
* <li>2. the connection will be modeled a new curve
* </ul>
* Curves can be connected by direct connection, by stretching each each curve till intersection and by an arc.
*
* @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a>
* @author <a href="mailto:name@deegree.org">Andreas Poth</a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*/
public class JoinCurvesCommand extends AbstractCommand {
private static final ILogger LOG = LoggerFactory.getLogger( JoinCurvesCommand.class );
private static final QualifiedName name = new QualifiedName( "Connect Curves" );
public enum CURVE_CONNECTION_TYPE {
tangent, circleFixedRadiusEndNodes, circleFixedRadiusShortenCurves, spline, direct, stretchLines, shortenLines, stretchAndShortenLines, splitLines, splitAndAlternate
};
private CURVE_CONNECTION_TYPE curveConnType;
private boolean connectionAsNewCurve;
private Curve curve1;
private Curve curve2;
private Feature feature1;
private Feature feature2;
private boolean performed = false;
private FeatureAdapter featureAdapter;
private QualifiedName geomProperty;
private Feature newFeature;
private int noOfSegments;
private Point point1;
private Point point2;
/**
*
* @param featureAdapter
* @param geomProperty
* @param curveConnType
* @param connectionAsNewCurve
* @param feature1
* @param point1
* @param feature2
* @param point2
* @param noOfSegments
*/
public JoinCurvesCommand( FeatureAdapter featureAdapter, QualifiedName geomProperty,
CURVE_CONNECTION_TYPE curveConnType, boolean connectionAsNewCurve, Feature feature1,
Point point1, Feature feature2, Point point2, int noOfSegments ) {
this.featureAdapter = featureAdapter;
this.geomProperty = geomProperty;
this.curveConnType = curveConnType;
this.connectionAsNewCurve = connectionAsNewCurve;
this.feature1 = feature1;
this.feature2 = feature2;
this.point1 = point1;
this.point2 = point2;
if ( geomProperty == null ) {
this.curve1 = (Curve) feature1.getDefaultGeometryPropertyValue();
this.curve2 = (Curve) feature2.getDefaultGeometryPropertyValue();
} else {
this.curve1 = (Curve) feature1.getDefaultProperty( geomProperty ).getValue();
this.curve2 = (Curve) feature2.getDefaultProperty( geomProperty ).getValue();
}
this.noOfSegments = noOfSegments;
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#execute()
*/
public void execute()
throws Exception {
LOG.logInfo( "connect with method: " + curveConnType );
switch ( curveConnType ) {
case tangent:
executeTangent();
break;
case circleFixedRadiusEndNodes:
executeCircleFixedRadiusEndNodes();
break;
case circleFixedRadiusShortenCurves:
executeCircleFixedRadiusShortenCurves();
break;
case spline:
executeSpline();
break;
case direct:
executeDirect();
break;
case stretchLines:
executeStretchLines();
break;
case shortenLines:
executeShortenLines();
break;
case stretchAndShortenLines:
executeStretchAndShortenLines();
break;
case splitLines:
executeSplitLines();
break;
case splitAndAlternate:
executeSplitAndAlternate();
break;
default:
executeDirect();
break;
}
performed = true;
}
private void executeTangent()
throws Exception {
Pair<Position[], Position[]> segments = findSegements( );
Position[] segment1 = segments.first;
Position[] segment2 = segments.second;
// ensure that segments intersection is not part one of the segments
double[] line1 = LineUtils.getLineFromPoints( segment1[0], segment1[1] );
double[] line2 = LineUtils.getLineFromPoints( segment2[0], segment2[1] );
Position intersection = LineUtils.getLineIntersection( line1[0], line1[1], line2[0], line2[1] );
// if ( tmp1.contains( intersection ) || tmp2.contains( intersection ) ) {
// throw new Exception( "intersection between lines constructed on curve segements to "
// + "connect must not been contained by one of the segments. Use direct, "
// + "stretchLines, shortenLines or stretchAndShortenLines instead" );
// }
// correct line segment that is farthest from intersection point and get intersection
// between perpendicular lines going through points to connect
double d1 = GeometryUtils.distance( point1.getX(), point1.getY(), intersection.getX(), intersection.getY() );
double d2 = GeometryUtils.distance( point2.getX(), point2.getY(), intersection.getX(), intersection.getY() );
Pair<Position, Position> pair = null;
double[] line1p = null;
double[] line2p = null;
Position p1 = point1.getPosition();
Position p2 = point2.getPosition();
if ( d1 > d2 ) {
pair = LineUtils.getSymmetricPoints( intersection.getX(), intersection.getY(), line1[0], d2 );
if ( GeometryUtils.distance( pair.first, point1.getPosition() ) < GeometryUtils.distance(
pair.second,
point1.getPosition() ) ) {
p1 = pair.first;
} else {
p1 = pair.second;
}
line1p = LineUtils.getPerpendicularLine( line1[0], p1.getX(), p1.getY() );
line2p = LineUtils.getPerpendicularLine( line2[0], p2.getX(), p2.getY() );
QualifiedName qn = feature1.getFeatureType().getGeometryProperties()[0].getName();
curve1 = stretchCurve( curve1, p1 );
feature1.getDefaultProperty( qn ).setValue( curve1 );
} else {
pair = LineUtils.getSymmetricPoints( intersection.getX(), intersection.getY(), line2[0], d1 );
if ( GeometryUtils.distance( pair.first, point2.getPosition() ) < GeometryUtils.distance(
pair.second,
point2.getPosition() ) ) {
p2 = pair.first;
} else {
p2 = pair.second;
}
line2p = LineUtils.getPerpendicularLine( line2[0], p2.getX(), p2.getY() );
line1p = LineUtils.getPerpendicularLine( line1[0], p1.getX(), p1.getY() );
QualifiedName qn = feature2.getFeatureType().getGeometryProperties()[0].getName();
curve2 = stretchCurve( curve2, p2 );
feature2.getDefaultProperty( qn ).setValue( curve2 );
}
// intersection between perpendicular lines is center of required circle
Position center = LineUtils.getLineIntersection( line1p[0], line1p[1], line2p[0], line2p[1] );
double r = GeometryUtils.distance( center, p1 );
Curve connection = GeometryUtils.calcCircleCoordinates( center, r, noOfSegments, p1, p2,
curve1.getCoordinateSystem() );
if ( connectionAsNewCurve ) {
insertNewCurve( connection );
} else {
uniteCurves( connection );
}
}
private void executeCircleFixedRadiusEndNodes() {
}
private void executeCircleFixedRadiusShortenCurves() {
}
private void executeSpline() {
}
private void executeDirect()
throws Exception {
Pair<Point, Point> points = getNewCurveStartAndEndPoints();
Position[] positions = new Position[] { points.first.getPosition(), points.second.getPosition() };
Curve connection = GeometryFactory.createCurve( positions, points.first.getCoordinateSystem() );
if ( connectionAsNewCurve ) {
insertNewCurve( connection );
} else {
uniteCurves( connection );
}
}
private void executeStretchLines()
throws Exception {
Pair<Position[], Position[]> segments = findSegements( );
Position[] segment1 = segments.first;
Position[] segment2 = segments.second;
// find intersection of segments
double[] line1 = LineUtils.getLineFromPoints( segment1[0], segment1[1] );
double[] line2 = LineUtils.getLineFromPoints( segment2[0], segment2[1] );
Position intersection = LineUtils.getLineIntersection( line1[0], line1[1], line2[0], line2[1] );
Envelope tmp1 = GeometryFactory.createCurve( segment1, curve1.getCoordinateSystem() ).getEnvelope();
Envelope tmp2 = GeometryFactory.createCurve( segment2, curve1.getCoordinateSystem() ).getEnvelope();
if ( tmp1.contains( intersection ) ) {
// intersection is contained within curve segment 1, so just segment 2 must be stretched
if ( connectionAsNewCurve ) {
Position[] positions = new Position[] { segment2[0], intersection };
Curve connection1 = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
insertNewCurve( connection1 );
} else {
// positions = new Position[] { segment2[1], intersection };
// Curve connection = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
// uniteCurves( connection );
LOG.logWarning( "should never happen" );
}
} else if ( tmp2.contains( intersection ) ) {
// intersection is contained within curve segment 2, so just segment 1 must be stretched
if ( connectionAsNewCurve ) {
Position[] positions = new Position[] { segment1[0], intersection };
Curve connection1 = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
insertNewCurve( connection1 );
} else {
// positions = new Position[] { segment1[1], intersection };
// Curve connection = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
// uniteCurves( connection );
LOG.logWarning( "should never happen" );
}
} else {
// intersection is outside of both curve segments
if ( connectionAsNewCurve ) {
Position[] positions = new Position[] { segment1[1], intersection };
Curve connection1 = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
positions = new Position[] { segment2[1], intersection };
Curve connection2 = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
insertNewCurve( connection1 );
insertNewCurve( connection2 );
} else {
Position[] positions = new Position[] { segment1[1], intersection, segment2[1] };
// Curve connection = GeometryFactory.createCurve( positions, curve1.getCoordinateSystem() );
// uniteCurves( connection );
stretchCurves( positions );
}
}
}
private void executeShortenLines() {
}
private void executeStretchAndShortenLines()
throws Exception {
// Pair<Position[], Position[]> segments = findSegements( );
// Position[] segment1 = segments.first;
// Position[] segment2 = segments.second;
// // find intersection of segments
// double[] line1 = LineUtils.getLineFromPoints( segment1[0], segment1[1] );
// double[] line2 = LineUtils.getLineFromPoints( segment2[0], segment2[1] );
// Position intersection = LineUtils.getLineIntersection( line1[0], line1[1], line2[0], line2[1] );
// Envelope tmp1 = GeometryFactory.createCurve( segment1, curve1.getCoordinateSystem() ).getEnvelope();
// Envelope tmp2 = GeometryFactory.createCurve( segment2, curve1.getCoordinateSystem() ).getEnvelope();
// if ( tmp1.contains( intersection ) ) {
// // intersection is contained within curve segment 1, so just segment 2 must be stretched
// LOG.logWarning( "not implemented yet" );
// } else if ( tmp2.contains( intersection ) ) {
// // intersection is contained within curve segment 2, so just segment 1 must be stretched
// LOG.logWarning( "not implemented yet" );
// } else {
// // intersection is outside of both curve segments
// LOG.logWarning( "not implemented yet" );
// }
LOG.logWarning( "stretchAndShortenLines is not implemented yet" );
}
private void executeSplitLines() {
LOG.logWarning( "not implemented yet" );
}
private void executeSplitAndAlternate() {
LOG.logWarning( "not implemented yet" );
}
private Pair<Position[], Position[]> findSegements( )
throws Exception {
Position[] segment1 = new Position[2];
Position[] segment2 = new Position[2];
// get required curve segments (line string)
Position[] positions = curve1.getAsLineString().getAsLineString().getPositions();
if ( point1.equals( curve1.getEndPoint() ) || point2.equals( curve1.getEndPoint() ) ) {
segment1[0] = positions[positions.length - 2];
segment1[1] = positions[positions.length - 1];
} else {
segment1[0] = positions[1];
segment1[1] = positions[0];
}
positions = curve2.getAsLineString().getAsLineString().getPositions();
if ( point1.equals( curve2.getEndPoint() ) || point2.equals( curve2.getEndPoint() ) ) {
segment2[0] = positions[positions.length - 2];
segment2[1] = positions[positions.length - 1];
} else {
segment2[0] = positions[1];
segment2[1] = positions[0];
}
return new Pair<Position[], Position[]>( segment1, segment2 );
}
/**
*
* @return two points to connect to each other
* @throws Exception
*/
private Pair<Point, Point> getNewCurveStartAndEndPoints()
throws Exception {
Point p1 = curve1.getStartPoint();
if ( !p1.equals( point1 ) ) {
p1 = curve1.getEndPoint();
}
Point p2 = curve2.getStartPoint();
if ( !p2.equals( point2 ) ) {
p2 = curve2.getEndPoint();
}
return new Pair<Point, Point>( p1, p2 );
}
/**
* @param connection
* @throws CloneNotSupportedException
*/
private void insertNewCurve( Curve connection )
throws CloneNotSupportedException {
newFeature = featureAdapter.getDefaultFeature( featureAdapter.getSchema().getName() );
// create a feature for connection curve
newFeature = newFeature.cloneDeep();
// create geometry property for connection curve
FeatureProperty fp = null;
if ( geomProperty != null ) {
fp = FeatureFactory.createFeatureProperty( geomProperty, connection );
} else {
QualifiedName qn = newFeature.getFeatureType().getGeometryProperties()[0].getName();
fp = FeatureFactory.createFeatureProperty( qn, connection );
}
// update new feature with connection curve as geometry
newFeature.setProperty( fp, 0 );
featureAdapter.insertFeature( newFeature );
}
/**
* @param connection
* @throws GeometryException
* @throws CloneNotSupportedException
*/
private void uniteCurves( Curve connection )
throws GeometryException, CloneNotSupportedException {
Position[] pos1 = curve1.getAsLineString().getPositions();
Position[] pos2 = curve2.getAsLineString().getPositions();
Position[] conPos = connection.getAsLineString().getPositions();
List<Position> list = new ArrayList<Position>( pos1.length + pos2.length + conPos.length );
if ( pos1[0].equals( conPos[0] ) ) {
invertOrder( pos1 );
} else if ( pos1[pos1.length - 1].equals( conPos[0] ) ) {
LOG.logWarning( "should never happen" );
} else if ( pos1[0].equals( conPos[conPos.length - 1] ) ) {
invertOrder( pos1 );
invertOrder( conPos );
} else if ( pos1[pos1.length - 1].equals( conPos[conPos.length - 1] ) ) {
invertOrder( conPos );
}
if ( pos2[pos2.length - 1].equals( conPos[conPos.length - 1] ) ) {
invertOrder( pos2 );
}
for ( Position position : pos1 ) {
list.add( position );
}
for ( Position position : conPos ) {
list.add( position );
}
for ( Position position : pos2 ) {
list.add( position );
}
// must subtract 2 from size of new position array because first and last point
// of connection curve must be ignored
Position[] newPositions = list.toArray( new Position[list.size()] );
Curve newCurve = GeometryFactory.createCurve( newPositions, curve1.getCoordinateSystem() );
// delete old curves first ...
featureAdapter.deleteFeature( feature1 );
featureAdapter.deleteFeature( feature2 );
// .. and replace by new one
insertNewCurve( newCurve );
}
/**
*
* @param connection
* @throws GeometryException
* @throws CloneNotSupportedException
*/
private void stretchCurves( Position[] positions )
throws GeometryException, CloneNotSupportedException {
Position[] pos1 = curve1.getAsLineString().getPositions();
Position[] pos2 = curve2.getAsLineString().getPositions();
List<Position> list = new ArrayList<Position>( pos1.length + pos2.length );
if ( pos1[0].equals( positions[0] ) || pos1[0].equals( positions[positions.length - 1] ) ) {
invertOrder( pos1 );
}
if ( pos2[0].equals( positions[0] ) || pos2[0].equals( positions[positions.length - 1] ) ) {
invertOrder( pos2 );
}
// delete old curves first
featureAdapter.deleteFeature( feature1 );
featureAdapter.deleteFeature( feature2 );
// first curve
for ( Position position : pos1 ) {
list.add( position );
}
list.add( positions[1] );
Position[] newPositions = list.toArray( new Position[list.size()] );
Curve newCurve = GeometryFactory.createCurve( newPositions, curve1.getCoordinateSystem() );
// .. and replace first by new one
insertNewCurve( newCurve );
list.clear();
// second curve
for ( Position position : pos2 ) {
list.add( position );
}
list.add( positions[1] );
newPositions = list.toArray( new Position[list.size()] );
newCurve = GeometryFactory.createCurve( newPositions, curve1.getCoordinateSystem() );
// .. and replace second by new one
insertNewCurve( newCurve );
}
private void invertOrder( Position[] positions ) {
for ( int i = 0; i < positions.length / 2; i++ ) {
Position p = positions[i];
positions[i] = positions[positions.length - i - 1];
positions[positions.length - i - 1] = p;
}
}
/*
* (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 newFeature;
}
@Override
public boolean isUndoSupported() {
return false;
}
@Override
public void undo()
throws Exception {
if ( performed ) {
LOG.logWarning( "undo not implemented yet" );
}
}
/**
* @param curve
* @param intersection
* @throws GeometryException
*/
private Curve stretchCurve( Curve curve, Position intersection )
throws GeometryException {
Position[] pos = curve.getAsLineString().getPositions();
ArrayList<Position> list = new ArrayList<Position>( pos.length + 1 );
if ( GeometryUtils.distance( pos[0], intersection ) < GeometryUtils.distance( pos[pos.length - 1], intersection ) ) {
list.add( intersection );
for ( Position position2 : pos ) {
list.add( position2 );
}
} else {
for ( Position position2 : pos ) {
list.add( position2 );
}
list.add( intersection );
}
return GeometryFactory.createCurve( list.toArray( new Position[list.size()] ), curve.getCoordinateSystem() );
}
}