[Schmitzm-commits] r284 - in trunk/src/schmitzm/jfree: . feature/style
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Mon Aug 3 12:49:19 CEST 2009
Author: alfonx
Date: 2009-08-03 12:49:18 +0200 (Mon, 03 Aug 2009)
New Revision: 284
Modified:
trunk/src/schmitzm/jfree/JFreeChartUtil.java
trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java
Log:
* FeatureChartUtil now supports normalization of numeric data on DOMAIN and/or series data. * This extends the dependency of SCHMITZM to include colt.jar (which can be found in Atlas-Framework/lib)
Modified: trunk/src/schmitzm/jfree/JFreeChartUtil.java
===================================================================
--- trunk/src/schmitzm/jfree/JFreeChartUtil.java 2009-08-02 23:16:13 UTC (rev 283)
+++ trunk/src/schmitzm/jfree/JFreeChartUtil.java 2009-08-03 10:49:18 UTC (rev 284)
@@ -436,12 +436,18 @@
renderer.setURLGenerator(urlGenerator);
plot.setRenderer(renderer);
plot.setOrientation(PlotOrientation.VERTICAL);
-
- // Regression line
- if ( regressionLine ) {
- // create sample data for curve (plot function directly is not yet available)
- XYDataset regressionData = createRegressionLineDataset((XYSeriesCollection)dataset,0,title+" (RegressionLine)",2);
- addRegressionLineToPlot(plot, regressionData, Color.blue);
+
+ // Create Regression line
+ if ( regressionLine) {
+
+ // only if there are at least 2 items, otherwise we would get a IllegalArgumentException
+ if (((XYSeriesCollection)dataset).getItemCount(0)>=2) {
+ // create sample data for curve (plot function directly is not yet available)
+ XYDataset regressionData = createRegressionLineDataset((XYSeriesCollection)dataset,0,title+" (RegressionLine)",2);
+ addRegressionLineToPlot(plot, regressionData, Color.blue);
+ } else {
+ LOGGER.info("Not enought items in the series to create a regression line.");
+ }
}
JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
Modified: trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java 2009-08-02 23:16:13 UTC (rev 283)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java 2009-08-03 10:49:18 UTC (rev 284)
@@ -29,14 +29,20 @@
******************************************************************************/
package schmitzm.jfree.feature.style;
+import hep.aida.bin.DynamicBin1D;
+import hep.aida.bin.StaticBin1D;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
+import org.apache.log4j.Logger;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
@@ -51,10 +57,13 @@
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
+import com.sun.xml.internal.xsom.util.DomAnnotationParserFactory;
+
import schmitzm.geotools.feature.FeatureUtil;
import schmitzm.geotools.feature.PipedFeatureIterator;
import schmitzm.jfree.chart.selection.DatasetSelectionModel;
import schmitzm.jfree.chart.selection.DatasetSelectionModelProvider;
+import schmitzm.jfree.chart.style.ChartStyle;
import schmitzm.jfree.chart.style.ChartStyleXMLFactory;
import schmitzm.jfree.feature.Feature2SeriesDatasetMapping;
import schmitzm.jfree.feature.FeatureDatasetMetaData;
@@ -63,254 +72,392 @@
/**
* This class contains static utility methods related to chart styles based on
* {@link FeatureCollection}.
+ *
* @author <a href="mailto:Martin.Schmitz at koeln.de">Martin Schmitz</a>
* @version 1.0
*/
public class FeatureChartUtil {
- /** Instance of {@link ChartStyleXMLFactory}. */
- public static final FeatureChartStyleXMLFactory FEATURE_CHART_STYLE_FACTORY = new FeatureChartStyleXMLFactory();
+ final static Logger LOGGER = Logger.getLogger(FeatureChartUtil.class);
+
+ /** Instance of {@link ChartStyleXMLFactory}. */
+ public static final FeatureChartStyleXMLFactory FEATURE_CHART_STYLE_FACTORY = new FeatureChartStyleXMLFactory();
- /**
- * Returns all {@link DatasetSelectionModel DatasetSelectionModels} that can be reached
- * via the renderers of a chart.
- * @param chart a chart
- */
- public static List<FeatureDatasetSelectionModel<?>> getFeatureDatasetSelectionModelFor(JFreeChart chart) {
- ArrayList<FeatureDatasetSelectionModel<?>> renderers = new ArrayList<FeatureDatasetSelectionModel<?>>();
-
- Plot plot = chart.getPlot();
-
- /** Collect renderers from XYPlot */
- if (plot instanceof XYPlot) {
- XYPlot xyplot = (XYPlot)plot;
+ /**
+ * Holds the statistics needed to normalize the attribute values. Key =
+ * attributeName
+ */
+ private static HashMap<String,StaticBin1D> attribStats = new HashMap<String, StaticBin1D>();
- for (int i = 0; i < xyplot.getRendererCount(); i++) {
- XYItemRenderer renderer = xyplot.getRenderer(i);
- if (renderer instanceof DatasetSelectionModelProvider &&
- ((DatasetSelectionModelProvider)renderer).getSelectionModel() instanceof FeatureDatasetSelectionModel){
- renderers.add( (FeatureDatasetSelectionModel)((DatasetSelectionModelProvider)renderer).getSelectionModel());
- }
- }
- }
-
- /** Collect renderers from CategoryPlot */
- if (plot instanceof CategoryPlot) {
- CategoryPlot catPlot = (CategoryPlot)plot;
-
- for (int i = 0; i < catPlot.getRendererCount(); i++) {
- CategoryItemRenderer renderer = catPlot.getRenderer(i);
- if (renderer instanceof DatasetSelectionModelProvider &&
- ((DatasetSelectionModelProvider)renderer).getSelectionModel() instanceof FeatureDatasetSelectionModel){
- renderers.add( (FeatureDatasetSelectionModel)((DatasetSelectionModelProvider)renderer).getSelectionModel());
- }
- }
- }
-
- return renderers;
- }
-
- /**
- * Creates a {@link Dataset} for 1 or more attributes of a {@link FeatureCollection}.
- * According to the feature attribute type the method decides whether a {@link XYDataset}
- * or a {@link CategoryDataset} is created:<br>
- * In case of an non-numeric X attribute a {@link CategoryDataset} is created always.
- * Otherwise the default is to create a {@link XYDataset}. The flag {@code forceCat}
- * can be used to create {@link CategoryDataset} for numeric X attributes.
- * @param fc a {@link FeatureCollection}
- * @param forceCat forces a {@link CategoryDataset} also for numeric X attributes
- * @param sort sorts the features according to {@code xAttr} before creating
- * the dataset
- * @param xAttr feature attribute used for the X-value
- * @param yAttr feature attribute(s) used for the Y-value (at least one; for each a
- * series is created in the dataset)
- */
- public static Dataset createDataset(FeatureCollection fc, FeatureChartStyle style) {
- String xAttr = style.getAttributeName(0);
- // Check the X attribute to differ between XY- and CategoryDataset
- AttributeType xType = fc.getSchema().getAttributeType(xAttr);
- if ( xType == null )
- throw new UnsupportedOperationException("Unknown attribute: "+xAttr);
+ /**
+ * Returns all {@link DatasetSelectionModel DatasetSelectionModels} that can
+ * be reached via the renderers of a chart.
+ *
+ * @param chart
+ * a chart
+ */
+ public static List<FeatureDatasetSelectionModel<?>> getFeatureDatasetSelectionModelFor(
+ JFreeChart chart) {
+ ArrayList<FeatureDatasetSelectionModel<?>> renderers = new ArrayList<FeatureDatasetSelectionModel<?>>();
- // If domain attribute is numeric (and category not forced) create
- // a XYDataset
- if ( !style.isForceCategories() && Number.class.isAssignableFrom(xType.getBinding()) )
- return createXYDataset(fc, style);
- else
- return createCategoryDataset(fc, style);
- }
-
- /**
- * Checks whether an attribute exists and whether it is of
- * a specific type.
- * Otherwise an {@link IllegalArgumentException} is thrown.
- * @param fType a feature type
- * @param aName the attribute name checked for numeric type
- * @param typeToCheck the type the attribute must support (if {@code null}
- * only the attribute existing is checked!)
- */
- private static void checkAttributeType(FeatureType fType, String aName, Class typeToCheck, String... errDesc) {
- AttributeType aType = fType.getAttributeType(aName);
- if ( aType == null )
- throw new UnsupportedOperationException("Unknown attribute: "+aName);
+ Plot plot = chart.getPlot();
- String attrDesc = errDesc.length > 0 ? errDesc[0] : "Attribute";
- String datasetDesc = errDesc.length > 1 ? errDesc[1] : "Dataset";
-
- if ( typeToCheck != null && !typeToCheck.isAssignableFrom(aType.getBinding()) )
- throw new UnsupportedOperationException(attrDesc+" must be "+typeToCheck.getSimpleName()+" for "+datasetDesc+": "+aName);
- }
-
- /**
- * Creates a {@link XYDataset} for 2 (or more) attributes of a {@link FeatureCollection}.
- * XYDateset can only be created for <b>numeric</b> attributes.
- * @param fc a {@link FeatureCollection}
- * @param style defines the attributes used to create the dataset from, as well
- * as the sorting and normalization properties
- * @throws IllegalArgumentException if less then 2 attributes are specified
- * @throws UnsupportedOperationException if attributes are not numeric
- */
- public static XYSeriesCollection createXYDataset(FeatureCollection fc, FeatureChartStyle chartStyle) {
- int attrCount = chartStyle.getAttributeCount();
- if ( attrCount < 2 )
- throw new IllegalArgumentException("FeatureChartStyle must define at least 2 attributes to create XYDataset: "+attrCount);
- String xAttrName = chartStyle.getAttributeName(0);
- // check data types of X attribute
- checkAttributeType(fc.getSchema(), xAttrName, Number.class, "Domain attribute", "XYDataset");
- // check data types of Y attribute
- for (int i=1; i<attrCount; i++)
- checkAttributeType(fc.getSchema(), chartStyle.getAttributeName(i), Number.class, "Range attribute", "XYDataset");
-
- // Create a series for each range attribute (note: the 0
- // array element useless, but it is more comfortable to
- // use an array of the same size as "attributes"
- XYSeries[] xySeries = new XYSeries[attrCount];
- for (int i=1; i<xySeries.length; i++)
- xySeries[i] = new XYSeries(chartStyle.getAttributeName(i), false, true); // IMPORTANT: Do not use autosort!!
-
- // Create a new dataset and insert the series
- XYSeriesCollection dataset = new XYSeriesCollection();
- Feature2SeriesDatasetMapping mapping = new Feature2SeriesDatasetMapping(fc,dataset);
- dataset.setGroup( new FeatureDatasetMetaData(mapping) );
- for (int i=1; i<xySeries.length; i++)
- dataset.addSeries(xySeries[i]);
+ /** Collect renderers from XYPlot */
+ if (plot instanceof XYPlot) {
+ XYPlot xyplot = (XYPlot) plot;
- // If dataset should be sorted, the features must be sorted first
- // according to the domain attribute. The "autoSort" functionality
- // of XYSeries can NOT BE USED because of the following reason:
- // To realize the synchronized feature/dataitem a mapping
- // between the feature IDs and the data item index (in the series)
- // must be created. Using the "autoSort" functionality, the
- // indices inside the series change with every XYSeries.add(..)
- // call, so that the mapping is destroyed!!
- //
- // Only solution: Pre-sort the features so that the XYSeries internal
- // order does not change!
- Iterator<Feature> fi = null;
- if ( chartStyle.isSortDomainAxis() ) {
- Vector<Feature> sortedFeatures = FeatureUtil.sortFeatures(fc.features(), xAttrName);
- // Create an Iterator for the sorted Features
- fi = sortedFeatures.iterator();
- } else
- fi = new PipedFeatureIterator( fc.features() );
-
- // Iterate the FeatureCollection and fill the series
- for (int fIdx=0; fi.hasNext(); fIdx++) {
- Feature f = fi.next();
- // Determine X value (NULL not permitted for XYDateset!)
- Number xValue = (Number)f.getAttribute(xAttrName);
- if ( xValue == null )
- continue;
- // Determine the Y values and fill the series
- for (int attrIdx=1; attrIdx<attrCount; attrIdx++) {
- String yAttrName = chartStyle.getAttributeName(attrIdx);
- Number yValue = (Number)f.getAttribute(yAttrName);
-// // TODO: here maybe filtering the several NULL aliases
-// if ( yValue is a NULL alias )
-// yValue = null;
-// // TODO: her maybe ignore NULL values completely
-// if ( yValue == null )
-// continue;
-// // TODO: here maybe implement the normalization
-// // ...
+ for (int i = 0; i < xyplot.getRendererCount(); i++) {
+ XYItemRenderer renderer = xyplot.getRenderer(i);
+ if (renderer instanceof DatasetSelectionModelProvider
+ && ((DatasetSelectionModelProvider) renderer)
+ .getSelectionModel() instanceof FeatureDatasetSelectionModel) {
+ renderers
+ .add((FeatureDatasetSelectionModel) ((DatasetSelectionModelProvider) renderer)
+ .getSelectionModel());
+ }
+ }
+ }
+ /** Collect renderers from CategoryPlot */
+ if (plot instanceof CategoryPlot) {
+ CategoryPlot catPlot = (CategoryPlot) plot;
- xySeries[attrIdx].add(xValue, yValue);
+ for (int i = 0; i < catPlot.getRendererCount(); i++) {
+ CategoryItemRenderer renderer = catPlot.getRenderer(i);
+ if (renderer instanceof DatasetSelectionModelProvider
+ && ((DatasetSelectionModelProvider) renderer)
+ .getSelectionModel() instanceof FeatureDatasetSelectionModel) {
+ renderers
+ .add((FeatureDatasetSelectionModel) ((DatasetSelectionModelProvider) renderer)
+ .getSelectionModel());
+ }
+ }
+ }
- // Mapping between FID and data index in series
- mapping.setMapping(f.getID(), yAttrName, fIdx);
+ return renderers;
+ }
- //LOGGER.debug(f.getID() + " --> "+i);
- }
-
- }
- return dataset;
- }
+ /**
+ * Creates a {@link Dataset} for 1 or more attributes of a
+ * {@link FeatureCollection}. According to the feature attribute type the
+ * method decides whether a {@link XYDataset} or a {@link CategoryDataset}
+ * is created:<br>
+ * In case of an non-numeric X attribute a {@link CategoryDataset} is
+ * created always. Otherwise the default is to create a {@link XYDataset}.
+ * The flag {@code forceCat} can be used to create {@link CategoryDataset}
+ * for numeric X attributes.
+ *
+ * @param fc
+ * a {@link FeatureCollection}
+ * @param forceCat
+ * forces a {@link CategoryDataset} also for numeric X attributes
+ * @param sort
+ * sorts the features according to {@code xAttr} before creating
+ * the dataset
+ * @param xAttr
+ * feature attribute used for the X-value
+ * @param yAttr
+ * feature attribute(s) used for the Y-value (at least one; for
+ * each a series is created in the dataset)
+ */
+ public static Dataset createDataset(FeatureCollection fc,
+ FeatureChartStyle style) {
+ String xAttr = style.getAttributeName(0);
+ // Check the X attribute to differ between XY- and CategoryDataset
+ AttributeType xType = fc.getSchema().getAttributeType(xAttr);
+ if (xType == null)
+ throw new UnsupportedOperationException("Unknown attribute: "
+ + xAttr);
- /**
- * Creates a {@link XYDataset} for 2 (or more) attributes of a {@link FeatureCollection}.
- * XYDateset can only be created for <b>numeric</b> attributes.
- * @param fc a {@link FeatureCollection}
- * @param style defines the attributes used to create the dataset from, as well
- * as the sorting and normalization properties
- * @throws IllegalArgumentException if less then 2 attributes are specified
- * @throws UnsupportedOperationException if attributes are not numeric
- */
- public static DefaultCategoryDataset createCategoryDataset(FeatureCollection fc, FeatureChartStyle chartStyle) {
- int attrCount = chartStyle.getAttributeCount();
- if ( attrCount < 2 )
- throw new IllegalArgumentException("FeatureChartStyle must define at least 2 attributes to create CategoryDataset: "+attrCount);
- String xAttrName = chartStyle.getAttributeName(0);
- // only check whether X attribute exists (numeric and not-numeric allowed)
- checkAttributeType(fc.getSchema(), xAttrName, Comparable.class, "Domain attribute", "CategoryDataset");
- // check data types of Y attribute (only numeric allowed)
- for (int i=1; i<attrCount; i++)
- checkAttributeType(fc.getSchema(), chartStyle.getAttributeName(i), Number.class, "Range attribute", "CategoryDataset");
-
- // Create a new dataset and insert the series
- DefaultCategoryDataset dataset = new DefaultCategoryDataset();
-// TODO: Mapping for CategoryDataset!! (CategoryDataset is no SeriesDataset!)
-// Feature2SeriesDatasetMapping mapping = new Feature2SeriesDatasetMapping(fc,dataset);
-// dataset.setGroup( new FeatureDatasetMetaData(mapping) );
+ // If domain attribute is numeric (and category not forced) create
+ // a XYDataset
+ if (!style.isForceCategories()
+ && Number.class.isAssignableFrom(xType.getBinding()))
+ return createXYDataset(fc, style);
+ else
+ return createCategoryDataset(fc, style);
+ }
- // If dataset should be sorted, the features must be sorted first
- // according to the domain attribute. CategoryDataset has no
- // "autoSort", and this - at all - would not work (see createXYDataset(..))
- Iterator<Feature> fi = null;
- if ( chartStyle.isSortDomainAxis() ) {
- Vector<Feature> sortedFeatures = FeatureUtil.sortFeatures(fc.features(), xAttrName);
- // Create an Iterator for the sorted Features
- fi = sortedFeatures.iterator();
- } else
- fi = new PipedFeatureIterator( fc.features() );
-
- // Iterate the FeatureCollection and fill the series
- for (int fIdx=0; fi.hasNext(); fIdx++) {
- Feature f = fi.next();
- // Determine the category (NULL not permitted!)
- Comparable catValue = (Comparable)f.getAttribute(xAttrName);
- if ( catValue == null )
- continue;
- // Determine the Y values and fill the dataset
- for (int attrIdx=1; attrIdx<attrCount; attrIdx++) {
- String yAttrName = chartStyle.getAttributeName(attrIdx);
- Number yValue = (Number)f.getAttribute(yAttrName);
-// // TODO: here maybe filtering the several NULL aliases
-// if ( yValue is a NULL alias )
-// yValue = null;
-// // TODO: here maybe ignore NULL values completely
-// if ( yValue == null )
-// continue;
-// // TODO: here maybe implement the normalization
-// // ...
-
- // Add data to dataset
- dataset.addValue(yValue, yAttrName, catValue);
-
-//TODO: Implement the Mapping for CategoryDataset
-// // Mapping between FID and data index in series
-// mapping.setMapping(f.getID(), yAttrName, fIdx);
- }
-
- }
- return dataset;
- }
+ /**
+ * Checks whether an attribute exists and whether it is of a specific type.
+ * Otherwise an {@link IllegalArgumentException} is thrown.
+ *
+ * @param fType
+ * a feature type
+ * @param aName
+ * the attribute name checked for numeric type
+ * @param typeToCheck
+ * the type the attribute must support (if {@code null} only the
+ * attribute existing is checked!)
+ */
+ private static void checkAttributeType(FeatureType fType, String aName,
+ Class typeToCheck, String... errDesc) {
+ AttributeType aType = fType.getAttributeType(aName);
+ if (aType == null)
+ throw new UnsupportedOperationException("Unknown attribute: "
+ + aName);
+
+ String attrDesc = errDesc.length > 0 ? errDesc[0] : "Attribute";
+ String datasetDesc = errDesc.length > 1 ? errDesc[1] : "Dataset";
+
+ if (typeToCheck != null
+ && !typeToCheck.isAssignableFrom(aType.getBinding()))
+ throw new UnsupportedOperationException(attrDesc + " must be "
+ + typeToCheck.getSimpleName() + " for " + datasetDesc
+ + ": " + aName);
+ }
+
+ /**
+ * Creates a {@link XYDataset} for 2 (or more) attributes of a
+ * {@link FeatureCollection}. XYDateset can only be created for
+ * <b>numeric</b> attributes.
+ *
+ * @param fc
+ * a {@link FeatureCollection}
+ * @param style
+ * defines the attributes used to create the dataset from, as
+ * well as the sorting and normalization properties
+ * @throws IllegalArgumentException
+ * if less then 2 attributes are specified
+ * @throws UnsupportedOperationException
+ * if attributes are not numeric
+ */
+ public static XYSeriesCollection createXYDataset(FeatureCollection fc,
+ FeatureChartStyle chartStyle) {
+ int attrCount = chartStyle.getAttributeCount();
+ if (attrCount < 2)
+ throw new IllegalArgumentException(
+ "FeatureChartStyle must define at least 2 attributes to create XYDataset: "
+ + attrCount);
+ String xAttrName = chartStyle.getAttributeName(0);
+ // check data types of X attribute
+ checkAttributeType(fc.getSchema(), xAttrName, Number.class,
+ "Domain attribute", "XYDataset");
+ // check data types of Y attribute
+ for (int i = 1; i < attrCount; i++)
+ checkAttributeType(fc.getSchema(), chartStyle.getAttributeName(i),
+ Number.class, "Range attribute", "XYDataset");
+
+ // Create a series for each range attribute (note: the 0
+ // array element useless, but it is more comfortable to
+ // use an array of the same size as "attributes"
+ XYSeries[] xySeries = new XYSeries[attrCount];
+ for (int i = 1; i < xySeries.length; i++)
+ xySeries[i] = new XYSeries(chartStyle.getAttributeName(i), false,
+ true); // IMPORTANT: Do not use autosort!!
+
+ // Create a new dataset and insert the series
+ XYSeriesCollection dataset = new XYSeriesCollection();
+ Feature2SeriesDatasetMapping mapping = new Feature2SeriesDatasetMapping(
+ fc, dataset);
+ dataset.setGroup(new FeatureDatasetMetaData(mapping));
+ for (int i = 1; i < xySeries.length; i++)
+ dataset.addSeries(xySeries[i]);
+
+ // NORMALIZATION:
+ // We have to create a descriptive statistic of all items before we can
+ // transform the first one. So we iterate over all items before we do
+ // optional sorting and insertion.
+ {
+
+ // First check if any attribute needs normalization. If not we can
+ // skip the whole iteration
+ boolean doNormalization = false;
+ for (int attrIdx = 0; attrIdx < attrCount; attrIdx++) {
+ if (chartStyle.isAttributeNormalized(attrIdx)) {
+ doNormalization = true;
+ break;
+ }
+ }
+
+ if (doNormalization) {
+ final FeatureIterator fIt = fc.features();
+ // Loop over all features, and collect the attribute values we are interested in.
+ while (fIt.hasNext()){
+ Feature f = fIt.next();
+
+ for (int attrIdx = 0; attrIdx < attrCount; attrIdx++) {
+
+
+ if (chartStyle.isAttributeNormalized(attrIdx)) {
+ String attrName = chartStyle.getAttributeName(attrIdx);
+ StaticBin1D stat = attribStats.get(attrName);
+ if (stat == null){
+ stat = new DynamicBin1D();
+ attribStats.put(attrName, stat);
+ }
+// // TODO: here also filtering the several NULL aliases and do not
+// // insert them into the statistics calculation
+// // if ( yValue is a NULL alias )
+// // yValue = null;
+ final Double doubleValue = new Double(((Number) f.getAttribute(attrName)).doubleValue());
+ stat.add(doubleValue);
+// LOGGER.debug("Adding "+doubleValue+" and sd = "+stat.standardDeviation()+" and mean = "+stat.mean());
+ }
+ }
+ } // Featuer iterator
+ } // doNormlization
+
+ } // Normalization stuff block
+
+
+ // If dataset should be sorted, the features must be sorted first
+ // according to the domain attribute. The "autoSort" functionality
+ // of XYSeries can NOT BE USED because of the following reason:
+ // To realize the synchronized feature/dataitem a mapping
+ // between the feature IDs and the data item index (in the series)
+ // must be created. Using the "autoSort" functionality, the
+ // indices inside the series change with every XYSeries.add(..)
+ // call, so that the mapping is destroyed!!
+ //
+ // Only solution: Pre-sort the features so that the XYSeries internal
+ // order does not change!
+ Iterator<Feature> fi = null;
+ if (chartStyle.isSortDomainAxis()) {
+ Vector<Feature> sortedFeatures = FeatureUtil.sortFeatures(fc.features(), xAttrName);
+ // Create an Iterator for the sorted Features
+ fi = sortedFeatures.iterator();
+ } else
+ fi = new PipedFeatureIterator(fc.features());
+
+ // Iterate the FeatureCollection and fill the series
+ for (int fIdx = 0; fi.hasNext(); fIdx++) {
+ Feature f = fi.next();
+ // Determine X value (NULL not permitted for XYDateset!)
+ Number xValue = (Number) f.getAttribute(xAttrName);
+ if (xValue == null)
+ continue;
+ // Determine the Y values and fill the series
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ Number yValue = (Number) f.getAttribute(yAttrName);
+ // // TODO: here maybe filtering the several NULL aliases
+ // if ( yValue is a NULL alias )
+ // yValue = null;
+
+ // // TODO: her maybe ignore NULL values completely
+ // if ( yValue == null )
+ // continue;
+
+ /* Normalization of a range axis value */
+ if (chartStyle.isAttributeNormalized(attrIdx))
+ yValue = normalize(yValue, yAttrName);
+
+ /* Normalization of the domain axis value (if they are not handled as categories) */
+ if (chartStyle.isAttributeNormalized(ChartStyle.DOMAIN_AXIS))
+ xValue = normalize(xValue, xAttrName);
+
+ xySeries[attrIdx].add(xValue, yValue);
+
+ // Mapping between FID and data index in series
+ mapping.setMapping(f.getID(), yAttrName, fIdx);
+
+ // LOGGER.debug(f.getID() + " --> "+i);
+ }
+
+ }
+ return dataset;
+ }
+
+ /**
+ * Do a z-transformation on the data item.
+ */
+ private static Number normalize(Number yValue, String attrName) {
+ Number zValue;
+
+ if (attribStats.get(attrName) == null) {
+ throw new IllegalArgumentException("The statistics needed for the normlization need to be created before normalizing.");
+ }
+
+ StaticBin1D stats = attribStats.get(attrName);
+
+ zValue = (yValue.doubleValue() - stats.mean())
+ / stats.standardDeviation();
+
+ return zValue;
+ }
+
+ /**
+ * Creates a {@link XYDataset} for 2 (or more) attributes of a
+ * {@link FeatureCollection}. XYDateset can only be created for
+ * <b>numeric</b> attributes.
+ *
+ * @param fc
+ * a {@link FeatureCollection}
+ * @param style
+ * defines the attributes used to create the dataset from, as
+ * well as the sorting and normalization properties
+ * @throws IllegalArgumentException
+ * if less then 2 attributes are specified
+ * @throws UnsupportedOperationException
+ * if attributes are not numeric
+ */
+ public static DefaultCategoryDataset createCategoryDataset(
+ FeatureCollection fc, FeatureChartStyle chartStyle) {
+ int attrCount = chartStyle.getAttributeCount();
+ if (attrCount < 2)
+ throw new IllegalArgumentException(
+ "FeatureChartStyle must define at least 2 attributes to create CategoryDataset: "
+ + attrCount);
+ String xAttrName = chartStyle.getAttributeName(0);
+ // only check whether X attribute exists (numeric and not-numeric
+ // allowed)
+ checkAttributeType(fc.getSchema(), xAttrName, Comparable.class,
+ "Domain attribute", "CategoryDataset");
+ // check data types of Y attribute (only numeric allowed)
+ for (int i = 1; i < attrCount; i++)
+ checkAttributeType(fc.getSchema(), chartStyle.getAttributeName(i),
+ Number.class, "Range attribute", "CategoryDataset");
+
+ // Create a new dataset and insert the series
+ DefaultCategoryDataset dataset = new DefaultCategoryDataset();
+ // TODO: Mapping for CategoryDataset!! (CategoryDataset is no
+ // SeriesDataset!)
+ // Feature2SeriesDatasetMapping mapping = new
+ // Feature2SeriesDatasetMapping(fc,dataset);
+ // dataset.setGroup( new FeatureDatasetMetaData(mapping) );
+
+ // If dataset should be sorted, the features must be sorted first
+ // according to the domain attribute. CategoryDataset has no
+ // "autoSort", and this - at all - would not work (see
+ // createXYDataset(..))
+ Iterator<Feature> fi = null;
+ if (chartStyle.isSortDomainAxis()) {
+ Vector<Feature> sortedFeatures = FeatureUtil.sortFeatures(fc
+ .features(), xAttrName);
+ // Create an Iterator for the sorted Features
+ fi = sortedFeatures.iterator();
+ } else
+ fi = new PipedFeatureIterator(fc.features());
+
+ // Iterate the FeatureCollection and fill the series
+ for (int fIdx = 0; fi.hasNext(); fIdx++) {
+ Feature f = fi.next();
+ // Determine the category (NULL not permitted!)
+ Comparable catValue = (Comparable) f.getAttribute(xAttrName);
+ if (catValue == null)
+ continue;
+ // Determine the Y values and fill the dataset
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ Number yValue = (Number) f.getAttribute(yAttrName);
+ // // TODO: here maybe filtering the several NULL aliases
+ // if ( yValue is a NULL alias )
+ // yValue = null;
+
+ // // TODO: here maybe ignore NULL values completely.
+ // if ( yValue == null )
+ // continue;
+
+ /* Note: Normalization does not make sense for a category dataset! */
+
+ // Add data to dataset
+ dataset.addValue(yValue, yAttrName, catValue);
+
+ // TODO: Implement the Mapping for CategoryDataset
+ // // Mapping between FID and data index in series
+ // mapping.setMapping(f.getID(), yAttrName, fIdx);
+ }
+
+ }
+ return dataset;
+ }
}
Modified: trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java 2009-08-02 23:16:13 UTC (rev 283)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java 2009-08-03 10:49:18 UTC (rev 284)
@@ -176,7 +176,7 @@
*/
@Override
public boolean isAttributeNormalized(int idx) {
- return dummyFeatureChartStyle.isAttributeNormalized(idx);
+ return dummyFeatureChartStyle.isAttributeNormalized(idx);
}
/**
More information about the Schmitzm-commits
mailing list