//This file is part of SECONDO.
//Copyright (C) 2004, University in Hagen, Department of Computer Science,
//Database Systems for New Applications.
//SECONDO is free software; you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation; either version 2 of the License, or
//(at your option) any later version.
//SECONDO 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 General Public License for more details.
//You should have received a copy of the GNU General Public License
//along with SECONDO; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package viewer.hoese.algebras.network;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.Vector;
import sj.lang.ListExpr;
import tools.Reporter;
import viewer.hoese.LEUtils;
import viewer.hoese.ProjectionManager;
import viewer.hoese.algebras.Dsplline;
/**
* A route in a network
*
* @author Martin Scheppokat
*
*/
public class Route
{
/**
* ID for this route
*/
private int m_iId;
/**
* Length of the route
*/
private double m_dLength;
/**
* All segments of this route
*/
private Segment[] m_xSegments;
/**
* First segment of route
*/
private Segment m_xFirstSegment;
/**
* Last segment of route
*/
private Segment m_xLastSegment;
/**
* Flag indication wether this route has two distinct sides
*/
private boolean m_bIsDualRoute;
/**
* Flag indication that this route starts at the smaller end
*/
private boolean m_bStartSmaller;
/**
* Path to be returned by getRenderObject
*
*/
private GeneralPath m_xPath;
/**
* Constructor
*
* @param in_xList Secondos representation of a Route as a list.
* @throws Exception If the list contains errors
*/
public Route(ListExpr in_xList)
throws Exception
{
Vector xSegments = new Vector();
// Read values for the list
m_iId = in_xList.first().intValue();
m_dLength = in_xList.second().realValue();
ListExpr xLineList = in_xList.third();
m_bIsDualRoute = in_xList.fourth().boolValue();
m_bStartSmaller = in_xList.fifth().boolValue();
System.out.println("Route " + m_iId);
// Read segments
if(xLineList.listLength()==2 && xLineList.second().atomType()==ListExpr.BOOL_ATOM){ // new style
// startSmaller = xLineList.second().boolxLineList();
// ignore startSmaller of the simple line because we have stored it also in the network
xLineList = xLineList.first();
}
while (!xLineList.isEmpty()) {
ListExpr xSegmentList = xLineList.first();
if (xSegmentList.listLength() != 4) {
throw new Exception("Error: No correct line expression: 4 elements needed");
}
double x1 = LEUtils.readNumeric(xSegmentList.first()).doubleValue();
double y1 = LEUtils.readNumeric(xSegmentList.second()).doubleValue();
double x2 = LEUtils.readNumeric(xSegmentList.third()).doubleValue();
double y2 = LEUtils.readNumeric(xSegmentList.fourth()).doubleValue();
Point2D.Double xPoint1 = new Point2D.Double(x1,y1);
//boolean bSuccess = ProjectionManager.project(x1 ,y1 ,xPoint1);
boolean bSuccess = true;
Point2D.Double xPoint2 = new Point2D.Double(x2,y2);
//bSuccess &= ProjectionManager.project(x2 ,y2 ,xPoint2);
if(!bSuccess){
throw new Exception("error in projection of segment (" +
x1 + "," + y1 +
")->(" +
x2 + "," + y2 + ")");
}
// Segment
xSegments.add(new Segment(xPoint1,
xPoint2));
// Look at next element in the list
xLineList = xLineList.rest();
}
m_xSegments = (Segment[])xSegments.toArray(new Segment[0]);
System.out.println(m_iId);
maintainSegmentsOrdering();
m_xPath = buildPath(m_xSegments);
}
/**
* Constructor to copy only part of the route. Needed by
* getPartOfRoute
*
* @param in_xOtherRoute
* @param in_dStart
* @param in_dEnd
*/
public Route(Route in_xOtherRoute,
double in_dStart,
double in_dEnd)
{
if(in_dStart >= in_dEnd)
{
double dTemp = in_dStart;
in_dStart = in_dEnd;
in_dEnd = dTemp;
}
Vector xSegments = new Vector();
m_iId = in_xOtherRoute.getId();
m_dLength = in_xOtherRoute.getLength();
m_bIsDualRoute = in_xOtherRoute.isDualRoute();
m_bStartSmaller = in_xOtherRoute.startsSmaller();
double dCurrentPosition = 0;
Segment xCurrentSegment = in_xOtherRoute.m_xFirstSegment;
if (!m_bStartSmaller)
xCurrentSegment = in_xOtherRoute.m_xLastSegment;
while(xCurrentSegment != null)
{
// Get absolut start and end-position from current segment
double dSegmentStart = dCurrentPosition;
double dSegmentEnd = dSegmentStart + xCurrentSegment.getLength();
// Segment lies before the choosen part
if(dSegmentEnd < in_dStart)
{
// Calculate new position
dCurrentPosition += xCurrentSegment.getLength();
// Next segment
if (m_bStartSmaller)
xCurrentSegment = xCurrentSegment.getNextSegment();
else
xCurrentSegment = xCurrentSegment.getPreviousSegment();
continue;
}
// Segment lies behind the choosen part
if(dSegmentStart > in_dEnd)
{
// No more segments
break;
}
// Calculate relative positions for the segment
double dSegmentRelativeStart = Math.max(0,
in_dStart - dCurrentPosition);
double dSegmentRelativeEnd = Math.min(xCurrentSegment.getLength(),
in_dEnd - dCurrentPosition);
if (m_bStartSmaller)
xSegments.add(xCurrentSegment.subSegment(dSegmentRelativeStart,
dSegmentRelativeEnd));
else
xSegments.add(xCurrentSegment.subSegment(xCurrentSegment.getLength()-
dSegmentRelativeStart,
xCurrentSegment.getLength()-
dSegmentRelativeEnd));
// Calculate new position
dCurrentPosition += xCurrentSegment.getLength();
if (m_bStartSmaller)
xCurrentSegment = xCurrentSegment.getNextSegment();
else
xCurrentSegment = xCurrentSegment.getPreviousSegment();
}
m_xSegments = (Segment[])xSegments.toArray(new Segment[0]);
if(m_xSegments.length > 0)
{
maintainSegmentsOrdering();
}
m_xPath = buildPath(m_xSegments);
}
/**
* Get a segment of the route
* @param in_iIndex between 0 and getSegmentCount()
*
* @return A segment
*/
private Segment getSegmentAt(int in_iIndex)
{
return m_xSegments[in_iIndex];
}
/**
* Returns the number of segments this route has.
*/
private int getSegmentCount()
{
return m_xSegments.length;
}
/**
* Returns wether this route starts at its smaller end.
*
* @return
*/
private boolean startsSmaller()
{
return m_bStartSmaller;
}
/**
* Returns wether this route has two distinct sides.
*
* @return
*/
private boolean isDualRoute()
{
return m_bIsDualRoute;
}
/**
* Returns the absolute coordinates for a point somewhere on the route.
*
* @param in_dDistance From the start of the route
*
* @return A Point
*/
public Point2D.Double getPointOnRoute(double in_dDistance)
{
// Look for segment
Segment xSegment = m_xFirstSegment;
if (!m_bStartSmaller) xSegment = m_xLastSegment;
double dDistanceOnRoute = 0;
while(xSegment != null)
{
dDistanceOnRoute += xSegment.getLength();
if(dDistanceOnRoute >= (in_dDistance-0.00000001))
{
break;
}
if (m_bStartSmaller)
xSegment = xSegment.getNextSegment();
else
xSegment = xSegment.getPreviousSegment();
}
// Calculate offset for this segment
double dDistanceOnSegment = in_dDistance -
dDistanceOnRoute +
xSegment.getLength();
Point2D.Double result;
if (m_bStartSmaller)
result = xSegment.getPointOnSegment(dDistanceOnSegment);
else
result = xSegment.getPointOnSegment(xSegment.getLength() -
dDistanceOnSegment);
return result;
}
/**
* Return the Id of the route.
*
* @return Id
*/
public int getId()
{
return m_iId;
}
/**
* Returns the length of the route
*
* @return Length
*/
public double getLength()
{
return m_dLength;
}
/**
* Returns a representation of the route to be displayed in the hoese-viewer.
*
* @return A Path following all segments
*/
public Shape getRenderObject()
{
return m_xPath;
}
/**
* Returns a part of the route. Needed by RouteInterval
* @param in_dStart Start of the route
* @param in_dEnd End of the route.
* @return A possibly shorter route.
*/
public Route getPartOfRoute(double in_dStart,
double in_dEnd)
{
return new Route(this,
in_dStart,
in_dEnd);
}
/**
* Maintain a linear ordering of the segments
*
*/
private void maintainSegmentsOrdering()
{
// Start at any segment
Segment xStartSegment = m_xSegments[0];
// Follow from the end of this segment until
// one end of the route is reached. We don't
// know yet in which direction we are traveling.
Segment xCurrentSegment = xStartSegment;
xCurrentSegment.setStarts(Segment.STARTS_SMALLER);
m_xLastSegment = xCurrentSegment;
while(xCurrentSegment != null)
{
for (int i = 0; i < m_xSegments.length; i++)
{
Segment xOtherSegment = m_xSegments[i];
// Don't compare a segment to itself and
// don't build circles by using the start again
if(xOtherSegment == xCurrentSegment ||
xOtherSegment.getPreviousSegment() != null ||
xOtherSegment.getNextSegment() != null)
{
continue;
}
if(xCurrentSegment.getLastPoint().x == xOtherSegment.getPoint1().x &&
xCurrentSegment.getLastPoint().y == xOtherSegment.getPoint1().y)
{
xCurrentSegment.setNextSegment(xOtherSegment);
xOtherSegment.setPreviousSegment(xCurrentSegment);
xOtherSegment.setStarts(Segment.STARTS_SMALLER);
m_xLastSegment = xOtherSegment;
break;
}
if(xCurrentSegment.getLastPoint().x == xOtherSegment.getPoint2().x &&
xCurrentSegment.getLastPoint().y == xOtherSegment.getPoint2().y)
{
xCurrentSegment.setNextSegment(xOtherSegment);
xOtherSegment.setPreviousSegment(xCurrentSegment);
xOtherSegment.setStarts(Segment.STARTS_BIGGER);
m_xLastSegment = xOtherSegment;
break;
}
}
// Update last segment and current segment
xCurrentSegment = xCurrentSegment.getNextSegment();
}
// Follow from the other end of the start segment
// until we find the other end of the route
xCurrentSegment = xStartSegment;
m_xFirstSegment = xCurrentSegment;
while(xCurrentSegment != null)
{
for (int i = 0; i < m_xSegments.length; i++)
{
Segment xOtherSegment = m_xSegments[i];
// Don't compare a segment to itself and
// don't build circles by using the start again
if(xOtherSegment == xCurrentSegment ||
xOtherSegment.getPreviousSegment() != null ||
xOtherSegment.getNextSegment() != null)
{
continue;
}
if(xCurrentSegment.getFirstPoint().x == xOtherSegment.getPoint2().x &&
xCurrentSegment.getFirstPoint().y == xOtherSegment.getPoint2().y)
{
xCurrentSegment.setPreviousSegment(xOtherSegment);
xOtherSegment.setNextSegment(xCurrentSegment);
xOtherSegment.setStarts(Segment.STARTS_SMALLER);
m_xFirstSegment = xOtherSegment;
break;
}
if(xCurrentSegment.getFirstPoint().x == xOtherSegment.getPoint1().x &&
xCurrentSegment.getFirstPoint().y == xOtherSegment.getPoint1().y)
{
xCurrentSegment.setPreviousSegment(xOtherSegment);
xOtherSegment.setNextSegment(xCurrentSegment);
xOtherSegment.setStarts(Segment.STARTS_BIGGER);
m_xFirstSegment = xOtherSegment;
break;
}
}
// Update last segment and current segment
xCurrentSegment = xCurrentSegment.getPreviousSegment();
}
// Maybe we mixed-up start and end of the route. This can be
// corrected in O(n) time
if(m_xFirstSegment.getFirstPoint().x > m_xLastSegment.getLastPoint().x ||
(
m_xFirstSegment.getFirstPoint().x == m_xLastSegment.getLastPoint().x &&
m_xFirstSegment.getFirstPoint().y > m_xLastSegment.getLastPoint().y
)
)
{
int j = 0;
for (int i = 0; i < m_xSegments.length; i++)
{
// Exchange next and previous segment
Segment xNextSegment = m_xSegments[i].getNextSegment();
Segment xPreviousSegment = m_xSegments[i].getPreviousSegment();
m_xSegments[i].setNextSegment(xPreviousSegment);
m_xSegments[i].setPreviousSegment(xNextSegment);
if(m_xSegments[i].getStarts() == Segment.STARTS_SMALLER)
{
m_xSegments[i].setStarts(Segment.STARTS_BIGGER);
}
else
{
m_xSegments[i].setStarts(Segment.STARTS_SMALLER);
}
}
Segment xFirstSegment = m_xFirstSegment;
m_xFirstSegment = m_xLastSegment;
m_xLastSegment = xFirstSegment;
}
}
/**
* Method building a path for the method getRenderObject.
*
* @param in_xSegments
* @return
*/
private static GeneralPath buildPath(Segment[] in_xSegments)
{
GeneralPath xPath = new GeneralPath();
Point2D.Double p1 = new Point2D.Double(0,0);
Point2D.Double p2 = new Point2D.Double(0,0);
for (int i = 0; i < in_xSegments.length; i++)
{
Segment xCurrentSegment = in_xSegments[i];
// Draw segment
if(ProjectionManager.project(xCurrentSegment.getPoint1().x,
xCurrentSegment.getPoint1().y, p1)){
if(ProjectionManager.project(xCurrentSegment.getPoint2().x,
xCurrentSegment.getPoint2().y, p2)){
xPath.moveTo((float) p1.x, (float)p1.y);
xPath.lineTo((float) p2.x, (float)p2.y);
} else {
System.err.println("problem in projection of p2");
}
} else {
System.err.print("Problem in projection of p1");
}
}
return xPath;
}
}