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

ucar.nc2.ft2.coverage.writer.CoverageAsPoint Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2020 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.ft2.coverage.writer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;

import ucar.ma2.*;
import ucar.nc2.VariableSimpleBuilder;
import ucar.nc2.VariableSimpleIF;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.ft.*;
import ucar.nc2.ft.point.*;
import ucar.nc2.ft2.coverage.*;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateUnit;
import ucar.nc2.util.IOIterator;
import ucar.unidata.geoloc.EarthLocation;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.LatLonPoints;
import ucar.unidata.util.StringUtil2;

/**
 * Write DSG CF-1.9 file from a Coverage Dataset
 *
 * @author caron
 * @since 7/8/2015
 */
public class CoverageAsPoint {
  // TODO: refactor for less duplicate code
  private static final boolean debug = false;

  private List varGroups;
  private SubsetParams subset;
  private LatLonPoint latLonPoint;
  private LatLonPoint nearestLatLonPoint;

  private class VarGroup {
    private String name;
    private List varData;
    private FeatureType fType;

    private CoverageCoordAxis1D timeAxis;
    private CalendarDateUnit dateUnit;
    private CoverageCoordAxis1D zAxis;
    private String zUnit;

    VarGroup(String name, List wantCovs) throws IOException {
      this.name = name;
      this.varData = new ArrayList<>();
      for (Coverage cov : wantCovs) {
        try {
          this.varData.add(new VarData(cov));
        } catch (InvalidRangeException e) {
          e.printStackTrace();
        }
      }

      if (varData.isEmpty()) {
        throw new IllegalArgumentException("No coverage data found for parameters " + subset);
      }

      CoverageCoordSys subsetSys = this.varData.get(0).array.getCoordSysForData();

      // single point subset, so only one lat/lon to grab, and this will be the lat/lon
      // closest to the one requested in the subset
      if (nearestLatLonPoint == null) {
        nearestLatLonPoint = subsetSys.getHorizCoordSys().getLatLon(0, 0);
      }

      this.timeAxis = (CoverageCoordAxis1D) subsetSys.getTimeAxis();
      if (this.timeAxis != null) {
        this.dateUnit = this.timeAxis.getCalendarDateUnit();
      }
      this.zAxis = (CoverageCoordAxis1D) subsetSys.getZAxis();
      if (this.zAxis != null) {
        this.zUnit = this.zAxis.getUnits();
      }
      this.fType =
          (this.zAxis == null || this.zAxis.getNcoords() <= 1) ? FeatureType.STATION : FeatureType.STATION_PROFILE;
    }
  }

  private class VarData {
    Coverage cov;
    GeoReferencedArray array;

    VarData(Coverage cov) throws IOException, InvalidRangeException {
      this.cov = cov;
      this.array = cov.readData(subset);
      if (debug) {
        System.out.printf(" Coverage %s data shape = %s%n", cov.getName(), Arrays.toString(array.getData().getShape()));
      }
    }
  }

  public CoverageAsPoint(CoverageCollection gcd, List varNames, SubsetParams subset) throws IOException {
    this.subset = subset;

    this.latLonPoint = (LatLonPoint) subset.get(SubsetParams.latlonPoint);
    if (latLonPoint == null) {
      throw new IllegalArgumentException("No latlon point");
    }

    // build var list, grouped by shared axes
    varGroups = new ArrayList<>();
    for (CoordSysSet css : gcd.getCoverageSets()) {
      List wantCovs =
          css.getCoverages().stream().filter(cov -> varNames.contains(cov.getName())).collect(Collectors.toList());
      if (!wantCovs.isEmpty()) {
        varGroups.add(new VarGroup(css.getCoordSys().getName(), wantCovs));
      }
    }
  }

  public FeatureDatasetPoint asFeatureDatasetPoint() {
    return varGroups.isEmpty() ? null : new CoverageAsFeatureDatasetPoint();
  }

  private class CoverageAsFeatureDatasetPoint extends ucar.nc2.ft.point.PointDatasetImpl {
    CoverageAsFeatureDatasetPoint() {
      super(parseFeatureType(varGroups));
      this.collectionList = new ArrayList<>();
      List dataVars = new ArrayList<>();
      // each group of vars is its own collection
      for (VarGroup vg : varGroups) {
        DsgFeatureCollection featCol =
            vg.fType == FeatureType.STATION_PROFILE ? new CoverageAsStationProfileCollection(vg)
                : new CoverageAsStationFeatureCollection(vg);
        this.collectionList.add(featCol);

        for (VarData vd : vg.varData) {
          Coverage cov = vd.cov;
          VariableSimpleIF simple = new VariableSimpleBuilder(cov.getName(), cov.getDescription(), cov.getUnitsString(),
              cov.getDataType(), cov.getDimensions()).build();
          dataVars.add(simple);
        }
      }
      this.dataVariables = dataVars;
    }
  }

  private static FeatureType parseFeatureType(List groups) {
    FeatureType fType = groups.get(0).fType;
    for (int i = 1; i < groups.size(); i++) {
      if (groups.get(i).fType != fType) {
        return FeatureType.ANY_POINT;
      }
    }
    return fType;
  }

  private class CoverageAsStationProfileCollection extends StationProfileCollectionImpl {
    private VarGroup varGroup;

    CoverageAsStationProfileCollection(VarGroup varGroup) {
      super(varGroup.name + " AsStationProfileCollection", varGroup.dateUnit, varGroup.zUnit);
      this.timeName = varGroup.timeAxis != null ? varGroup.timeAxis.getName() : "time";
      this.altName = varGroup.zAxis != null ? varGroup.zAxis.getName() : "altitude";
      this.varGroup = varGroup;
      this.collectionFeatureType = varGroup.fType;
    }

    @Override
    public IOIterator getCollectionIterator() throws IOException {
      return null;
    }

    @Override
    public PointFeatureCCIterator getNestedPointFeatureCollectionIterator() throws IOException {
      return null;
    }

    @Override
    protected StationHelper createStationHelper() throws IOException {
      StationHelper helper = new StationHelper();
      String name = String.format("GridPointRequestedAt[%s]", LatLonPoints.toString(latLonPoint, 3));
      name = StringUtil2.replace(name, ' ', "_");
      helper.addStation(createStationFeature(name));
      return helper;
    }

    private StationFeature createStationFeature(String name) {
      double stationZ = varGroup.zAxis != null ? varGroup.zAxis.getCoordEdgeFirst() : 0.0;
      return new CoverageAsStationProfile(name, name, null, nearestLatLonPoint.getLatitude(),
          nearestLatLonPoint.getLongitude(), stationZ, this.timeUnit, this.altUnits, -1, varGroup);
    }
  }

  private class CoverageAsStationFeatureCollection extends StationTimeSeriesCollectionImpl {

    private VarGroup varGroup;

    CoverageAsStationFeatureCollection(VarGroup varGroup) {
      super(varGroup.name + " AsStationFeatureCollection", varGroup.dateUnit, varGroup.zUnit);
      this.timeName = varGroup.timeAxis != null ? varGroup.timeAxis.getName() : "time";
      this.timeUnit =
          varGroup.timeAxis != null ? varGroup.timeAxis.getCalendarDateUnit() : CalendarDateUnit.unixDateUnit;
      this.altName = varGroup.zAxis != null ? varGroup.zAxis.getName() : "altitude";
      this.varGroup = varGroup;
      this.collectionFeatureType = varGroup.fType;
    }

    @Override
    protected StationHelper createStationHelper() {
      StationHelper helper = new StationHelper();
      String name = String.format("GridPointRequestedAt[%s]", LatLonPoints.toString(latLonPoint, 3));
      name = StringUtil2.replace(name, ' ', "_");
      helper.addStation(createStationFeature(name));
      return helper;
    }

    private StationFeature createStationFeature(String name) {
      double stationZ = varGroup.zAxis != null ? varGroup.zAxis.getCoordMidpoint(0) : 0.0;
      return new CoverageAsStationFeature(name, name, null, nearestLatLonPoint.getLatitude(),
          nearestLatLonPoint.getLongitude(), stationZ, this.timeUnit, this.altUnits, -1, varGroup);
    }
  }

  /**
   * Begin inner classes for Station Profile feature (w/ z dimension) as point
   */
  private class CoverageAsStationProfile extends StationProfileFeatureImpl {
    private VarGroup varGroup;

    private CoverageAsStationProfile(String name, String desc, String wmoId, double lat, double lon, double alt,
        CalendarDateUnit timeUnit, String altUnits, int npts, VarGroup varGroup) {
      super(name, desc, wmoId, lat, lon, alt, timeUnit, altUnits, npts);
      this.varGroup = varGroup;
    }

    ///////////////
    // NO-OPS
    @Override
    public List getTimes() {
      return null;
    }

    @Override
    public ProfileFeature getProfileByDate(CalendarDate date) {
      return null;
    }

    @Nonnull
    @Override
    public StructureData getFeatureData() throws IOException {
      return StructureData.EMPTY;
    }

    // end NO-OPS
    /////////////////

    @Override
    public IOIterator getCollectionIterator() throws IOException {
      return new TimeseriesProfileIterator();
    }

    @Override
    public PointFeatureCollectionIterator getPointFeatureCollectionIterator() throws IOException {
      return new TimeseriesProfileIterator();
    }

    private class VarIter {
      Coverage cov;
      GeoReferencedArray geoA;
      IndexIterator dataIter;

      VarIter(Coverage cov, GeoReferencedArray array, IndexIterator dataIter) {
        this.cov = cov;
        this.geoA = array;
        this.dataIter = dataIter;
      }
    }

    private class TimeseriesProfileIterator implements PointFeatureCollectionIterator {

      int curr;
      int nvalues;
      List varIters;
      CoverageCoordAxis1D timeAxis;

      TimeseriesProfileIterator() {
        this.timeAxis = varGroup.timeAxis;
        this.nvalues = this.timeAxis != null ? timeAxis.getNcoords() : 1;

        varIters = new ArrayList<>();
        for (VarData vd : varGroup.varData) {
          Array data = vd.array.getData();
          if (debug) {
            System.out.printf("%s shape=%s%n", vd.cov.getName(), Arrays.toString(data.getShape()));
          }
          varIters.add(new VarIter(vd.cov, vd.array, data.getIndexIterator()));
        }
      }

      @Override
      public boolean hasNext() throws IOException {
        return curr < nvalues;
      }

      @Override
      public PointFeatureCollection next() throws IOException {
        double obsTime = this.timeAxis != null ? this.timeAxis.getCoordMidpoint(curr) : 0.0;
        curr++;
        return new CoverageAsProfileFeature(obsTime, varGroup.dateUnit, varGroup.zUnit, getLatitude(), getLongitude(),
            this.varIters);
      }
    }

    private class CoverageAsProfileFeature extends ProfileFeatureImpl {

      List varIters;

      CoverageAsProfileFeature(double obsTime, CalendarDateUnit timeUnit, String altUnits, double lat, double lon,
          List varIters) {
        super("", timeUnit, altUnits, lat, lon, obsTime, -1);
        this.varIters = varIters;
      }

      @Override
      public PointFeatureIterator getPointFeatureIterator() throws IOException {
        return new ProfileFeatureIterator();
      }

      private class ProfilePoint implements EarthLocation {
        private final double lat;
        private final double lon;
        private final double alt;
        private final LatLonPoint latlon;

        public ProfilePoint(double lat, double lon, double alt) {
          this.lat = lat;
          this.lon = lon;
          this.alt = alt;
          this.latlon = LatLonPoint.create(lat, lon);
        }

        @Override
        public double getLatitude() {
          return lat;
        }

        @Override
        public double getLongitude() {
          return lon;
        }

        @Override
        public double getAltitude() {
          return alt;
        }

        @Override
        public LatLonPoint getLatLon() {
          return latlon;
        }

        @Override
        public boolean isMissing() {
          return false;
        }
      }

      private class ProfileFeatureIterator extends PointIteratorAbstract {
        int curr;
        int nvalues;

        ProfileFeatureIterator() {
          this.nvalues = varGroup.zAxis.getNcoords();
        }

        @Override
        public boolean hasNext() {
          boolean more = curr < nvalues;
          if (!more) {
            close();
          }
          return more;
        }

        @Override
        public PointFeature next() {
          double alt = varGroup.zAxis.getCoordMidpoint(curr);

          StructureMembers.Builder smb = StructureMembers.builder().setName("Coords");
          for (VarIter vi : varIters) {
            smb.addMemberScalar(vi.cov.getName(), null, null, vi.cov.getDataType(),
                (Number) vi.dataIter.getObjectNext());
          }
          StructureData coords = new StructureDataFromMember(smb.build());
          curr++;
          EarthLocation location = new ProfilePoint(CoverageAsStationProfile.this.getLatitude(),
              CoverageAsStationProfile.this.getLongitude(), alt);
          PointFeature pf = new CoverageAsStationProfile.CoverageAsPointFeature(CoverageAsStationProfile.this, time,
              0.0, timeUnit, location, coords);
          calcBounds(pf);
          return pf;
        }

        @Override
        public void close() {
          finishCalcBounds();
        }
      }

      @Nonnull
      @Override
      public CalendarDate getTime() {
        return timeUnit != null ? timeUnit.makeCalendarDate(time) : CalendarDate.UNKNOWN;
      }

      @Nonnull
      @Override
      public StructureData getFeatureData() throws IOException {
        return StructureData.EMPTY;
      }
    }

    private class CoverageAsPointFeature extends PointFeatureImpl implements StationPointFeature {
      StationFeature stn;
      StructureData sdata;

      CoverageAsPointFeature(StationFeature stn, double obsTime, double nomTime, CalendarDateUnit timeUnit,
          EarthLocation location, StructureData sdata) {
        super(CoverageAsStationProfile.this, stn, obsTime, nomTime, timeUnit);
        this.stn = stn;
        this.sdata = sdata;
        this.location = location;
      }

      @Override
      @Nonnull
      public StationFeature getStation() {
        return stn;
      }

      @Override
      @Nonnull
      public StructureData getFeatureData() {
        return sdata;
      }

      @Override
      @Nonnull
      public StructureData getDataAll() {
        return sdata;
      }
    }
  }

  /**
   * Begin inner classes for Station feature (no z) as point
   */

  private class CoverageAsStationFeature extends StationTimeSeriesFeatureImpl {

    private VarGroup varGroup;

    private CoverageAsStationFeature(String name, String desc, String wmoId, double lat, double lon, double alt,
        CalendarDateUnit timeUnit, String altUnits, int npts, VarGroup varGroup) {
      super(name, desc, wmoId, lat, lon, alt, timeUnit, altUnits, npts, StructureData.EMPTY);
      this.varGroup = varGroup;
    }

    @Nonnull
    @Override
    public StructureData getFeatureData() {
      return StructureData.EMPTY;
    }

    @Override
    public PointFeatureIterator getPointFeatureIterator() {
      return new TimeseriesIterator();
    }

    private class VarIter {
      Coverage cov;
      GeoReferencedArray geoA;
      IndexIterator dataIter;

      VarIter(Coverage cov, GeoReferencedArray array, IndexIterator dataIter) {
        this.cov = cov;
        this.geoA = array;
        this.dataIter = dataIter;
      }
    }
    private class TimeseriesIterator extends PointIteratorAbstract {
      int curr;
      int nvalues;
      List varIters;
      CoverageCoordAxis1D timeAxis;

      TimeseriesIterator() {
        this.timeAxis = varGroup.timeAxis;
        this.nvalues = timeAxis != null ? timeAxis.getNcoords() : 1;

        varIters = new ArrayList<>();
        for (VarData vd : varGroup.varData) {
          Array data = vd.array.getData();
          if (debug) {
            System.out.printf("%s shape=%s%n", vd.cov.getName(), Arrays.toString(data.getShape()));
          }
          varIters.add(new VarIter(vd.cov, vd.array, data.getIndexIterator()));
        }
      }

      @Override
      public boolean hasNext() {
        boolean more = curr < nvalues;
        if (!more) {
          close();
        }
        return more;
      }

      @Override
      public PointFeature next() {
        double obsTime = this.timeAxis != null ? this.timeAxis.getCoordMidpoint(curr) : 0.0;

        StructureMembers.Builder smb = StructureMembers.builder().setName("Coords");
        for (VarIter vi : varIters) {
          smb.addMemberScalar(vi.cov.getName(), null, null, vi.cov.getDataType(), (Number) vi.dataIter.getObjectNext());
        }
        StructureData coords = new StructureDataFromMember(smb.build());
        curr++;
        PointFeature pf = new CoverageAsPointFeature(CoverageAsStationFeature.this, obsTime, 0.0, timeUnit, coords);
        calcBounds(pf);
        return pf;
      }

      @Override
      public void close() {
        finishCalcBounds();
      }
    }

    private class CoverageAsPointFeature extends PointFeatureImpl implements StationPointFeature {
      StationFeature stn;
      StructureData sdata;

      CoverageAsPointFeature(StationFeature stn, double obsTime, double nomTime, CalendarDateUnit timeUnit,
          StructureData sdata) {
        super(CoverageAsStationFeature.this, stn, obsTime, nomTime, timeUnit);
        this.stn = stn;
        this.sdata = sdata;
      }

      @Override
      @Nonnull
      public StationFeature getStation() {
        return stn;
      }

      @Override
      @Nonnull
      public StructureData getFeatureData() {
        return sdata;
      }

      @Override
      @Nonnull
      public StructureData getDataAll() {
        return sdata;
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy