/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, 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:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@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.graphics.displayelements;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Walks along the given <tt>GM_Curve</tt> and generates positions on the line
* string in regular intervals (i.e. with the same distance).
* <p>
*
* @author <a href="mailto:mschneider@lat-lon.de>Markus Schneider </a>
* @version $Revision: 1.8 $ $Date: 2006/07/12 14:46:16 $
*/
public class CurveWalker {
private double minX;
private double minY;
private double maxX;
private double maxY;
/**
*
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
* @version 2.11.2002
*/
public CurveWalker(Rectangle bounds) {
minX = bounds.getMinX();
minY = bounds.getMinY();
maxX = bounds.getMaxX();
maxY = bounds.getMaxY();
}
/**
* Determines positions on the given <tt>GM_Curve</tt> where a caption
* could be drawn. For each of this positons, three candidates are produced;
* one on the line, one above of it and one below.
* <p>
*
* @param pos
* @return ArrayList containing Arrays of Label-objects
*/
public ArrayList createPositions(int[][] pos, double width) {
// walk along the linestring and "collect" possible placement positions
int w = (int) width;
double lastX = pos[0][0];
double lastY = pos[1][0];
double count = pos[2][0];
double boxStartX = lastX;
double boxStartY = lastY;
ArrayList labels = new ArrayList(300);
List eCandidates = new ArrayList(300);
int i = 0;
while (i < count) {
double x = pos[0][i];
double y = pos[1][i];
// segment found where endpoint of box should be located?
if (getDistance(boxStartX, boxStartY, x, y) >= w) {
double[] p0 = new double[] { boxStartX, boxStartY };
double[] p1 = new double[] { lastX, lastY };
double[] p2 = new double[] { x, y };
double[] p = findPointWithDistance(p0, p1, p2, w);
x = p[0];
y = p[1];
lastX = x;
lastY = y;
double boxEndX = x;
double boxEndY = y;
double rotation = getRotation(boxStartX, boxStartY, boxEndX, boxEndY);
/*
double[] deviation = calcDeviation(new double[] { boxStartX, boxStartY },
new double[] { boxEndX, boxEndY },
eCandidates);
*/
// only add position if it is visible
if (boxStartX >= minX && boxStartX <= maxX && boxStartY >= minY
&& boxStartY <= maxY) {
labels.add(new double[] { boxStartX, boxStartY, rotation });
}
boxStartX = lastX;
boxStartY = lastY;
eCandidates.clear();
} else {
eCandidates.add(new double[] { x, y });
lastX = x;
lastY = y;
i++;
}
}
return labels;
}
/**
* Calculates the maximum deviation that points on a linestring have to the
* ideal line between the starting point and the end point.
* <p>
* The ideal line is thought to be running from left to right, the left
* deviation value generally is above the line, the right value is below.
* <p>
*
* @param start starting point of the linestring
* @param end end point of the linestring
* @param points points in between
* @return
*/
public double[] calcDeviation(double[] start, double[] end, List points) {
// extreme deviation to the left
double d1 = 0.0;
// extreme deviation to the right
double d2 = 0.0;
Iterator it = points.iterator();
// eventually swap start and end point
if (start[0] > end[0]) {
double[] tmp = start;
start = end;
end = tmp;
}
if (start[0] != end[0]) {
// label orientation is not completly vertical
if (start[1] != end[1]) {
// label orientation is not completly horizontal
while (it.hasNext()) {
double[] point = (double[]) it.next();
double u = (end[1] - start[1])
/ ( end[0] - start[0]);
double x = (u * u * start[0] - u
* ( start[1] - point[1]) + point[0])
/ (1.0 + u * u);
double y = (x - start[0]) * u + start[1];
double d = getDistance(point, new double[] { x,y });
if (y >= point[1]) {
// candidate for left extreme value
if (d > d1) {
d1 = d;
}
} else if (d > d2) {
// candidate for right extreme value
d2 = d;
}
}
} else {
// label orientation is completly horizontal
while (it.hasNext()) {
double[] point = (double[]) it.next();
double d = point[1] - start[1];
if (d < 0) {
// candidate for left extreme value
if (-d > d1) {
d1 = -d;
}
} else if (d > d2) {
// candidate for left extreme value
d2 = d;
}
}
}
} else {
// label orientation is completly vertical
while (it.hasNext()) {
double[] point = (double[]) it.next();
double d = point[0] - start[0];
if (d < 0) {
// candidate for left extreme value
if (-d > d1) {
d1 = -d;
}
} else if (d > d2) {
// candidate for right extreme value
d2 = d;
}
}
}
return new double[] { d1, d2 };
}
/**
* Finds a point on the line between p1 and p2 that has a certain distance
* from point p0 (provided that there is such a point).
* <p>
*
* @param p0 point that is used as reference point for the distance
* @param p1 starting point of the line
* @param p2 end point of the line
* @param d distance
* @return
*/
public static double[] findPointWithDistance(double[] p0, double[] p1, double[] p2, double d) {
double x, y;
double x0 = p0[0];
double y0 = p0[1];
double x1 = p1[0];
double y1 = p1[1];
double x2 = p2[0];
double y2 = p2[1];
if (x1 != x2) {
// line segment does not run vertical
double u = (y2 - y1) / (x2 - x1);
double p = -2 * (x0 + u * u * x1 - u * (y1 - y0)) / (u * u + 1);
double q = ((y1 - y0) * (y1 - y0) + u * u * x1 * x1 + x0 * x0 - 2 * u * x1
* (y1 - y0) - d * d)
/ (u * u + 1);
double minX = p1[0];
double maxX = p2[0];
double minY = p1[1];
double maxY = p2[1];
if (minX > maxX) {
minX = p2[0];
maxX = p1[0];
}
if (minY > maxY) {
minY = p2[1];
maxY = p1[1];
}
if (x1 < x2) {
// to the right
x = -p / 2 + Math.sqrt((p / 2) * (p / 2) - q);
} else {
// to the left
x = -p / 2 - Math.sqrt((p / 2) * (p / 2) - q);
}
// if ((int) (x + 0.5) <= minX || (int) (x + 0.5) >= maxX) {
// x = -p / 2 + Math.sqrt ((p / 2) * (p / 2) - q);
// }
y = (x - x1) * u + y1;
} else {
// vertical line segment
x = x1;
double minY = p1[1];
double maxY = p2[1];
if (minY > maxY) {
minY = p2[1];
maxY = p1[1];
}
double p = -2 * y0;
double q = y0 * y0 + (x1 - x0) * (x1 - x0) - d * d;
if (y1 > y2) {
// down
y = -p / 2 - Math.sqrt((p / 2) * (p / 2) - q);
} else {
// up
y = -p / 2 + Math.sqrt((p / 2) * (p / 2) - q);
}
// y = -p / 2 - Math.sqrt ((p / 2) * (p / 2) - q);
// if ((int) (y + 0.5) <= minY || (int) (y + 0.5) >= maxY) {
// y = -p / 2 + Math.sqrt ((p / 2) * (p / 2) - q);
// }
}
return new double[] { x, y };
}
// public double getRotation (double x1, double y1, double x2, double y2) {
// double dx = x2-x1;
// double dy = y2-y1;
// double alpha = -Math.atan (dy / dx);
// if (dx >= 0) {
// if (dy > 0) {
// alpha = 2 * Math.PI + alpha;
// }
// } else {
// if (dy > 0) {
// alpha = Math.PI + alpha;
// } else {
// alpha = Math.PI + alpha;
// }
// }
// return Math.atan (alpha);
// }
/**
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
public double getRotation(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = -(y2 - y1);
double rotation = 0.0;
if (dx <= 0) {
//BUG FIX: case: dx = 0. division not possible.
if( dx == 0 ) {
dx = 1;
}
if (dy <= 0) {
// left down
rotation = -Math.atan(dy / dx);
} else {
// left up
rotation = -Math.atan(dy / dx);
}
} else {
if (dy <= 0) {
// right down
rotation = -Math.PI - Math.atan(dy / dx);
} else {
// right up
rotation = -Math.PI - Math.atan(dy / dx);
}
}
return Math.toDegrees(rotation);
}
/**
* @param p1
* @param p2
* @return
*/
public double getDistance(double[] p1, double[] p2) {
double dx = p1[0] - p2[0];
double dy = p1[1] - p2[1];
return Math.sqrt(dx * dx + dy * dy);
}
/**
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
public double getDistance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
}/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: CurveWalker.java,v $
Revision 1.8 2006/07/12 14:46:16 poth
comment footer added
********************************************************************** */