[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