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

src.gov.nasa.worldwind.formats.rpf.RPFPolarFrameTransform Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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.formats.rpf;

import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.util.Logging;

import java.awt.image.*;

/**
 * @author dcollins
 * @version $Id: RPFPolarFrameTransform.java 1171 2013-02-11 21:45:02Z dcollins $
 */
@SuppressWarnings({"UnusedDeclaration"})
class RPFPolarFrameTransform extends RPFFrameTransform
{
    private final char zoneCode;
    private final String rpfDataType;
    private final double resolution;
    private final RPFPolarFrameStructure frameStructure;

    private static final PixelTransformer northernPixels = new NorthPixelTransformer();
    private static final PixelTransformer southernPixels = new SouthPixelTransformer();

    private RPFPolarFrameTransform(char zoneCode, String rpfDataType, double resolution,
        RPFPolarFrameStructure frameStructure)
    {
        this.zoneCode = zoneCode;
        this.rpfDataType = rpfDataType;
        this.resolution = resolution;
        this.frameStructure = frameStructure;
    }

    static RPFPolarFrameTransform createPolarFrameTransform(char zoneCode, String rpfDataType, double resolution)
    {
        if (!RPFZone.isZoneCode(zoneCode))
        {
            String message = Logging.getMessage("RPFZone.UnknownZoneCode", zoneCode);
            Logging.logger().fine(message);
            throw new IllegalArgumentException(message);
        }
        if (rpfDataType == null || !RPFDataSeries.isRPFDataType(rpfDataType))
        {
            String message = Logging.getMessage("RPFDataSeries.UnkownDataType", rpfDataType);
            Logging.logger().fine(message);
            throw new IllegalArgumentException(message);
        }
        if (resolution < 0)
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", rpfDataType);
            Logging.logger().fine(message);
            throw new IllegalArgumentException(message);
        }

        RPFPolarFrameStructure frameStructure = RPFPolarFrameStructure.computeStructure(
            zoneCode, rpfDataType, resolution);
        return new RPFPolarFrameTransform(zoneCode, rpfDataType, resolution, frameStructure);
    }

    public final char getZoneCode()
    {
        return this.zoneCode;
    }

    public final String getRpfDataType()
    {
        return this.rpfDataType;
    }

    public final double getResolution()
    {
        return this.resolution;
    }

    public final RPFFrameStructure getFrameStructure()
    {
        return this.frameStructure;
    }

    public int getFrameNumber(int row, int column)
    {
        return frameNumber(row, column, this.frameStructure.getPolarFrames());
    }

    public int getMaximumFrameNumber()
    {
        return maxFrameNumber(this.frameStructure.getPolarFrames(), this.frameStructure.getPolarFrames());
    }

    public int getRows()
    {
        return this.frameStructure.getPolarFrames();
    }

    public int getColumns()
    {
        return this.frameStructure.getPolarFrames();
    }

    public LatLon computeFrameOrigin(int frameNumber)
    {

        if (frameNumber < 0 || frameNumber > getMaximumFrameNumber())
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", frameNumber);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        int originX = pixelColumn(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int originY = pixelRow(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());

        double lat, lon;
        PixelTransformer pt = (this.zoneCode == '9') ? northernPixels : southernPixels;
        lat = pt.pixel2Latitude(originX, originY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(originX, originY);

        return LatLon.fromDegrees(lat, lon);
    }

    public Sector computeFrameCoverage(int frameNumber)
    {
        int maxFrameNumber = getMaximumFrameNumber();
        if (frameNumber < 0 || frameNumber > maxFrameNumber)
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", frameNumber);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        int minX = pixelColumn(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int maxY = pixelRow(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());

        int maxX = pixelColumn(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());
        int minY = pixelRow(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());

        // we'll need these below...
        int midX = (minX + maxX) / 2;
        int midY = (minY + maxY) / 2;

        // Find the bounds. This is kind of tedious...
        PixelTransformer pt = (this.zoneCode == '9') ? northernPixels : southernPixels;
        MinMaxLatLon bounds = new MinMaxLatLon();

        // LL
        double lat = pt.pixel2Latitude(minX, minY, this.frameStructure.getPolarPixelConstant());
        double lon = pt.pixel2Longitude(minX, minY);
        bounds.setMinMax(lat, lon);

        // LR
        lat = pt.pixel2Latitude(maxX, minY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(maxX, minY);
        bounds.setMinMax(lat, lon);

        // UL
        lat = pt.pixel2Latitude(minX, maxY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(minX, maxY);
        bounds.setMinMax(lat, lon);

        // UR
        lat = pt.pixel2Latitude(maxX, maxY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(maxX, maxY);
        bounds.setMinMax(lat, lon);

        // middle top
        lat = pt.pixel2Latitude(midX, maxY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(midX, maxY);
        bounds.setMinMax(lat, lon);

        // middle right
        lat = pt.pixel2Latitude(maxX, midY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(maxX, midY);
        bounds.setMinMax(lat, lon);

        // middle bottom
        lat = pt.pixel2Latitude(midX, minY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(midX, minY);
        bounds.setMinMax(lat, lon);

        // middle left
        lat = pt.pixel2Latitude(minX, midY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(minX, midY);
        bounds.setMinMax(lat, lon);

        // center
        lat = pt.pixel2Latitude(midX, midY, this.frameStructure.getPolarPixelConstant());
        lon = pt.pixel2Longitude(midX, midY);
        bounds.setMinMax(lat, lon);

        return Sector.fromDegrees(bounds.minLat, bounds.maxLat, bounds.minLon, bounds.maxLon);
    }

    public RPFImage[] deproject(int frameNumber, BufferedImage frame)
    {
        if (frame == null)
        {
            String message = Logging.getMessage("nullValue.ImageSource");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        PixelTransformer pt = (this.zoneCode == '9') ? northernPixels : southernPixels;

        RPFImage[] images;
        if (isDatelineSpanningFrame(frameNumber, pt))
        {
            if (pt == northernPixels)
                images = deprojectNorthernDatelineFrames(frameNumber, frame, pt);
            else
                images = deprojectSouthernDatelineFrames(frameNumber, frame, pt);
        }
        else
        {
            // non-dateline spanning frames are more straightforward...
            Sector sector = computeFrameCoverage(frameNumber);
            BufferedImage destImage = new BufferedImage(frame.getWidth(), frame.getHeight(),
                BufferedImage.TYPE_4BYTE_ABGR);
            resampleFrameFile(sector, frame, destImage, frameNumber, pt);
            images = new RPFImage[1];
            images[0] = new RPFImage(sector, destImage);
        }

        return images;
    }

    private RPFImage[] deprojectNorthernDatelineFrames(int frameNumber, BufferedImage frame, PixelTransformer pt)
    {
        // We have to split this frame at the dateline.
        RPFImage[] images = new RPFImage[2];

        // Compute a tight bounds for western half...
        int minX = pixelColumn(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int maxX = pixelColumn(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());
        int minY = pixelRow(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int maxY = pixelRow(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());
        int midX = (minX + maxX) / 2;
        int midY = (minY + maxY) / 2;

        // Below we are using knowledge about the frames that make up the upper 1/2 part of the middle
        // column, and which borders and edges constitute the extrema...

        MinMaxLatLon bndsWest = new MinMaxLatLon();
        bndsWest.minLon = -180.;
        // center-most frame is different...
        if (isCenterFrame(frameNumber))
        {
            bndsWest.maxLon = 0.;
            // here max lat is at center of frame
            bndsWest.maxLat = pt.pixel2Latitude(midX, midY, this.frameStructure.getPolarPixelConstant());
            // min lat is at an arbitrary corner...
            bndsWest.minLat = pt.pixel2Latitude(minX, minY, this.frameStructure.getPolarPixelConstant());
        }
        else
        {
            // min lat is one of the upper corners...
            bndsWest.minLat = pt.pixel2Latitude(minX, minY, this.frameStructure.getPolarPixelConstant());
            // max lat is center of bottom edge...
            bndsWest.maxLat = pt.pixel2Latitude(midX, maxY, this.frameStructure.getPolarPixelConstant());
            // LL corner of frame gives us max lon...
            bndsWest.maxLon = pt.pixel2Longitude(minX, maxY);
        }
        Sector sector = Sector.fromDegrees(bndsWest.minLat, bndsWest.maxLat, bndsWest.minLon, bndsWest.maxLon);
        BufferedImage destImage = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        resampleFrameFile(sector, frame, destImage, frameNumber, pt);
        images[0] = new RPFImage(sector, destImage);

        // East half...
        MinMaxLatLon bndsEast = new MinMaxLatLon();
        // has same latitude bounds...
        bndsEast.minLat = bndsWest.minLat;
        bndsEast.maxLat = bndsWest.maxLat;
        // max lon is LR corner, unless we're center frame...
        if (isCenterFrame(frameNumber))
        {
            bndsEast.minLon = 0.;
            bndsEast.maxLon = 180.;
        }
        else
        {
            bndsEast.minLon = pt.pixel2Longitude(maxX, maxY);
            bndsEast.maxLon = 180.;
        }
        sector = Sector.fromDegrees(bndsEast.minLat, bndsEast.maxLat, bndsEast.minLon, bndsEast.maxLon);
        destImage = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        resampleFrameFile(sector, frame, destImage, frameNumber, pt);
        images[1] = new RPFImage(sector, destImage);

        return images;
    }

    private RPFImage[] deprojectSouthernDatelineFrames(int frameNumber, BufferedImage frame, PixelTransformer pt)
    {
        // We have to split this frame at the dateline.
        RPFImage[] images = new RPFImage[2];

        // Compute a tight bounds for western half...
        int minX = pixelColumn(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int maxX = pixelColumn(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());
        int minY = pixelRow(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int maxY = pixelRow(this.frameStructure.getPixelRowsPerFrame(), frameNumber,
            this.frameStructure.getPixelRowsPerFrame(), this.frameStructure.getPolarFrames());
        int midX = (minX + maxX) / 2;
        int midY = (minY + maxY) / 2;

        // Below we are using knowledge about the frames that make up the lower 1/2 part of the middle
        // column, and which borders and edges constitute the extrema...

        MinMaxLatLon bndsWest = new MinMaxLatLon();
        bndsWest.minLon = -180.;
        // center-most frame is different...
        if (isCenterFrame(frameNumber))
        {
            bndsWest.maxLon = 0.;
            // here max lat is at center of frame
            bndsWest.maxLat = pt.pixel2Latitude(midX, midY, this.frameStructure.getPolarPixelConstant());
            // min lat is at an arbitrary corner...
            bndsWest.minLat = pt.pixel2Latitude(minX, minY, this.frameStructure.getPolarPixelConstant());
        }
        else
        {
            // min lat is one of the lower corners...
            bndsWest.minLat = pt.pixel2Latitude(minX, maxY, this.frameStructure.getPolarPixelConstant());
            // max lat is center of top edge...
            bndsWest.maxLat = pt.pixel2Latitude(midX, minY, this.frameStructure.getPolarPixelConstant());
            // UL corner of frame gives us max lon...
            bndsWest.maxLon = pt.pixel2Longitude(minX, minY);
        }
        Sector sector = Sector.fromDegrees(bndsWest.minLat, bndsWest.maxLat, bndsWest.minLon, bndsWest.maxLon);
        BufferedImage destImage = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        resampleFrameFile(sector, frame, destImage, frameNumber, pt);
        images[0] = new RPFImage(sector, destImage);

        // East half...
        MinMaxLatLon bndsEast = new MinMaxLatLon();
        // has same latitude bounds...
        bndsEast.minLat = bndsWest.minLat;
        bndsEast.maxLat = bndsWest.maxLat;
        // max lon is LR corner, unless we're center frame...
        if (isCenterFrame(frameNumber))
        {
            bndsEast.minLon = 0.;
            bndsEast.maxLon = 180.;
        }
        else
        {
            bndsEast.minLon = pt.pixel2Longitude(maxX, minY);
            bndsEast.maxLon = 180.;
        }
        sector = Sector.fromDegrees(bndsEast.minLat, bndsEast.maxLat, bndsEast.minLon, bndsEast.maxLon);
        destImage = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        resampleFrameFile(sector, frame, destImage, frameNumber, pt);
        images[1] = new RPFImage(sector, destImage);

        return images;
    }

    private void resampleFrameFile(Sector sector, BufferedImage srcImage, BufferedImage destImage, int frameNumber,
        PixelTransformer pt)
    {
        int frameULX = pixelColumn(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());
        int frameULY = pixelRow(0, frameNumber, this.frameStructure.getPixelRowsPerFrame(),
            this.frameStructure.getPolarFrames());

        int width = destImage.getWidth();
        int height = destImage.getHeight();

        double deltaLon = (sector.getMaxLongitude().degrees - sector.getMinLongitude().degrees) / width;
        double deltaLat = (sector.getMaxLatitude().degrees - sector.getMinLatitude().degrees) / height;

        // unbundle these values that are used in the nested loop below -- its compute intensive enough...
        double minLon = sector.getMinLongitude().degrees;
        double minLat = sector.getMinLatitude().degrees;
        double polarConstant = this.frameStructure.getPolarPixelConstant();
        int srcWidth = srcImage.getWidth();
        int srcHeight = srcImage.getHeight();

        for (int y = 0; y < height; y++)
        {
            double lat = minLat + y * deltaLat;
            for (int x = 0; x < width; x++)
            {
                double lon = minLon + x * deltaLon;

                int pixelX = pt.latLon2X(lat, lon, polarConstant);
                int pixelY = pt.latLon2Y(lat, lon, polarConstant);
                int i = pixelX - frameULX;
                int j = frameULY - pixelY;

                if (i < 0 || i >= srcWidth || j < 0 || j >= srcHeight)
                    continue;

                int color = srcImage.getRGB(i, j);

                // Remove black trim known to be present in these maps....
                if ((color & 0x00FFFFFF) == 0)
                    color = 0;
                destImage.setRGB(x, height - 1 - y, color);
            }
        }
    }

    private boolean isDatelineSpanningFrame(int frameNumber, PixelTransformer pt)
    {
        // By definition, the center column of the polar frame grid...
        int row = frameNumber / getColumns();
        int col = frameNumber % getColumns();
        if (pt == northernPixels)
            return (row >= (getRows()/2) && col == (getColumns() / 2));
        else
            return (row <= (getRows()/2) && col == (getColumns() / 2));
    }

    private boolean isCenterFrame(int frameNumber)
    {
        int row = frameNumber / getRows();
        int col = frameNumber % getColumns();
        return (row == (getRows() / 2) && col == (getColumns() / 2));
    }

    private static int pixelRow(int rowInFrame, int frameNumber, int pixelsPerFrameRow, int numFrames)
    {
        int row = frameRow(frameNumber, numFrames);
        return ((row + 1) * pixelsPerFrameRow - rowInFrame) - (numFrames * pixelsPerFrameRow / 2);
    }

    private static int pixelColumn(int colInFrame, int frameNumber, int pixelsPerFrameRow, int numFrames)
    {
        int row = frameRow(frameNumber, numFrames);
        int col = frameColumn(frameNumber, row, numFrames);
        return (col * pixelsPerFrameRow + colInFrame) - (numFrames * pixelsPerFrameRow / 2);
    }

    //
    // The pixel<-->lat/lon calculations vary slight between north and south poles. We'll hide that
    // with this notion of a PixelTransformer and these classes below.
    //
    private interface PixelTransformer
    {
        public double pixel2Latitude(int x, int y, double polarPixelConstant);

        public double pixel2Longitude(int x, int y);

        public int latLon2X(double lat, double lon, double polarPixelConstant);

        public int latLon2Y(double lat, double lon, double polarPixelConstant);
    }

    private static class NorthPixelTransformer implements PixelTransformer
    {
        /* [Section 30.4.1, MIL-C-89038] */
        public double pixel2Latitude(int x, int y, double polarPixelConstant)
        {
            return 90. - (Math.sqrt(x * x + y * y) / (polarPixelConstant / 360.));
        }

        /* [Section 30.4.1, MIL-C-89038] */
        public double pixel2Longitude(int x, int y)
        {
            if (x == 0 && y > 0)
                return 180.;

            if (x == 0 && y <= 0)
                return 0.;

            double lambda = Math.acos(-y / Math.sqrt(x * x + y * y)) * 180 / Math.PI;
            return (x > 0) ? lambda : -lambda;
        }

        public int latLon2X(double lat, double lon, double polarPixelConstant)
        {
            return (int) (polarPixelConstant / 360. * (90. - lat) * Math.sin(lon * Math.PI / 180.));
        }

        public int latLon2Y(double lat, double lon, double polarPixelConstant)
        {
            return (int) (-polarPixelConstant / 360. * (90. - lat) * Math.cos(lon * Math.PI / 180.));
        }
    }

    private static class SouthPixelTransformer implements PixelTransformer
    {
        /* [Section 30.4.2, MIL-C-89038] */
        public double pixel2Latitude(int x, int y, double polarPixelConstant)
        {
            return -90. + (Math.sqrt(x * x + y * y) / (polarPixelConstant / 360.));
        }

        /* [Section 30.4.2, MIL-C-89038] */
        public double pixel2Longitude(int x, int y)
        {
            if (x == 0 && y > 0)
                return 0.;

            if (x == 0 && y <= 0)
                return 180.;

            double lambda = Math.acos(y / Math.sqrt(x * x + y * y)) * 180 / Math.PI;
            return (x > 0) ? lambda : -lambda;
        }

        public int latLon2X(double lat, double lon, double polarPixelConstant)
        {
            return (int) (polarPixelConstant / 360. * (90. + lat) * Math.sin(lon * Math.PI / 180.));
        }

        public int latLon2Y(double lat, double lon, double polarPixelConstant)
        {
            return (int) (polarPixelConstant / 360. * (90. + lat) * Math.cos(lon * Math.PI / 180.));
        }
    }

    //
    // A little helper class to eliminate some of the tedium of finding bounds of a polar sector.
    //
    private class MinMaxLatLon
    {
        double minLon, minLat, maxLon, maxLat;

        public MinMaxLatLon()
        {
            minLon = minLat = Double.MAX_VALUE;
            maxLon = maxLat = -Double.MAX_VALUE;
        }

        public void setMinMax(double lat, double lon)
        {
            if (lon < this.minLon)
                this.minLon = lon;
            if (lat < this.minLat)
                this.minLat = lat;
            if (lon > this.maxLon)
                this.maxLon = lon;
            if (lat > this.maxLat)
                this.maxLat = lat;

            // We can't get -180 out of the pixel->longitude calculations as defined in the spec.
            // But if a polar sector contains 180, it also contains -180.
            if (lon == 180)
                setMinMax(lat, -lon);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy