// **********************************************************************
//
// <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/proj/LLXY.java,v $
// $RCSfile: LLXY.java,v $
// $Revision: 1.10 $
// $Date: 2006/04/07 15:21:10 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.proj;
import java.awt.geom.Point2D;
import com.bbn.openmap.proj.coords.LatLonPoint;
import com.bbn.openmap.util.Debug;
/**
* Implements the LLXY projection, which is basically something where the
* lat/lon and pixel ratios are the same.
*/
public class LLXY
extends Cylindrical
implements EqualArc {
private static final long serialVersionUID = 1L;
/**
* The LLXY name.
*/
public final static transient String LLXYName = "LLXY";
public final static transient double epsilon = 0.0001;
// world<->screen coordinate offsets
protected double hy, wx;
protected double cLon;
protected double cLonRad;
protected double cLat;
/** Pixel per degree */
protected double ppd;
/**
* Construct a LLXY projection.
*
* @param center LatLonPoint center of projection
* @param scale float scale of projection
* @param width width of screen
* @param height height of screen
*/
public LLXY(LatLonPoint center, float scale, int width, int height) {
super(center, scale, width, height);
}
// protected void finalize() {
// Debug.message("gc", "LLXY finalized");
// }
/**
* Return stringified description of this projection.
*
* @return String
* @see Projection#getProjectionID
*/
public String toString() {
return "LLXY[" + super.toString() + "]";
}
/**
* Called when some fundamental parameters change.
*
* Each projection will decide how to respond to this change. For instance,
* they may need to recalculate "constant" parameters used in the forward()
* and inverse() calls.
*/
protected void computeParameters() {
Debug.message("proj", "LLXY.computeParameters()");
super.computeParameters();
// compute the offsets
hy = height / 2;
wx = width / 2;
// Degrees longitude of the center of the projection.
cLon = ProjMath.radToDeg(centerX);
cLat = ProjMath.radToDeg(centerY);
ppd = world.x / 360f;
double latLimit = 90 - (hy / ppd);
// Add check for zoom allowing more than 90 degrees viewable
if (latLimit < 0.0f)
latLimit = 0.0f;
if (cLat > latLimit) {
cLat = latLimit;
centerY = ProjMath.degToRad(cLat);
} else if (cLat < -latLimit) {
cLat = -latLimit;
centerY = ProjMath.degToRad(cLat);
}
cLonRad = Math.toRadians(cLon);
if (Debug.debugging("llxy")) {
Debug.output("LLXY.computeParameters: with center lat:" + cLat + ", lon:" + cLon + " | width:" + width + ", height:"
+ height + " | scale:" + scale);
}
}
/**
* Sets radian latitude to something sane. This is an abstract function since
* some projections don't deal well with extreme latitudes.
*
* @param lat float latitude in radians
* @return float latitude (-PI/2 <= y <= PI/2)
*/
public double normalizeLatitude(double lat) {
if (lat > NORTH_POLE) {
return NORTH_POLE;
} else if (lat < SOUTH_POLE) {
return SOUTH_POLE;
}
return lat;
}
public double normalizeLatitudeDeg(double lat) {
if (lat > ProjMath.NORTH_POLE_DEG_D) {
return ProjMath.NORTH_POLE_DEG_D;
} else if (lat < ProjMath.SOUTH_POLE_DEG_D) {
return ProjMath.SOUTH_POLE_DEG_D;
}
return lat;
}
/**
* Checks if a LatLonPoint is plot-able.
*
* This method is changed for 5.0. Previously, a point is always plot-able in
* the LLXY projection and that's because the llxy projection was kind of the
* cartesian coordinate system for OpenMap. I think we should start assuming
* that the llxy projection is kind of limited to plotable points on the
* Earth. So, we're checking that now.
*
* @param lat float latitude in decimal degrees
* @param lon float longitude in decimal degrees
* @return boolean if lat is between 90 and -90, lon between 180 and -180;
*/
public boolean isPlotable(double lat, double lon) {
return lat < 90.0 && lat > -90.0 && lon <= 180 && lon >= -180;
}
/**
* Forward projects lat,lon into XY space and returns a Point2D.
*
* @param lat float latitude in radians
* @param lon float longitude in radians
* @param p Resulting XY Point2D
* @param isRadian bogus argument indicating that lat,lon arguments are in
* radians
* @return Point2D p
*/
public Point2D forward(double lat, double lon, Point2D p, boolean isRadian) {
if (isRadian) {
lat = Math.toDegrees(normalizeLatitude(lat));
lon = Math.toDegrees(ProjMath.wrapLongitude(lon - cLonRad));
} else {
lat = normalizeLatitudeDeg(lat);
lon = wrapLongitudeDeg(lon - cLon);
}
double x = wx + (lon * ppd);
double y = hy - ((lat - cLat) * ppd);
p.setLocation(x, y);
return p;
}
/**
* Inverse project x,y coordinates into a LatLonPoint.
*
* @param x integer x coordinate
* @param y integer y coordinate
* @param llp LatLonPoint
* @return LatLonPoint llp
* @see Proj#inverse(Point2D)
*/
public <T extends Point2D> T inverse(double x, double y, T llp) {
if (llp == null) {
llp = (T) new LatLonPoint.Double();
}
// convert from screen to world coordinates, and then
// basically undo the math from the forward method.
double lon = ((x - wx) / ppd) + cLon;
double lat = ((hy - y) / ppd) + cLat;
llp.setLocation(lon, lat);
return llp;
}
/**
* Get the name string of the projection.
*/
public String getName() {
return LLXYName;
}
/**
* Returns the x pixel constant of the projection. This was calculated when
* the projection was created. Represents the number of pixels around the
* earth (360 degrees).
*/
public double getXPixConstant() {
return ppd * 360.0;
}
/**
* Returns the y pixel constant of the projection. This was calculated when
* the projection was created. Represents the number of pixels from 0 to 90
* degrees.
*/
public double getYPixConstant() {
return ppd * 90.0;
}
public static LLXY convertProjection(Projection proj) {
if (proj instanceof LLXY) {
return (LLXY) proj;
}
LLXY llxy =
new LLXY((LatLonPoint) proj.getCenter(new LatLonPoint.Float()), proj.getScale(), proj.getWidth(), proj.getHeight());
Point2D ulp = llxy.forward(proj.getUpperLeft());
Point2D lrp = llxy.forward(proj.getLowerRight());
int w = (int) Math.abs(lrp.getX() - ulp.getX());
int h = (int) Math.abs(lrp.getY() - ulp.getY());
return new LLXY((LatLonPoint) proj.getCenter(new LatLonPoint.Float()), proj.getScale(), w, h);
}
}