[Schmitzm-commits] r754 - in trunk/src/schmitzm/jfree: feature feature/style resource/locales
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Wed Mar 3 20:13:23 CET 2010
Author: mojays
Date: 2010-03-03 20:13:22 +0100 (Wed, 03 Mar 2010)
New Revision: 754
Modified:
trunk/src/schmitzm/jfree/feature/AggregationFunction.java
trunk/src/schmitzm/jfree/feature/style/FeatureChartStyle.java
trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle.properties
trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle_de.properties
Log:
- new AggregationFunctions SUM_WEIGHTED, SUM_ABS
- getResult(.) moved from AggregationFunction to FeatureChartUtil
- Dataset generation in FeatureChartUtil restructured with private utility methods
Modified: trunk/src/schmitzm/jfree/feature/AggregationFunction.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/AggregationFunction.java 2010-03-03 15:35:55 UTC (rev 753)
+++ trunk/src/schmitzm/jfree/feature/AggregationFunction.java 2010-03-03 19:13:22 UTC (rev 754)
@@ -15,121 +15,129 @@
/**
* Defines possible aggregation function to calculate on attribute categories.
- *
* @see FeatureChartStyle#setAttributeAggregation(int, AggregationFunction)
* @see FeatureChartStyle#getAttributeAggregation(int)
* @see optionally FeatureChartStyle#setNoDataWeightValues(int, java.util.Set))
- * @see optionally FeatureChartStyle#getAttributeAggregationWeightAttributeName(int)
+ * @see optionally
+ * FeatureChartStyle#getAttributeAggregationWeightAttributeName(int)
*/
public enum AggregationFunction {
- /** Count of the attribute values. */
- COUNT(false),
- /** Sum of the attribute values. */
- SUM(false),
- // /** Sum of the absolute attribute values. */
- // SUM_ABS,
- /** Average of the attribute values. */
- AVG(false),
- /**
- * Weighted average of the attribute values. Needs an attribute for
- * weighting
- **/
- AVG_WEIGHTED(true),
- /** Median of the attribute values. */
- MEDIAN(false),
- /** Minimum attribute value. */
- MIN(false),
- /** Maximum attribute value. */
- MAX(false),
- /** Variance of the attribute values. */
- VARIANCE(false),
- /** Standard deviation of the attribute values. */
- STND_DEV(false);
+ /** Count of the attribute values. */
+ COUNT(),
+ /** Sum of the attribute values. */
+ SUM(),
+ /** Sum of the absolute attribute values. */
+ SUM_ABS(true,false, false),
+ /** Weighted sum of the attribute values. Needs an attribute for weighting. */
+ SUM_WEIGHTED(false, true, false),
+ /** Average of the attribute values. */
+ AVG(),
+ /**
+ * Weighted average of the attribute values. Needs an attribute for weighting.
+ */
+ AVG_WEIGHTED(false, true, true),
+ /** Median of the attribute values. */
+ MEDIAN(),
+ /** Minimum attribute value. */
+ MIN(),
+ /** Maximum attribute value. */
+ MAX(),
+ /** Variance of the attribute values. */
+ VARIANCE(),
+ /** Standard deviation of the attribute values. */
+ STND_DEV();
- private final boolean weighted;
+ // Indicates whether the attribute is must be transformed to positive
+ // values
+ private final boolean absoluted;
+ // Indicates whether the attribute is weighted with another attribute
+ private final boolean weighted;
+ // Indicates whether the attribute is weighted and averaged with the weight
+ // sum
+ private final boolean averaged;
- /**
- * Constructed for this {@link Enum}, defining whether it uses a weight.
- * @param weighted
- */
- private AggregationFunction(boolean weighted) {
- this.weighted = weighted;
- }
+ /**
+ * Creates a default {@link Enum}, which is not absoluted, not weighted and
+ * not averaged.
+ */
+ private AggregationFunction() {
+ this(false,false,false);
+ }
- /**
- * Returns <code>true</code>, if this aggregation method is used a second attribute for
- * weighting.
- **/
- public boolean isWeighted() {
- return weighted;
- }
+ /**
+ * Creates an {@link Enum} instance.
+ * @param absoluted Indicates whether the attribute is must be transformed to
+ * positive values
+ * @param weighted Indicates whether the attribute is weighted with another
+ * attribute
+ * @param averaged Indicates whether the attribute is weighted and averaged
+ * with the weight sum (if averaged, weighted is automatically
+ * implied!)
+ */
+ private AggregationFunction(boolean absoluted, boolean weighted,
+ boolean averaged) {
+ this.absoluted = absoluted;
+ this.weighted = weighted || averaged; // average implies weighted
+ this.averaged = averaged;
+ }
- /**
- * Prefix for the title (.TITLE) and description (.DESC) key in the resource
- * bundle.
- *
- * @see #getTitle()
- * @see #getDescription()
- */
- public final String RESOURCE_PREFIX = getClass().getSimpleName() + "."
- + toString();
+ /**
+ * Returns <code>true</code>, if this aggregation method needs the
+ * absolute attribute value.
+ * @see Math#abs(double)
+ **/
+ public boolean isAbsoluted() {
+ return absoluted;
+ }
- /**
- * Returns the result of the function from a statistic.
- *
- * @param statistics
- * Statistic to take the result from.
- */
- public Double getResult(QuantileBin1D statistics) {
- switch (this) {
- case AVG:
- return statistics.mean();
- case MEDIAN:
- return statistics.median();
- case MAX:
- return statistics.max();
- case MIN:
- return statistics.min();
- // case SUM_ABS: // SK: Man müsste beim
- // "packen der daten in die Statistik" schon abs() auf die werte
- // anwenden und dann diese zeile einkommentieren
- case AVG_WEIGHTED:
- case SUM:
- return statistics.sum();
- case COUNT:
- return ((Integer) statistics.size()).doubleValue();
- case STND_DEV:
- return statistics.size() > 1 ? statistics.standardDeviation() : 0.;
- case VARIANCE:
- return statistics.size() > 1 ? statistics.variance() : 0.;
- }
- throw new UnsupportedOperationException(
- "Aggregation function not yet supported: " + this);
- }
+ /**
+ * Returns <code>true</code>, if this aggregation method is used a second
+ * attribute for weighting.
+ **/
+ public boolean isWeighted() {
+ return weighted;
+ }
- /**
- * Returns a description of this kind of chart. Can be used for tool-tips.
- * May return <code>null</code> if no localized String found.
- */
- public String getDescription() {
- final String resource = JFreeChartUtil.R(RESOURCE_PREFIX + ".Desc");
- if (resource == null
- || resource.equals(ResourceProvider.MISSING_RESOURCE_STRING))
- return null;
- return resource;
- }
+ /**
+ * Returns <code>true</code>, if this aggregation method is used a second
+ * attribute for averaging.
+ **/
+ public boolean isAveraged() {
+ return averaged;
+ }
- /**
- * Returns a localized title of this kind of chart. If no localized string
- * found, return the Enum.toString()
- */
- public String getTitle() {
- final String resource = JFreeChartUtil.R(RESOURCE_PREFIX + ".Title");
- if (resource == null
- || resource.equals(ResourceProvider.MISSING_RESOURCE_STRING))
- return toString();
- return resource;
- }
+ /**
+ * Prefix for the title (.TITLE) and description (.DESC) key in the resource
+ * bundle.
+ * @see #getTitle()
+ * @see #getDescription()
+ */
+ public final String RESOURCE_PREFIX = getClass().getSimpleName() + "." +
+ toString();
+ /**
+ * Returns a description of this kind of chart. Can be used for tool-tips. May
+ * return <code>null</code> if no localized String found.
+ */
+ public String getDescription() {
+ final String resource = JFreeChartUtil.R(RESOURCE_PREFIX + ".Desc");
+ if (resource == null ||
+ resource.equals(ResourceProvider.MISSING_RESOURCE_STRING))
+ return null;
+ return resource;
+ }
+
+ /**
+ * Returns a localized title of this kind of chart. If no localized string
+ * found, return the Enum.toString()
+ */
+ public String getTitle() {
+ final String resource = JFreeChartUtil.R(RESOURCE_PREFIX + ".Title");
+ if (resource == null ||
+ resource.equals(ResourceProvider.MISSING_RESOURCE_STRING))
+ return toString();
+ return resource;
+ }
+
}
Modified: trunk/src/schmitzm/jfree/feature/style/FeatureChartStyle.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureChartStyle.java 2010-03-03 15:35:55 UTC (rev 753)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureChartStyle.java 2010-03-03 19:13:22 UTC (rev 754)
@@ -625,7 +625,7 @@
*/
@Override
public void setAttributeAggregation(int idx, AggregationFunction func) {
- if (idx == 0)
+ if (idx == 0 && func != null)
throw new IllegalArgumentException(
"Aggregation function not allowed for attribute 0 (domain attribute)!");
aggrFuncs.put(idx, func);
@@ -736,7 +736,7 @@
@Override
public void setAttributeAggregationWeightAttributeName(int idx,
String weightAttrName) {
- if (idx == 0)
+ if (idx == 0 && weightAttrName != null)
throw new IllegalArgumentException(
"Weight attribute not allowed for attribute 0 because aggregation function not allowed for domain attribute!");
weightAttrNames.put(idx, weightAttrName);
@@ -767,7 +767,7 @@
*/
@Override
public void setWeightAttributeNoDataValues(int idx, Set<Object> noDataValues) {
- if (idx == 0)
+ if (idx == 0 && noDataValues != null)
throw new IllegalArgumentException(
"Weight attribute not allowed for attribute 0 because aggregation function not allowed for domain attribute!");
weightAttrNoDataValues.put(idx, noDataValues);
Modified: trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java
===================================================================
--- trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java 2010-03-03 15:35:55 UTC (rev 753)
+++ trunk/src/schmitzm/jfree/feature/style/FeatureChartUtil.java 2010-03-03 19:13:22 UTC (rev 754)
@@ -68,11 +68,13 @@
import schmitzm.jfree.chart.selection.DatasetSelectionModelProvider;
import schmitzm.jfree.chart.style.ChartStyle;
import schmitzm.jfree.chart.style.ChartStyleXMLFactory;
+import schmitzm.jfree.chart.style.ChartType;
import schmitzm.jfree.feature.AggregationFunction;
import schmitzm.jfree.feature.Feature2CategoryDatasetMapping;
import schmitzm.jfree.feature.Feature2SeriesDatasetMapping;
import schmitzm.jfree.feature.FeatureDatasetMetaData;
import schmitzm.jfree.feature.FeatureDatasetSelectionModel;
+import schmitzm.lang.LangUtil;
import skrueger.AttributeMetadata;
import skrueger.geotools.AttributeMetadataMap;
@@ -210,24 +212,32 @@
// check data types of X attribute
checkAttributeType(fc.getSchema(), xAttrName, Number.class,
"Domain attribute", "XYDataset");
- // check data types of Y attribute
+ // 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", "XYDataset");
+ // Calculate statistics to normalize not-aggregated attributes
+ HashMap<String, QuantileBin1D> statisticsForNormalization = calcStatisticsForNotAggregatedNormalization(
+ fc,
+ chartStyle);
+ // Calculate weight sums for all weight-aggregated attributes
+ HashMap<Comparable<?>, Double>[] weightSumsForAggregation = calcWeightSumForAggregation(
+ fc,
+ chartStyle);
+ // Prepare set for statistics to calculate aggregation functions (one
+ // statistic for each range attribute of each domain value)
+ HashMap<Comparable<?>, QuantileBin1D>[] statisticsForAggregation = new HashMap[attrCount];
+ for (int i = 1; i < attrCount; i++)
+ statisticsForAggregation[i] = new HashMap<Comparable<?>, QuantileBin1D>();
+
// 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
- // !
- // !
+ // IMPORTANT: Do not use autosort!!
+ xySeries[i] = new XYSeries(chartStyle.getAttributeName(i), false, true);
// Create a new dataset and insert the series
XYSeriesCollection dataset = new XYSeriesCollection();
@@ -237,17 +247,6 @@
for (int i = 1; i < xySeries.length; i++)
dataset.addSeries(xySeries[i]);
- // Calculate any statistics needed for normalization.
- HashMap<String, QuantileBin1D> statisticsForNormalization = calcStatisticsForNotAggregatedNormalization(
- fc,
- chartStyle);
- // Prepare set for statistics to calculate aggregation
- // functions (one statistic for each range attribute of
- // each domain value)
- HashMap<Number, QuantileBin1D>[] statisticsForAggregation = new HashMap[attrCount];
- for (int i = 1; i < attrCount; i++)
- statisticsForAggregation[i] = new HashMap<Number, QuantileBin1D>();
-
// 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:
@@ -259,101 +258,182 @@
//
// Only solution: Pre-sort the features so that the XYSeries internal
// order does not change!
+ FeatureIterator<SimpleFeature> features = null;
Iterator<SimpleFeature> fi = null;
if (chartStyle.isSortDomainAxis()) {
Vector<SimpleFeature> sortedFeatures = FeatureUtil.sortFeatures(fc,
xAttrName);
// Create an Iterator for the sorted Features
fi = sortedFeatures.iterator();
- } else
- fi = new PipedFeatureIterator(fc.features());
+ } else {
+ features = fc.features();
+ fi = new PipedFeatureIterator(features);
+ }
- // Iterate the FeatureCollection and fill the series
- int datasetIdx = 0;
- for (; fi.hasNext();) {
- SimpleFeature f = fi.next();
- // Determine X value (NULL not permitted for XYDateset!)
- Number xValue = (Number) f.getAttribute(xAttrName);
+ // Iterate the FeatureCollection and fill the dataset
+ try {
+ int datasetIdx = 0;
+ for (; fi.hasNext();) {
+ SimpleFeature f = fi.next();
+ // Determine X value (NULL not permitted for XYDateset!)
+ Number xValue = (Number) f.getAttribute(xAttrName);
- // Filter out NODATA values
- xValue = chartStyle.filterNoDataValue(ChartStyle.DOMAIN_AXIS, xValue);
- if (xValue == null)
- continue;
+ // Filter out NODATA values
+ xValue = chartStyle.filterNoDataValue(ChartStyle.DOMAIN_AXIS, xValue);
+ if (xValue == null)
+ continue;
- /*
- * Normalization of the domain axis value (if they are not handled as
- * categories)
- */
- if (chartStyle.isAttributeNormalized(ChartStyle.DOMAIN_AXIS))
- xValue = normalize(xValue, xAttrName, statisticsForNormalization);
+ // Normalization of the domain axis value (if they are not handled as
+ // categories)
+ if (chartStyle.isAttributeNormalized(ChartStyle.DOMAIN_AXIS))
+ xValue = normalize(xValue, xAttrName, statisticsForNormalization);
- // 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);
- // AggregationFunction attributeAggregation = chartStyle
- // .getAttributeAggregation(attrIdx);
+ // Determine the Y values and fill the series
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ Double yValue = determineRangeValueFromFeature(
+ f,
+ (Comparable<?>) xValue,
+ attrIdx,
+ chartStyle,
+ statisticsForNormalization,
+ weightSumsForAggregation);
+ // Ignore feature if value is invalid
+ if (yValue == null)
+ continue;
- // Filter out NoDataValues
- yValue = chartStyle.filterNoDataValue(attrIdx, yValue);
- if (yValue == null)
- continue;
+ // Fill series, if no aggregation function is defined.
+ // Otherwise fill statistic (dataset is filled later!)
+ if (chartStyle.getAttributeAggregation(attrIdx) == null) {
+ // Fill series
+ xySeries[attrIdx].add(xValue, yValue);
+ // Mapping between FID and data index in series
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ mapping.setMapping(f.getID(), yAttrName, datasetIdx++);
+ } else {
+ QuantileBin1D aggrStat = statisticsForAggregation[attrIdx].get(xValue);
+ if (aggrStat == null) {
+ aggrStat = new DynamicBin1D();
+ statisticsForAggregation[attrIdx].put((Double) xValue, aggrStat);
+ }
+ aggrStat.add(yValue.doubleValue());
+ // TODO: Mapping vormerken (??)
+ // Problem: siehe unten
- /* Normalization of a range axis value */
+ }
+ }
+ }
+ } finally {
+ if (features != null) {
+ fc.close(features);
+ }
+ }
+
+ // Fill series for aggregated range attributes
+ statisticsForNormalization = calcStatisticsForAggregatedNormalization(
+ statisticsForAggregation,
+ chartStyle);
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ AggregationFunction aggrFunc = chartStyle.getAttributeAggregation(attrIdx);
+ if (aggrFunc == null)
+ continue;
+ for (Comparable<?> xValue : statisticsForAggregation[attrIdx].keySet()) {
+ QuantileBin1D aggrStat = statisticsForAggregation[attrIdx].get(xValue);
+ Number yValue = getAggregationResult(aggrFunc, aggrStat);
+
+ // Normalize the aggregated value
if (chartStyle.isAttributeNormalized(attrIdx))
yValue = normalize(yValue, yAttrName, statisticsForNormalization);
- // Fill series, if no aggregation function is defined.
- // Otherwise fill statistic (dataset is filled later!)
- // if (attributeAggregation == null) {
// Fill series
- xySeries[attrIdx].add(xValue, yValue);
+ xySeries[attrIdx].add((Double) xValue, yValue);
+ // TODO: Mapping setzen
+ // Problem: Pro dataset item kann nur EINE FeatureID gesetzt
+ // werden (Feature2DatasetMapping, Zeile 124)
// Mapping between FID and data index in series
- mapping.setMapping(f.getID(), yAttrName, datasetIdx++);
- // } else {
- // // The values for this are aggregated
- // StaticBin1D aggrStat = statisticsForAggregation[attrIdx]
- // .get(xValue);
- // if (aggrStat == null) {
- // aggrStat = new DynamicBin1D();
- // statisticsForAggregation[attrIdx].put(xValue, aggrStat);
- // }
- //
- // // The default weight is 1
- // // for (int i = 0; i < yWeightValue; i++)
- // aggrStat.add(yValue.doubleValue());
- //
- // // TODO: Mapping vormerken (??)
- // // Problem: siehe unten
- // }
+ // mapping.setMapping(f.getID() ??, yAttrName,
+ // datasetIdx++);
}
}
+ return dataset;
+ }
- // // Fill series for aggregated range attributes
- // for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
- // // String yAttrName = chartStyle.getAttributeName(attrIdx);
- // AggregationFunction aggrFunc = chartStyle
- // .getAttributeAggregation(attrIdx);
- // if (aggrFunc == null)
- // continue;
- // for (Number xValue : statisticsForAggregation[attrIdx].keySet()) {
- // StaticBin1D aggrStat = statisticsForAggregation[attrIdx]
- // .get(xValue);
- // Number yValue = aggrFunc.getResult(aggrStat);
- // // Fill series
- // xySeries[attrIdx].add(xValue, yValue);
- // // TODO: Mapping setzen
- // // Problem: Pro dataset item kann nur EINE FeatureID gesetzt
- // // werden (Feature2DatasetMapping, Zeile 124)
- // // // Mapping between FID and data index in series
- // // mapping.setMapping(f.getID() ??, yAttrName, datasetIdx++);
- // }
- // }
+ /**
+ * Calculates the attribute sum for all weight-aggregated range attributes
+ * grouped by the domain attribute values.<br>
+ * The function returns a {@link HashMap} for each range attribute, but only
+ * the maps of {@linkplain AggregationFunction#isWeighted() weight-aggregated}
+ * attributes contain the weight sums (for each domain attribute value).
+ * @param fc the feature collection the data is taken from
+ * @param chartStyle the chart style
+ * @return an array indexed for awhich contains a {@link HashMap} for
+ */
+ private static HashMap<Comparable<?>, Double>[] calcWeightSumForAggregation(
+ FeatureCollection fc, FeatureChartStyle chartStyle) {
+ int attrCount = chartStyle.getAttributeCount();
+ String domAttrName = chartStyle.getAttributeName(ChartStyle.DOMAIN_AXIS);
+ HashMap<Comparable<?>, Double>[] weightSumsForAggregation = new HashMap[attrCount];
+ for (int i = 1; i < attrCount; i++)
+ weightSumsForAggregation[i] = new HashMap<Comparable<?>, Double>();
- return dataset;
+ // Iterate all features
+ FeatureIterator<SimpleFeature> features = fc.features();
+ try {
+ while (features.hasNext()) {
+ SimpleFeature f = features.next();
+ // ignore features with NODATA in domain attribute
+ Comparable<?> catValue = (Comparable<?>) f.getAttribute(domAttrName);
+ catValue = chartStyle.filterNoDataValue(ChartStyle.DOMAIN_AXIS,
+ catValue);
+ if (catValue == null)
+ continue;
+
+ // Iterate the feature attributes
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ AggregationFunction attributeAggregation = chartStyle.getAttributeAggregation(attrIdx);
+ // Ignore
+ // - not aggregated attributes
+ // - not weighted aggregation attributes
+ if (attributeAggregation == null)
+ continue;
+ if (!attributeAggregation.isWeighted())
+ continue;
+ // weights must be defined for weighted aggregation
+ if (chartStyle.getAttributeAggregationWeightAttributeName(attrIdx) == null)
+ throw new RuntimeException(
+ JFreeChartUtil.R("Exception.noWeightAttributeDefinedforAWeightedAggregation"));
+ // ignore features with NODATA in range attribute
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ Number yValue = (Number) chartStyle.filterNoDataValue(
+ attrIdx,
+ f.getAttribute(yAttrName));
+ if (yValue == null)
+ continue;
+ // ignore features with NODATA in weight attribute
+ String weightAttrName = chartStyle.getAttributeAggregationWeightAttributeName(attrIdx);
+ Number weight = (Number) chartStyle.filterWeightAttributeNoDataValue(
+ attrIdx,
+ f.getAttribute(weightAttrName));
+ if (weight == null)
+ continue;
+
+ Double weightSum = weightSumsForAggregation[attrIdx].get(catValue);
+ if (weightSum == null)
+ weightSum = 0.0;
+ weightSum += weight.doubleValue();
+ weightSumsForAggregation[attrIdx].put(catValue, weightSum);
+ }
+ }
+ } finally {
+ if (features != null) {
+ // this is a hint
+ fc.close(features);
+ }
+ }
+
+ return weightSumsForAggregation;
}
-
/**
* Calculates statistics needed to normalize data. If normalization is not
* used, this function returns an empty map.
@@ -362,33 +442,34 @@
* normalized.
*/
private static HashMap<String, QuantileBin1D> calcStatisticsForAggregatedNormalization(
- HashMap<Comparable<?>,QuantileBin1D>[] aggrAttrValues, FeatureChartStyle chartStyle) {
-
+ HashMap<Comparable<?>, QuantileBin1D>[] aggrAttrValues,
+ FeatureChartStyle chartStyle) {
+
HashMap<String, QuantileBin1D> normStats = new HashMap<String, QuantileBin1D>();
for (int attrIdx = 0; attrIdx < chartStyle.getAttributeCount(); attrIdx++) {
- if ( aggrAttrValues[attrIdx] == null )
+ if (aggrAttrValues[attrIdx] == null)
continue;
if (!chartStyle.isAttributeNormalized(attrIdx))
continue;
String attrName = chartStyle.getAttributeName(attrIdx);
- QuantileBin1D stat = normStats.get(attrName);
+ QuantileBin1D stat = normStats.get(attrName);
if (stat == null) {
stat = new DynamicBin1D();
normStats.put(attrName, stat);
}
-
+
for (Comparable<?> catValue : aggrAttrValues[attrIdx].keySet())
- stat.add( chartStyle.getAttributeAggregation(attrIdx).getResult(aggrAttrValues[attrIdx].get(catValue)) );
+ stat.add(getAggregationResult(
+ chartStyle.getAttributeAggregation(attrIdx),
+ aggrAttrValues[attrIdx].get(catValue)));
}
-
return normStats;
}
-
+
/**
- }
- * Calculates statistics needed to normalize data. If normalization is not
+ * } Calculates statistics needed to normalize data. If normalization is not
* used, this function returns an empty map.
* @param fc {@link FeatureCollection} where the data comes from
* @param chartStyle {@link ChartStyle} to determine which attributes shall be
@@ -411,7 +492,8 @@
// skip the whole iteration
boolean doNormalization = false;
for (int attrIdx = 0; attrIdx < chartStyle.getAttributeCount(); attrIdx++) {
- if (chartStyle.isAttributeNormalized(attrIdx) && chartStyle.getAttributeAggregation(attrIdx) == null) {
+ if (chartStyle.isAttributeNormalized(attrIdx) &&
+ chartStyle.getAttributeAggregation(attrIdx) == null) {
doNormalization = true;
break;
}
@@ -427,7 +509,8 @@
SimpleFeature f = fIt.next();
for (int attrIdx = 0; attrIdx < chartStyle.getAttributeCount(); attrIdx++) {
- if (chartStyle.isAttributeNormalized(attrIdx) && chartStyle.getAttributeAggregation(attrIdx) == null) {
+ if (chartStyle.isAttributeNormalized(attrIdx) &&
+ chartStyle.getAttributeAggregation(attrIdx) == null) {
String attrName = chartStyle.getAttributeName(attrIdx);
@@ -483,10 +566,11 @@
private static Number normalize(Number yValue, String attrName,
HashMap<String, QuantileBin1D> statisticsForNormalization) {
Number zValue;
-
- // Variance and SD can only be calculated for n>1 elements in a category. In case of n = 1 it return NaN. We interpretet it as 0.0 here
- if (Double.isNaN(yValue.doubleValue())){
- return 0.;
+
+ // Variance and SD can only be calculated for n>1 elements in a category. In
+ // case of n = 1 it return NaN. We interpretet it as 0.0 here
+ if (Double.isNaN(yValue.doubleValue())) {
+ return 0.;
}
if (statisticsForNormalization.get(attrName) == null) {
@@ -502,9 +586,9 @@
}
/**
- * Creates a {@link XYDataset} for 2 (or more) attributes of a
- * {@link FeatureCollection}. XYDateset can only be created for <b>numeric</b>
- * attributes.
+ * Creates a {@link CategoryDataset} for 2 (or more) attributes of a
+ * {@link FeatureCollection}. CategoryDataset can only be created for
+ * <b>numeric</b> range 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
@@ -529,13 +613,16 @@
checkAttributeType(fc.getSchema(), chartStyle.getAttributeName(i),
Number.class, "Range attribute", "CategoryDataset");
- // Calculate any statistics needed for normalization.
+ // Calculate statistics to normalize not-aggregated attributes
HashMap<String, QuantileBin1D> statisticsForNormalization = calcStatisticsForNotAggregatedNormalization(
- fc,
- chartStyle);
- // Prepare set for statistics to calculate aggregation
- // functions (one statistic for each range attribute of
- // each domain value)
+ fc,
+ chartStyle);
+ // Calculate weight sums for all weight-aggregated attributes
+ HashMap<Comparable<?>, Double>[] weightSumsForAggregation = calcWeightSumForAggregation(
+ fc,
+ chartStyle);
+ // Prepare set for statistics to calculate aggregation functions (one
+ // statistic for each range attribute of each domain value)
HashMap<Comparable<?>, QuantileBin1D>[] statisticsForAggregation = new HashMap[attrCount];
for (int i = 1; i < attrCount; i++)
statisticsForAggregation[i] = new HashMap<Comparable<?>, QuantileBin1D>();
@@ -546,70 +633,11 @@
fc, dataset);
dataset.setGroup(new FeatureDatasetMetaData(mapping));
- FeatureIterator<SimpleFeature> features = null;
- // Für die Gewichtung muss zuerst Summe aller Gewichte (pro Category!)
- // ermittelt werden
- HashMap<Comparable<?>, Double>[] weightSumsForAggregation = new HashMap[attrCount];
- for (int i = 1; i < attrCount; i++)
- weightSumsForAggregation[i] = new HashMap<Comparable<?>, Double>();
- features = fc.features();
- try {
- while (features.hasNext()) {
- SimpleFeature f = features.next();
- // ignore features with NODATA in domain attribute
- Comparable<?> catValue = (Comparable<?>) f.getAttribute(xAttrName);
- catValue = chartStyle.filterNoDataValue(ChartStyle.DOMAIN_AXIS,
- catValue);
- if (catValue == null)
- continue;
-
- // Iterate the feature attributes
- for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
- AggregationFunction attributeAggregation = chartStyle.getAttributeAggregation(attrIdx);
- // Ignore
- // - not aggregated attributes
- // - not weighted aggregation attributes
- if (attributeAggregation == null)
- continue;
- if (!attributeAggregation.isWeighted())
- continue;
- // weights must be defined for weighted aggregation
- if (chartStyle.getAttributeAggregationWeightAttributeName(attrIdx) == null)
- throw new RuntimeException(
- JFreeChartUtil.R("Exception.noWeightAttributeDefinedforAWeightedAggregation"));
- // ignore features with NODATA in range attribute
- String yAttrName = chartStyle.getAttributeName(attrIdx);
- Number yValue = (Number) chartStyle.filterNoDataValue(
- attrIdx,
- f.getAttribute(yAttrName));
- if (yValue == null)
- continue;
- // ignore features with NODATA in weight attribute
- String weightAttrName = chartStyle.getAttributeAggregationWeightAttributeName(attrIdx);
- Number weight = (Number) chartStyle.filterWeightAttributeNoDataValue(
- attrIdx,
- f.getAttribute(weightAttrName));
- if (weight == null)
- continue;
-
- Double weightSum = weightSumsForAggregation[attrIdx].get(catValue);
- if (weightSum == null)
- weightSum = 0.0;
- weightSum += weight.doubleValue();
- weightSumsForAggregation[attrIdx].put(catValue, weightSum);
- }
- }
- } finally {
- if (features != null) {
- // this is a hint
- fc.close(features);
- }
- }
-
// 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(..))
+ FeatureIterator<SimpleFeature> features = null;
Iterator<SimpleFeature> fi = null;
if (chartStyle.isSortDomainAxis()) {
Vector<SimpleFeature> sortedFeatures = FeatureUtil.sortFeatures(fc,
@@ -621,8 +649,8 @@
fi = new PipedFeatureIterator(features);
}
+ // Iterate the FeatureCollection and fill the dataset
try {
- // Iterate the FeatureCollection and fill the series
for (int fIdx = 0; fi.hasNext(); fIdx++) {
SimpleFeature f = fi.next();
// Determine the category (NULL not permitted!)
@@ -635,30 +663,22 @@
// Determine the Y values and fill the dataset
for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
- AggregationFunction attributeAggregation = chartStyle.getAttributeAggregation(attrIdx);
-
- String yAttrName = chartStyle.getAttributeName(attrIdx);
- Number yValue = (Number) f.getAttribute(yAttrName);
-
- // By default the wight is 1 => no weighting. If the
- // aggregation uses weighting, read the weight attribnute
- // if (attributeAggregation != null &&
- // attributeAggregation.isWeighted())
- // yWeightValue = ((Number) f.getAttribute(WEIGHTATTRIB))
- // .longValue();
-
- // Filter out NoDataValues
- yValue = chartStyle.filterNoDataValue(attrIdx, yValue);
+ Double yValue = determineRangeValueFromFeature(
+ f,
+ catValue,
+ attrIdx,
+ chartStyle,
+ statisticsForNormalization,
+ weightSumsForAggregation);
+ // Ignore feature if value is invalid
if (yValue == null)
continue;
// Fill series, if no aggregation function is defined.
// Otherwise fill statistic (dataset is filled later!)
- if (attributeAggregation == null) {
- /* Normalization of a range axis value */
- if (chartStyle.isAttributeNormalized(attrIdx))
- yValue = normalize(yValue, yAttrName, statisticsForNormalization);
+ if (chartStyle.getAttributeAggregation(attrIdx) == null) {
// Add data to dataset
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
dataset.addValue(yValue, yAttrName, catValue);
// Mapping between FID and data index in series
mapping.setMapping(f.getID(), yAttrName, catValue);
@@ -668,57 +688,46 @@
aggrStat = new DynamicBin1D();
statisticsForAggregation[attrIdx].put(catValue, aggrStat);
}
-
- if (attributeAggregation.isWeighted()) {
- // weight attribute must not be NULL
- Object weightObj = f.getAttribute(chartStyle.getAttributeAggregationWeightAttributeName(attrIdx));
- Number weight = (Number)chartStyle.filterWeightAttributeNoDataValue(attrIdx, weightObj);
- if ( weight == null )
- continue;
- aggrStat.add(yValue.doubleValue() * weight.doubleValue() / weightSumsForAggregation[attrIdx].get(catValue));
- } else {
- aggrStat.add(yValue.doubleValue());
- }
+ aggrStat.add(yValue.doubleValue());
// TODO: Mapping vormerken (??)
// Problem: siehe unten
}
}
-
}
-
- // Fill series for aggregated range attributes
- statisticsForNormalization = calcStatisticsForAggregatedNormalization(statisticsForAggregation,chartStyle);
- for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
- String yAttrName = chartStyle.getAttributeName(attrIdx);
- AggregationFunction aggrFunc = chartStyle.getAttributeAggregation(attrIdx);
- if (aggrFunc == null)
- continue;
- for (Comparable<?> catValue : statisticsForAggregation[attrIdx].keySet()) {
- QuantileBin1D aggrStat = statisticsForAggregation[attrIdx].get(catValue);
- Number yValue = aggrFunc.getResult(aggrStat);
-
- // Normalize the aggregated value
- if ( chartStyle.isAttributeNormalized(attrIdx) ){
-
- yValue = normalize(yValue, yAttrName, statisticsForNormalization);
- }
-
- // Fill series
- dataset.addValue(yValue, yAttrName, catValue);
- // TODO: Mapping setzen
- // Problem: Pro dataset item kann nur EINE FeatureID gesetzt
- // werden (Feature2DatasetMapping, Zeile 124)
- // // Mapping between FID and data index in series
- // mapping.setMapping(f.getID() ??, yAttrName,
- // datasetIdx++);
- }
- }
} finally {
if (features != null) {
fc.close(features);
}
}
+ // Fill series for aggregated range attributes
+ statisticsForNormalization = calcStatisticsForAggregatedNormalization(
+ statisticsForAggregation,
+ chartStyle);
+ for (int attrIdx = 1; attrIdx < attrCount; attrIdx++) {
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ AggregationFunction aggrFunc = chartStyle.getAttributeAggregation(attrIdx);
+ if (aggrFunc == null)
+ continue;
+ for (Comparable<?> catValue : statisticsForAggregation[attrIdx].keySet()) {
+ QuantileBin1D aggrStat = statisticsForAggregation[attrIdx].get(catValue);
+ Number yValue = getAggregationResult(aggrFunc, aggrStat);
+
+ // Normalize the aggregated value
+ if (chartStyle.isAttributeNormalized(attrIdx))
+ yValue = normalize(yValue, yAttrName, statisticsForNormalization);
+
+ // Fill series
+ dataset.addValue(yValue, yAttrName, catValue);
+ // TODO: Mapping setzen
+ // Problem: Pro dataset item kann nur EINE FeatureID gesetzt
+ // werden (Feature2DatasetMapping, Zeile 124)
+ // // Mapping between FID and data index in series
+ // mapping.setMapping(f.getID() ??, yAttrName,
+ // datasetIdx++);
+ }
+ }
+
return dataset;
}
@@ -814,4 +823,129 @@
}
}
}
+
+ /**
+ * Returns the result of the function from a statistic.
+ * @param statistics Statistic to take the result from.
+ */
+ private static Double getAggregationResult(AggregationFunction func,
+ QuantileBin1D statistics) {
+ switch (func) {
+ case AVG:
+ return statistics.mean();
+ case MEDIAN:
+ return statistics.median();
+ case MAX:
+ return statistics.max();
+ case MIN:
+ return statistics.min();
+ case SUM_ABS: // assumes that statistic is already filled properly
+ case SUM_WEIGHTED: // assumes that statistic is already filled properly
+ case AVG_WEIGHTED: // assumes that statistic is already filled properly
+ case SUM:
+ return statistics.sum();
+ case COUNT:
+ return ((Integer) statistics.size()).doubleValue();
+ case STND_DEV:
+ return statistics.size() > 1 ? statistics.standardDeviation() : 0.;
+ case VARIANCE:
+ return statistics.size() > 1 ? statistics.variance() : 0.;
+ }
+ throw new UnsupportedOperationException(
+ "Aggregation function not supported in " +
+ LangUtil.getSimpleClassName(statistics) + ": " + func);
+ }
+
+ /**
+ * Determines the range attribute value from a feature and transforms it
+ * according to the (optional) aggregation function or normalization.
+ * @param feature a feature
+ * @param domValue the domain value (category or x-value)
+ * @param attrIdx the index of the range attribute
+ * @param chartStyle the chart style
+ * @param statisticsForNormalization the pre-calculated statistics used for
+ * normalization
+ * @param weightSumsForAggregation the pre-calculated sums of the (optional)
+ * weight attribute values
+ * @return {@code null} if the feature must be ignored because of an invalid
+ * (null) range or weight value
+ */
+ private static Double determineRangeValueFromFeature(SimpleFeature feature,
+ Comparable<?> domValue, int attrIdx, FeatureChartStyle chartStyle,
+ HashMap<String, QuantileBin1D> statisticsForNormalization,
+ HashMap<Comparable<?>, Double>[] weightSumsForAggregation) {
+
+ AggregationFunction aggrFunc = chartStyle.getAttributeAggregation(attrIdx);
+ String yAttrName = chartStyle.getAttributeName(attrIdx);
+ Number yValue = (Number) feature.getAttribute(yAttrName);
+ // Filter out NoDataValues
+ yValue = chartStyle.filterNoDataValue(attrIdx, yValue);
+ if (yValue == null)
+ return null;
+
+ // if no aggregation is set, the range value is normalized
+ // immediately
+ if (aggrFunc == null) {
+ // Normalization of a range axis value
+ if (chartStyle.isAttributeNormalized(attrIdx))
+ yValue = normalize(yValue, yAttrName, statisticsForNormalization);
+ return yValue.doubleValue();
+ }
+
+ // Determine weight
+ Number weight = null;
+ if (aggrFunc.isWeighted()) {
+ String weightAttrName = chartStyle.getAttributeAggregationWeightAttributeName(attrIdx);
+ weight = (Number) feature.getAttribute(weightAttrName);
+ // Filter out NoDataValues
+ weight = chartStyle.filterWeightAttributeNoDataValue(attrIdx, weight);
+ }
+ // Transform value
+ yValue = transformValueForAggregation(
+ aggrFunc,
+ yValue,
+ weight,
+ weightSumsForAggregation[attrIdx].get(domValue));
+ return yValue.doubleValue();
+ }
+
+ /**
+ * Transforms
+ * @param func
+ * @param value
+ * @param weight
+ * @param weightTotal
+ * @return
+ */
+ private static Double transformValueForAggregation(AggregationFunction func,
+ Number value, Number weight, Number weightTotal) {
+ // no aggregation function -> no transformation
+ if (func == null)
+ return value.doubleValue();
+ // NULL values should be ignored
+ if (value == null)
+ return null;
+
+ double val = value.doubleValue();
+
+ // Apply transformation for aggregation
+ if (func.isAbsoluted())
+ val = Math.abs(val);
+ if (func.isWeighted()) {
+ // if weight is NULL, ignore the value
+ if (weight == null)
+ return null;
+ val = val * weight.doubleValue();
+ }
+ if (func.isAveraged()) {
+ // if weight total is NULL, ignore the value
+ if (weightTotal == null || weightTotal.doubleValue() == 0)
+ return null;
+ val = val / weightTotal.doubleValue();
+ }
+
+ // Aggregation needs no transformation
+ return val;
+ }
+
}
Modified: trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle.properties
===================================================================
--- trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle.properties 2010-03-03 15:35:55 UTC (rev 753)
+++ trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle.properties 2010-03-03 19:13:22 UTC (rev 754)
@@ -61,6 +61,8 @@
AggregationFunction.SUM.Desc=Sum of attribute values
AggregationFunction.SUM_ABS.Title=Absolutes sum
AggregationFunction.SUM_ABS.Desc=Sum of the absolute attribute values
+AggregationFunction.SUM_WEIGHTED.Title=Weighted sum
+AggregationFunction.SUM_WEIGHTED.Desc=Weighted sum of attribute values
AggregationFunction.AVG.Title=Average
AggregationFunction.AVG.Desc=Average attribute value
AggregationFunction.AVG_WEIGHTED.Title=Weighted average
Modified: trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle_de.properties
===================================================================
--- trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle_de.properties 2010-03-03 15:35:55 UTC (rev 753)
+++ trunk/src/schmitzm/jfree/resource/locales/JFreeResourceBundle_de.properties 2010-03-03 19:13:22 UTC (rev 754)
@@ -59,6 +59,8 @@
AggregationFunction.SUM.Desc=Summe der Attribut-Werte
AggregationFunction.SUM_ABS.Title=Absolut-Summe
AggregationFunction.SUM_ABS.Desc=Summe der Attribut-Wert-Betr\u00e4ge
+AggregationFunction.SUM_WEIGHTED.Title=Gewichtete Summe
+AggregationFunction.SUM_WEIGHTED.Desc=Gewichtete Summe der Attribute-Werte
AggregationFunction.AVG.Title=Durschnitt
AggregationFunction.AVG.Desc=Durchschnittlicher Attribut-Wert
AggregationFunction.MEDIAN.Title=Median
More information about the Schmitzm-commits
mailing list