Repository: jtablesaw/tablesaw
Pull request #938 (2021-05-18 12:56:03)

Add support for contour plots
Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

Support drawing contour plots in #508 by adding two files: `ContourPlot.java` and `ContourTrace.java`

## Testing

Add three tests in `ContourTest.java`
1. `testAsJavascript` for `asJavascript` method in `ContourTrace.java`
2. `testContourTrace` for `ContourTrace.java` and draw a contour plot
3. `testContourPlot` for `create` method in `ContourPlot.java` and draw a contour plot



Patch:
diff --git a/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriteOptions.java b/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriteOptions.java
index d541789b..65523ce5 100644
--- a/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriteOptions.java
+++ b/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriteOptions.java
@@ -217,7 +217,7 @@ public class FixedWidthWriteOptions extends WriteOptions {
       return this;
     }
 
-    public FixedWidthWriteOptions.Builder header(FixedWidthFields columnSpecs) {
+    public FixedWidthWriteOptions.Builder columnSpecs(FixedWidthFields columnSpecs) {
       this.columnSpecs = columnSpecs;
       return this;
     }
diff --git a/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriter.java b/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriter.java
index 99eda589..34e1f34e 100644
--- a/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriter.java
+++ b/core/src/main/java/tech/tablesaw/io/fixed/FixedWidthWriter.java
@@ -97,6 +97,9 @@ public final class FixedWidthWriter implements DataWriter<FixedWidthWriteOptions
 
   protected FixedWidthWriterSettings fixedWidthWriterSettings(FixedWidthWriteOptions options) {
     FixedWidthWriterSettings settings = new FixedWidthWriterSettings();
+    if (options.columnSpecs() != null) {
+      settings = new FixedWidthWriterSettings(options.columnSpecs());
+    }
 
     if (options.autoConfigurationEnabled()) {
       settings.setAutoConfigurationEnabled(options.autoConfigurationEnabled());
diff --git a/jsplot/src/main/java/tech/tablesaw/plotly/api/ContourPlot.java b/jsplot/src/main/java/tech/tablesaw/plotly/api/ContourPlot.java
new file mode 100644
index 00000000..7f52b6c3
--- /dev/null
+++ b/jsplot/src/main/java/tech/tablesaw/plotly/api/ContourPlot.java
@@ -0,0 +1,32 @@
+package tech.tablesaw.plotly.api;
+
+import tech.tablesaw.api.Table;
+import tech.tablesaw.columns.Column;
+import tech.tablesaw.plotly.components.Figure;
+import tech.tablesaw.plotly.components.Layout;
+import tech.tablesaw.plotly.traces.ContourTrace;
+import tech.tablesaw.util.DoubleArrays;
+
+import java.util.List;
+
+public class ContourPlot {
+
+  private ContourPlot() {
+  }
+
+  public static Figure create(String title, Table table, String categoryCol1, String categoryCol2) {
+    Layout layout = Layout.builder(title).build();
+
+    Table counts = table.xTabCounts(categoryCol1, categoryCol2);
+    counts = counts.dropRows(counts.rowCount() - 1);
+    List<Column<?>> columns = counts.columns();
+    columns.remove(counts.columnCount() - 1);
+    Column<?> yColumn = columns.remove(0);
+    double[][] z = DoubleArrays.to2dArray(counts.numericColumns());
+
+    Object[] x = counts.columnNames().toArray();
+    Object[] y = yColumn.asObjectArray();
+    ContourTrace trace = ContourTrace.builder(x, y, z).build();
+    return new Figure(layout, trace);
+  }
+}
diff --git a/jsplot/src/main/java/tech/tablesaw/plotly/traces/ContourTrace.java b/jsplot/src/main/java/tech/tablesaw/plotly/traces/ContourTrace.java
new file mode 100644
index 00000000..aebee67c
--- /dev/null
+++ b/jsplot/src/main/java/tech/tablesaw/plotly/traces/ContourTrace.java
@@ -0,0 +1,95 @@
+package tech.tablesaw.plotly.traces;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.template.PebbleTemplate;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Map;
+
+import static tech.tablesaw.plotly.Utils.dataAsString;
+
+public class ContourTrace extends AbstractTrace {
+
+  private final Object[] x;
+  private final Object[] y;
+  private final double[][] z;
+  private final String type;
+
+  public ContourTrace(ContourBuilder builder) {
+    super(builder);
+    this.x = builder.x;
+    this.y = builder.y;
+    this.z = builder.z;
+    this.type = builder.getType();
+  }
+
+  @Override
+  public String asJavascript(int i) {
+    Writer writer = new StringWriter();
+    PebbleTemplate compiledTemplate;
+
+    try {
+      compiledTemplate = engine.getTemplate("trace_template.html");
+      compiledTemplate.evaluate(writer, getContext());
+    } catch (PebbleException e) {
+      throw new IllegalStateException(e);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+    return writer.toString();
+  }
+
+  @Override
+  protected Map<String, Object> getContext() {
+
+    Map<String, Object> context = super.getContext();
+    context.put("variableName", "trace0");
+    context.put("x", dataAsString(x));
+    context.put("y", dataAsString(y));
+    context.put("z", dataAsString(z));
+    context.put("type", type);
+    return context;
+  }
+
+  public static ContourTrace.ContourBuilder builder(Object[] x, Object[] y, double[][] z) {
+    return new ContourTrace.ContourBuilder(x, y, z);
+  }
+
+  public static class ContourBuilder extends TraceBuilder {
+
+    private static final String type = "contour";
+    private final Object[] x;
+    private final Object[] y;
+    private final double[][] z;
+
+    ContourBuilder(Object[] x, Object[] y, double[][] z) {
+      this.x = x;
+      this.y = y;
+      this.z = z;
+    }
+
+    @Override
+    public ContourTrace.ContourBuilder xAxis(String xAxis) {
+      super.xAxis(xAxis);
+      return this;
+    }
+
+    @Override
+    public ContourTrace.ContourBuilder yAxis(String yAxis) {
+      super.yAxis(yAxis);
+      return this;
+    }
+
+    public ContourTrace build() {
+      return new ContourTrace(this);
+    }
+
+    @Override
+    protected String getType() {
+      return type;
+    }
+  }
+}


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/io/fixed/FixedWidthWriterTest.java b/core/src/test/java/tech/tablesaw/io/fixed/FixedWidthWriterTest.java
new file mode 100644
index 00000000..f2d30336
--- /dev/null
+++ b/core/src/test/java/tech/tablesaw/io/fixed/FixedWidthWriterTest.java
@@ -0,0 +1,46 @@
+package tech.tablesaw.io.fixed;
+
+import static java.lang.Double.NaN;
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.univocity.parsers.fixed.FixedWidthFields;
+import java.io.*;
+import org.junit.jupiter.api.Test;
+import tech.tablesaw.api.DoubleColumn;
+import tech.tablesaw.api.Table;
+
+public class FixedWidthWriterTest {
+  private static final String LINE_END = System.lineSeparator();
+
+  private double[] v1 = {1, 2, NaN};
+  private double[] v2 = {1, 2, NaN};
+  private Table table =
+      Table.create("t", DoubleColumn.create("v1", v1), DoubleColumn.create("v2", v2));
+
+  @Test
+  public void testOutputInFixedWidthFormat() {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    FixedWidthFields fwf = new FixedWidthFields(10, 10);
+    FixedWidthWriteOptions options =
+        new FixedWidthWriteOptions.Builder(baos)
+            .header(true)
+            .columnSpecs(fwf)
+            .autoConfigurationEnabled(false)
+            .build();
+    FixedWidthWriter writer = new FixedWidthWriter();
+    writer.write(table, options);
+
+    String output = baos.toString();
+    assertEquals(
+        "v1________v2________"
+            + LINE_END
+            + "1.0_______1.0_______"
+            + LINE_END
+            + "2.0_______2.0_______"
+            + LINE_END
+            + "____________________"
+            + LINE_END
+            + "",
+        output);
+  }
+}
diff --git a/jsplot/src/test/java/tech/tablesaw/plotly/ContourTest.java b/jsplot/src/test/java/tech/tablesaw/plotly/ContourTest.java
new file mode 100644
index 00000000..f4a4e585
--- /dev/null
+++ b/jsplot/src/test/java/tech/tablesaw/plotly/ContourTest.java
@@ -0,0 +1,50 @@
+package tech.tablesaw.plotly;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import tech.tablesaw.api.StringColumn;
+import tech.tablesaw.api.Table;
+import tech.tablesaw.plotly.api.ContourPlot;
+import tech.tablesaw.plotly.components.Figure;
+import tech.tablesaw.plotly.traces.ContourTrace;
+
+import java.io.IOException;
+
+@Disabled
+public class ContourTest {
+
+  private final Object[] x = {-9, -6, -5, -3, -1};
+  private final Object[] y = {0, 1, 4, 5, 7};
+  private final double[][] z = {
+      {10, 10.625, 12.5, 15.625, 20},
+      {5.625, 6.25, 8.125, 11.25, 15.625},
+      {2.5, 3.125, 5.0, 8.125, 12.5},
+      {0.625, 1.25, 3.125, 6.25, 10.625},
+      {0, 0.625, 2.5, 5.625, 10}
+  };
+
+  @Test
+  public void testAsJavascript() {
+    ContourTrace trace = ContourTrace.builder(x, y, z).build();
+    System.out.println(trace.asJavascript(1));
+  }
+
+  @Test
+  public void testContourTrace() {
+    ContourTrace trace = ContourTrace.builder(x, y, z).build();
+    Figure figure = new Figure(trace);
+    Plot.show(figure);
+  }
+
+  @Test
+  public void testContourPlot() throws IOException {
+    Table table = Table.read().csv("../data/bush.csv");
+    StringColumn yearsMonth = table.dateColumn("date").yearMonth();
+    String name = "Year and month";
+    yearsMonth.setName(name);
+    table.addColumns(yearsMonth);
+
+    Figure figure = ContourPlot.create("Polls conducted by year and month", table, name, "who");
+    Plot.show(figure);
+  }
+}