/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

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

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

/*
RH looks interesting up to 10000 m
MR better near boundary layer (but tighter color range)
*/

package visad.aeri;

import visad.*;
import visad.util.*;
import visad.DisplayImpl;
import visad.java3d.DisplayImplJ3D;
import visad.java3d.TwoDDisplayRendererJ3D;
import visad.data.netcdf.*;
import visad.bom.WindPolarCoordinateSystem;
import visad.data.mcidas.BaseMapAdapter;
import visad.data.visad.VisADSerialForm;
import visad.data.mcidas.AreaAdapter;
import visad.meteorology.ImageSequenceManager;
import visad.meteorology.NavigatedImage;
import visad.meteorology.ImageSequence;
import java.rmi.RemoteException;
import java.io.IOException;
import java.io.File;

// JFC packages
import javax.swing.*;

// AWT packages
import java.awt.*;
import java.awt.event.*;

public class Qdiv
       implements ScalarMapListener
{
  RealType latitude;
  RealType longitude;
  RealType altitude;

  //- (lon,lat,alt)
  //
  RealTupleType spatial_domain;
  RealType time;
  RealType stn_idx;

  RealType temp;
  RealType dwpt;
  RealType wvmr;
  RealType RH;
  RealType theta;
  RealType thetaE;
  RealType u_wind;
  RealType v_wind;
  RealType wvmr_u;
  RealType wvmr_v;
  RealType band1;
  RealType div_qV;
  RealType q_divV;
  RealType qAdvct;
  RealType shape;

  RealTupleType wind_aeri;
  RealTupleType div_qV_comps;
  FunctionType alt_to_wind_aeri;
  FunctionType time_to_alt_to_wind_aeri;
  FunctionType alt_to_divqV;
  FunctionType time_to_alt_to_divqV;

  FieldImpl advect_field;
  FieldImpl stations_field;
  FieldImpl divqV_field;
  ImageSequence image_seq;
  Set timeDomain;

  int n_stations = 3;

  double[] station_lat;
  double[] station_lon;
  double[] station_alt;
  double[] station_id;
  double[] stat_xoffset = new double[n_stations];
  double[] stat_yoffset = new double[n_stations];
  double[][] centroid_ll;

  BaseMapAdapter baseMap;
  DataReference map_ref;

  ScalarMap xmap;
  ScalarMap ymap;
  ScalarMap zmap;
  ScalarMap img_map;
  boolean xmapEvent = false;
  boolean ymapEvent = false;
  boolean imgEvent = false;
  boolean firstEvent = false;
  boolean first_img_Event = false;

  float latmin, latmax;
  float lonmin, lonmax;
  float del_lat, del_lon;
  double[] x_range, y_range;

  int height_limit = 4000;     //- meters
  int time_intrvl  =  900;     //- seconds
  boolean rh = false;
  boolean tm = false;
  boolean pt = false;
  boolean ept = false;

  int start_date = 0;
  double start_time = 0.0;

  double[] scale_offset_x = new double[2];
  double[] scale_offset_y = new double[2];

  AnimationControl ani_cntrl;

  int   alt_factor = 8;
  int   n_hres_alt_samples;
  float alt_min;
  float alt_max;

  public static void main(String args[])
         throws VisADException, RemoteException, IOException
  {
    Qdiv qdiv = new Qdiv(args);
  }

  public Qdiv(String[] args)
         throws VisADException, RemoteException, IOException
  {
    String vadfile = null;
    String baseDate = "19991226";
    for (int i=0; i max) max = array[ii];
     if (array[ii] < min) min = array[ii];
    }
    min_max[0] = min;
    min_max[1] = max;
    
    return min_max;
  }

  double hgt_max = -Double.MAX_VALUE;

  DataImpl makePoles()
           throws VisADException, RemoteException
  {
    SampledSet[] set_s = new SampledSet[n_stations];
    int ii = 0;
    float[][] locs = new float[3][2];

    for ( int kk = 0; kk < n_stations; kk++ ) {
      boolean any = false;
      float hgt = -Float.MAX_VALUE;
      FieldImpl station = (FieldImpl) stations_field.getSample(kk);
      if (station == null) continue;
      int len = station.getLength();
      for (int i=0; i hgt) hgt = hi[0];
        if (!any && samples[0][0] == samples[0][0])
        {
          any = true;
          locs[0][0] = (float) station_lon[kk];
          locs[1][0] = (float) station_lat[kk];
          locs[0][1] = locs[0][0];
          locs[1][1] = locs[1][0];
        }
      }
      if (any) {
        locs[2][0] = 0.0f;
        locs[2][1] = hgt;
        set_s[ii++] = new Gridded3DSet(spatial_domain, locs, 2, null, null, null);
// System.out.println("set_s[" + kk + "] = " + set_s[kk]);
        if (hgt > hgt_max) hgt_max = hgt;
      }
    }
    SampledSet[] set_ss = new SampledSet[ii];
    System.arraycopy(set_s, 0, set_ss, 0, ii);
    return new UnionSet(spatial_domain, set_ss);
  }

  public void mapChanged(ScalarMapEvent e)
       throws VisADException, RemoteException
  {
    if ( xmap.equals(e.getScalarMap()) ) {
      xmapEvent = true;
    }
    else if ( ymap.equals(e.getScalarMap()) ) {
      ymapEvent = true;
    }
    else if ( img_map.equals(e.getScalarMap()) ) {
      imgEvent = true;
    }
    if (( xmapEvent && ymapEvent ) && !(firstEvent) ) {
      double[] minmax = getArrayMinMax(station_lat);
      latmin = (float)minmax[0];
      latmax = (float)minmax[1];

      minmax = getArrayMinMax(station_lon);
      lonmin = (float) minmax[0];
      lonmax = (float) minmax[1];

      firstEvent = true;
      xmap.setRange(lonmin, lonmax);
      ymap.setRange(latmin, latmax);
      double[] so = new double[2];
      double[] data = new double[2];
      double[] display = new double[2];
      xmap.getScale(so, data, display);
      scale_offset_x[0] = so[0];
      scale_offset_x[1] = so[1];
      ymap.getScale(so, data, display);
      scale_offset_y[0] = so[0];
      scale_offset_y[1] = so[1];
    }
    if ( imgEvent && !first_img_Event ) {
      double[] i_range = img_map.getRange();
      System.out.println(i_range[0]+" "+i_range[1]);
      first_img_Event = true;
      img_map.setRange(i_range[1], i_range[0]);
    }
  }

  public void controlChanged(ScalarMapControlEvent e)
  {
  }

  FieldImpl[] makeWinds(String[] files)
              throws VisADException, RemoteException, IOException
  {
    DataImpl[] file_data = new DataImpl[n_stations];
    FieldImpl[] time_field = new FieldImpl[n_stations];
    double[][] time_offset = null;
    double[] base_date = new double[n_stations];
    double[] base_time = new double[n_stations];
    Gridded1DSet d_set = null;
    FlatField new_ff = null;

    RealType alt;
    RealType spd;
    RealType dir;

    //- create a new netcdf reader
    Plain plain = new Plain();

    //- retrieve file data objects
    for ( int kk = 0; kk < n_stations; kk++) {
      file_data[kk] = plain.open(files[kk]);
    }

    //- make sub mathtype for file objects
    MathType file_type = file_data[0].getType();
    System.out.println(file_type.prettyString());
    System.out.println();

    FunctionType f_type0 =
      (FunctionType)((TupleType)file_type).getComponent(2);

    int n_comps = ((TupleType)f_type0.getRange()).getDimension();
    FunctionType f_type1 =
      (FunctionType)((TupleType)f_type0.getRange()).getComponent(n_comps-1);

    RealTupleType rt_type = (RealTupleType) f_type1.getRange();
    int alt_idx = rt_type.getIndex("Altitude");
    altitude = (RealType) rt_type.getComponent(alt_idx);
    int ws_idx = rt_type.getIndex("windSpeed");
    spd = (RealType) rt_type.getComponent(ws_idx);
    int wd_idx = rt_type.getIndex("windDir");
    dir = (RealType) rt_type.getComponent(wd_idx);

    RealType[] r_types = { dir, spd };

    /* WLH 28 Dec 99 */
    RealType[] uv_types = { u_wind, v_wind };
    // EarthVectorType uv = new EarthVectorType(uv_types); WLH meeds m/s
    RealTupleType uv = new RealTupleType(uv_types);
    CoordinateSystem cs = new WindPolarCoordinateSystem(uv);
    RealTupleType ds = new RealTupleType(r_types, cs, null);


    FunctionType alt_to_ds = new FunctionType(altitude, ds);
    FunctionType alt_to_uv = new FunctionType(altitude, uv);

    RealType domain_type = (RealType)
      ((TupleType)f_type0.getRange()).getComponent(0);
 //-time = domain_type;
    time = RealType.Time;
    FunctionType new_type = new FunctionType(RealType.Time, alt_to_uv);
 //-FunctionType new_type = new FunctionType(time, alt_to_uv);

    FieldImpl[] winds = new FieldImpl[n_stations];

    for ( int ii = 0; ii < n_stations; ii++ )
    {
      base_date[ii] = (double)
        ((Real)((Tuple)file_data[ii]).getComponent(0)).getValue();
      base_time[ii] = (double)
        ((Real)((Tuple)file_data[ii]).getComponent(1)).getValue();
      time_field[ii] = (FieldImpl)
        ((Tuple)file_data[ii]).getComponent(2);
      station_lat[ii] =
        ((Real)((Tuple)time_field[ii].getSample(0)).getComponent(6)).getValue();
      station_lon[ii] =
        -((Real)((Tuple)time_field[ii].getSample(0)).getComponent(7)).getValue();
      station_alt[ii] =
        ((Real)((Tuple)time_field[ii].getSample(0)).getComponent(8)).getValue();
      station_id[ii] =
        ((Real)((Tuple)time_field[ii].getSample(0)).getComponent(9)).getValue();
      if (ii == 0) {
        start_time = base_time[0];
        start_date = (int) base_date[0];
      }

/*
System.out.println("wind " + ii + " date: " + base_date[ii] +
                   " time: " + base_time[ii]);
wind 0 date: 1.9991226E7 time: 9.461664E8
wind 1 date: 1.9991226E7 time: 9.461664E8
wind 2 date: 1.9991226E7 time: 9.461664E8
wind 3 date: 1.9991226E7 time: 9.461664E8
wind 4 date: 1.9991226E7 time: 9.461664E8

looks like 19991226 and seconds since 1-1-70
time_offset looks like seconds since 0Z
*/

      int length = time_field[ii].getLength();
      double[][] times = new double[1][length];
      time_offset = new double[1][length];
      FlatField[] range_data = new FlatField[length];

      double[][] samples = null; // WLH
      int n_not_all_miss = 0;
      for ( int jj = 0; jj < length; jj++ )
      {
        Tuple range = (Tuple) time_field[ii].getSample(jj);
        time_offset[0][jj] = (double)((Real)range.getComponent(0)).getValue();

        FlatField p_field = (FlatField) range.getComponent(n_comps-1);
        double[][] values =
          p_field.getValues(); // WLH - (alt, ?, ?, dir, spd, ???)
        double[][] new_values = new double[2][values[0].length];

        if ( jj == 0 )  //- only once, vertical range gates don't change
        {
          samples = new double[1][values[0].length]; // WLH
          System.arraycopy(values[alt_idx], 0, samples[0], 0, samples[0].length);
          d_set = new Gridded1DSet(altitude, Set.doubleToFloat(samples),
                                   samples[0].length);
        }
        new_ff = new FlatField(alt_to_uv, d_set);

/* WLH - fill in missing winds - this also extrapolates to missing
start or end altitudes - but it does nothing if all winds are missing */
        int n_not_miss = 0;
        int n_levels = values[0].length;
        int[] not_miss = new int[n_levels];
        for ( int mm = 0; mm < n_levels; mm++ )
        {
          if ( values[wd_idx][mm] <= -9999 ) {
            new_values[0][mm] = Float.NaN;
          }
          else {
            new_values[0][mm] = values[wd_idx][mm];
          }

          if ( values[ws_idx][mm] <= -9999 ) {
            new_values[1][mm] = Float.NaN;
          }
          else {
            new_values[1][mm] = values[ws_idx][mm];
          }
       //-System.out.println("("+mm+", "+jj+") "+new_values[0][mm]+",  "+new_values[1][mm]);

          if (new_values[0][mm] == new_values[0][mm] &&
              new_values[1][mm] == new_values[1][mm]) {
            not_miss[n_not_miss] = mm;
            n_not_miss++;
          }
        }
     //-if ( (n_levels - 3) < n_not_miss && n_not_miss < n_levels) {
        if ( (n_levels - 7) < n_not_miss && n_not_miss <= n_levels) {
          int nn = n_not_miss;
          if (not_miss[0] > 0) nn += not_miss[0];
          int endlen = values[0].length - (not_miss[n_not_miss-1]+1);
          if (endlen > 0) nn += endlen;

          float[][] newer_values = new float[2][nn];
          float[][] newer_samples = new float[1][nn];
          // fill in non-missing values
          for (int i=0; i (alt) -> (U,V,T,TD,WV)
      //
      FieldImpl time_wind_aeri = new FieldImpl(time_to_alt_to_wind_aeri, timeDomain);

      //- resample winds to timeDomain
      //

      wind_to_timeDomain = (FieldImpl)
         winds[kk].resample(timeDomain,
                            Data.WEIGHTED_AVERAGE,
                            Data.NO_ERRORS );

      //- resample rtvls to timeDomain

      rtvl_to_timeDomain = (FieldImpl)
         rtvls[kk].resample(timeDomain,
                            Data.WEIGHTED_AVERAGE,
                            Data.NO_ERRORS );

   /**
      rtvl_to_timeDomain = linearInterp(rtvls[kk], timeDomain);
    **/

      //- resample rtvls domain (altitude) to winds at each rtvl time
      //
      int dim =
        ((RealTupleType)
          ((FunctionType)time_to_alt_to_wind_aeri.getRange()).getRange()).getDimension(); 
      int n_times = timeDomain.getLength();
      Set ds = null;
      int nn = 0;
      for ( int tt = 0; tt < n_times; tt++ )
      {
        alt_to_rtvl = (FieldImpl) rtvl_to_timeDomain.getSample(tt);
        alt_to_wind = (FieldImpl) wind_to_timeDomain.getSample(tt);


        if ( tt == 0 ) {  //- these won't change over time

        ds = alt_to_wind.getDomainSet();

/**--- WLH height limit  ---*/
        float[][] samples = ds.getSamples();
        float[] ns = new float[samples[0].length];
        for (int i=0; i 

Ads help maintain this website.