package com.bbn.openmap.proj;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.proj.coords.UTMPoint;
/**
* A OpenMap Projection class that uses the {@link UTMPoint} to do its calculation.
*/
public class UTMProjection extends Proj {
/**
* Center of view as xy coordinates relative to the underlying projection
*/
// TODO: find a better name?
protected Point2D.Double xycenter = new Point2D.Double();
protected int hy, wx;
/**
* Pixel per map unit. this is for a projection with a quadratic grid like
* utm
*/
protected float ppu;
// used for calculating wrapping of ArrayList graphics
// TODO: copied from Cylindrical. may need to change.
protected Point world; // world width in pixels.
protected int half_world; // world.x / 2
private int zone_number;
private boolean isnorthern;
private Ellipsoid ellps;
/**
* The UTMType number. Just a random int not used by any other.
*/
public final static transient int UTMType = 993387;
public UTMProjection(LatLonPoint center, float s, int w, int h, int type, int zone_number,
boolean isnorthern, Ellipsoid ellps) {
super(center, s, w, h, type);
this.zone_number = zone_number;
this.isnorthern = isnorthern;
this.ellps = ellps;
}
protected void computeParameters() {
// super.computeParameters();
hy = height / 2;
wx = width / 2;
if (xycenter != null) {
UTMPoint c = UTMPoint.LLtoUTM(getCenter(), ellps, new UTMPoint(), zone_number, isnorthern);
xycenter.setLocation(c.easting, c.northing);
}
// width of the world in pixels at current scale
// TODO: copied from Cylindrical. may need to change
if (world == null) {
world = new Point();
}
world.x = (int) (planetPixelCircumference / scale);
half_world = world.x / 2;
ppu = (((float) pixelsPerMeter) / getScale());
}
public float normalize_latitude(float lat) {
if (lat > NORTH_POLE) {
return NORTH_POLE;
} else if (lat < SOUTH_POLE) {
return SOUTH_POLE;
}
return lat;
}
public Point forward(LatLonPoint llp, Point pt) {
return forward(llp, pt, new UTMPoint());
}
private Point forward(LatLonPoint llp, Point pt, UTMPoint utmPoint) {
utmPoint = UTMPoint.LLtoUTM(llp, ellps, utmPoint, zone_number, isnorthern);
pt.x = (int) (wx + Math.round(ppu * (utmPoint.easting - xycenter.getX())));
pt.y = (int) (hy - Math.round(ppu * (utmPoint.northing - xycenter.getY())));
return pt;
}
public Point forward(float lat, float lon, Point pt) {
return forward(new LatLonPoint(lat, lon), pt);
}
public Point forward(float lat, float lon, Point pt, boolean isRadian) {
LatLonPoint ll = new LatLonPoint();
ll.setLatLon(lat, lon, isRadian);
return forward(ll, pt);
}
public LatLonPoint inverse(Point point, LatLonPoint llpt) {
return inverse(point.x, point.y, llpt);
}
public LatLonPoint inverse(int x, int y, LatLonPoint llpt) {
double northing = xycenter.getY() + ((hy - y) / ppu);
double easting = xycenter.getX() + ((x - wx) / ppu);
llpt = UTMPoint.UTMtoLL(ellps, (float) northing, (float) easting, zone_number,
isnorthern, llpt);
return llpt;
}
/**
* Given a couple of points representing a bounding box, find out what the
* scale should be in order to make those points appear at the corners of
* the projection.
*
* @param ll1
* the upper left coordinates of the bounding box.
* @param ll2
* the lower right coordinates of the bounding box.
* @param point1
* a java.awt.Point reflecting a pixel spot on the projection
* that matches the ll1 coordinate, the upper left corner of the
* area of interest.
* @param point2
* a java.awt.Point reflecting a pixel spot on the projection
* that matches the ll2 coordinate, usually the lower right
* corner of the area of interest.
*/
public float getScale(LatLonPoint ll1, LatLonPoint ll2, Point point1, Point point2) {
// super does not calculate scale correct for projections that does use
// the same earth radius up north..
float widthPX = point2.x - point1.x;
float heightPX = point2.y - point1.y;
UTMPoint xx1 = UTMPoint.LLtoUTM(ll1, ellps, new UTMPoint(), zone_number, isnorthern);
UTMPoint xx2 = UTMPoint.LLtoUTM(ll2, ellps, new UTMPoint(), zone_number, isnorthern);
float widthMap = (xx2.easting - xx1.easting);
float widthScale = (float) (((double) getPPM()) * (widthMap / widthPX));
float heightMap = (xx2.northing - xx1.northing);
float heightScale = (float) (((double) getPPM()) * (heightMap / heightPX));
// TODO: use width-, height- or medium scale? I guess width- and height scale should be
// equal as the grid inside a single UTM zone is quadratic.
return widthScale;
}
public boolean isPlotable(float lat, float lon) {
return true;
}
protected ArrayList _forwardPoly(float[] rawllpts, int ltype, int nsegs, boolean isFilled) {
// TODO: copied from Cylindrical. may need to change.
int n, k, flag = 0, min = 0, max = 0, xp, xadj = 0;
// determine length of pairs list
int len = rawllpts.length >> 1; // len/2, chop off extra
if (len < 2)
return new ArrayList(0);
// handle complicated line in specific routines
if (isComplicatedLineType(ltype)) {
return doPolyDispatch(rawllpts, ltype, nsegs, isFilled);
}
// determine when to stop
Point temp = new Point(0, 0);
int[] xs = new int[len];
int[] ys = new int[len];
// more temp objects to limit number of new objects that needs to be created
UTMPoint tempUtm = new UTMPoint();
LatLonPoint tempLL = new LatLonPoint();
// forward project the first point
tempLL.setLatLon(rawllpts[0], rawllpts[1], true);
forward(tempLL, temp, tempUtm);
//forward(rawllpts[0], rawllpts[1], temp, true, tempUtm);
xp = temp.x;
xs[0] = temp.x;
ys[0] = temp.y;
// forward project the other points
for (n = 1, k = 2; n < len; n++, k += 2) {
tempLL.setLatLon(rawllpts[k], rawllpts[k + 1], true);
forward(tempLL, temp, tempUtm);
//forward(rawllpts[k], rawllpts[k + 1], temp, true, tempUtm);
xs[n] = temp.x;
ys[n] = temp.y;
// segment crosses longitude along screen edge
if (Math.abs(xp - xs[n]) >= half_world) {
flag += (xp < xs[n]) ? -1 : 1;// inc/dec the wrap
// count
min = (flag < min) ? flag : min;// left wrap count
max = (flag > max) ? flag : max;// right wrap count
xadj = flag * world.x;// adjustment to x coordinates
// Debug.output("flag=" + flag + " xadj=" + xadj);
}
xp = temp.x;// save previous unshifted x coordinate
if (flag != 0) {
xs[n] += xadj;// adjust x coordinates
}
}
min *= -1;// positive magnitude
// now create the return list
ArrayList ret_val = null;
ret_val = new ArrayList(2 + 2 * (max + min));
ret_val.add(xs);
ret_val.add(ys);
int[] altx = null;
/*
* if (Debug.debugging("proj")) { dumpPoly(rawllpts, xs, ys); }
*/
// add the extra left-wrap polys
for (int i = 1; i <= min; i++) {
altx = new int[xs.length];
xadj = i * world.x;// shift opposite
for (int j = 0; j < altx.length; j++) {
altx[j] = xs[j] + xadj;
}
ret_val.add(altx);
ret_val.add(ys);
/*
* if (Debug.debugging("proj")) { dumpPoly(rawllpts, altx, ys); }
*/
}
// add the extra right-wrap polys
for (int i = 1; i <= max; i++) {
altx = new int[xs.length];
xadj = -i * world.x;// shift opposite
for (int j = 0; j < altx.length; j++) {
altx[j] = xs[j] + xadj;
}
ret_val.add(altx);
ret_val.add(ys);
/*
* if (Debug.debugging("proj")) { dumpPoly(rawllpts, altx, ys); }
*/
}
return ret_val;
}// _forwardPoly()
/**
* Draw the background for the projection.
*
* @param g
* Graphics2D
* @param paint
* java.awt.Paint to use for the background
*/
public void drawBackground(Graphics2D g, java.awt.Paint paint) {
g.setPaint(paint);
drawBackground(g);
}
/**
* Assume that the Graphics has been set with the Paint/Color needed, just
* render the shape of the background.
*/
public void drawBackground(Graphics g) {
g.fillRect(0, 0, getWidth(), getHeight());
}
public boolean forwardRaw(float[] rawllpts, int rawoff, int[] xcoords, int[] ycoords,
boolean[] visible, int copyoff, int copylen) {
// TODO: copied from Cylindrical. may need a change
Point temp = new Point();
UTMPoint tempUtm = new UTMPoint();
LatLonPoint tempLL = new LatLonPoint();
int end = copylen + copyoff;
for (int i = copyoff, j = rawoff; i < end; i++, j += 2) {
tempLL.setLatLon(rawllpts[j], rawllpts[j + 1], true);
forward(tempLL, temp, tempUtm);
//forward(rawllpts[j], rawllpts[j + 1], temp, true, tempUtm);
xcoords[i] = temp.x;
ycoords[i] = temp.y;
visible[i] = true;
}
// everything is visible
return true;
}
public LatLonPoint getLowerRight() {
return inverse(width - 1, height - 1);
}
public LatLonPoint getUpperLeft() {
return inverse(0, 0);
}
}