ref #7549 Implement historgram tooltip for categorical data
authorPatrick Plitzner <p.plitzner@bgbm.org>
Thu, 12 Jul 2018 10:45:16 +0000 (12:45 +0200)
committerPatrick Plitzner <p.plitzner@bgbm.org>
Thu, 12 Jul 2018 10:45:16 +0000 (12:45 +0200)
eu.etaxonomy.taxeditor.cdmlib/.classpath
eu.etaxonomy.taxeditor.cdmlib/META-INF/MANIFEST.MF
eu.etaxonomy.taxeditor.cdmlib/build.properties
eu.etaxonomy.taxeditor.cdmlib/lib/org.swtchart_0.10.0.v20160212.jar [new file with mode: 0644]
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/CategoricalDataHistogram.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/CharacterMatrix.java
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/CharacterMatrixBottomToolbar.java
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/ChartTooltip.java [new file with mode: 0644]

index f39e559..c03a7c6 100644 (file)
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
+       <classpathentry exported="true" kind="lib" path="lib/org.swtchart_0.10.0.v20160212.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/redmine-java-api-3.1.0.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry exported="true" kind="lib" path="lib/aspectjrt-1.8.8.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/aspectjweaver-1.8.8.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/batik-xml-1.7.jar"/>
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-commons-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-ext-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-io-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-model-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-persistence-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-print-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-remote-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-cache-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-services-5.2.0-SNAPSHOT.jar"/>\r
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-test-5.2.0-SNAPSHOT.jar"/>\r
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-commons-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-ext-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-io-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-model-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-persistence-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-print-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-remote-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-cache-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-services-5.2.0-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-test-5.2.0-SNAPSHOT.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/com.springsource.org.aopalliance-1.0.0.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/com.springsource.org.apache.commons.logging-1.1.1.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/commons-beanutils-1.9.2.jar"/>
index ff32952..50d2891 100644 (file)
@@ -635,7 +635,8 @@ Export-Package: com.google.api,
    org.springframework.context.event,
    javax.servlet,
    org.springframework.ui,
-   javax.servlet.http"
+   javax.servlet.http",
+ org.swtchart
 Bundle-Vendor: EDIT
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Require-Bundle: org.eclipse.osgi,
@@ -867,5 +868,6 @@ Bundle-ClassPath: .,
  lib/lucene-analyzers-common-5.4.1.jar,
  lib/lucene-sandbox-5.4.1.jar,
  lib/lucene-suggest-5.4.1.jar,
- lib/redmine-java-api-3.1.0.jar
+ lib/redmine-java-api-3.1.0.jar,
+ lib/org.swtchart_0.10.0.v20160212.jar
 Import-Package: eu.etaxonomy.cdm.api.application
index 72a7a58..0ece366 100644 (file)
@@ -222,7 +222,8 @@ bin.includes = META-INF/,\
                lib/lucene-sandbox-5.4.1.jar,\
                lib/lucene-suggest-5.4.1.jar,\
                lib/cdmlib-cache-5.2.0-SNAPSHOT.jar,\
-               lib/redmine-java-api-3.1.0.jar
+               lib/redmine-java-api-3.1.0.jar,\
+               lib/org.swtchart_0.10.0.v20160212.jar
 
 jars.compile.order = .
 output.. = bin/
diff --git a/eu.etaxonomy.taxeditor.cdmlib/lib/org.swtchart_0.10.0.v20160212.jar b/eu.etaxonomy.taxeditor.cdmlib/lib/org.swtchart_0.10.0.v20160212.jar
new file mode 100644 (file)
index 0000000..51c69dc
Binary files /dev/null and b/eu.etaxonomy.taxeditor.cdmlib/lib/org.swtchart_0.10.0.v20160212.jar differ
diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/CategoricalDataHistogram.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/CategoricalDataHistogram.java
new file mode 100644 (file)
index 0000000..5a25d8c
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+* Copyright (C) 2018 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.taxeditor.editor.descriptiveDataSet.matrix;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.State;
+
+/**
+ * @author pplitzner
+ * @since Jul 12, 2018
+ *
+ */
+public class CategoricalDataHistogram {
+
+    private Feature feature;
+    private Map<State, Integer> stateCountMap;
+
+    public CategoricalDataHistogram(Feature feature) {
+        this.feature = feature;
+        this.stateCountMap = new HashMap<>();
+        feature.getSupportedCategoricalEnumerations()
+        .forEach(voc->voc.getTerms()
+                .forEach(state->stateCountMap.put(state, 0)));
+    }
+
+    public void addState(State state){
+        Integer integer = stateCountMap.get(state);
+        stateCountMap.put(state, integer+1);
+    }
+
+    public Map<State, Integer> getStateCountMap() {
+        return stateCountMap;
+    }
+
+    public Feature getFeature() {
+        return feature;
+    }
+}
index 397ec94..5adb931 100644 (file)
@@ -63,6 +63,7 @@ import org.eclipse.nebula.widgets.nattable.summaryrow.FixedSummaryRowLayer;
 import org.eclipse.nebula.widgets.nattable.summaryrow.SummaryRowLayer;
 import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;
 import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.ui.menu.DebugMenuConfiguration;
 import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -121,10 +122,13 @@ public class CharacterMatrix extends Composite {
 
     private LinkedMap<String, String> propertyToLabelMap = new LinkedMap<>();
 
+    //FIXME use more concrete generic
     private EventList<Object> descriptions;
 
     private Collection<SpecimenNodeWrapper> specimenCache = null;
 
+    private Map<Feature, CategoricalDataHistogram> featureToHistogramMap = new HashMap<>();
+
     private ListDataProvider<Object> bodyDataProvider;
 
     private FreezeLayer freezeLayer;
@@ -221,6 +225,10 @@ public class CharacterMatrix extends Composite {
         //initial freeze of supplemental columns
         freezeSupplementalColumns(true);
 
+
+        //add tooltip to table
+        new ChartTooltip(this);
+
         this.layout();
     }
 
@@ -586,4 +594,8 @@ public class CharacterMatrix extends Composite {
         part.addUpdateResult(result);
     }
 
+    public Map<Feature, CategoricalDataHistogram> getFeatureToHistogramMap() {
+        return featureToHistogramMap;
+    }
+
 }
index af121a2..3069e41 100644 (file)
@@ -130,6 +130,7 @@ public class CharacterMatrixBottomToolbar extends Composite{
         Button btnAggregate = new Button(this, SWT.PUSH);
         btnAggregate.setText("Aggregate");
         btnAggregate.addSelectionListener(new SelectionAdapter() {
+            @SuppressWarnings("unchecked")
             @Override
             public void widgetSelected(SelectionEvent e) {
                 Set<TaxonNode> taxonSubtreeFilter = matrix.getDescriptiveDataSet().getTaxonSubtreeFilter();
@@ -157,9 +158,25 @@ public class CharacterMatrixBottomToolbar extends Composite{
                 matrix.addUpdateResult(result);
                 matrix.setDirty();
 
+                //aggregate histogram for categorical tooltip
+                Map<Feature, CategoricalDataHistogram> featureToHistogramMap = matrix.getFeatureToHistogramMap();
+                featureToHistogramMap.clear();
+                // matrix.getFeatures().forEach(feature->{
+                matrix.getDescriptions()
+                        .forEach(o -> ((RowWrapperDTO) o).getSpecimenDescription().getElements().stream()
+                                .filter(descriptionElement -> descriptionElement instanceof CategoricalData)
+                                .forEach(categoricalData -> {
+                                    Feature feature = ((CategoricalData) categoricalData).getFeature();
+                                    CategoricalDataHistogram dataHistogram = featureToHistogramMap.get(feature);
+                                    if(dataHistogram==null){
+                                        dataHistogram = new CategoricalDataHistogram(feature);
+                                    }
+                                    featureToHistogramMap.put(feature, dataHistogram);
+                                    ((CategoricalData) categoricalData).getStateData()
+                                            .forEach(stateData -> featureToHistogramMap.get(feature).addState(stateData.getState()));
+                                }));
             }
         });
-
     }
     private SpecimenDescription getDescriptionForDescriptiveDataSet(SpecimenOrObservationBase specimen){
         Set<Feature> wsFeatures = matrix.getDescriptiveDataSet().getDescriptiveSystem().getDistinctFeatures();
diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/ChartTooltip.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/descriptiveDataSet/matrix/ChartTooltip.java
new file mode 100644 (file)
index 0000000..fc16426
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+* Copyright (C) 2018 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.taxeditor.editor.descriptiveDataSet.matrix;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.jface.window.ToolTip;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.swtchart.Chart;
+import org.swtchart.IAxis;
+import org.swtchart.IBarSeries;
+import org.swtchart.ISeries.SeriesType;
+import org.swtchart.ISeriesLabel;
+
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.State;
+
+/**
+ * @author pplitzner
+ * @since Jul 6, 2018
+ *
+ */
+public class ChartTooltip extends ToolTip {
+
+    private CharacterMatrix matrix;
+
+    public ChartTooltip(CharacterMatrix matrix) {
+        super(matrix.getNatTable(), ToolTip.NO_RECREATE, false);
+        this.matrix = matrix;
+}
+
+    @Override
+    protected Object getToolTipArea(Event event) {
+        int col = matrix.getNatTable().getColumnPositionByX(event.x);
+        int row = matrix.getNatTable().getRowPositionByY(event.y);
+        return new Point(col, row);
+    }
+
+    @Override
+    protected Composite createToolTipContentArea(Event event,
+            Composite parent) {
+        parent.setLayout(new GridLayout());
+
+        Chart chart = new Chart(parent, SWT.NONE);
+        chart.setLayoutData(new GridData(600, 300));
+
+        int colPos = matrix.getNatTable().getColumnPositionByX(event.x);
+        int colIndex = matrix.getNatTable().getColumnIndexByPosition(colPos);
+        Feature feature = matrix.getIndexToFeatureMap().get(colIndex);
+        CategoricalDataHistogram histogram = matrix.getFeatureToHistogramMap().get(feature);
+
+        // set titles
+        chart.getTitle().setText(feature.getLabel());
+        IAxis xAxis = chart.getAxisSet().getXAxis(0);
+        Map<State, Integer> stateCountMap = histogram.getStateCountMap();
+        String[] states = new String[stateCountMap.size()];
+        double[] counts = new double[stateCountMap.size()];
+        int index = 0;
+        for(Iterator<Entry<State, Integer>> iterator = histogram.getStateCountMap().entrySet().iterator();iterator.hasNext();){
+            Entry<State, Integer> entry = iterator.next();
+            states[index] = entry.getKey().getLabel();
+            counts[index] = entry.getValue();
+            index++;
+        }
+        xAxis.setCategorySeries(states);
+        xAxis.enableCategory(true);
+        xAxis.getTitle().setVisible(false);
+        xAxis.getTick().setTickLabelAngle(25);
+
+        IAxis yAxis = chart.getAxisSet().getYAxis(0);
+        yAxis.getTitle().setVisible(false);
+//        yAxis.getTick().setVisible(false);
+
+//        chart.setOrientation(SWT.VERTICAL);
+        chart.getLegend().setVisible(false);
+
+        // create bar series
+        IBarSeries barSeries = (IBarSeries) chart.getSeriesSet()
+            .createSeries(SeriesType.BAR, "state counts");
+        ISeriesLabel seriesLabel = barSeries.getLabel();
+        seriesLabel.setFormat("##.0");
+        seriesLabel.setVisible(true);
+        barSeries.setYSeries(counts);
+
+        // adjust the axis range
+        chart.getAxisSet().adjustRange();
+
+        return chart;
+    }
+
+    @Override
+    protected boolean shouldCreateToolTip(Event event) {
+        //Differentiation between "index" and "position" is important here
+        //"position" is the current visible index
+        //"index" refers to the underlying data model
+        int colPos = matrix.getNatTable().getColumnPositionByX(event.x);
+        int colIndex = matrix.getNatTable().getColumnIndexByPosition(colPos);
+        int rowPos = matrix.getNatTable().getRowPositionByY(event.y);
+        Feature feature = matrix.getIndexToFeatureMap().get(colIndex);
+        CategoricalDataHistogram histogram = matrix.getFeatureToHistogramMap().get(feature);
+
+        if(rowPos==1
+                && colIndex>=CharacterMatrix.LEADING_COLUMN_COUNT
+                && histogram!=null
+                && !histogram.getStateCountMap().isEmpty()) {
+            return true;
+        }
+        return false;
+    }
+
+}