src.gov.nasa.worldwind.layers.Earth.UTMBaseGraticuleLayer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.layers.Earth;
import gov.nasa.worldwind.View;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.geom.coords.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.view.orbit.OrbitView;
import java.awt.*;
import java.util.*;
/**
* Displays the UTM graticule.
*
* @author Patrick Murris
* @version $Id: UTMBaseGraticuleLayer.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class UTMBaseGraticuleLayer extends AbstractGraticuleLayer
{
public static final String GRATICULE_UTM = "Graticule.UTM";
protected static final double ONEHT = 100e3;
protected static final int UTM_MIN_LATITUDE = -80;
protected static final int UTM_MAX_LATITUDE = 84;
protected MetricScaleSupport metricScaleSupport = new MetricScaleSupport();
protected long frameCount = 0;
// Exceptions for some meridians. Values: longitude, min latitude, max latitude
private static final int[][] specialMeridians = {{3, 56, 64}, {6, 64, 72}, {9, 72, 84}, {21, 72, 84}, {33, 72, 84}};
// Latitude bands letters - from south to north
private static final String latBands = "CDEFGHJKLMNPQRSTUVWX";
public UTMBaseGraticuleLayer()
{
createUTMRenderables();
initRenderingParams();
this.setPickEnabled(false);
this.setName(Logging.getMessage("layers.Earth.UTMGraticule.Name"));
}
/**
* Returns whether or not graticule lines will be rendered.
*
* @return true if graticule lines will be rendered; false otherwise.
*/
public boolean isDrawGraticule()
{
return getUTMRenderingParams().isDrawLines();
}
/**
* Sets whether or not graticule lines will be rendered.
*
* @param drawGraticule true to render graticule lines; false to disable rendering.
*/
public void setDrawGraticule(boolean drawGraticule)
{
getUTMRenderingParams().setDrawLines(drawGraticule);
}
/**
* Returns the graticule line Color.
*
* @return Color used to render graticule lines.
*/
public Color getGraticuleLineColor()
{
return getUTMRenderingParams().getLineColor();
}
/**
* Sets the graticule line Color.
*
* @param color Color that will be used to render graticule lines.
*
* @throws IllegalArgumentException if color
is null.
*/
public void setGraticuleLineColor(Color color)
{
if (color == null)
{
String message = Logging.getMessage("nullValue.ColorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
getUTMRenderingParams().setLineColor(color);
}
/**
* Returns the graticule line width.
*
* @return width of the graticule lines.
*/
public double getGraticuleLineWidth()
{
return getUTMRenderingParams().getLineWidth();
}
/**
* Sets the graticule line width.
*
* @param lineWidth width of the graticule lines.
*/
public void setGraticuleLineWidth(double lineWidth)
{
getUTMRenderingParams().setLineWidth(lineWidth);
}
/**
* Returns the graticule line rendering style.
*
* @return rendering style of the graticule lines.
*/
public String getGraticuleLineStyle()
{
return getUTMRenderingParams().getLineStyle();
}
/**
* Sets the graticule line rendering style.
*
* @param lineStyle rendering style of the graticule lines. One of LINE_STYLE_PLAIN, LINE_STYLE_DASHED, or
* LINE_STYLE_DOTTED.
*
* @throws IllegalArgumentException if lineStyle
is null.
*/
public void setGraticuleLineStyle(String lineStyle)
{
if (lineStyle == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
getUTMRenderingParams().setLineStyle(lineStyle);
}
/**
* Returns whether or not graticule labels will be rendered.
*
* @return true if graticule labels will be rendered; false otherwise.
*/
public boolean isDrawLabels()
{
return getUTMRenderingParams().isDrawLabels();
}
/**
* Sets whether or not graticule labels will be rendered.
*
* @param drawLabels true to render graticule labels; false to disable rendering.
*/
public void setDrawLabels(boolean drawLabels)
{
getUTMRenderingParams().setDrawLabels(drawLabels);
}
/**
* Returns the graticule label Color.
*
* @return Color used to render graticule labels.
*/
public Color getLabelColor()
{
return getUTMRenderingParams().getLabelColor();
}
/**
* Sets the graticule label Color.
*
* @param color Color that will be used to render graticule labels.
*
* @throws IllegalArgumentException if color
is null.
*/
public void setLabelColor(Color color)
{
if (color == null)
{
String message = Logging.getMessage("nullValue.ColorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
getUTMRenderingParams().setLabelColor(color);
}
/**
* Returns the Font used for graticule labels.
*
* @return Font used to render graticule labels.
*/
public Font getLabelFont()
{
return getUTMRenderingParams().getLabelFont();
}
/**
* Sets the Font used for graticule labels.
*
* @param font Font that will be used to render graticule labels.
*
* @throws IllegalArgumentException if font
is null.
*/
public void setLabelFont(Font font)
{
if (font == null)
{
String message = Logging.getMessage("nullValue.FontIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
getUTMRenderingParams().setLabelFont(font);
}
// --- Graticule Rendering --------------------------------------------------------------
protected String getTypeFor(int resolution)
{
return GRATICULE_UTM;
}
protected void initRenderingParams()
{
GraticuleRenderingParams params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, new Color(.8f, .8f, .8f, .5f));
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, new Color(1f, 1f, 1f, .8f));
params.setValue(GraticuleRenderingParams.KEY_LABEL_FONT, Font.decode("Arial-Bold-14"));
params.setValue(GraticuleRenderingParams.KEY_DRAW_LABELS, Boolean.TRUE);
setRenderingParams(GRATICULE_UTM, params);
}
private GraticuleRenderingParams getUTMRenderingParams()
{
return this.graticuleSupport.getRenderingParams(GRATICULE_UTM);
}
/**
* Select the visible grid elements
*
* @param dc the current DrawContext
.
*/
protected void selectRenderables(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Sector vs = dc.getVisibleSector();
OrbitView view = (OrbitView) dc.getView();
// Compute labels offset from view center
Position centerPos = view.getCenterPosition();
Double pixelSizeDegrees = Angle.fromRadians(view.computePixelSizeAtDistance(view.getZoom())
/ dc.getGlobe().getEquatorialRadius()).degrees;
Double labelOffsetDegrees = pixelSizeDegrees * view.getViewport().getWidth() / 4;
Position labelPos = Position.fromDegrees(centerPos.getLatitude().degrees - labelOffsetDegrees,
centerPos.getLongitude().degrees - labelOffsetDegrees, 0);
Double labelLatDegrees = labelPos.getLatitude().normalizedLatitude().degrees;
labelLatDegrees = Math.min(Math.max(labelLatDegrees, -76), 78);
labelPos = new Position(Angle.fromDegrees(labelLatDegrees), labelPos.getLongitude().normalizedLongitude(), 0);
if (vs != null)
{
for (GridElement ge : this.gridElements)
{
if (ge.isInView(dc))
{
if (ge.renderable instanceof GeographicText)
{
GeographicText gt = (GeographicText) ge.renderable;
if (labelPos.getLatitude().degrees < 72 || "*32*34*36*".indexOf("*" + gt.getText() + "*") == -1)
{
// Adjust label position according to eye position
Position pos = gt.getPosition();
if (ge.type.equals(GridElement.TYPE_LATITUDE_LABEL))
pos = Position.fromDegrees(pos.getLatitude().degrees,
labelPos.getLongitude().degrees, pos.getElevation());
else if (ge.type.equals(GridElement.TYPE_LONGITUDE_LABEL))
pos = Position.fromDegrees(labelPos.getLatitude().degrees,
pos.getLongitude().degrees, pos.getElevation());
gt.setPosition(pos);
}
}
this.graticuleSupport.addRenderable(ge.renderable, GRATICULE_UTM);
}
}
//System.out.println("Total elements: " + count + " visible sector: " + vs);
}
}
/** Create the graticule grid elements */
private void createUTMRenderables()
{
this.gridElements = new ArrayList();
ArrayList positions = new ArrayList();
// Generate meridians and zone labels
int lon = -180;
int zoneNumber = 1;
int maxLat;
for (int i = 0; i < 60; i++)
{
Angle longitude = Angle.fromDegrees(lon);
// Meridian
positions.clear();
positions.add(new Position(Angle.fromDegrees(-80), longitude, 10e3));
positions.add(new Position(Angle.fromDegrees(-60), longitude, 10e3));
positions.add(new Position(Angle.fromDegrees(-30), longitude, 10e3));
positions.add(new Position(Angle.ZERO, longitude, 10e3));
positions.add(new Position(Angle.fromDegrees(30), longitude, 10e3));
if (lon < 6 || lon > 36)
{
// 'regular' UTM meridians
maxLat = 84;
positions.add(new Position(Angle.fromDegrees(60), longitude, 10e3));
positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3));
}
else
{
// Exceptions: shorter meridians around and north-east of Norway
if (lon == 6)
{
maxLat = 56;
positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3));
}
else
{
maxLat = 72;
positions.add(new Position(Angle.fromDegrees(60), longitude, 10e3));
positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3));
}
}
Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
Sector sector = Sector.fromDegrees(-80, maxLat, lon, lon);
this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE));
// Zone label
GeographicText text = new UserFacingText(zoneNumber + "",
Position.fromDegrees(0, lon + 3, 0));
sector = Sector.fromDegrees(-90, 90, lon + 3, lon + 3);
this.gridElements.add(new GridElement(sector, text, GridElement.TYPE_LONGITUDE_LABEL));
// Increase longitude and zone number
lon += 6;
zoneNumber++;
}
// Generate special meridian segments for exceptions around and north-east of Norway
for (int i = 0; i < 5; i++)
{
positions.clear();
lon = specialMeridians[i][0];
positions.add(new Position(Angle.fromDegrees(specialMeridians[i][1]), Angle.fromDegrees(lon), 10e3));
positions.add(new Position(Angle.fromDegrees(specialMeridians[i][2]), Angle.fromDegrees(lon), 10e3));
Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
Sector sector = Sector.fromDegrees(specialMeridians[i][1], specialMeridians[i][2], lon, lon);
this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE));
}
// Generate parallels - no exceptions
int lat = -80;
for (int i = 0; i < 21; i++)
{
Angle latitude = Angle.fromDegrees(lat);
for (int j = 0; j < 4; j++)
{
// Each prallel is divided into four 90 degrees segments
positions.clear();
lon = -180 + j * 90;
positions.add(new Position(latitude, Angle.fromDegrees(lon), 10e3));
positions.add(new Position(latitude, Angle.fromDegrees(lon + 30), 10e3));
positions.add(new Position(latitude, Angle.fromDegrees(lon + 60), 10e3));
positions.add(new Position(latitude, Angle.fromDegrees(lon + 90), 10e3));
Object polyline = createLineRenderable(positions, Polyline.LINEAR);
Sector sector = Sector.fromDegrees(lat, lat, lon, lon + 90);
this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE));
}
// Latitude band label
if (i < 20)
{
GeographicText text = new UserFacingText(latBands.charAt(i) + "",
Position.fromDegrees(lat + 4, 0, 0));
Sector sector = Sector.fromDegrees(lat + 4, lat + 4, -180, 180);
this.gridElements.add(new GridElement(sector, text, GridElement.TYPE_LATITUDE_LABEL));
}
// Increase latitude
lat += lat < 72 ? 8 : 12;
}
}
//=== Support classes and methods ====================================================
protected Position computePosition(int zone, String hemisphere, double easting, double northing)
{
return zone > 0 ?
computePositionFromUTM(zone, hemisphere, easting, northing) :
computePositionFromUPS(hemisphere, easting, northing);
}
protected Position computePositionFromUTM(int zone, String hemisphere, double easting, double northing)
{
try
{
UTMCoord UTM = UTMCoord.fromUTM(zone, hemisphere, easting, northing, globe);
return new Position(Angle.fromRadiansLatitude(UTM.getLatitude().radians),
Angle.fromRadiansLongitude(UTM.getLongitude().radians), 10e3);
}
catch (IllegalArgumentException e)
{
return null;
}
}
protected Position computePositionFromUPS(String hemisphere, double easting, double northing)
{
try
{
UPSCoord UPS = UPSCoord.fromUPS(hemisphere, easting, northing, globe);
return new Position(Angle.fromRadiansLatitude(UPS.getLatitude().radians),
Angle.fromRadiansLongitude(UPS.getLongitude().radians), 10e3);
}
catch (IllegalArgumentException e)
{
return null;
}
}
//--- Metric scale support -----------------------------------------------------------
protected class MetricScaleSupport
{
private int zone;
private double offsetFactorX = -.5;
private double offsetFactorY = -.5;
private double visibleDistanceFactor = 10;
private int scaleModulo = (int) 10e6;
private double maxResolution = 1e5;
// 5 levels 100km to 10m
UTMExtremes[] extremes;
private class UTMExtremes
{
protected double minX, maxX, minY, maxY;
protected String minYHemisphere, maxYHemisphere;
public UTMExtremes()
{
this.clear();
}
public void clear()
{
minX = 1e6;
maxX = 0;
minY = 10e6;
minYHemisphere = AVKey.NORTH;
maxY = 0;
maxYHemisphere = AVKey.SOUTH;
}
}
public int getZone()
{
return this.zone;
}
public void setScaleModulo(int modulo)
{
this.scaleModulo = modulo;
}
public void setMaxResolution(double resolutionInMeter)
{
this.maxResolution = resolutionInMeter;
this.clear();
}
public void computeZone(DrawContext dc)
{
try
{
Position centerPos = ((OrbitView) dc.getView()).getCenterPosition();
if (centerPos != null)
{
if (centerPos.latitude.degrees <= UTM_MAX_LATITUDE
&& centerPos.latitude.degrees >= UTM_MIN_LATITUDE)
{
UTMCoord UTM = UTMCoord.fromLatLon(centerPos.getLatitude(), centerPos.getLongitude(),
dc.getGlobe());
this.zone = UTM.getZone();
}
else
this.zone = 0;
}
}
catch (Exception ex)
{
this.zone = 0;
}
}
public void clear()
{
int numLevels = (int) Math.log10(this.maxResolution);
this.extremes = new UTMExtremes[numLevels];
for (int i = 0; i < numLevels; i++)
{
this.extremes[i] = new UTMExtremes();
this.extremes[i].clear();
}
}
public void computeMetricScaleExtremes(int UTMZone, String hemisphere, GridElement ge, double size)
{
if (UTMZone != this.zone)
return;
if (size < 1 || size > this.maxResolution)
return;
UTMExtremes levelExtremes = this.extremes[(int) Math.log10(size) - 1];
if (ge.type.equals(GridElement.TYPE_LINE_EASTING)
|| ge.type.equals(GridElement.TYPE_LINE_EAST)
|| ge.type.equals(GridElement.TYPE_LINE_WEST))
{
levelExtremes.minX = ge.value < levelExtremes.minX ? ge.value : levelExtremes.minX;
levelExtremes.maxX = ge.value > levelExtremes.maxX ? ge.value : levelExtremes.maxX;
}
else if (ge.type.equals(GridElement.TYPE_LINE_NORTHING)
|| ge.type.equals(GridElement.TYPE_LINE_SOUTH)
|| ge.type.equals(GridElement.TYPE_LINE_NORTH))
{
if (hemisphere.equals(levelExtremes.minYHemisphere))
levelExtremes.minY = ge.value < levelExtremes.minY ? ge.value : levelExtremes.minY;
else if (hemisphere.equals(AVKey.SOUTH))
{
levelExtremes.minY = ge.value;
levelExtremes.minYHemisphere = hemisphere;
}
if (hemisphere.equals(levelExtremes.maxYHemisphere))
levelExtremes.maxY = ge.value > levelExtremes.maxY ? ge.value : levelExtremes.maxY;
else if (hemisphere.equals(AVKey.NORTH))
{
levelExtremes.maxY = ge.value;
levelExtremes.maxYHemisphere = hemisphere;
}
}
}
public void selectRenderables(DrawContext dc)
{
try
{
OrbitView view = (OrbitView) dc.getView();
// Compute easting and northing label offsets
Double pixelSize = view.computePixelSizeAtDistance(view.getZoom());
Double eastingOffset = view.getViewport().width * pixelSize * offsetFactorX / 2;
Double northingOffset = view.getViewport().height * pixelSize * offsetFactorY / 2;
// Derive labels center pos from the view center
Position centerPos = view.getCenterPosition();
double labelEasting;
double labelNorthing;
String labelHemisphere;
if (this.zone > 0)
{
UTMCoord UTM = UTMCoord.fromLatLon(centerPos.getLatitude(), centerPos.getLongitude(),
dc.getGlobe());
labelEasting = UTM.getEasting() + eastingOffset;
labelNorthing = UTM.getNorthing() + northingOffset;
labelHemisphere = UTM.getHemisphere();
if (labelNorthing < 0)
{
labelNorthing = 10e6 + labelNorthing;
labelHemisphere = AVKey.SOUTH;
}
}
else
{
UPSCoord UPS = UPSCoord.fromLatLon(centerPos.getLatitude(), centerPos.getLongitude(),
dc.getGlobe());
labelEasting = UPS.getEasting() + eastingOffset;
labelNorthing = UPS.getNorthing() + northingOffset;
labelHemisphere = UPS.getHemisphere();
}
Position labelPos;
for (int i = 0; i < this.extremes.length; i++)
{
UTMExtremes levelExtremes = this.extremes[i];
double gridStep = Math.pow(10, i);
double gridStepTimesTen = gridStep * 10;
String graticuleType = getTypeFor((int) gridStep);
if (levelExtremes.minX <= levelExtremes.maxX)
{
// Process easting scale labels for this level
for (double easting = levelExtremes.minX; easting <= levelExtremes.maxX; easting += gridStep)
{
// Skip multiples of ten grid steps except for last (higher) level
if (i == this.extremes.length - 1 || easting % gridStepTimesTen != 0)
{
try
{
labelPos = computePosition(this.zone, labelHemisphere, easting, labelNorthing);
if (labelPos == null)
continue;
Angle lat = labelPos.getLatitude();
Angle lon = labelPos.getLongitude();
Vec4 surfacePoint = getSurfacePoint(dc, lat, lon);
if (viewFrustum.contains(surfacePoint) && isPointInRange(dc, surfacePoint))
{
String text = String.valueOf((int) (easting % this.scaleModulo));
GeographicText gt = new UserFacingText(text, new Position(lat, lon, 0));
gt.setPriority(gridStepTimesTen);
addRenderable(gt, graticuleType);
}
}
catch (IllegalArgumentException ignore)
{
}
}
}
}
if (!(levelExtremes.maxYHemisphere.equals(AVKey.SOUTH) && levelExtremes.maxY == 0))
{
// Process northing scale labels for this level
String currentHemisphere = levelExtremes.minYHemisphere;
for (double northing = levelExtremes.minY; (northing <= levelExtremes.maxY)
|| !currentHemisphere.equals(levelExtremes.maxYHemisphere); northing += gridStep)
{
// Skip multiples of ten grid steps except for last (higher) level
if (i == this.extremes.length - 1 || northing % gridStepTimesTen != 0)
{
try
{
labelPos = computePosition(this.zone, currentHemisphere, labelEasting, northing);
if (labelPos == null)
continue;
Angle lat = labelPos.getLatitude();
Angle lon = labelPos.getLongitude();
Vec4 surfacePoint = getSurfacePoint(dc, lat, lon);
if (viewFrustum.contains(surfacePoint) && isPointInRange(dc, surfacePoint))
{
String text = String.valueOf((int) (northing % this.scaleModulo));
GeographicText gt = new UserFacingText(text, new Position(lat, lon, 0));
gt.setPriority(gridStepTimesTen);
addRenderable(gt, graticuleType);
}
}
catch (IllegalArgumentException ignore)
{
}
if (!currentHemisphere.equals(levelExtremes.maxYHemisphere)
&& northing >= 10e6 - gridStep)
{
// Switch hemisphere
currentHemisphere = levelExtremes.maxYHemisphere;
northing = -gridStep;
}
}
}
} // end northing
} // for levels
}
catch (IllegalArgumentException ignore)
{
}
}
private boolean isPointInRange(DrawContext dc, Vec4 point)
{
double altitudeAboveGround = computeAltitudeAboveGround(dc);
return dc.getView().getEyePoint().distanceTo3(point)
< altitudeAboveGround * this.visibleDistanceFactor;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++)
{
sb.append("level ");
sb.append(String.valueOf(i));
sb.append(" : ");
UTMExtremes levelExtremes = this.extremes[i];
if (levelExtremes.minX < levelExtremes.maxX ||
!(levelExtremes.maxYHemisphere.equals(AVKey.SOUTH) && levelExtremes.maxY == 0))
{
sb.append(levelExtremes.minX);
sb.append(", ");
sb.append(levelExtremes.maxX);
sb.append(" - ");
sb.append(levelExtremes.minY);
sb.append(AVKey.NORTH.equals(levelExtremes.minYHemisphere) ? "N" : "S");
sb.append(", ");
sb.append(levelExtremes.maxY);
sb.append(AVKey.NORTH.equals(levelExtremes.maxYHemisphere) ? "N" : "S");
}
else
{
sb.append("empty");
}
sb.append("\n");
}
return sb.toString();
}
}
// --- UTM/UPS square zone ------------------------------------------------------------------
protected ArrayList createSquaresGrid(int UTMZone, String hemisphere, Sector UTMZoneSector,
double minEasting, double maxEasting, double minNorthing, double maxNorthing)
{
ArrayList squares = new ArrayList();
double startEasting = Math.floor(minEasting / ONEHT) * ONEHT;
double startNorthing = Math.floor(minNorthing / ONEHT) * ONEHT;
int cols = (int) Math.ceil((maxEasting - startEasting) / ONEHT);
int rows = (int) Math.ceil((maxNorthing - startNorthing) / ONEHT);
SquareZone[][] squaresArray = new SquareZone[rows][cols];
int col = 0;
for (double easting = startEasting; easting < maxEasting; easting += ONEHT)
{
int row = 0;
for (double northing = startNorthing; northing < maxNorthing; northing += ONEHT)
{
SquareZone sz = new SquareZone(UTMZone, hemisphere, UTMZoneSector, easting, northing, ONEHT);
if (sz.boundingSector != null && !sz.isOutsideGridZone())
{
squares.add(sz);
squaresArray[row][col] = sz;
}
row++;
}
col++;
}
// Keep track of neighbors
for (col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
SquareZone sz = squaresArray[row][col];
if (sz != null)
{
sz.setNorthNeighbor(row + 1 < rows ? squaresArray[row + 1][col] : null);
sz.setEastNeighbor(col + 1 < cols ? squaresArray[row][col + 1] : null);
}
}
}
return squares;
}
/** Represent a generic UTM/UPS square area */
private class SquareSector
{
public static final int MIN_CELL_SIZE_PIXELS = 50;
protected int UTMZone;
protected String hemisphere;
protected Sector UTMZoneSector;
protected double SWEasting;
protected double SWNorthing;
protected double size;
protected Position sw, se, nw, ne; // Four corners position
protected Sector boundingSector;
protected LatLon centroid;
protected LatLon squareCenter;
protected boolean isTruncated = false;
public SquareSector(int UTMZone, String hemisphere, Sector UTMZoneSector, double SWEasting,
double SWNorthing, double size)
{
this.UTMZone = UTMZone;
this.hemisphere = hemisphere;
this.UTMZoneSector = UTMZoneSector;
this.SWEasting = SWEasting;
this.SWNorthing = SWNorthing;
this.size = size;
// Compute corners positions
this.sw = computePosition(this.UTMZone, this.hemisphere, SWEasting, SWNorthing);
this.se = computePosition(this.UTMZone, this.hemisphere, SWEasting + size, SWNorthing);
this.nw = computePosition(this.UTMZone, this.hemisphere, SWEasting, SWNorthing + size);
this.ne = computePosition(this.UTMZone, this.hemisphere, SWEasting + size, SWNorthing + size);
this.squareCenter = computePosition(this.UTMZone, this.hemisphere, SWEasting + size / 2,
SWNorthing + size / 2);
// Compute approximate bounding sector and center point
if (this.sw != null && this.se != null && this.nw != null && this.ne != null)
{
adjustDateLineCrossingPoints();
this.boundingSector = Sector.boundingSector(Arrays.asList(sw, se, nw, ne));
if (!isInsideGridZone())
this.boundingSector = this.UTMZoneSector.intersection(this.boundingSector);
this.centroid = this.boundingSector != null ? this.boundingSector.getCentroid() : this.squareCenter;
//this.squareCenter = this.boundingSector.getCentroid();
}
// Check whether this square is truncated by the grid zone boundary
this.isTruncated = !isInsideGridZone();
}
private void adjustDateLineCrossingPoints()
{
ArrayList corners = new ArrayList(Arrays.asList(sw, se, nw, ne));
if (!LatLon.locationsCrossDateLine(corners))
return;
double lonSign = 0;
for (LatLon corner : corners)
{
if (Math.abs(corner.getLongitude().degrees) != 180)
lonSign = Math.signum(corner.getLongitude().degrees);
}
if (lonSign == 0)
return;
if (Math.abs(sw.getLongitude().degrees) == 180 && Math.signum(sw.getLongitude().degrees) != lonSign)
sw = new Position(sw.getLatitude(), sw.getLongitude().multiply(-1), sw.getElevation());
if (Math.abs(se.getLongitude().degrees) == 180 && Math.signum(se.getLongitude().degrees) != lonSign)
se = new Position(se.getLatitude(), se.getLongitude().multiply(-1), se.getElevation());
if (Math.abs(nw.getLongitude().degrees) == 180 && Math.signum(nw.getLongitude().degrees) != lonSign)
nw = new Position(nw.getLatitude(), nw.getLongitude().multiply(-1), nw.getElevation());
if (Math.abs(ne.getLongitude().degrees) == 180 && Math.signum(ne.getLongitude().degrees) != lonSign)
ne = new Position(ne.getLatitude(), ne.getLongitude().multiply(-1), ne.getElevation());
}
public Extent getExtent(Globe globe, double ve)
{
return Sector.computeBoundingCylinder(globe, ve, this.boundingSector);
}
@SuppressWarnings({"RedundantIfStatement"})
public boolean isInView(DrawContext dc)
{
if (!viewFrustum.intersects(this.getExtent(dc.getGlobe(), dc.getVerticalExaggeration())))
return false;
// Check apparent size
if (getSizeInPixels(dc) <= MIN_CELL_SIZE_PIXELS)
return false;
return true;
}
/**
* Determines whether this square is fully inside its parent grid zone.
*
* @return true if this square is totaly inside its parent grid zone.
*/
@SuppressWarnings({"RedundantIfStatement"})
public boolean isInsideGridZone()
{
if (!this.isPositionInside(this.nw))
return false;
if (!this.isPositionInside(this.ne))
return false;
if (!this.isPositionInside(this.sw))
return false;
if (!this.isPositionInside(this.se))
return false;
return true;
}
/**
* Determines whether this square is fully outside its parent grid zone.
*
* @return true if this square is totaly outside its parent grid zone.
*/
@SuppressWarnings({"RedundantIfStatement"})
public boolean isOutsideGridZone()
{
if (this.isPositionInside(this.nw))
return false;
if (this.isPositionInside(this.ne))
return false;
if (this.isPositionInside(this.sw))
return false;
if (this.isPositionInside(this.se))
return false;
return true;
}
public boolean isPositionInside(Position position)
{
return position != null && this.UTMZoneSector.contains(position);
}
public double getSizeInPixels(DrawContext dc)
{
View view = dc.getView();
Vec4 centerPoint = getSurfacePoint(dc, this.centroid.getLatitude(), this.centroid.getLongitude());
Double distance = view.getEyePoint().distanceTo3(centerPoint);
return this.size / view.computePixelSizeAtDistance(distance);
}
}
/** Represent a 100km square zone inside an UTM zone. */
protected class SquareZone extends SquareSector
{
protected String name;
protected SquareGrid squareGrid;
protected ArrayList gridElements;
private SquareZone northNeighbor, eastNeighbor;
public SquareZone(int UTMZone, String hemisphere, Sector UTMZoneSector, double SWEasting,
double SWNorthing, double size)
{
super(UTMZone, hemisphere, UTMZoneSector, SWEasting, SWNorthing, size);
}
public void setName(String name)
{
this.name = name;
}
public void setNorthNeighbor(SquareZone sz)
{
this.northNeighbor = sz;
}
public void setEastNeighbor(SquareZone sz)
{
this.eastNeighbor = sz;
}
public void selectRenderables(DrawContext dc, Sector vs)
{
// Select our renderables
if (this.gridElements == null)
createRenderables();
boolean drawMetricLabels = getSizeInPixels(dc) > MIN_CELL_SIZE_PIXELS * 2;
String graticuleType = getTypeFor((int) this.size);
for (GridElement ge : this.gridElements)
{
if (ge.isInView(dc, vs))
{
if (ge.type.equals(GridElement.TYPE_LINE_NORTH) && this.isNorthNeighborInView(dc))
continue;
if (ge.type.equals(GridElement.TYPE_LINE_EAST) && this.isEastNeighborInView(dc))
continue;
if (drawMetricLabels)
metricScaleSupport.computeMetricScaleExtremes(this.UTMZone, this.hemisphere, ge,
this.size * 10);
addRenderable(ge.renderable, graticuleType);
}
}
if (getSizeInPixels(dc) <= MIN_CELL_SIZE_PIXELS * 2)
return;
// Select grid renderables
if (this.squareGrid == null)
this.squareGrid = new SquareGrid(this.UTMZone, this.hemisphere, this.UTMZoneSector, this.SWEasting,
this.SWNorthing, this.size);
if (this.squareGrid.isInView(dc))
{
this.squareGrid.selectRenderables(dc, vs);
}
else
this.squareGrid.clearRenderables();
}
private boolean isNorthNeighborInView(DrawContext dc)
{
return this.northNeighbor != null && this.northNeighbor.isInView(dc);
}
private boolean isEastNeighborInView(DrawContext dc)
{
return this.eastNeighbor != null && this.eastNeighbor.isInView(dc);
}
public void clearRenderables()
{
if (this.gridElements != null)
{
this.gridElements.clear();
this.gridElements = null;
}
if (this.squareGrid != null)
{
this.squareGrid.clearRenderables();
this.squareGrid = null;
}
}
public void createRenderables()
{
this.gridElements = new ArrayList();
ArrayList positions = new ArrayList();
Position p1, p2;
Object polyline;
Sector lineSector;
// left segment
positions.clear();
if (this.isTruncated)
{
computeTruncatedSegment(sw, nw, this.UTMZoneSector, positions);
}
else
{
positions.add(sw);
positions.add(nw);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_WEST);
ge.setValue(this.SWEasting);
this.gridElements.add(ge);
}
// right segment
positions.clear();
if (this.isTruncated)
{
computeTruncatedSegment(se, ne, this.UTMZoneSector, positions);
}
else
{
positions.add(se);
positions.add(ne);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_EAST);
ge.setValue(this.SWEasting + this.size);
this.gridElements.add(ge);
}
// bottom segment
positions.clear();
if (this.isTruncated)
{
computeTruncatedSegment(sw, se, this.UTMZoneSector, positions);
}
else
{
positions.add(sw);
positions.add(se);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_SOUTH);
ge.setValue(this.SWNorthing);
this.gridElements.add(ge);
}
// top segment
positions.clear();
if (this.isTruncated)
{
computeTruncatedSegment(nw, ne, this.UTMZoneSector, positions);
}
else
{
positions.add(nw);
positions.add(ne);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_NORTH);
ge.setValue(this.SWNorthing + this.size);
this.gridElements.add(ge);
}
// Label
if (this.name != null)
{
// Only add a label to squares above some dimension
if (this.boundingSector.getDeltaLon().degrees * Math.cos(this.centroid.getLatitude().radians) > .2
&& this.boundingSector.getDeltaLat().degrees > .2)
{
LatLon labelPos = null;
if (this.UTMZone != 0) // Not at poles
{
labelPos = this.centroid;
}
else if (this.isPositionInside(new Position(this.squareCenter, 0)))
{
labelPos = this.squareCenter;
}
else if (this.squareCenter.getLatitude().degrees <= this.UTMZoneSector.getMaxLatitude().degrees
&& this.squareCenter.getLatitude().degrees >= this.UTMZoneSector.getMinLatitude().degrees)
{
labelPos = this.centroid;
}
if (labelPos != null)
{
GeographicText text = new UserFacingText(this.name, new Position(labelPos, 0));
text.setPriority(this.size * 10);
this.gridElements.add(
new GridElement(this.boundingSector, text, GridElement.TYPE_GRIDZONE_LABEL));
}
}
}
}
}
/** Represent a square 10x10 grid and recursive tree in easting/northing coordinates */
protected class SquareGrid extends SquareSector
{
private ArrayList gridElements;
private ArrayList subGrids;
public SquareGrid(int UTMZone, String hemisphere, Sector UTMZoneSector, double SWEasting,
double SWNorthing, double size)
{
super(UTMZone, hemisphere, UTMZoneSector, SWEasting, SWNorthing, size);
}
@SuppressWarnings({"RedundantIfStatement"})
public boolean isInView(DrawContext dc)
{
if (!viewFrustum.intersects(this.getExtent(dc.getGlobe(), dc.getVerticalExaggeration())))
return false;
// Check apparent size
if (getSizeInPixels(dc) <= MIN_CELL_SIZE_PIXELS * 4)
return false;
return true;
}
public void selectRenderables(DrawContext dc, Sector vs)
{
// Select our renderables
if (this.gridElements == null)
createRenderables();
int gridStep = (int) this.size / 10;
boolean drawMetricLabels = getSizeInPixels(dc) > MIN_CELL_SIZE_PIXELS * 4 * 1.7;
String graticuleType = getTypeFor(gridStep);
for (GridElement ge : this.gridElements)
{
if (ge.isInView(dc, vs))
{
if (drawMetricLabels)
metricScaleSupport.computeMetricScaleExtremes(this.UTMZone, this.hemisphere, ge, this.size);
addRenderable(ge.renderable, graticuleType);
}
}
if (getSizeInPixels(dc) <= MIN_CELL_SIZE_PIXELS * 4 * 2)
return;
// Select sub grids renderables
if (this.subGrids == null)
createSubGrids();
for (SquareGrid sg : this.subGrids)
{
if (sg.isInView(dc))
{
sg.selectRenderables(dc, vs);
}
else
sg.clearRenderables();
}
}
public void clearRenderables()
{
if (this.gridElements != null)
{
this.gridElements.clear();
this.gridElements = null;
}
if (this.subGrids != null)
{
for (SquareGrid sg : this.subGrids)
{
sg.clearRenderables();
}
this.subGrids.clear();
this.subGrids = null;
}
}
public void createSubGrids()
{
this.subGrids = new ArrayList();
double gridStep = this.size / 10;
for (int i = 0; i < 10; i++)
{
double easting = this.SWEasting + gridStep * i;
for (int j = 0; j < 10; j++)
{
double northing = this.SWNorthing + gridStep * j;
SquareGrid sg = new SquareGrid(this.UTMZone, this.hemisphere, this.UTMZoneSector,
easting, northing, gridStep);
if (!sg.isOutsideGridZone())
this.subGrids.add(sg);
}
}
}
public void createRenderables()
{
this.gridElements = new ArrayList();
double gridStep = this.size / 10;
Position p1, p2;
ArrayList positions = new ArrayList();
// South-North lines
for (int i = 1; i <= 9; i++)
{
double easting = this.SWEasting + gridStep * i;
positions.clear();
p1 = computePosition(this.UTMZone, this.hemisphere, easting, SWNorthing);
p2 = computePosition(this.UTMZone, this.hemisphere, easting, SWNorthing + this.size);
if (this.isTruncated)
{
computeTruncatedSegment(p1, p2, this.UTMZoneSector, positions);
}
else
{
positions.add(p1);
positions.add(p2);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
Sector lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_EASTING);
ge.setValue(easting);
this.gridElements.add(ge);
}
}
// West-East lines
for (int i = 1; i <= 9; i++)
{
double northing = this.SWNorthing + gridStep * i;
positions.clear();
p1 = computePosition(this.UTMZone, this.hemisphere, SWEasting, northing);
p2 = computePosition(this.UTMZone, this.hemisphere, SWEasting + this.size, northing);
if (this.isTruncated)
{
computeTruncatedSegment(p1, p2, this.UTMZoneSector, positions);
}
else
{
positions.add(p1);
positions.add(p2);
}
if (positions.size() > 0)
{
p1 = positions.get(0);
p2 = positions.get(1);
Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE);
Sector lineSector = Sector.boundingSector(p1, p2);
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_NORTHING);
ge.setValue(northing);
this.gridElements.add(ge);
}
}
}
}
}