[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