//
// AREACoordinateSystem.java
//

/*
The software in this file is Copyright(C) 2017 by Tom Whittaker.
It is designed to be used with the VisAD system for interactive
analysis and visualization of numerical data.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License in file NOTICE for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package visad.data.mcidas;

import edu.wisc.ssec.mcidas.AREAnav;
import edu.wisc.ssec.mcidas.AreaFile;
import edu.wisc.ssec.mcidas.McIDASException;
import edu.wisc.ssec.mcidas.AreaFileException;

import java.awt.geom.Rectangle2D;

import visad.CoordinateSystemException;
import visad.QuickSort;
import visad.RealTupleType;
import visad.Unit;
import visad.VisADException;

/**
 * AREACoordinateSystem is the VisAD CoordinateSystem class
 * for conversions to/from (Latitude, Longitude) and Cartesian (element,line),
 * and with Latitude and Longitude in degrees.
 */
public class AREACoordinateSystem
    extends visad.georef.MapProjection
{

  private static final long serialVersionUID = 1L;
  protected AREAnav anav = null;
  private int lines;
  private int elements;
  private int[] dirBlock;
  private int[] navBlock;
  private int[] auxBlock;
  private boolean useSpline = true;

  private static Unit[] coordinate_system_units =
    {null, null};

  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param af is the associated AreaFile 
    *
    */
  public AREACoordinateSystem(AreaFile af) 
                  throws VisADException, AreaFileException {
    this(RealTupleType.LatitudeLongitudeTuple, 
                 af.getDir(), af.getNav(), af.getAux());
  }

  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param ref	 the CoordinateSystem reference (must be equivalent
    *                to RealTupleType.LatitudeLongitudeTuple)
    * @param af is the associated AreaFile 
    *
    */
  public AREACoordinateSystem(RealTupleType ref, AreaFile af) 
                  throws VisADException, AreaFileException {
    this(ref, af.getDir(), af.getNav(), af.getAux());
  }


  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    *
    */
  public AREACoordinateSystem(int[] dir, int[] nav) throws VisADException {
      this(RealTupleType.LatitudeLongitudeTuple, dir, nav, null);
  }

  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    * @param aux is the AREA file auxillary block
    *
    */
  public AREACoordinateSystem(int[] dir, int[] nav, int[] aux) 
                                             throws VisADException {
      this(dir, nav, aux, true);
  }

  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    * @param aux is the AREA file auxillary block
    * @param useSpline  use a spline approximation for speed
    *
    */
  public AREACoordinateSystem(int[] dir, int[] nav, int[] aux, boolean useSpline) 
                                             throws VisADException {
      this(RealTupleType.LatitudeLongitudeTuple, dir, nav, aux, useSpline);
  }

  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param reference the CoordinateSystem reference (must be equivalent
    *                  to RealTupleType.LatitudeLongitudeTuple)
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    *
    */
  public AREACoordinateSystem(RealTupleType reference, int[] dir,
                                 int[] nav) throws VisADException {

      this(reference, dir, nav, null);
  }


  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param reference the CoordinateSystem reference (must be equivalent
    *                  to RealTupleType.LatitudeLongitudeTuple)
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    * @param aux is the AREA file auxillary block
    *
    */

  public AREACoordinateSystem(RealTupleType reference, int[] dir,
                                 int[] nav, int[] aux) throws VisADException {
      this(reference, dir, nav, aux, true);
  }

  /** create a AREA coordinate system with nothing initialized.
    * This allows for derived classes to do a lazy initialization
    * of the coordinate system. To do this they must overwrite getAreaNav
    * in order to create the nav.
    */
   protected AREACoordinateSystem() throws VisADException {
       super(RealTupleType.LatitudeLongitudeTuple,coordinate_system_units);
   }


  /** create a AREA coordinate system from the Area file's
    * directory and navigation blocks.
    *
    * This routine uses a flipped Y axis (first line of
    * the image file is number 0)
    *
    * @param reference the CoordinateSystem reference (must be equivalent
    *                  to RealTupleType.LatitudeLongitudeTuple)
    * @param dir is the AREA file directory block
    * @param nav is the AREA file navigation block
    * @param aux is the AREA file auxillary block
    * @param useSpline  use a spline approximation for speed
    *
    */
  public AREACoordinateSystem(RealTupleType reference, int[] dir,
                                 int[] nav, int[] aux,
                                 boolean useSpline) throws VisADException {


    super(reference, coordinate_system_units);
    init(dir,nav,aux,useSpline);
  }


  /**
   * This is used by the methods that do the work and can be overwritten
   * by a derived class to do a lazy instantiation of the coordinate system.
   * 
   * @return The area nav 
   */
  protected AREAnav getAreaNav() {
      return anav;
  }

  /**
   * Create and initialize the areanav.
   * This used to be in the constructor is snow its own method to enable 
   * derived classes to lazily create the areanav
   *
   * @param dir is the AREA file directory block
   * @param nav is the AREA file navigation block
   * @param aux is the AREA file auxillary block
   * @param useSpline  use a spline approximation for speed
   */
  
  protected void init(int[]dir, int[] nav, int[] aux, boolean useSpline) throws VisADException {
    try {
        anav = AREAnav.makeAreaNav(nav, aux);
    } catch (McIDASException excp) {
      throw new CoordinateSystemException(
          "AREACoordinateSystem: problem creating navigation" + excp);
    }
    dirBlock = dir;
    navBlock = nav;
    auxBlock = aux;
    this.useSpline = !useSpline 
                       ? false  // user overrode
                       : anav.canApproximateWithSpline(); // let nav decide
    anav.setImageStart(dir[5], dir[6]);
    anav.setRes(dir[11], dir[12]);
    anav.setStart(0,0);
    anav.setMag(1,1);
    lines = dir[8];
    elements = dir[9];
    // the following is done because VisAD's (0,0) coordinate is in
    // the lower-left corner; whereas, AREA files are in the upper-left

    anav.setFlipLineCoordinates(dir[8]-1); // invert Y axis coordinates
  }


  /** Get the directory block used to initialize this AREACoordinateSystem */
  public int[] getDirBlock() {
    getAreaNav();
    return dirBlock;
  }

  /** Get the navigation block used to initialize this AREACoordinateSystem */
  public int[] getNavBlock() {
    getAreaNav();
    return navBlock;
  }

  /** Get the navigation block used to initialize this AREACoordinateSystem */
  public int[] getAuxBlock() {
    getAreaNav();
    return auxBlock;
  }

  /** get the subpoint if available
  */
  public double[] getSubpoint() {
    return getAreaNav().getSubpoint();
  }

  /** 
   * Get whether we are using a spline or not 
   */
  public boolean getUseSpline() {
    return useSpline;
  }

  /** convert from image element,line to latitude,longitude
    *
    * @param tuples contains the element,line pairs to convert
    *
    */
  public double[][] toReference(double[][] tuples) throws VisADException {
    if (tuples == null || tuples.length != 2) {
      throw new CoordinateSystemException("AREACoordinateSystem." +
             "toReference: tuples wrong dimension");
    }

    AREAnav  anav = getAreaNav();

    if (anav == null) {
      throw new CoordinateSystemException("AREA O & A data not availble");
    }

    int[] nums = new int[2];
    double[] mins = new double[2];
    double[] maxs = new double[2];
    double[][] newval = makeSpline(tuples, mins, maxs, nums);
    if (newval != null) {
// System.out.println("new 1 " + tuples[0].length + " " + newval[0].length);
      double[][] newtrans = anav.toLatLon(newval);

      int len = tuples[0].length;
      double[][] misstrans = new double[2][len];
      int[][] miss_to_trans = new int[1][len];
      double[][] val = applySpline(tuples, mins, maxs, nums, newtrans,
                                   misstrans, miss_to_trans);
      if (miss_to_trans[0] != null) {
        double[][] newmiss = anav.toLatLon(misstrans);
        for (int i=0; i 

Ads help maintain this website.