All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.layers.Earth.UTMGraticuleLayer Maven / Gradle / Ivy

The newest version!
/*
 * 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 2153 2014-07-17 17:33:13Z tgaskins $
 */
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.terrainConformance;
            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 (!dc.getView().getFrustumInModelCoordinates().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(new ArrayList(positions), AVKey.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(new ArrayList(positions), AVKey.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(new ArrayList(positions), AVKey.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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy