[Schmitzm-commits] r263 - in trunk/src/schmitzm: geotools/feature jfree/feature/style

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Fri Jul 31 21:35:12 CEST 2009


Author: mojays
Date: 2009-07-31 21:35:11 +0200 (Fri, 31 Jul 2009)
New Revision: 263

Added:
   trunk/src/schmitzm/geotools/feature/PipedFeatureIterator.java
Modified:
   trunk/src/schmitzm/geotools/feature/FeatureUtil.java
   trunk/src/schmitzm/jfree/feature/style/FeatureBasicChartStyle.java
   trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
   trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java
Log:
sort functionality in dataset creation for FeatureCollection

Modified: trunk/src/schmitzm/geotools/feature/FeatureUtil.java
===================================================================
--- trunk/src/schmitzm/geotools/feature/FeatureUtil.java	2009-07-31 17:19:34 UTC (rev 262)
+++ trunk/src/schmitzm/geotools/feature/FeatureUtil.java	2009-07-31 19:35:11 UTC (rev 263)
@@ -42,6 +42,8 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.Vector;
 
 import org.apache.log4j.Logger;
@@ -517,8 +519,54 @@
 
     return resultType;
   }
+  
+  /**
+   * Orderes {@link Feature Features} according to an attribute (ascending). 
+   * @param fi       the features to order
+   * @param attrName the attribute to order by
+   * @return a list of ordered features
+   */
+  public static Vector<Feature> sortFeatures(FeatureCollection fc, String attrName) {
+    return sortFeatures(fc.features(), attrName);
+  }
 
   /**
+   * Orderes {@link Feature Features} according to an attribute (ascending). 
+   * @param fi       an iterator for the features
+   * @param attrName the attribute to order by
+   * @return a list of ordered features
+   */
+  public static Vector<Feature> sortFeatures(FeatureIterator fi, String attrName) {
+    // First create a SortedMap with CollisionList
+    SortedMap<Number, Vector<Feature>> sortedFeatureLists = new TreeMap<Number, Vector<Feature>>();
+    for (;fi.hasNext();) {
+      Feature f = fi.next();
+      // Determine X value as sort attribute (NULL not permitted for XYDateset!)
+      Number xValue = (Number)f.getAttribute(attrName);
+      if ( xValue == null )
+        continue;
+      
+      // Check whether collision list exists
+      Vector<Feature> collList = sortedFeatureLists.get(xValue);
+      if ( collList == null ) {
+        collList = new Vector<Feature>();
+        sortedFeatureLists.put(xValue, collList);
+      }
+      // Add feature to collision list
+      collList.add(f);
+    }
+    
+    // Put the (now ordered) features from the collision lists
+    // into an temporary list to create a list of Features
+    Vector<Feature> sortedFeatures = new Vector<Feature>();
+    for (Vector<Feature> collList : sortedFeatureLists.values())
+      for (Feature f : collList)
+        sortedFeatures.add(f);
+
+    return sortedFeatures;
+  }
+
+  /**
    * Erweitert das Schema einer {@link FeatureCollection} um eine Reihe von
    * Attributen.
    * @param fc     zu erweiternde FeatureCollection
@@ -876,7 +924,7 @@
 
     return destMap;
   }
-
+  
   /**
    * Fuehrt eine Join-Operation zwischen einer {@link FeatureCollection} und
    * einem {@link ResultSet} durch, in dem Zusatz-Informationen hinterlegt.

Added: trunk/src/schmitzm/geotools/feature/PipedFeatureIterator.java
===================================================================
--- trunk/src/schmitzm/geotools/feature/PipedFeatureIterator.java	2009-07-31 17:19:34 UTC (rev 262)
+++ trunk/src/schmitzm/geotools/feature/PipedFeatureIterator.java	2009-07-31 19:35:11 UTC (rev 263)
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Martin O. J. Schmitz.
+ * 
+ * This file is part of the SCHMITZM library - a collection of utility 
+ * classes based on Java 1.6, focusing (not only) on Java Swing 
+ * and the Geotools library.
+ * 
+ * The SCHMITZM project is hosted at:
+ * http://wald.intevation.org/projects/schmitzm/
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License (license.txt)
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * or try this link: http://www.gnu.org/licenses/lgpl.html
+ * 
+ * Contributors:
+ *     Martin O. J. Schmitz - initial API and implementation
+ *     Stefan A. Krüger - additional utility classes
+ ******************************************************************************/
+
+package schmitzm.geotools.feature;
+
+import java.util.Iterator;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureIterator;
+
+/**
+ * This class is a workaround since {@link FeatureIterator} does not
+ * implement {@link Iterator}.
+ * This class simply pipes all function calls to the {@link FeatureIterator}
+ * specified in the constructor, but can be used with the Java standard
+ * {@link Iterator} interface.
+ * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+ *
+ */
+public class PipedFeatureIterator implements Iterator<Feature> {
+  /** Holds the {@link FeatureIterator} which is piped. */
+  protected FeatureIterator fIter = null;
+  
+  /**
+   * Creates a new iterator.
+   * @param fIter {@link FeatureIterator} which is piped
+   */
+  public PipedFeatureIterator(FeatureIterator fIter) {
+    this.fIter = fIter;
+  }
+  
+  /**
+   * Checks whether a next element exists.
+   */
+  @Override
+  public boolean hasNext() {
+    return fIter.hasNext();
+  }
+
+  /**
+   * Returns the next Feature;
+   */
+  @Override
+  public Feature next() {
+    return fIter.next();
+  }
+
+  /**
+   * Not supported, because the {@link FeatureIterator} does not
+   * support this functionality.
+   * @throws UnsupportedOperationException always 
+   */
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException("PipedFeatureIterator.remove() not supported!");
+  }
+  
+  
+
+}

Modified: trunk/src/schmitzm/jfree/feature/style/FeatureBasicChartStyle.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureBasicChartStyle.java	2009-07-31 17:19:34 UTC (rev 262)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureBasicChartStyle.java	2009-07-31 19:35:11 UTC (rev 263)
@@ -185,7 +185,12 @@
     @Override
     public JFreeChart applyToFeatureCollection(FeatureCollection fc) {
         //    Dataset dataset = null; // TODO: Create dataset from FeatureCollection by utility method
-        Dataset dataset = FeatureChartUtil.createXYDataset(fc, getTitleStyle().getLabel(), getAttributeName(DOMAIN_AXIS), getAttributeName(RANGE_AXIS));
+        Dataset dataset = FeatureChartUtil.createXYDataset(
+            fc,
+            isSortDomainAxis(),
+            getAttributeName(DOMAIN_AXIS),
+            getAttributeName(RANGE_AXIS)
+        );
         return applyToDataset(dataset);
     }
 }

Modified: trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java	2009-07-31 17:19:34 UTC (rev 262)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java	2009-07-31 19:35:11 UTC (rev 263)
@@ -30,12 +30,17 @@
 package schmitzm.jfree.feature.style;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Vector;
 
 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;
 import org.jfree.chart.plot.Plot;
@@ -48,6 +53,8 @@
 import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;
 
+import schmitzm.geotools.feature.FeatureUtil;
+import schmitzm.geotools.feature.PipedFeatureIterator;
 import schmitzm.jfree.JFreeChartUtil;
 import schmitzm.jfree.chart.selection.DatasetSelectionModel;
 import schmitzm.jfree.chart.selection.DatasetSelectionModelProvider;
@@ -106,7 +113,7 @@
   }
   
   /**
-   * Creates a {@link Dataset} for 2 or more attributes of a {@link FeatureCollection}.
+   * 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.
@@ -114,62 +121,115 @@
    * 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, boolean forceCat, boolean sot, String xAttr, String... yAttr) {
+  public static Dataset createDataset(FeatureCollection fc, boolean forceCat, boolean sort, String xAttr, String... yAttr) {
+    // 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);
+
+    // If domain attribute is numeric (and category not forced) create
+    // a XYDataset
+    if ( !forceCat && Number.class.isAssignableFrom(xType.getBinding()) )
+      ;//sdsd
+    else
+      ;//
     
-    
-    
-    
     return null;
   }
   
   /**
-   * Creates a {@link XYDataset} for 2 attributes of a {@link FeatureCollection}.
+   * Checks whether an attribute exists and whether it is numeric.
+   * Otherwise an {@link IllegalArgumentException} is thrown.
+   * @param fType a feature type
+   * @param aName the attribute name checked for numeric type
+   */
+  private static void checkAttributeNumeric(FeatureType fType, String aName, String... errDesc) {
+    String attrDesc = errDesc.length > 0 ? errDesc[0] : "Attribute";
+    String datasetDesc = errDesc.length > 1 ? errDesc[1] : "Dataset";
+    
+    AttributeType aType = fType.getAttributeType(aName);
+    if ( aType == null )
+      throw new UnsupportedOperationException("Unknown attribute: "+aName);
+    if ( !Number.class.isAssignableFrom(aType.getBinding()) )
+      throw new UnsupportedOperationException(attrDesc+" must be numeric for "+datasetDesc+": "+aName);
+  }
+  
+  /**
+   * Creates a {@link XYDataset} for 2 (or more) attributes of a {@link FeatureCollection}.
    * @param fc    a {@link FeatureCollection}
    * @param seriesKey   a (unique) key for the {@link XYSeries} in the dataset
    * @param xAttr feature attribute used for the X-value
-   * @param yAttr feature attribute used for the Y-value
+   * @param yAttr feature attribute(s) used for the Y-value (for each a series
+   *              is created in the dataset)
    */
-  public static XYSeriesCollection createXYDataset(FeatureCollection fc, String seriesKey, String xAttr, String yAttr) {
-    // check data types of X- and Y-column
-    AttributeType xType = fc.getSchema().getAttributeType(xAttr);
-    if ( xType == null )
-      throw new UnsupportedOperationException("Unknown attribute: "+xAttr);
-    AttributeType yType = fc.getSchema().getAttributeType(yAttr);
-    if ( yType == null )
-      throw new UnsupportedOperationException("Unknown attribute: "+yAttr);
-    if ( !Number.class.isAssignableFrom(xType.getBinding()) )
-      throw new UnsupportedOperationException("Attribute must be numeric: "+xAttr);
-    if ( !Number.class.isAssignableFrom(yType.getBinding()) )
-      throw new UnsupportedOperationException("Attribute must be numeric: "+yAttr);
+  public static XYSeriesCollection createXYDataset(FeatureCollection fc, boolean sort, String xAttr, String... yAttr) {
+    // check data types of X attribute
+    checkAttributeNumeric(fc.getSchema(), xAttr, "Domain attribute", "XYDataset");
+    // check data types of Y attribute
+    for (String attr : yAttr)
+      checkAttributeNumeric(fc.getSchema(), attr, "Range attribute", "XYDataset");
     
+    // Create a series for each range attribute
+    XYSeries[] xySeries = new XYSeries[yAttr.length];
+    for (int i=0; i<xySeries.length; i++)
+      xySeries[i] = new XYSeries(yAttr[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);
-    FeatureIterator              fi        = fc.features();
-    XYSeries                     xySeries  = new XYSeries(seriesKey,false,true);
-    dataset.addSeries(xySeries);
     dataset.setGroup( new FeatureDatasetMetaData(mapping) );
+    for (XYSeries series : xySeries)
+      dataset.addSeries(series);
+
+    // 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 ( sort ) {
+      Vector<Feature> sortedFeatures = FeatureUtil.sortFeatures(fc.features(), xAttr);
+      // 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 i=0; fi.hasNext(); i++) {
       Feature f = fi.next();
+      // Determine X value (NULL not permitted for XYDateset!)
       Number xValue = (Number)f.getAttribute(xAttr);
-      Number yValue = (Number)f.getAttribute(yAttr);
-      // x-value NULL not permitted for XY dateset
       if ( xValue == null )
         continue;
+      // Determine the Y values and fill the series
+      for (int attrIdx=0; attrIdx<yAttr.length; attrIdx++) {
+        Number yValue = (Number)f.getAttribute(yAttr[attrIdx]);
 //      // 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;
-      xySeries.add(xValue, yValue);
-      // Mapping between FID and data index in series
-      mapping.setMapping(f.getID(), seriesKey, i);
-      //LOGGER.debug(f.getID() + " --> "+i);
+        xySeries[attrIdx].add(xValue, yValue);
+
+        // Mapping between FID and data index in series
+        mapping.setMapping(f.getID(), yAttr[attrIdx], i);
+
+        //LOGGER.debug(f.getID() + " --> "+i);
+      }
+      
     }
     return dataset;
   }
@@ -184,7 +244,7 @@
    * @param regressionLine indicates whether the regression line is shown
    */
   public static JFreeChart createRegressionChart(FeatureCollection fc, String title, String xAttr, String yAttr, boolean regressionLine) {
-    XYSeriesCollection  dataset = createXYDataset(fc, title, xAttr, yAttr);
+    XYSeriesCollection  dataset = createXYDataset(fc, false, xAttr, yAttr);
     return JFreeChartUtil.createRegressionChart(dataset, title, xAttr, yAttr, regressionLine);
   }
 

Modified: trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java	2009-07-31 17:19:34 UTC (rev 262)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureScatterChartStyle.java	2009-07-31 19:35:11 UTC (rev 263)
@@ -177,7 +177,7 @@
     //       a non-numeric column is specified
     Dataset dataset = FeatureChartUtil.createXYDataset(
         fc,
-        getTitleStyle().getLabel(),
+        isSortDomainAxis(),
         getAttributeName(0),
         getAttributeName(1)
     );



More information about the Schmitzm-commits mailing list