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

ucar.nc2.write.Ncdump 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.write;

import com.google.common.base.Preconditions;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Formatter;
import java.util.StringTokenizer;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.jdom2.Element;
import ucar.ma2.Array;
import ucar.ma2.ArrayChar;
import ucar.ma2.ArrayObject;
import ucar.ma2.ArraySequence;
import ucar.ma2.ArrayStructure;
import ucar.ma2.DataType;
import ucar.ma2.Index;
import ucar.ma2.IndexIterator;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.StructureData;
import ucar.ma2.StructureDataIterator;
import ucar.ma2.StructureMembers;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.dataset.NetcdfDatasets;
import ucar.nc2.util.CancelTask;
import ucar.nc2.util.Indent;

/**
 * Utility to implement ncdump.
 * A difference with ncdump is that the nesting of multidimensional array data is represented by nested brackets,
 * so the output is not legal CDL that can be used as input for ncgen. Also, the default is header only (-h).
 * Moved from ucar.nc2.NCdumpW
 */
@Immutable
public class Ncdump {

  /** Tell Ncdump if you want values printed. */
  public enum WantValues {
    none, coordsOnly, all
  }

  /**
   * ncdump that parses a command string.
   *
   * @param command command string
   * @param out send output here
   * @param ct allow task to be cancelled; may be null.
   * @throws IOException on write error
   */
  public static void ncdump(String command, Writer out, CancelTask ct) throws IOException {
    // pull out the filename from the command
    String filename;
    StringTokenizer stoke = new StringTokenizer(command);
    if (stoke.hasMoreTokens())
      filename = stoke.nextToken();
    else {
      out.write(usage);
      return;
    }

    try (NetcdfFile nc = NetcdfDatasets.openFile(filename, ct)) {
      // the rest of the command
      int pos = command.indexOf(filename);
      command = command.substring(pos + filename.length());
      ncdump(nc, command, out, ct);

    } catch (FileNotFoundException e) {
      out.write("file not found= ");
      out.write(filename);

    } finally {
      out.close();
    }
  }

  /**
   * ncdump, parsing command string, file already open.
   *
   * @param nc apply command to this file
   * @param command : command string
   * @param out send output here
   * @param ct allow task to be cancelled; may be null.
   */
  public static void ncdump(NetcdfFile nc, String command, Writer out, CancelTask ct) throws IOException {
    WantValues showValues = WantValues.none;

    Builder builder = builder(nc).setCancelTask(ct);

    if (command != null) {
      StringTokenizer stoke = new StringTokenizer(command);

      while (stoke.hasMoreTokens()) {
        String toke = stoke.nextToken();
        if (toke.equalsIgnoreCase("-help")) {
          out.write(usage);
          out.write('\n');
          return;
        }
        if (toke.equalsIgnoreCase("-vall")) {
          showValues = WantValues.all;
        }
        if (toke.equalsIgnoreCase("-c") && (showValues == WantValues.none)) {
          showValues = WantValues.coordsOnly;
        }
        if (toke.equalsIgnoreCase("-ncml")) {
          builder.setNcml(true);
        }
        if (toke.equalsIgnoreCase("-cdl") || toke.equalsIgnoreCase("-strict")) {
          builder.setStrict(true);
        }
        if (toke.equalsIgnoreCase("-v") && stoke.hasMoreTokens()) {
          builder.setVarNames(stoke.nextToken());
        }
        if (toke.equalsIgnoreCase("-datasetname") && stoke.hasMoreTokens()) {
          builder.setLocationName(stoke.nextToken());
        }
      }
    }
    builder.setWantValues(showValues);

    out.write(builder.build().print());
    out.flush();
  }

  ///////////////////////////////////////////////////////////////////////////////////////////////////////

  public static Builder builder(NetcdfFile ncfile) {
    return new Builder(ncfile);
  }

  public static class Builder {
    private NetcdfFile ncfile;
    private WantValues wantValues = WantValues.none;
    private boolean ncml;
    private boolean strict;
    private String varNames;
    @Nullable
    private String locationName; // NcML location attribute
    private CancelTask cancelTask;

    private Builder(NetcdfFile ncfile) {
      this.ncfile = ncfile;
    }

    /** show all Variable's values */
    public Builder setShowAllValues() {
      this.wantValues = WantValues.all;
      return this;
    }

    /** show Coordinate Variable's values only */
    public Builder setShowCoordValues() {
      this.wantValues = WantValues.coordsOnly;
      return this;
    }

    public Builder setLocationName(String locationName) {
      if (locationName != null && !locationName.isEmpty()) {
        this.locationName = locationName;
      }
      return this;
    }

    /** set what Variables' values you want output. */
    public Builder setWantValues(WantValues wantValues) {
      this.wantValues = wantValues;
      return this;
    }

    /** set true if outout should be ncml, otherwise CDL. */
    public Builder setNcml(boolean ncml) {
      this.ncml = ncml;
      return this;
    }

    /** strict CDL representation, default false */
    public Builder setStrict(boolean strict) {
      this.strict = strict;
      return this;
    }

    /**
     * @param varNames semicolon delimited list of variables whose data should be printed. May have
     *        Fortran90 like selector: eg varName(1:2,*,2)
     */
    public Builder setVarNames(String varNames) {
      this.varNames = varNames;
      return this;
    }

    /** allow task to be cancelled */
    public Builder setCancelTask(CancelTask cancelTask) {
      this.cancelTask = cancelTask;
      return this;
    }

    public Ncdump build() {
      return new Ncdump(this);
    }
  }

  private final NetcdfFile ncfile;
  private final WantValues wantValues;
  private final boolean ncml;
  private final boolean strict;
  private final String varNames;
  private final String locationName;
  private final CancelTask cancelTask;

  private Ncdump(Builder builder) {
    this.ncfile = builder.ncfile;
    this.wantValues = builder.wantValues;
    this.ncml = builder.ncml;
    this.strict = builder.strict;
    this.varNames = builder.varNames;
    this.locationName = builder.locationName;
    this.cancelTask = builder.cancelTask;
  }

  public String print() {
    boolean headerOnly = (wantValues == WantValues.none) && (varNames == null);
    Formatter out = new Formatter();

    try {
      if (ncml) {
        return writeNcml(ncfile, wantValues, locationName); // output schema in NcML
      } else if (headerOnly) {
        CDLWriter.writeCDL(ncfile, out, strict, locationName);
      } else {
        Indent indent = new Indent(2);
        CDLWriter cdlWriter = new CDLWriter(ncfile, out, strict);
        cdlWriter.toStringStart(indent, strict, locationName);

        indent.incr();
        out.format("%n%sdata:%n", indent);
        indent.incr();

        if (wantValues == WantValues.all) { // dump all data
          for (Variable v : ncfile.getVariables()) {
            printArray(out, v.read(), v.getFullName(), indent, cancelTask);
            if (cancelTask != null && cancelTask.isCancel())
              return out.toString();
          }
        } else if (wantValues == WantValues.coordsOnly) { // dump coordVars
          for (Variable v : ncfile.getVariables()) {
            if (v.isCoordinateVariable())
              printArray(out, v.read(), v.getFullName(), indent, cancelTask);
            if (cancelTask != null && cancelTask.isCancel())
              return out.toString();
          }
        }

        if ((wantValues != WantValues.all) && (varNames != null)) { // dump the list of variables
          StringTokenizer stoke = new StringTokenizer(varNames, ";");
          while (stoke.hasMoreTokens()) {
            String varSubset = stoke.nextToken(); // variable name and optionally a subset

            if (varSubset.indexOf('(') >= 0) { // has a selector
              Array data = ncfile.readSection(varSubset);
              printArray(out, data, varSubset, indent, cancelTask);

            } else { // do entire variable
              Variable v = ncfile.findVariable(varSubset);
              if (v == null) {
                out.format(" cant find variable: %s%n   %s", varSubset, usage);
                continue;
              }
              // dont print coord vars if they are already printed
              if ((wantValues != WantValues.coordsOnly) || v.isCoordinateVariable())
                printArray(out, v.read(), v.getFullName(), indent, cancelTask);
            }
            if (cancelTask != null && cancelTask.isCancel())
              return out.toString();
          }
        }

        indent.decr();
        indent.decr();
        cdlWriter.toStringEnd();
      }

    } catch (Exception e) {
      out.format("%n%s%n", e.getMessage());
    }

    return out.toString();
  }


  /**
   * Print all the data of the given Variable.
   *
   * @param v variable to print
   * @param ct allow task to be cancelled; may be null.
   * @return String result
   * @throws IOException on write error
   */
  public static String printVariableData(Variable v, CancelTask ct) throws IOException {
    Array data = v.read();
    Formatter out = new Formatter();
    printArray(out, data, v.getFullName(), new Indent(2), ct);
    return out.toString();
  }

  /**
   * Print a section of the data of the given Variable.
   *
   * @param v variable to print
   * @param sectionSpec string specification
   * @param ct allow task to be cancelled; may be null.
   * @return String result formatted data ouptut
   * @throws IOException on write error
   * @throws InvalidRangeException is specified section doesnt match variable shape
   */
  private static String printVariableDataSection(Variable v, String sectionSpec, CancelTask ct)
      throws IOException, InvalidRangeException {
    Array data = v.read(sectionSpec);

    Formatter out = new Formatter();
    printArray(out, data, v.getFullName(), new Indent(2), ct);
    return out.toString();
  }

  /**
   * Print array as undifferentiated sequence of values.
   *
   * @param ma any Array except ArrayStructure
   */
  public static String printArrayPlain(Array ma) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    ma.resetLocalIterator();
    while (ma.hasNext()) {
      pw.print(ma.next());
      pw.print(' ');
    }
    return sw.toString();
  }

  /** Print array to returned String. */
  public static String printArray(Array ma) {
    return printArray(ma, "", null);
  }

  /** Print named array to returned String. */
  public static String printArray(Array array, String name, CancelTask ct) {
    Formatter out = new Formatter();
    printArray(out, array, name, null, new Indent(2), ct, true);
    return out.toString();
  }

  private static void printArray(Formatter out, Array array, String name, Indent indent, CancelTask ct) {
    printArray(out, array, name, null, indent, ct, true);
    out.flush();
  }

  private static void printArray(Formatter out, Array array, String name, String units, Indent ilev, CancelTask ct,
      boolean printSeq) {
    if (ct != null && ct.isCancel())
      return;

    if (name != null)
      out.format("%s%s = ", ilev, name);
    ilev.incr();

    if (array == null) {
      out.format("null array for %s", name);
      ilev.decr();
      return;
    }

    if ((array instanceof ArrayChar) && (array.getRank() > 0)) {
      printStringArray(out, (ArrayChar) array, ilev, ct);

    } else if (array.getElementType() == String.class) {
      printStringArray(out, array, ilev, ct);

    } else if (array instanceof ArraySequence) {
      if (printSeq)
        printSequence(out, (ArraySequence) array, ilev, ct);

    } else if (array instanceof ArrayStructure) {
      printStructureDataArray(out, (ArrayStructure) array, ilev, ct);

    } else if (array.getElementType() == ByteBuffer.class) { // opaque type
      array.resetLocalIterator();
      while (array.hasNext()) {
        printByteBuffer(out, (ByteBuffer) array.next(), ilev);
        out.format("%s%n", array.hasNext() ? "," : ";"); // peek ahead
        if (ct != null && ct.isCancel())
          return;
      }
    } else if (array instanceof ArrayObject) {
      printVariableArray(out, (ArrayObject) array, ilev, ct);
    } else {
      printArray(out, array, ilev, ct);
    }

    if (units != null)
      out.format(" %s", units);
    out.format("%n");
    ilev.decr();
    out.flush();
  }

  private static void printArray(Formatter out, Array ma, Indent indent, CancelTask ct) {
    if (ct != null && ct.isCancel())
      return;

    int rank = ma.getRank();
    Index ima = ma.getIndex();

    // scalar
    if (rank == 0) {
      Object value = ma.getObject(ima);

      if (ma.isUnsigned()) {
        assert value instanceof Number : "A data type being unsigned implies that it is numeric.";

        // "value" is an unsigned number, but it will be treated as signed when we print it below, because Java only
        // has signed types. If it's large enough ( >= 2^(BIT_WIDTH-1) ), its most-significant bit will be interpreted
        // as the sign bit, which will result in an invalid (negative) value being printed. To prevent that, we're
        // going to widen the number before printing it, but only if the unsigned number is being seen as negative.
        value = DataType.widenNumberIfNegative((Number) value);
      }

      out.format("%s", value);
      return;
    }

    int[] dims = ma.getShape();
    int last = dims[0];

    out.format("%n%s{", indent);

    if ((rank == 1) && (ma.getElementType() != StructureData.class)) {
      for (int ii = 0; ii < last; ii++) {
        Object value = ma.getObject(ima.set(ii));

        if (ma.isUnsigned()) {
          assert value instanceof Number : "A data type being unsigned implies that it is numeric.";
          value = DataType.widenNumberIfNegative((Number) value);
        }

        if (ii > 0)
          out.format(", ");
        out.format("%s", value);
        if (ct != null && ct.isCancel())
          return;
      }
      out.format("}");
      return;
    }

    indent.incr();
    for (int ii = 0; ii < last; ii++) {
      Array slice = ma.slice(0, ii);
      if (ii > 0)
        out.format(",");
      printArray(out, slice, indent, ct);
      if (ct != null && ct.isCancel())
        return;
    }
    indent.decr();

    out.format("%n%s}", indent);
  }

  private static void printStringArray(Formatter out, ArrayChar ma, Indent indent, CancelTask ct) {
    if (ct != null && ct.isCancel())
      return;

    int rank = ma.getRank();

    if (rank == 1) {
      out.format("  \"%s\"", ma.getString());
      return;
    }

    if (rank == 2) {
      boolean first = true;
      ArrayChar.StringIterator iter = ma.getStringIterator();
      while (iter.hasNext()) {
        if (!first)
          out.format(", ");
        out.format("  \"%s\"", iter.next());
        first = false;
        if (ct != null && ct.isCancel())
          return;
      }
      return;
    }

    int[] dims = ma.getShape();
    int last = dims[0];

    out.format("%n%s{", indent);
    indent.incr();
    for (int ii = 0; ii < last; ii++) {
      ArrayChar slice = (ArrayChar) ma.slice(0, ii);
      if (ii > 0)
        out.format(",");
      printStringArray(out, slice, indent, ct);
      if (ct != null && ct.isCancel())
        return;
    }
    indent.decr();

    out.format("%n%s}", indent);
  }

  private static void printByteBuffer(Formatter out, ByteBuffer bb, Indent indent) {
    out.format("%s0x", indent);
    int last = bb.limit() - 1;
    if (last < 0)
      out.format("00");
    else
      for (int i = bb.position(); i <= last; i++) {
        out.format("%02x", bb.get(i));
      }
  }

  private static void printStringArray(Formatter out, Array ma, Indent indent, CancelTask ct) {
    if (ct != null && ct.isCancel())
      return;

    int rank = ma.getRank();
    Index ima = ma.getIndex();

    if (rank == 0) {
      out.format("  \"%s\"", ma.getObject(ima));
      return;
    }

    if (rank == 1) {
      boolean first = true;
      for (int i = 0; i < ma.getSize(); i++) {
        if (!first)
          out.format(", ");
        out.format("  \"%s\"", ma.getObject(ima.set(i)));
        first = false;
      }
      return;
    }

    int[] dims = ma.getShape();
    int last = dims[0];

    out.format("%n%s{", indent);
    indent.incr();
    for (int ii = 0; ii < last; ii++) {
      Array slice = (Array) ma.slice(0, ii); // replaces ArrayObject slice = (ArrayObject)o; because ArrayObject is
                                             // over-casting because printStringArray takes Array type as argument.
      if (ii > 0)
        out.format(",");
      printStringArray(out, slice, indent, ct);
    }
    indent.decr();
    out.format("%n%s}", indent);
  }

  private static void printStructureDataArray(Formatter out, ArrayStructure array, Indent indent, CancelTask ct) {
    try (StructureDataIterator sdataIter = array.getStructureDataIterator()) {
      int count = 0;
      while (sdataIter.hasNext()) {
        StructureData sdata = sdataIter.next();
        out.format("%n%s{", indent);
        printStructureData(out, sdata, indent, ct);
        out.format("%s} %s(%d)", indent, sdata.getName(), count);
        if (ct != null && ct.isCancel())
          return;
        count++;
      }
    } catch (IOException ioe) {
      out.format("%n%s%n", ioe.getMessage());
    }
  }

  private static void printVariableArray(Formatter out, ArrayObject array, Indent indent, CancelTask ct) {
    out.format("%n%s{", indent);
    indent.incr();
    IndexIterator iter = array.getIndexIterator();
    boolean first = true;
    while (iter.hasNext()) {
      Array data = (Array) iter.next();
      if (!first) {
        out.format(", ");
      }
      printArray(out, data, indent, ct);
      first = false;
    }
    indent.decr();
    out.format("%n%s}", indent);
  }

  private static void printSequence(Formatter out, ArraySequence seq, Indent indent, CancelTask ct) {
    try (StructureDataIterator iter = seq.getStructureDataIterator()) {
      while (iter.hasNext()) {
        StructureData sdata = iter.next();
        out.format("%n%s{", indent);
        printStructureData(out, sdata, indent, ct);
        out.format("%s} %s", indent, sdata.getName());
        if (ct != null && ct.isCancel())
          return;
      }
    } catch (IOException ioe) {
      out.format("%n%s%n", ioe.getMessage());
    }
  }

  /** Print StructureData to returned String. */
  public static String printStructureData(StructureData sdata) {
    Formatter out = new Formatter();
    for (StructureMembers.Member m : sdata.getMembers()) {
      Array memData = sdata.getArray(m);
      if (memData instanceof ArrayChar) {
        out.format("%s", ((ArrayChar) memData).getString());
      } else {
        printArray(out, memData, null, null, new Indent(2), null, true);
      }
      out.format(",");
    }
    return out.toString();
  }

  private static void printStructureData(Formatter out, StructureData sdata, Indent indent, CancelTask ct) {
    indent.incr();
    for (StructureMembers.Member m : sdata.getMembers()) {
      Array sdataArray = sdata.getArray(m);
      printArray(out, sdataArray, m.getName(), m.getUnitsString(), indent, ct, true);
      if (ct != null && ct.isCancel())
        return;
    }
    indent.decr();
  }

  //////////////////////////////////////////////////////////////////////////////////////
  // standard NCML writing.

  /**
   * Write the NcML representation for a file.
   * Note that ucar.nc2.dataset.NcMLWriter has a JDOM implementation, for complete NcML.
   * This method implements only the "core" NcML for plain ole netcdf files.
   *
   * @param ncfile write NcML for this file
   * @param showValues do you want the variable values printed?
   * @param url use this for the url attribute; if null use getLocation(). // ??
   */
  private static String writeNcml(NetcdfFile ncfile, WantValues showValues, @Nullable String url) {
    Preconditions.checkNotNull(ncfile);
    Preconditions.checkNotNull(showValues);

    Predicate writeVarsPred;
    switch (showValues) {
      case none:
        writeVarsPred = NcmlWriter.writeNoVariablesPredicate;
        break;
      case coordsOnly:
        writeVarsPred = NcmlWriter.writeCoordinateVariablesPredicate;
        break;
      case all:
        writeVarsPred = NcmlWriter.writeAllVariablesPredicate;
        break;
      default:
        String message =
            String.format("CAN'T HAPPEN: showValues (%s) != null and checked all possible enum values.", showValues);
        throw new AssertionError(message);
    }

    NcmlWriter ncmlWriter = new NcmlWriter(null, null, writeVarsPred);
    Element netcdfElement = ncmlWriter.makeNetcdfElement(ncfile, url);
    return ncmlWriter.writeToString(netcdfElement);
  }

  ////////////////////////////////////////////////////////////////////////////////////////////
  // TODO use jcommander?
  private static String usage =
      "usage: Ncdump  [-cdl | -ncml] [-c | -vall] [-v varName1;varName2;..] [-v varName(0:1,:,12)]\n";

  /**
   * Main program.
   * 

* ucar.nc2.NCdumpW filename [-cdl | -ncml] [-c | -vall] [-v varName1;varName2;..] [-v varName(0:1,:,12)] * *

* where: *

    *
  • filename : path of any CDM readable file *
  • cdl or ncml: output format is CDL or NcML *
  • -vall : dump all variable data *
  • -c : dump coordinate variable data *
  • -v varName1;varName2; : dump specified variable(s) *
  • -v varName(0:1,:,12) : dump specified variable section *
* Default is to dump the header info only. * * @param args arguments */ public static void main(String[] args) { if (args.length == 0) { System.out.println(usage); return; } // pull out the filename from the command String filename = args[0]; try (Writer writer = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); NetcdfFile nc = NetcdfDatasets.openFile(filename, null)) { // the rest of the command StringBuilder command = new StringBuilder(); for (int i = 1; i < args.length; i++) { command.append(args[i]); command.append(" "); } ncdump(nc, command.toString(), writer, null); } catch (IOException ioe) { ioe.printStackTrace(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy