src.gov.nasa.worldwind.layers.Earth.UTMGraticuleLayer 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.UTMCoord;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.GraticuleRenderingParams;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.Logging;
import java.awt.*;
import java.awt.geom.*;
import java.util.ArrayList;
/**
* Displays the UTM graticule.
*
* @author Patrick Murris
* @version $Id: UTMGraticuleLayer.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class UTMGraticuleLayer extends UTMBaseGraticuleLayer
{
/** Graticule for the UTM zone grid. */
public static final String GRATICULE_UTM_GRID = "Graticule.UTM.Grid";
/** Graticule for the 100,000 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_100000M = "Graticule.100000m";
/** Graticule for the 10,000 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_10000M = "Graticule.10000m";
/** Graticule for the 1,000 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_1000M = "Graticule.1000m";
/** Graticule for the 100 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_100M = "Graticule.100m";
/** Graticule for the 10 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_10M = "Graticule.10m";
/** Graticule for the 1 meter grid, nested inside the UTM grid. */
public static final String GRATICULE_1M = "Graticule.1m";
protected static final int MIN_CELL_SIZE_PIXELS = 40; // TODO: make settable
protected static final int GRID_ROWS = 8;
protected static final int GRID_COLS = 60;
protected GraticuleTile[][] gridTiles = new GraticuleTile[GRID_ROWS][GRID_COLS];
public UTMGraticuleLayer()
{
initRenderingParams();
this.setPickEnabled(false);
this.setName(Logging.getMessage("layers.Earth.UTMGraticule.Name"));
this.metricScaleSupport.setMaxResolution(1e6);
}
// --- Graticule Rendering --------------------------------------------------------------
protected void initRenderingParams()
{
GraticuleRenderingParams params;
// UTM zone grid
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, Color.WHITE);
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, Color.WHITE);
params.setValue(GraticuleRenderingParams.KEY_LABEL_FONT, Font.decode("Arial-Bold-16"));
setRenderingParams(GRATICULE_UTM_GRID, params);
// 100km
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, Color.GREEN);
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, Color.GREEN);
params.setValue(GraticuleRenderingParams.KEY_LABEL_FONT, Font.decode("Arial-Bold-14"));
setRenderingParams(GRATICULE_100000M, params);
// 10km
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, new Color(0, 102, 255));
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, new Color(0, 102, 255));
setRenderingParams(GRATICULE_10000M, params);
// 1km
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, Color.CYAN);
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, Color.CYAN);
setRenderingParams(GRATICULE_1000M, params);
// 100m
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, new Color(0, 153, 153));
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, new Color(0, 153, 153));
setRenderingParams(GRATICULE_100M, params);
// 10m
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, new Color(102, 255, 204));
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, new Color(102, 255, 204));
setRenderingParams(GRATICULE_10M, params);
// 1m
params = new GraticuleRenderingParams();
params.setValue(GraticuleRenderingParams.KEY_LINE_COLOR, new Color(153, 153, 255));
params.setValue(GraticuleRenderingParams.KEY_LABEL_COLOR, new Color(153, 153, 255));
setRenderingParams(GRATICULE_1M, params);
}
protected String[] getOrderedTypes()
{
return new String[] {
GRATICULE_UTM_GRID,
GRATICULE_100000M,
GRATICULE_10000M,
GRATICULE_1000M,
GRATICULE_100M,
GRATICULE_10M,
GRATICULE_1M,
};
}
protected String getTypeFor(int resolution)
{
if (resolution >= 500000)
return GRATICULE_UTM_GRID;
if (resolution >= 100000)
return GRATICULE_100000M;
else if (resolution >= 10000)
return GRATICULE_10000M;
else if (resolution >= 1000)
return GRATICULE_1000M;
else if (resolution >= 100)
return GRATICULE_100M;
else if (resolution >= 10)
return GRATICULE_10M;
else if (resolution >= 1)
return GRATICULE_1M;
return null;
}
protected void clear(DrawContext dc)
{
super.clear(dc);
this.applyTerrainConformance();
this.metricScaleSupport.clear();
this.metricScaleSupport.computeZone(dc);
}
private void applyTerrainConformance()
{
String[] graticuleType = getOrderedTypes();
for (String type : graticuleType)
{
double lineConformance = type.equals(GRATICULE_UTM_GRID) ? 20 : this.polylineTerrainConformance;
getRenderingParams(type).setValue(GraticuleRenderingParams.KEY_LINE_CONFORMANCE, lineConformance);
}
}
protected void selectRenderables(DrawContext dc)
{
this.selectUTMRenderables(dc);
this.metricScaleSupport.selectRenderables(dc);
}
/**
* Select the visible grid elements
*
* @param dc the current DrawContext
.
*/
protected void selectUTMRenderables(DrawContext dc)
{
ArrayList tileList = getVisibleTiles(dc);
if (tileList.size() > 0)
{
for (GraticuleTile gt : tileList)
{
// Select tile visible elements
gt.selectRenderables(dc);
}
}
}
protected ArrayList getVisibleTiles(DrawContext dc)
{
ArrayList tileList = new ArrayList();
Sector vs = dc.getVisibleSector();
if (vs != null)
{
Rectangle2D gridRectangle = getGridRectangleForSector(vs);
if (gridRectangle != null)
{
for (int row = (int) gridRectangle.getY(); row <= gridRectangle.getY() + gridRectangle.getHeight();
row++)
{
for (int col = (int) gridRectangle.getX(); col <= gridRectangle.getX() + gridRectangle.getWidth();
col++)
{
if (gridTiles[row][col] == null)
gridTiles[row][col] = new GraticuleTile(getGridSector(row, col));
if (gridTiles[row][col].isInView(dc))
tileList.add(gridTiles[row][col]);
else
gridTiles[row][col].clearRenderables();
}
}
}
}
return tileList;
}
private Rectangle2D getGridRectangleForSector(Sector sector)
{
int x1 = getGridColumn(sector.getMinLongitude().degrees);
int x2 = getGridColumn(sector.getMaxLongitude().degrees);
int y1 = getGridRow(sector.getMinLatitude().degrees);
int y2 = getGridRow(sector.getMaxLatitude().degrees);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
private Sector getGridSector(int row, int col)
{
double deltaLat = UTM_MAX_LATITUDE * 2 / GRID_ROWS;
double deltaLon = 360 / GRID_COLS;
double minLat = row == 0 ? UTM_MIN_LATITUDE : -UTM_MAX_LATITUDE + deltaLat * row;
double maxLat = -UTM_MAX_LATITUDE + deltaLat * (row + 1);
double minLon = -180 + deltaLon * col;
double maxLon = minLon + deltaLon;
return Sector.fromDegrees(minLat, maxLat, minLon, maxLon);
}
private int getGridColumn(double longitude)
{
double deltaLon = 360 / GRID_COLS;
int col = (int) Math.floor((longitude + 180) / deltaLon);
return Math.min(col, GRID_COLS - 1);
}
private int getGridRow(double latitude)
{
double deltaLat = UTM_MAX_LATITUDE * 2 / GRID_ROWS;
int row = (int) Math.floor((latitude + UTM_MAX_LATITUDE) / deltaLat);
return Math.max(0, Math.min(row, GRID_ROWS - 1));
}
protected void clearTiles()
{
for (int row = 0; row < 2; row++)
{
for (int col = 0; col < 60; col++)
{
if (this.gridTiles[row][col] != null)
{
this.gridTiles[row][col].clearRenderables();
this.gridTiles[row][col] = null;
}
}
}
}
// --- Graticule tile ----------------------------------------------------------------------
protected class GraticuleTile
{
private Sector sector;
private int zone;
private String hemisphere;
private ArrayList gridElements;
private ArrayList squares;
public GraticuleTile(Sector sector)
{
this.sector = sector;
this.zone = getGridColumn(this.sector.getCentroid().getLongitude().degrees) + 1;
this.hemisphere = this.sector.getCentroid().latitude.degrees > 0 ? AVKey.NORTH : AVKey.SOUTH;
}
public Extent getExtent(Globe globe, double ve)
{
return Sector.computeBoundingCylinder(globe, ve, this.sector);
}
@SuppressWarnings({"RedundantIfStatement"})
public boolean isInView(DrawContext dc)
{
if (!viewFrustum.intersects(this.getExtent(dc.getGlobe(), dc.getVerticalExaggeration())))
return false;
return true;
}
public double getSizeInPixels(DrawContext dc)
{
View view = dc.getView();
Vec4 centerPoint = getSurfacePoint(dc, this.sector.getCentroid().getLatitude(),
this.sector.getCentroid().getLongitude());
double distance = view.getEyePoint().distanceTo3(centerPoint);
double tileSizeMeter = this.sector.getDeltaLatRadians() * dc.getGlobe().getRadius();
return tileSizeMeter / view.computePixelSizeAtDistance(distance);
}
public void selectRenderables(DrawContext dc)
{
if (this.gridElements == null)
this.createRenderables();
// Select tile grid elements
int resolution = 500000; // Top level 6 degrees zones
String graticuleType = getTypeFor(resolution);
for (GridElement ge : this.gridElements)
{
if (ge.isInView(dc))
addRenderable(ge.renderable, graticuleType);
}
if (getSizeInPixels(dc) / 10 < MIN_CELL_SIZE_PIXELS * 2)
return;
// Select child elements
if (this.squares == null)
createSquares();
for (SquareZone sz : this.squares)
{
if (sz.isInView(dc))
{
sz.selectRenderables(dc, dc.getVisibleSector());
}
else
sz.clearRenderables();
}
}
public void clearRenderables()
{
if (this.gridElements != null)
{
this.gridElements.clear();
this.gridElements = null;
}
if (this.squares != null)
{
for (SquareZone sz : this.squares)
{
sz.clearRenderables();
}
this.squares.clear();
this.squares = null;
}
}
private void createSquares()
{
try
{
// Find grid zone easting and northing boundaries
UTMCoord UTM;
UTM = UTMCoord.fromLatLon(this.sector.getMinLatitude(), this.sector.getCentroid().getLongitude(),
globe);
double minNorthing = UTM.getNorthing();
UTM = UTMCoord.fromLatLon(this.sector.getMaxLatitude(), this.sector.getCentroid().getLongitude(),
globe);
double maxNorthing = UTM.getNorthing();
maxNorthing = maxNorthing == 0 ? 10e6 : maxNorthing;
UTM = UTMCoord.fromLatLon(this.sector.getMinLatitude(), this.sector.getMinLongitude(), globe);
double minEasting = UTM.getEasting();
UTM = UTMCoord.fromLatLon(this.sector.getMaxLatitude(), this.sector.getMinLongitude(), globe);
minEasting = UTM.getEasting() < minEasting ? UTM.getEasting() : minEasting;
double maxEasting = 1e6 - minEasting;
// Create squares
this.squares = createSquaresGrid(this.zone, this.hemisphere, this.sector, minEasting, maxEasting,
minNorthing, maxNorthing);
}
catch (IllegalArgumentException ignore)
{
}
}
/** Create the grid elements */
private void createRenderables()
{
this.gridElements = new ArrayList();
ArrayList positions = new ArrayList();
// Generate west meridian
positions.clear();
positions.add(new Position(this.sector.getMinLatitude(), this.sector.getMinLongitude(), 0));
positions.add(new Position(this.sector.getMaxLatitude(), this.sector.getMinLongitude(), 0));
Object polyline = createLineRenderable(positions, Polyline.LINEAR);
Sector lineSector = new Sector(this.sector.getMinLatitude(), this.sector.getMaxLatitude(),
this.sector.getMinLongitude(), this.sector.getMinLongitude());
GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE);
ge.value = this.sector.getMinLongitude().degrees;
this.gridElements.add(ge);
// Generate south parallel at south pole and equator
if (this.sector.getMinLatitude().degrees == UTM_MIN_LATITUDE || this.sector.getMinLatitude().degrees == 0)
{
positions.clear();
positions.add(new Position(this.sector.getMinLatitude(), this.sector.getMinLongitude(), 0));
positions.add(new Position(this.sector.getMinLatitude(), this.sector.getMaxLongitude(), 0));
polyline = createLineRenderable(positions, Polyline.LINEAR);
lineSector = new Sector(this.sector.getMinLatitude(), this.sector.getMinLatitude(),
this.sector.getMinLongitude(), this.sector.getMaxLongitude());
ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE);
ge.value = this.sector.getMinLatitude().degrees;
this.gridElements.add(ge);
}
// Generate north parallel at north pole
if (this.sector.getMaxLatitude().degrees == UTM_MAX_LATITUDE)
{
positions.clear();
positions.add(new Position(this.sector.getMaxLatitude(), this.sector.getMinLongitude(), 0));
positions.add(new Position(this.sector.getMaxLatitude(), this.sector.getMaxLongitude(), 0));
polyline = createLineRenderable(positions, Polyline.LINEAR);
lineSector = new Sector(this.sector.getMaxLatitude(), this.sector.getMaxLatitude(),
this.sector.getMinLongitude(), this.sector.getMaxLongitude());
ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE);
ge.value = this.sector.getMaxLatitude().degrees;
this.gridElements.add(ge);
}
// Add label
if (this.hasLabel())
{
StringBuilder sb = new StringBuilder();
sb.append(this.zone).append(AVKey.NORTH.equals(this.hemisphere) ? "N" : "S");
GeographicText text = new UserFacingText(sb.toString(), new Position(this.sector.getCentroid(), 0));
this.gridElements.add(new GridElement(this.sector, text, GridElement.TYPE_GRIDZONE_LABEL));
}
}
private boolean hasLabel()
{
// Has label if it contains hemisphere mid latitude
double southLat = UTM_MIN_LATITUDE / 2;
boolean southLabel = this.sector.getMinLatitude().degrees < southLat
&& southLat <= this.sector.getMaxLatitude().degrees;
double northLat = UTM_MAX_LATITUDE / 2;
boolean northLabel = this.sector.getMinLatitude().degrees < northLat
&& northLat <= this.sector.getMaxLatitude().degrees;
return southLabel || northLabel;
}
}
}