// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/OMDistance.java,v $
// $RCSfile: OMDistance.java,v $
// $Revision: 1.12 $
// $Date: 2009/01/21 01:24:41 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.text.DecimalFormat;
import java.util.Iterator;
import com.bbn.openmap.geo.Geo;
import com.bbn.openmap.proj.Length;
import com.bbn.openmap.proj.LineCoordinateGenerator;
import com.bbn.openmap.proj.ProjMath;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.DeepCopyUtil;
/**
* OMGraphic object that represents a polyline, labeled with distances.
*/
public class OMDistance extends OMPoly {
protected OMGraphicList labels = new OMGraphicList();
protected OMGraphicList points = new OMGraphicList();
protected Length distUnits = Length.NM;
public DecimalFormat df = new DecimalFormat("0.#");
/**
* Paint used for labels
*/
protected Paint labelPaint;
/**
* Font used for labels
*/
protected Font labelFont;
/**
* Construct a default OMDistance.
*/
public OMDistance() {
super();
setRenderType(RENDERTYPE_LATLON);
}
/**
* Create an OMDistance from a list of float lat/lon pairs.
* <p>
* NOTES:
* <ul>
* <li>llPoints array is converted into radians IN PLACE for more efficient
* handling internally if it's not already in radians! For even better
* performance, you should send us an array already in radians format!
* <li>If you want the poly to be connected (as a polygon), you need to
* ensure that the first and last coordinate pairs are the same.
* </ul>
*
* @param llPoints array of lat/lon points, arranged lat, lon, lat, lon, etc.
* @param units radians or decimal degrees. Use OMGraphic.RADIANS or
* OMGraphic.DECIMAL_DEGREES
* @param lType line type, from a list defined in OMGraphic.
*/
public OMDistance(double[] llPoints, int units, int lType, Length distanceUnits) {
this(llPoints, units, lType, -1, distanceUnits);
}
/**
* Create an OMDistance from a list of float lat/lon pairs.
* <p>
* NOTES:
* <ul>
* <li>llPoints array is converted into radians IN PLACE for more efficient
* handling internally if it's not already in radians! For even better
* performance, you should send us an array already in radians format!
* <li>If you want the poly to be connected (as a polygon), you need to
* ensure that the first and last coordinate pairs are the same.
* </ul>
*
* @param llPoints array of lat/lon points, arranged lat, lon, lat, lon, etc.
* @param units radians or decimal degrees. Use OMGraphic.RADIANS or
* OMGraphic.DECIMAL_DEGREES
* @param lType line type, from a list defined in OMGraphic.
* @param nsegs number of segment points (only for LINETYPE_GREATCIRCLE or
* LINETYPE_RHUMB line types, and if < 1, this value is generated
* internally)
*/
public OMDistance(double[] llPoints, int units, int lType, int nsegs, Length distanceUnits) {
super(llPoints, units, lType, nsegs);
setDistUnits(distanceUnits);
}
/**
* Set the Length object used to represent distances.
*/
public void setDistUnits(Length distanceUnits) {
distUnits = distanceUnits;
}
/**
* Get the Length object used to represent distances.
*/
public Length getDistUnits() {
return distUnits;
}
public void setLocation(double[] llPoints, int units) {
this.units = OMGraphic.RADIANS;
if (units == OMGraphic.DECIMAL_DEGREES) {
ProjMath.arrayDegToRad(llPoints);
}
rawllpts = llPoints;
setNeedToRegenerate(true);
setRenderType(RENDERTYPE_LATLON);
}
public void createLabels() {
labels.clear();
points.clear();
if (rawllpts == null) {
return;
}
if (rawllpts.length < 4) {
return;
}
Geo lastGeo = new Geo(rawllpts[0], rawllpts[1], units == DECIMAL_DEGREES);
double latpnt = rawllpts[0];
double lonpnt = rawllpts[1];
if (units == RADIANS) {
latpnt = ProjMath.radToDeg(latpnt);
lonpnt = ProjMath.radToDeg(lonpnt);
}
points.add(new OMPoint(latpnt, lonpnt, 1));
Geo curGeo = null;
double cumulativeDist = 0.0;
for (int p = 2; p < rawllpts.length; p += 2) {
if (curGeo == null) {
curGeo = new Geo(rawllpts[p], rawllpts[p + 1], units == DECIMAL_DEGREES);
} else {
if (units == DECIMAL_DEGREES) {
curGeo.initialize(rawllpts[p], rawllpts[p + 1]);
} else {
curGeo.initializeRadians(rawllpts[p], rawllpts[p + 1]);
}
}
double dist = getDist(lastGeo, curGeo);
cumulativeDist += dist;
labels.add(createLabel(lastGeo, curGeo, dist, cumulativeDist, distUnits));
latpnt = rawllpts[p];
lonpnt = rawllpts[p + 1];
if (units == RADIANS) {
latpnt = ProjMath.radToDeg(latpnt);
lonpnt = ProjMath.radToDeg(lonpnt);
}
points.add(new OMPoint(latpnt, lonpnt, 1));
lastGeo.initialize(curGeo);
}
}
/**
* Get an OMText label for a segments between the given lat/lon points whose
* given distance and cumulative distance is specified.
*/
public OMText createLabel(Geo g1, Geo g2, double dist, double cumulativeDist, Length distanceUnits) {
Geo mid;
switch (getLineType()) {
case LINETYPE_STRAIGHT:
double lat = (g1.getLatitude() + g2.getLatitude()) / 2.0;
double lon = (g1.getLongitude() + g2.getLongitude()) / 2.0;
mid = new Geo(lat, lon);
break;
case LINETYPE_RHUMB:
System.err.println("Rhumb distance calculation not implemented.");
case LINETYPE_GREATCIRCLE:
case LINETYPE_UNKNOWN:
default:
mid = g1.midPoint(g2);
}
// String text = ((int)dist) + " (" + ((int)cumulativeDist) +
// ")";
String text = (df.format(distanceUnits.fromRadians(dist))) + " ("
+ (df.format(distanceUnits.fromRadians(cumulativeDist))) + ") " + distanceUnits.getAbbr();
OMText omtext = new OMText(mid.getLatitude(), mid.getLongitude(), text, OMText.JUSTIFY_LEFT);
// omtext.setLinePaint(new Color(200, 200, 255));
return omtext;
}
/**
* Return the distance between that lat/lons defined in radians. The
* returned value is in radians and LINETYPE is taken into consideration.
* LINETYPE_STRAIGHT returns same as LINETYPE_GREATCIRCLE, the ground
* distance between all nodes and not the degree distance of the lines.
*/
public double getDist(Geo g1, Geo g2) {
switch (getLineType()) {
case LINETYPE_RHUMB:
double[] coords = new double[] { g1.getLatitudeRadians(), g1.getLongitudeRadians(), g2.getLatitudeRadians(),
g2.getLongitudeRadians() };
return ProjMath.distance(LineCoordinateGenerator.fromRadians(coords).rhumbLineDoubles());
case LINETYPE_STRAIGHT:
case LINETYPE_GREATCIRCLE:
case LINETYPE_UNKNOWN:
default:
return g1.distance(g2);
}
}
/**
* Prepare the poly for rendering.
*
* @param proj Projection
* @return true if generate was successful
*/
public boolean generate(Projection proj) {
boolean ret = super.generate(proj);
createLabels();
labels.generate(proj);
points.generate(proj);
return ret;
}
/**
* Flag used by the EditableOMDistance to do quick movement paints in a
* cleaner way.
*/
protected boolean paintOnlyPoly = false;
/**
* Paint the poly. This works if generate() has been successful.
*
* @param g java.awt.Graphics to paint the poly onto.
*/
public void render(Graphics g) {
super.render(g);
if (!paintOnlyPoly) {
renderPoints(g);
renderLabels(g);
}
}
/**
* render points
*/
protected void renderPoints(Graphics g) {
Paint pointPaint = getLabelPaint();
for (Iterator<OMGraphic> it = points.iterator(); it.hasNext();) {
OMGraphic point = (OMPoint) it.next();
point.setLinePaint(pointPaint);
point.setFillPaint(pointPaint);
point.render(g);
}
}
/**
* render labels
*/
protected void renderLabels(Graphics g) {
Font f = getFont();
Paint labelPaint = getLabelPaint();
Paint mattingPaint = getMattingPaint();
boolean isMatted = isMatted();
for (Iterator<OMGraphic> it = labels.iterator(); it.hasNext();) {
OMText text = (OMText) it.next();
text.setFont(f);
text.setLinePaint(labelPaint);
if (isMatted) {
text.setFillPaint(mattingPaint);
}
text.render(g);
}
}
/**
* Set paint used for labels
*
* @param lPaint paint used for labels
*/
public void setLabelPaint(Paint lPaint) {
labelPaint = lPaint;
}
/**
* @return normal paint used for labels
*/
public Paint getLabelPaint() {
if (labelPaint == null) {
return getLinePaint();
}
return labelPaint;
}
/**
* @param font font used for labels
*/
public void setFont(Font font) {
if (font == null) {
labelFont = OMText.DEFAULT_FONT;
} else {
labelFont = font;
}
}
/**
* @return font used for labels
*/
public Font getFont() {
if (labelFont == null) {
labelFont = OMText.DEFAULT_FONT;
}
return labelFont;
}
private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
stream.defaultWriteObject();
stream.writeObject(distUnits.getAbbr());
}
private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
stream.defaultReadObject();
distUnits = Length.get((String) stream.readObject());
}
public void restore(OMGeometry source) {
super.restore(source);
if (source instanceof OMDistance) {
OMDistance dist = (OMDistance) source;
this.labels = DeepCopyUtil.deepCopy(dist.labels);
this.points = DeepCopyUtil.deepCopy(dist.points);
this.distUnits = dist.distUnits;
this.df = new DecimalFormat(dist.df.toLocalizedPattern());
this.labelPaint = dist.labelPaint;
if (dist.labelFont != null) {
this.labelFont = dist.labelFont.deriveFont(AffineTransform.TYPE_IDENTITY);
}
}
}
}