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

ucar.unidata.geoloc.projection.sat.Geostationary Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2020 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.unidata.geoloc.projection.sat;

import ucar.nc2.constants.CF;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonPoints;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionPoint;
import ucar.unidata.geoloc.ProjectionPointImpl;
import ucar.unidata.geoloc.ProjectionRect;

/**
 * Describe: https://cf-pcmdi.llnl.gov/trac/ticket/72
 * Accepted for CF-1.7
 *
 * grid_mapping_name = geostationary
 * Map parameters:
 * latitude_of_projection_origin
 * longitude_of_projection_origin
 * perspective_point_height
 * semi_minor_axis
 * semi_major_axis
 * inverse_flattening
 * sweep_angle_axis
 * fixed_angle_axis
 * 
 * Map coordinates:
 * The x (abscissa) and y (ordinate) rectangular coordinates are identified by the standard_name attribute value
 * projection_x_coordinate and projection_y_coordinate
 * respectively. In the case of this projection, the projection coordinates in this projection are directly related to
 * the scanning angle of the satellite instrument,
 * and their units are radians.
 * 
 * Notes:
 * 
 * The algorithm for computing the mapping may be found at
 * http://www.eumetsat.int/idcplg?IdcService=GET_FILE&dDocName=PDF_CGMS_03&RevisionSelectionMethod=LatestReleased.
 * This document assumes the point of observation is directly over the equator, and that the sweep_angle_axis is y.
 * 
 * Notes on using the PROJ.4 software packages for computing the mapping may be found at
 * http://trac.osgeo.org/proj/wiki/proj%3Dgeos and
 * http://remotesensing.org/geotiff/proj_list/geos.html .
 * 
 * The "perspective_point_height" is the distance to the surface of the ellipsoid. Adding the earth major axis gives the
 * distance from the centre of the earth.
 * 
 * The "sweep_angle_axis" attribute indicates which axis the instrument sweeps. The value = "y" corresponds to the
 * spin-stabilized Meteosat satellites,
 * the value = "x" to the GOES-R satellite.
 * 
 * The "fixed_angle_axis" attribute indicates which axis the instrument is fixed. The values are opposite to
 * "sweep_angle_axis". Only one of those two attributes are
 * mandatory.
 * 
 * latitude_of_projection_origin will be taken as zero (at the Equator).
 * 
 * inverse_flattening may be specified independent of the semi_minor/major axes (GRS80). If left unspecified it will be
 * computed
 * from semi_minor/major_axis values.
 *
 * @author caron
 * @since 12/5/13
 */

public class Geostationary extends ProjectionImpl {

  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Geostationary.class);

  private static final String NAME = CF.GEOSTATIONARY;
  private static final String DEFAULT_UNITS = "radians";

  // Remove in v6.x
  private boolean scaleGeoCoordinate;
  private double geoCoordinateScaleFactor = Double.MIN_VALUE;

  private GEOSTransform navigation;

  public Geostationary(double subLonDegrees, double perspective_point_height, double semi_minor_axis,
      double semi_major_axis, double inv_flattening, boolean isSweepX) {

    // scale factors less than zero indicate no scaling of map x, y coordinates
    this(subLonDegrees, perspective_point_height, semi_minor_axis, semi_major_axis, inv_flattening, isSweepX, -1.0);
  }

  /**
   * @deprecated Remove in v6.x.
   *             Use constructor without geoCoordinateScaleFactor as units are handled outside of projection classes
   */
  @Deprecated
  public Geostationary(double subLonDegrees, double perspective_point_height, double semi_minor_axis,
      double semi_major_axis, double inv_flattening, boolean isSweepX, double geoCoordinateScaleFactor) {
    super(NAME, DEFAULT_UNITS, false);

    String sweepAngleAxis = "y";
    if (isSweepX) {
      sweepAngleAxis = "x";
    }

    /* Must assume incoming distances are SI units, so convert 'm' -> 'km' for GEOSTransform */
    perspective_point_height /= 1000.0;
    semi_minor_axis /= 1000.0;
    semi_major_axis /= 1000.0;

    navigation = new GEOSTransform(subLonDegrees, perspective_point_height, semi_minor_axis, semi_major_axis,
        inv_flattening, sweepAngleAxis);
    makePP();

    if (geoCoordinateScaleFactor > 0) {
      scaleGeoCoordinate = true;
      this.geoCoordinateScaleFactor = geoCoordinateScaleFactor;
    }

    logger.debug("scaleGeoCoordinate {}, geoCoordinateScaleFactor {}", scaleGeoCoordinate, geoCoordinateScaleFactor);
  }

  public Geostationary() {
    super(NAME, DEFAULT_UNITS, false);
    navigation = new GEOSTransform();
    makePP();
  }

  public Geostationary(double subLonDegrees) {
    super(NAME, DEFAULT_UNITS, false);
    navigation = new GEOSTransform(subLonDegrees, GEOSTransform.GOES);
    makePP();
  }

  public Geostationary(double subLonDegrees, boolean isSweepX) {
    super(NAME, DEFAULT_UNITS, false);

    String sweepAngleAxis = "y";
    if (isSweepX) {
      sweepAngleAxis = "x";
    }

    String scanGeometry = GEOSTransform.sweepAngleAxisToScanGeom(sweepAngleAxis);

    navigation = new GEOSTransform(subLonDegrees, scanGeometry);
    makePP();
  }

  /**
   * @deprecated Remove in v6.x.
   *             Use constructor without geoCoordinateScaleFactor as units are handled outside of projection classes
   */
  @Deprecated
  public Geostationary(double subLonDegrees, String sweepAngleAxis, double geoCoordinateScaleFactor) {
    super(NAME, DEFAULT_UNITS, false);

    String scanGeometry = GEOSTransform.sweepAngleAxisToScanGeom(sweepAngleAxis);

    navigation = new GEOSTransform(subLonDegrees, scanGeometry);

    if (geoCoordinateScaleFactor > 0) {
      scaleGeoCoordinate = true;
      this.geoCoordinateScaleFactor = geoCoordinateScaleFactor;
    }

    logger.debug("scaleGeoCoordinate {}, geoCoordinateScaleFactor {}", scaleGeoCoordinate, geoCoordinateScaleFactor);

    makePP();
  }

  private void makePP() {
    addParameter(CF.GRID_MAPPING_NAME, NAME);
    addParameter(CF.LONGITUDE_OF_PROJECTION_ORIGIN, navigation.sub_lon_degrees);
    addParameter(CF.LATITUDE_OF_PROJECTION_ORIGIN, 0.0);
    addParameter(CF.PERSPECTIVE_POINT_HEIGHT, navigation.sat_height * 1000.0);
    addParameter(CF.SWEEP_ANGLE_AXIS, GEOSTransform.scanGeomToSweepAngleAxis(navigation.scan_geom));
    addParameter(CF.SEMI_MAJOR_AXIS, navigation.r_eq * 1000.0);
    addParameter(CF.SEMI_MINOR_AXIS, navigation.r_pol * 1000.0);
  }

  /**
   * @deprecated Remove in v6.x.
   *             Units are handled outside of projection classes
   */
  @Deprecated
  private boolean isGeoCoordinateScaled() {
    return scaleGeoCoordinate && geoCoordinateScaleFactor > Double.MIN_VALUE;
  }

  /**
   * copy constructor - avoid clone !!
   */
  @Override
  public ProjectionImpl constructCopy() {
    // constructor takes sweep_angle_axis, so need to translate between
    // scan geometry and sweep_angle_axis first
    // GOES: x
    // GEOS: y
    String sweepAxisAngle = GEOSTransform.scanGeomToSweepAngleAxis(navigation.scan_geom);

    return new Geostationary(navigation.sub_lon_degrees, sweepAxisAngle, geoCoordinateScaleFactor);
  }

  @Override
  public String paramsToString() {
    return "";
  }

  /**
   * Returns an x/y grid point in projection coordinate matching a lat/lon point.
   * The units of the returned result will be in radians unless the {@code Geostationary} object
   * was created using one of the constructors that takes a {@code geoCoordinateScaleFactor}
   * parameter. If that parameter is provided, then the units of x and y are in radians
   * divided by the scaling factor.
   *
   * @param latlon convert from these lat, lon coordinates
   * @param destPoint the object to write to
   * @return destPoint
   */
  @Override
  public ProjectionPoint latLonToProj(LatLonPoint latlon, ProjectionPointImpl destPoint) {
    final double[] satCoords = navigation.earthToSat(latlon.getLongitude(), latlon.getLatitude());

    double x = satCoords[0];
    double y = satCoords[1];

    if (isGeoCoordinateScaled()) {
      x /= geoCoordinateScaleFactor;
      y /= geoCoordinateScaleFactor;
    }

    destPoint.setLocation(x, y);
    return destPoint;
  }

  @Override
  public LatLonPoint projToLatLon(ProjectionPoint ppt, LatLonPointImpl destPoint) {
    double x = ppt.getX();
    double y = ppt.getY();

    if (isGeoCoordinateScaled()) {
      x *= geoCoordinateScaleFactor;
      y *= geoCoordinateScaleFactor;
    }

    final double[] lonlat = navigation.satToEarth(x, y);
    destPoint.setLongitude(lonlat[0]);
    destPoint.setLatitude(lonlat[1]);
    return destPoint;
  }

  @Override
  public boolean crossSeam(ProjectionPoint pt1, ProjectionPoint pt2) {
    // either point is infinite
    if (LatLonPoints.isInfinite(pt1) || LatLonPoints.isInfinite(pt2)) {
      return true;
    }

    double x1 = pt1.getX();
    double x2 = pt2.getX();

    if (isGeoCoordinateScaled()) {
      x1 *= geoCoordinateScaleFactor;
      x2 *= geoCoordinateScaleFactor;
    }

    // opposite signed X values, larger then 100 km
    // LOOK! BUG? This proj works in units of radians rather than km.
    return (x1 * x2 < 0) && (Math.abs(x1 - x2) > 100);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    Geostationary that = (Geostationary) o;

    if (!navigation.equals(that.navigation)) {
      return false;
    }

    return geoCoordinateScaleFactor == that.geoCoordinateScaleFactor;
  }

  @Override
  public int hashCode() {
    return navigation.hashCode();
  }

  /**
   * Create a ProjectionRect from the given LatLonRect.
   * Handles lat/lon points that do not intersect the projection panel.
   * LOOK NEEDS OVERRIDDING
   * 
   * @param rect the LatLonRect
   * @return ProjectionRect, or null if no part of the LatLonRect intersects the projection plane
   */
  @Override
  public ProjectionRect latLonToProjBB(LatLonRect rect) {
    return super.latLonToProjBB(rect);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy