# HG changeset patch # User Andre Heinecke # Date 1380018584 -7200 # Node ID 3c7471b929d1391033129e42571066f00b2ce421 # Parent bdadffad35b10cb2205f10198e99fd1ad5898ff6# Parent a0b00cfe1799406c11a323b501e6339726bc5be5 Merge branch generator-refectoring into default. diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/doc/conf/conf.xml --- a/artifacts/doc/conf/conf.xml Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/doc/conf/conf.xml Tue Sep 24 12:29:44 2013 +0200 @@ -7,6 +7,7 @@ + ]> YOUR_SECRET @@ -284,104 +285,7 @@ - - org.dive4elements.river.exports.DischargeCurveGenerator - org.dive4elements.river.exports.DischargeCurveInfoGenerator - org.dive4elements.river.exports.CrossSectionGenerator - org.dive4elements.river.exports.CrossSectionInfoGenerator - org.dive4elements.river.exports.ComputedDischargeCurveGenerator - org.dive4elements.river.exports.ComputedDischargeCurveInfoGenerator - org.dive4elements.river.exports.LongitudinalSectionGenerator - org.dive4elements.river.exports.LongitudinalSectionInfoGenerator - org.dive4elements.river.exports.DurationCurveGenerator - org.dive4elements.river.exports.DurationCurveInfoGenerator - org.dive4elements.river.exports.DischargeLongitudinalSectionGenerator - org.dive4elements.river.exports.DischargeLongitudinalSectionInfoGenerator - org.dive4elements.river.exports.WaterlevelExporter - org.dive4elements.river.exports.extreme.ExtremeWQCurveGenerator - org.dive4elements.river.exports.WaterlevelExporter - org.dive4elements.river.exports.extreme.ExtremeWQCurveInfoGenerator - org.dive4elements.river.exports.fixings.FixWQCurveGenerator - org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator - org.dive4elements.river.exports.DurationCurveExporter - org.dive4elements.river.exports.ComputedDischargeCurveExporter - org.dive4elements.river.exports.DischargeLongitudinalSectionExporter - org.dive4elements.river.exports.WDifferencesCurveGenerator - org.dive4elements.river.exports.WDifferencesCurveInfoGenerator - org.dive4elements.river.exports.WDifferencesExporter - org.dive4elements.river.exports.MapGenerator - org.dive4elements.river.exports.MapGenerator - org.dive4elements.river.exports.ReferenceCurveGenerator - org.dive4elements.river.exports.NormalizedReferenceCurveGenerator - org.dive4elements.river.exports.NormalizedReferenceCurveInfoGenerator - org.dive4elements.river.exports.ReferenceCurveInfoGenerator - org.dive4elements.river.exports.ReferenceCurveExporter - org.dive4elements.river.exports.HistoricalDischargeCurveGenerator - org.dive4elements.river.exports.HistoricalDischargeCurveInfoGenerator - org.dive4elements.river.exports.HistoricalDischargeWQCurveGenerator - org.dive4elements.river.exports.HistoricalDischargeWQCurveInfoGenerator - org.dive4elements.river.exports.HistoricalDischargeCurveExporter - org.dive4elements.river.exports.FlowVelocityGenerator - org.dive4elements.river.exports.FlowVelocityInfoGenerator - org.dive4elements.river.exports.FlowVelocityExporter - org.dive4elements.river.exports.MiddleBedHeightGenerator - org.dive4elements.river.exports.MiddleBedHeightInfoGenerator - org.dive4elements.river.exports.MiddleBedHeightExporter - org.dive4elements.river.exports.minfo.BedQualityGenerator - org.dive4elements.river.exports.minfo.BedQualityInfoGenerator - org.dive4elements.river.exports.minfo.BedQualityExporter - org.dive4elements.river.exports.minfo.BedDifferenceYearGenerator - org.dive4elements.river.exports.minfo.BedDiffYearInfoGenerator - org.dive4elements.river.exports.minfo.BedDifferenceEpochGenerator - org.dive4elements.river.exports.minfo.BedDiffEpochInfoGenerator - org.dive4elements.river.exports.minfo.BedDiffHeightYearGenerator - org.dive4elements.river.exports.minfo.BedDiffHeightYearInfoGenerator - org.dive4elements.river.exports.minfo.BedDifferenceExporter - org.dive4elements.river.exports.sq.SQRelationGeneratorA - org.dive4elements.river.exports.sq.SQRelationGeneratorB - org.dive4elements.river.exports.sq.SQRelationGeneratorC - org.dive4elements.river.exports.sq.SQRelationGeneratorD - org.dive4elements.river.exports.sq.SQRelationGeneratorE - org.dive4elements.river.exports.sq.SQRelationGeneratorF - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationInfoGenerator - org.dive4elements.river.exports.sq.SQRelationExporter - org.dive4elements.river.exports.sq.SQOverviewGenerator - org.dive4elements.river.exports.fixings.ParametersExporter - org.dive4elements.river.exports.fixings.DeltaWtExporter - org.dive4elements.river.exports.fixings.FixDeltaWtGenerator - org.dive4elements.river.exports.fixings.FixDeltaWtInfoGenerator - org.dive4elements.river.exports.fixings.FixLongitudinalSectionGenerator - org.dive4elements.river.exports.fixings.FixLongitudinalSectionInfoGenerator - org.dive4elements.river.exports.fixings.FixDerivedCurveGenerator - org.dive4elements.river.exports.fixings.FixDerivedCurveInfoGenerator - org.dive4elements.river.exports.WaterlevelExporter - org.dive4elements.river.exports.fixings.FixWQCurveGenerator - org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator - org.dive4elements.river.exports.minfo.SedimentLoadLSGenerator - org.dive4elements.river.exports.minfo.SedimentLoadExporter - org.dive4elements.river.exports.minfo.SedimentLoadLSInfoGenerator - - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - org.dive4elements.river.exports.ReportGenerator - - org.dive4elements.river.exports.ATExporter - org.dive4elements.river.exports.ATExporter - org.dive4elements.river.exports.fixings.FixATExport - org.dive4elements.river.exports.ShapeExporter - + &generators; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/doc/conf/generators.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/doc/conf/generators.xml Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + <subtitle key="chart.longitudinal.section.subtitle" default="-"> + <arg expr="artifact.river"/> + <arg expr="artifact.ld_from" type="double"/> + <arg expr="artifact.ld_to" type="double"/> + </subtitle> + <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km"> + <arg expr="artifact.river"/> + </domain-axis> + <processor class="org.dive4elements.river.exports.process.AnnotationProcessor" axis="none"/> + <processor class="org.dive4elements.river.exports.process.WOutProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.WDiffProcessor" axis="D"/> + <processor class="org.dive4elements.river.exports.process.QOutProcessor" axis="Q"/> + <processor class="org.dive4elements.river.exports.process.BedheightProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.BedDiffYearProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.BedDiffHeightYearProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/> + <!-- The area processor decides based on the facet name which axis to choose. + You have to ensure that there is a W and Q axis accordingly --> + <processor class="org.dive4elements.river.exports.process.AreaProcessor" axis="none"/> + </output-generator> + <output-generator names="w_differences,w_differences_chartinfo" + class="org.dive4elements.river.exports.DiagramGenerator" + converter="org.dive4elements.river.exports.DiagramAttributes"> + <axis name="W"/> + <axis name="D"/> + <axis name="Q" include-zero="true"/> + <axis name="" type="X"/> + <title key="chart.w_differences.title" default="Differences"/> + <subtitle key="chart.w_differences.subtitle" default="-"> + <arg expr="artifact.river"/> + </subtitle> + <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km"> + <arg expr="artifact.river"/> + </domain-axis> + <processor class="org.dive4elements.river.exports.process.AnnotationProcessor" axis="none"/> + <processor class="org.dive4elements.river.exports.process.WOutProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.WDiffProcessor" axis="D"/> + <processor class="org.dive4elements.river.exports.process.QOutProcessor" axis="Q"/> + <processor class="org.dive4elements.river.exports.process.BedheightProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.BedDiffYearProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.BedDiffHeightYearProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/> + <processor class="org.dive4elements.river.exports.process.AreaProcessor" axis="none"/> + </output-generator> + <output-generator names="duration_curve" class="org.dive4elements.river.exports.DurationCurveGenerator"/> + <output-generator names="duration_curve_chartinfo" class="org.dive4elements.river.exports.DurationCurveInfoGenerator"/> + <output-generator names="waterlevel_export" class="org.dive4elements.river.exports.WaterlevelExporter"/> + <output-generator names="extreme_wq_curve" class="org.dive4elements.river.exports.extreme.ExtremeWQCurveGenerator"/> + <output-generator names="extreme_curve_export" class="org.dive4elements.river.exports.WaterlevelExporter"/> + <output-generator names="extreme_wq_curve_chartinfo" class="org.dive4elements.river.exports.extreme.ExtremeWQCurveInfoGenerator"/> + <output-generator names="fix_wq_curve" class="org.dive4elements.river.exports.fixings.FixWQCurveGenerator"/> + <output-generator names="fix_wq_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator"/> + <output-generator names="durationcurve_export" class="org.dive4elements.river.exports.DurationCurveExporter"/> + <output-generator names="computed_dischargecurve_export" class="org.dive4elements.river.exports.ComputedDischargeCurveExporter"/> + <output-generator names="discharge_longitudinal_section_export" class="org.dive4elements.river.exports.DischargeLongitudinalSectionExporter"/> + <output-generator names="w_differences_export" class="org.dive4elements.river.exports.WDifferencesExporter"/> + <output-generator names="floodmap" class="org.dive4elements.river.exports.MapGenerator"/> + <output-generator names="map" class="org.dive4elements.river.exports.MapGenerator"/> + <output-generator names="reference_curve" class="org.dive4elements.river.exports.ReferenceCurveGenerator"/> + <output-generator names="reference_curve_normalized" class="org.dive4elements.river.exports.NormalizedReferenceCurveGenerator"/> + <output-generator names="reference_curve_normalized_chartinfo" class="org.dive4elements.river.exports.NormalizedReferenceCurveInfoGenerator"/> + <output-generator names="reference_curve_chartinfo" class="org.dive4elements.river.exports.ReferenceCurveInfoGenerator"/> + <output-generator names="reference_curve_export" class="org.dive4elements.river.exports.ReferenceCurveExporter"/> + <output-generator names="historical_discharge" class="org.dive4elements.river.exports.HistoricalDischargeCurveGenerator"/> + <output-generator names="historical_discharge_chartinfo" class="org.dive4elements.river.exports.HistoricalDischargeCurveInfoGenerator"/> + <output-generator names="historical_discharge_wq" class="org.dive4elements.river.exports.HistoricalDischargeWQCurveGenerator"/> + <output-generator names="historical_discharge_wq_chartinfo" class="org.dive4elements.river.exports.HistoricalDischargeWQCurveInfoGenerator"/> + <output-generator names="historical_discharge_export" class="org.dive4elements.river.exports.HistoricalDischargeCurveExporter"/> + <output-generator names="flow_velocity" class="org.dive4elements.river.exports.FlowVelocityGenerator"/> + <output-generator names="flow_velocity_chartinfo" class="org.dive4elements.river.exports.FlowVelocityInfoGenerator"/> + <output-generator names="flow_velocity_export" class="org.dive4elements.river.exports.FlowVelocityExporter"/> + <output-generator names="bedheight_middle" class="org.dive4elements.river.exports.MiddleBedHeightGenerator"/> + <output-generator names="bedheight_middle_chartinfo" class="org.dive4elements.river.exports.MiddleBedHeightInfoGenerator"/> + <output-generator names="bedheight_middle_export" class="org.dive4elements.river.exports.MiddleBedHeightExporter"/> + <output-generator names="bed_longitudinal_section" class="org.dive4elements.river.exports.minfo.BedQualityGenerator"/> + <output-generator names="bed_longitudinal_section_chartinfo" class="org.dive4elements.river.exports.minfo.BedQualityInfoGenerator"/> + <output-generator names="bed_quality_export" class="org.dive4elements.river.exports.minfo.BedQualityExporter"/> + <output-generator names="bed_difference_year" class="org.dive4elements.river.exports.minfo.BedDifferenceYearGenerator"/> + <output-generator names="bed_difference_year_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffYearInfoGenerator"/> + <output-generator names="bed_difference_epoch" class="org.dive4elements.river.exports.minfo.BedDifferenceEpochGenerator"/> + <output-generator names="bed_difference_epoch_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffEpochInfoGenerator"/> + <output-generator names="bed_difference_height_year" class="org.dive4elements.river.exports.minfo.BedDiffHeightYearGenerator"/> + <output-generator names="bed_difference_height_year_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffHeightYearInfoGenerator"/> + <output-generator names="bedheight_difference_export" class="org.dive4elements.river.exports.minfo.BedDifferenceExporter"/> + <output-generator names="sq_relation_a" class="org.dive4elements.river.exports.sq.SQRelationGeneratorA"/> + <output-generator names="sq_relation_b" class="org.dive4elements.river.exports.sq.SQRelationGeneratorB"/> + <output-generator names="sq_relation_c" class="org.dive4elements.river.exports.sq.SQRelationGeneratorC"/> + <output-generator names="sq_relation_d" class="org.dive4elements.river.exports.sq.SQRelationGeneratorD"/> + <output-generator names="sq_relation_e" class="org.dive4elements.river.exports.sq.SQRelationGeneratorE"/> + <output-generator names="sq_relation_f" class="org.dive4elements.river.exports.sq.SQRelationGeneratorF"/> + <output-generator names="sq_relation_a_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_b_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_c_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_d_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_e_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_f_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/> + <output-generator names="sq_relation_export" class="org.dive4elements.river.exports.sq.SQRelationExporter"/> + <output-generator names="sq_overview" class="org.dive4elements.river.exports.sq.SQOverviewGenerator"/> + <output-generator names="fix_parameters_export" class="org.dive4elements.river.exports.fixings.ParametersExporter"/> + <output-generator names="fix_deltawt_export" class="org.dive4elements.river.exports.fixings.DeltaWtExporter"/> + <output-generator names="fix_deltawt_curve" class="org.dive4elements.river.exports.fixings.FixDeltaWtGenerator"/> + <output-generator names="fix_deltawt_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixDeltaWtInfoGenerator"/> + <output-generator names="fix_longitudinal_section_curve" class="org.dive4elements.river.exports.fixings.FixLongitudinalSectionGenerator"/> + <output-generator names="fix_longitudinal_section_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixLongitudinalSectionInfoGenerator"/> + <output-generator names="fix_derivate_curve" class="org.dive4elements.river.exports.fixings.FixDerivedCurveGenerator"/> + <output-generator names="fix_derivate_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixDerivedCurveInfoGenerator"/> + <output-generator names="fix_waterlevel_export" class="org.dive4elements.river.exports.WaterlevelExporter"/> + <output-generator names="fix_vollmer_wq_curve" class="org.dive4elements.river.exports.fixings.FixWQCurveGenerator"/> + <output-generator names="fix_vollmer_wq_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator"/> + <output-generator names="sedimentload_ls" class="org.dive4elements.river.exports.minfo.SedimentLoadLSGenerator"/> + <output-generator names="sedimentload_ls_export" class="org.dive4elements.river.exports.minfo.SedimentLoadExporter"/> + <output-generator names="sedimentload_ls_chartinfo" class="org.dive4elements.river.exports.minfo.SedimentLoadLSInfoGenerator"/> + <!-- Error report generators. --> + <output-generator names="discharge_longitudinal_section_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="waterlevel_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="computed_dischargecurve_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="durationcurve_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="wsplgen_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="historical_discharge_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="reference_curve_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="fix_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="extreme_curve_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <output-generator names="sedimentload_ls_report" class="org.dive4elements.river.exports.ReportGenerator"/> + <!-- AT exporter. --> + <output-generator names="computed_dischargecurve_at_export" class="org.dive4elements.river.exports.ATExporter"/> + <output-generator names="gauge_discharge_curve_at_export" class="org.dive4elements.river.exports.ATExporter"/> + <output-generator names="fix_wq_curve_at_export" class="org.dive4elements.river.exports.fixings.FixATExport"/> + <output-generator names="wsplgen" class="org.dive4elements.river.exports.ShapeExporter"/> +</output-generators> + diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/pom.xml --- a/artifacts/pom.xml Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/pom.xml Tue Sep 24 12:29:44 2013 +0200 @@ -75,9 +75,9 @@ <version>1.0-SNAPSHOT</version> </dependency> <dependency> - <groupId>jfree</groupId> + <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> - <version>1.0.13</version> + <version>1.0.15</version> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java Tue Sep 24 12:29:44 2013 +0200 @@ -17,6 +17,7 @@ import org.dive4elements.artifactdatabase.DefaultArtifactContext; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.exports.OutGenerator; +import org.dive4elements.river.utils.Pair; /** @@ -99,17 +100,26 @@ ? (RiverContext) context : (RiverContext) context.globalContext(); - Map<String, Class> generators = (Map<String, Class>) - flysContext.get(RiverContext.OUTGENERATORS_KEY); + Map<String, Pair<Class<OutGenerator>, Object>> generators = + (Map<String, Pair<Class<OutGenerator>, Object>>)flysContext + .get(RiverContext.OUTGENERATORS_KEY); if (generators == null) { return null; } - Class clazz = generators.get(name); + Pair<Class<OutGenerator>, Object> pair = generators.get(name); + + if (pair == null) { + logger.warn("No generator class found for " + name); + return null; + } try { - return clazz != null ? (OutGenerator) clazz.newInstance() : null; + Class<OutGenerator> clazz = pair.getA(); + OutGenerator generator = clazz.newInstance(); + generator.setup(pair.getB()); + return generator; } catch (InstantiationException ie) { logger.error(ie, ie); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java Tue Sep 24 12:29:44 2013 +0200 @@ -8,24 +8,8 @@ package org.dive4elements.river.artifacts.context; -import org.dive4elements.artifactdatabase.state.State; -import org.dive4elements.artifactdatabase.state.StateEngine; -import org.dive4elements.artifactdatabase.transition.Transition; -import org.dive4elements.artifactdatabase.transition.TransitionEngine; -import org.dive4elements.artifacts.ArtifactContextFactory; -import org.dive4elements.artifacts.GlobalContext; -import org.dive4elements.artifacts.common.utils.Config; -import org.dive4elements.artifacts.common.utils.XMLUtils; -import org.dive4elements.river.artifacts.model.Module; -import org.dive4elements.river.artifacts.model.ZoomScale; -import org.dive4elements.river.artifacts.states.StateFactory; -import org.dive4elements.river.artifacts.transitions.TransitionFactory; -import org.dive4elements.river.themes.Theme; -import org.dive4elements.river.themes.ThemeFactory; -import org.dive4elements.river.themes.ThemeGroup; -import org.dive4elements.river.themes.ThemeMapping; +import java.io.File; -import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -34,12 +18,41 @@ import javax.xml.xpath.XPathConstants; import org.apache.log4j.Logger; + +import org.dive4elements.artifactdatabase.state.State; +import org.dive4elements.artifactdatabase.state.StateEngine; + +import org.dive4elements.artifactdatabase.transition.Transition; +import org.dive4elements.artifactdatabase.transition.TransitionEngine; + +import org.dive4elements.artifacts.ArtifactContextFactory; +import org.dive4elements.artifacts.GlobalContext; + +import org.dive4elements.artifacts.common.utils.Config; +import org.dive4elements.artifacts.common.utils.ElementConverter; +import org.dive4elements.artifacts.common.utils.XMLUtils; + +import org.dive4elements.river.artifacts.model.Module; +import org.dive4elements.river.artifacts.model.ZoomScale; + +import org.dive4elements.river.artifacts.states.StateFactory; + +import org.dive4elements.river.artifacts.transitions.TransitionFactory; + +import org.dive4elements.river.exports.OutGenerator; + +import org.dive4elements.river.themes.Theme; +import org.dive4elements.river.themes.ThemeFactory; +import org.dive4elements.river.themes.ThemeGroup; +import org.dive4elements.river.themes.ThemeMapping; + +import org.dive4elements.river.utils.Pair; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; - /** * The ArtifactContextFactory is used to initialize basic components and put * them into the global context of the application. @@ -307,7 +320,6 @@ * @param context the RiverContext. */ protected void configureOutGenerators(Document config, RiverContext context){ - Map<String, Class<?>> generators = new HashMap<String, Class<?>>(); NodeList outGenerators = (NodeList) XMLUtils.xpath( config, @@ -323,28 +335,60 @@ logger.info("Found " + num + " configured output generators."); + Map<String, Pair<Class<OutGenerator>, Object>> generators = + new HashMap<String, Pair<Class<OutGenerator>, Object>>(); + int idx = 0; for (int i = 0; i < num; i++) { - Node item = outGenerators.item(i); + Element item = (Element)outGenerators.item(i); - String name = (String) XMLUtils.xpath( - item, "@name", XPathConstants.STRING); + String names = item.getAttribute("names").trim(); + String clazz = item.getAttribute("class").trim(); + String converter = item.getAttribute("converter").trim(); - String clazz = (String) XMLUtils.xpath( - item, "text()", XPathConstants.STRING); - - if (name == null || clazz == null) { + if (names.isEmpty() || clazz.isEmpty()) { continue; } + Class<OutGenerator> generatorClass = null; + try { - generators.put(name, Class.forName(clazz)); - - idx++; + generatorClass = (Class<OutGenerator>)Class.forName(clazz); } catch (ClassNotFoundException cnfe) { - logger.warn(cnfe, cnfe); + logger.error(cnfe, cnfe); + continue; + } + + Object cfg = null; + + if (!converter.isEmpty()) { + try { + ElementConverter ec = + (ElementConverter)Class.forName(converter) + .newInstance(); + cfg = ec.convert(item); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe, cnfe); + } + catch (InstantiationException ie) { + logger.error(ie); + } + catch (IllegalAccessException iae) { + logger.error(iae); + } + } + + Pair<Class<OutGenerator>, Object> pair = + new Pair<Class<OutGenerator>, Object>(generatorClass, cfg); + + for (String key: names.split("[\\s,]")) { + if (!(key = key.trim()).isEmpty()) { + generators.put(key, pair); + idx++; + } } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java Tue Sep 24 12:29:44 2013 +0200 @@ -109,7 +109,7 @@ } public boolean guessWaterIncreasing(float factor) { - return DataUtil.guessWaterIncreasing(ws, factor); + return DataUtil.guessDataIncreasing(ws, factor); } public int [] longestIncreasingWRangeIndices() { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java Tue Sep 24 12:29:44 2013 +0200 @@ -24,5 +24,10 @@ TDoubleArrayList allWs(); boolean guessWaterIncreasing(); + + /** Guess if the Water flows from right to left. + * + * @return True if km's and ws's both grow in the same direction */ + boolean guessRTLData(); } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java Tue Sep 24 12:29:44 2013 +0200 @@ -82,11 +82,16 @@ @Override public boolean guessWaterIncreasing() { - return guessWaterIncreasing(0.05f); + return guessDataIncreasing(0.05f); } - protected boolean guessWaterIncreasing(float factor) { - return DataUtil.guessWaterIncreasing(ws, factor); + protected boolean guessDataIncreasing(float factor) { + return DataUtil.guessDataIncreasing(ws, factor); + } + + @Override + public boolean guessRTLData() { + return DataUtil.guessSameDirectionData(ws, allKms()); } @Override diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java Tue Sep 24 12:29:44 2013 +0200 @@ -8,6 +8,7 @@ package org.dive4elements.river.artifacts.model; +import org.dive4elements.river.utils.DataUtil; import org.dive4elements.river.utils.DoubleUtil; import gnu.trove.TDoubleArrayList; @@ -153,5 +154,10 @@ /* Behold the first km might be larger then the last! */ return new double[] {getKm(0), getKm(size()-1)}; } + + @Override + public boolean guessRTLData() { + return DataUtil.guessSameDirectionData(ws, allKms()); + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java Tue Sep 24 12:29:44 2013 +0200 @@ -77,7 +77,7 @@ CALCULATION_DISCHARGE_CURVE, CALCULATION_HISTORICAL_DISCHARGE_CURVE, CALCULATION_DURATION_CURVE, -// CALCULATION_DISCHARGE_LONGITUDINAL_CURVE, + CALCULATION_DISCHARGE_LONGITUDINAL_CURVE, CALCULATION_W_DIFFERENCES, CALCULATION_REFERENCE_CURVE //, // CALCULATION_EXTREME diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java --- a/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java Tue Sep 24 12:29:44 2013 +0200 @@ -330,7 +330,7 @@ // XXX NOTE: the outGen is not able to process its generate() operation, // because it has no OutputStream set! - outGen.init(XMLUtils.newDocument(), null, getContext()); + outGen.init(out, XMLUtils.newDocument(), null, getContext()); prepareMasterArtifact(outGen); try { @@ -382,16 +382,12 @@ // If type contains 'chartinfo' use a generator that // just allow access to width, height etc. - OutGenerator generator = null; - if (type != null - && type.length() > 0 - && type.indexOf("chartinfo") > 0) - { - generator = RiverContext.getOutGenerator(context, type, subtype); - } - else { - generator = RiverContext.getOutGenerator(context, name, subtype); - } + + String key = type != null && !type.isEmpty() && type.indexOf("chartinfo") > 0 + ? type + : name; + + OutGenerator generator = RiverContext.getOutGenerator(context, key, subtype); if (generator == null) { log.error("There is no generator specified for output: " + name); @@ -417,7 +413,7 @@ } } - generator.init(format, out, context); + generator.init(key, format, out, context); generator.setSettings(settings); generator.setCollection(this); prepareMasterArtifact(generator); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -44,7 +44,8 @@ protected WQ data; protected CallContext context; protected OutputStream out; - protected D4EArtifact master; + protected D4EArtifact master; + protected String outName; protected D4EArtifactCollection collection; @@ -53,7 +54,13 @@ } @Override - public void init(Document request, OutputStream out, CallContext context) { + public void setup(Object config) { + logger.debug("ATExporter.setup"); + } + + @Override + public void init(String outName, Document request, OutputStream out, CallContext context) { + this.outName = outName; this.context = context; this.out = out; } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -65,6 +65,9 @@ /** XPath that points to the desired export facet. */ public static final String XPATH_FACET = "/art:action/@art:type"; + /** The out name to serve. */ + protected String outName; + /** The document of the incoming out() request. */ protected Document request; @@ -115,11 +118,21 @@ */ protected abstract void addData(Object data); + public void setup(Object config) { + logger.debug("AbstractExporter.setup"); + } + @Override - public void init(Document request, OutputStream out, CallContext context) { + public void init( + String outName, + Document request, + OutputStream out, + CallContext context + ) { logger.debug("AbstractExporter.init"); + this.outName = outName; this.request = request; this.out = out; this.context = context; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -30,6 +30,7 @@ import org.dive4elements.river.jfree.Style; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledSeries; +import org.dive4elements.river.jfree.AxisDataset; import org.dive4elements.river.model.River; import org.dive4elements.river.themes.LineStyle; import org.dive4elements.river.themes.TextStyle; @@ -136,6 +137,8 @@ /** List of annotations to insert in plot. */ protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>(); + protected String outName; + /** * A mini interface that allows to walk over the YAXIS enums defined in * subclasses. @@ -149,26 +152,6 @@ - public interface AxisDataset { - - void addDataset(XYDataset dataset); - - XYDataset[] getDatasets(); - - boolean isEmpty(); - - void setRange(Range range); - - Range getRange(); - - boolean isArea(XYDataset dataset); - - void setPlotAxisIndex(int idx); - - int getPlotAxisIndex(); - - } // end of AxisDataset interface - /** @@ -178,6 +161,10 @@ datasets = new TreeMap<Integer, AxisDataset>(); } + @Override + public void setup(Object config) { + logger.debug("ChartGenerator.setup"); + } /** * Adds annotations to list. The given annotation will be visible. @@ -699,9 +686,10 @@ @Override - public void init(Document request, OutputStream out, CallContext context) { + public void init(String outName, Document request, OutputStream out, CallContext context) { logger.debug("ChartGenerator.init"); + this.outName = outName; this.request = request; this.out = out; this.context = context; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,1572 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.TexturePaint; +import java.awt.Transparency; + +import java.awt.geom.Rectangle2D; + +import java.awt.image.BufferedImage; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifactdatabase.state.Settings; + +import org.dive4elements.artifacts.Artifact; +import org.dive4elements.artifacts.ArtifactNamespaceContext; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.artifacts.CallMeta; +import org.dive4elements.artifacts.PreferredLocale; + +import org.dive4elements.artifacts.common.utils.XMLUtils; + +import org.dive4elements.river.artifacts.D4EArtifact; + +import org.dive4elements.river.artifacts.access.RangeAccess; + +import org.dive4elements.river.artifacts.resources.Resources; + +import org.dive4elements.river.collections.D4EArtifactCollection; + +import org.dive4elements.river.java2d.NOPGraphics2D; + +import org.dive4elements.river.jfree.AxisDataset; +import org.dive4elements.river.jfree.Bounds; +import org.dive4elements.river.jfree.DoubleBounds; +import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer; +import org.dive4elements.river.jfree.RiverAnnotation; +import org.dive4elements.river.jfree.StableXYDifferenceRenderer; +import org.dive4elements.river.jfree.Style; +import org.dive4elements.river.jfree.StyledAreaSeriesCollection; +import org.dive4elements.river.jfree.StyledSeries; + +import org.dive4elements.river.model.River; + +import org.dive4elements.river.themes.ThemeDocument; + +import org.dive4elements.river.utils.Formatter; +import org.dive4elements.river.utils.RiverUtils; + +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; + +import org.jfree.chart.axis.NumberAxis; + +import org.jfree.chart.plot.XYPlot; + +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; + +import org.jfree.chart.title.TextTitle; + +import org.jfree.data.Range; + +import org.jfree.data.general.Series; + +import org.jfree.data.xy.XYDataset; + +import org.jfree.ui.RectangleInsets; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Implementation of the OutGenerator interface for charts. + * It should provide some basic things that equal in all chart types. + * + */ +public abstract class ChartGenerator2 implements OutGenerator { + + private static Logger logger = Logger.getLogger(ChartGenerator2.class); + + public static final boolean USE_NOP_GRAPHICS = + Boolean.getBoolean("info.rendering.nop.graphics"); + + + public static final int DEFAULT_CHART_WIDTH = 600; + public static final int DEFAULT_CHART_HEIGHT = 400; + public static final String DEFAULT_CHART_FORMAT = "png"; + public static final Color DEFAULT_GRID_COLOR = Color.GRAY; + public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; + public static final int DEFAULT_FONT_SIZE = 12; + public static final String DEFAULT_FONT_NAME = "Tahoma"; + + + public static final String XPATH_CHART_SIZE = + "/art:action/art:attributes/art:size"; + + public static final String XPATH_CHART_FORMAT = + "/art:action/art:attributes/art:format/@art:value"; + + public static final String XPATH_CHART_X_RANGE = + "/art:action/art:attributes/art:xrange"; + + public static final String XPATH_CHART_Y_RANGE = + "/art:action/art:attributes/art:yrange"; + + + /** The document of the incoming out() request.*/ + protected Document request; + + /** The output stream where the data should be written to.*/ + protected OutputStream out; + + /** The CallContext object.*/ + protected CallContext context; + + protected D4EArtifactCollection collection; + + /** The artifact that is used to decorate the chart with meta information.*/ + protected Artifact master; + + /** The settings that should be used during output creation.*/ + protected Settings settings; + + /** Map of datasets ("index"). */ + protected SortedMap<Integer, AxisDataset> datasets; + + /** List of annotations to insert in plot. */ + protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>(); + + protected abstract List<AxisSection> buildYAxisSections(); + + protected String outName; + + /** + * Default constructor that initializes internal data structures. + */ + public ChartGenerator2() { + datasets = new TreeMap<Integer, AxisDataset>(); + } + + /** + * Adds annotations to list. The given annotation will be visible. + */ + public void addAnnotations(RiverAnnotation annotation) { + annotations.add(annotation); + } + + /** + * This method needs to be implemented by concrete subclasses to create new + * instances of JFreeChart. + * + * @return a new instance of a JFreeChart. + */ + public abstract JFreeChart generateChart(); + + + /** For every outable (i.e. facets), this function is + * called and handles the data accordingly. */ + @Override + public abstract void doOut( + ArtifactAndFacet bundle, + ThemeDocument attr, + boolean visible); + + + + protected abstract Series getSeriesOf(XYDataset dataset, int idx); + + /** + * Returns the default title of a chart. + * + * @return the default title of a chart. + */ + protected abstract String getDefaultChartTitle(); + + protected abstract String getDefaultYAxisLabel(String axisName); + + + /** + * Returns the default X-Axis label of a chart. + * + * @return the default X-Axis label of a chart. + */ + protected abstract String getDefaultXAxisLabel(); + + /** + * This method is used to create new AxisDataset instances which may differ + * in concrete subclasses. + * + * @param idx The index of an axis. + */ + protected abstract AxisDataset createAxisDataset(int idx); + + + /** + * Combines the ranges of the X axis at index <i>idx</i>. + * + * @param bounds A new Bounds. + * @param idx The index of the X axis that should be comined with + * <i>range</i>. + */ + protected abstract void combineXBounds(Bounds bounds, int idx); + + + /** + * Combines the ranges of the Y axis at index <i>idx</i>. + * + * @param bounds A new Bounds. + * @param index The index of the Y axis that should be comined with. + * <i>range</i>. + */ + protected abstract void combineYBounds(Bounds bounds, int index); + + + /** + * This method is used to determine the ranges for axes at a given index. + * + * @param index The index of the axes at the plot. + * + * @return a Range[] with [xrange, yrange]; + */ + public abstract Range[] getRangesForAxis(int index); + + public abstract Bounds getXBounds(int axis); + + protected abstract void setXBounds(int axis, Bounds bounds); + + public abstract Bounds getYBounds(int axis); + + protected abstract void setYBounds(int axis, Bounds bounds); + + + /** + * This method retrieves the chart subtitle by calling getChartSubtitle() + * and adds it as TextTitle to the chart. + * The default implementation of getChartSubtitle() returns the same + * as getDefaultChartSubtitle() which must be implemented by derived + * classes. If you want to add multiple subtitles to the chart override + * this method and add your subtitles manually. + * + * @param chart The JFreeChart chart object. + */ + protected void addSubtitles(JFreeChart chart) { + String subtitle = getChartSubtitle(); + + if (subtitle != null && subtitle.length() > 0) { + chart.addSubtitle(new TextTitle(subtitle)); + } + } + + /** + * Generate chart. + */ + @Override + public void generate() throws IOException { + + logger.debug("ChartGenerator2.generate"); + + if (outName.indexOf("chartinfo") > 0) { + generateInfo(); + } + else { + generateImage(); + } + } + + + /** Generate only meta infos */ + private void generateInfo() throws IOException { + + logger.debug("ChartInfoGenerator2.generateInfo"); + + JFreeChart chart = generateChart(); + + int[] size = getSize(); + if (size == null) { + size = getDefaultSize(); + } + + ChartRenderingInfo info = new ChartRenderingInfo(); + + long startTime = System.currentTimeMillis(); + + if (USE_NOP_GRAPHICS) { + BufferedImage image = + new BufferedImage(size[0], size[1], Transparency.BITMASK); + + Graphics2D g2d = image.createGraphics(); + Graphics2D nop = new NOPGraphics2D(g2d); + + chart.draw( + nop, + new Rectangle2D.Double(0, 0, size[0], size[1]), + null, + info); + + nop.dispose(); + } + else { + chart.createBufferedImage( + size[0], size[1], Transparency.BITMASK, info); + } + + long stopTime = System.currentTimeMillis(); + + if (logger.isDebugEnabled()) { + logger.debug("Rendering info took: " + + (stopTime-startTime) + "ms"); + } + + + InfoGeneratorHelper2 helper = new InfoGeneratorHelper2(this); + Document doc = helper.createInfoDocument(chart, info); + + XMLUtils.toStream(doc, out); + } + + /** Generate the diagram as an image. */ + private void generateImage() throws IOException { + logger.debug("ChartGenerator2.generateImage"); + + JFreeChart chart = generateChart(); + + String format = getFormat(); + int[] size = getSize(); + + if (size == null) { + size = getExportDimension(); + } + + context.putContextValue("chart.width", size[0]); + context.putContextValue("chart.height", size[1]); + + if (format.equals(ChartExportHelper.FORMAT_PNG)) { + context.putContextValue("chart.image.format", "png"); + + ChartExportHelper.exportImage( + out, + chart, + context); + } + else if (format.equals(ChartExportHelper.FORMAT_PDF)) { + preparePDFContext(context); + + ChartExportHelper.exportPDF( + out, + chart, + context); + } + else if (format.equals(ChartExportHelper.FORMAT_SVG)) { + prepareSVGContext(context); + + ChartExportHelper.exportSVG( + out, + chart, + context); + } + else if (format.equals(ChartExportHelper.FORMAT_CSV)) { + context.putContextValue("chart.image.format", "csv"); + + ChartExportHelper.exportCSV( + out, + chart, + context); + } + } + + + @Override + public void init(String outName, Document request, OutputStream out, CallContext context) { + logger.debug("ChartGenerator2.init"); + + this.outName = outName; + this.request = request; + this.out = out; + this.context = context; + } + + + /** Sets the master artifact. */ + @Override + public void setMasterArtifact(Artifact master) { + this.master = master; + } + + + /** + * Gets the master artifact. + * @return the master artifact. + */ + public Artifact getMaster() { + return master; + } + + + /** Sets the collection. */ + @Override + public void setCollection(D4EArtifactCollection collection) { + this.collection = collection; + } + + + @Override + public void setSettings(Settings settings) { + this.settings = settings; + } + + + /** + * Returns an instance of <i>ChartSettings</i> with a chart specific section + * but with no axes settings. + * + * @return an instance of <i>ChartSettings</i>. + */ + @Override + public Settings getSettings() { + if (this.settings != null) { + return this.settings; + } + + ChartSettings settings = new ChartSettings(); + + ChartSection chartSection = buildChartSection(); + LegendSection legendSection = buildLegendSection(); + ExportSection exportSection = buildExportSection(); + + settings.setChartSection(chartSection); + settings.setLegendSection(legendSection); + settings.setExportSection(exportSection); + + List<AxisSection> axisSections = buildAxisSections(); + for (AxisSection axisSection: axisSections) { + settings.addAxisSection(axisSection); + } + + return settings; + } + + + /** + * Creates a new <i>ChartSection</i>. + * + * @return a new <i>ChartSection</i>. + */ + protected ChartSection buildChartSection() { + ChartSection chartSection = new ChartSection(); + chartSection.setTitle(getChartTitle()); + chartSection.setSubtitle(getChartSubtitle()); + chartSection.setDisplayGrid(isGridVisible()); + chartSection.setDisplayLogo(showLogo()); + chartSection.setLogoVPlacement(logoVPlace()); + chartSection.setLogoHPlacement(logoHPlace()); + return chartSection; + } + + + /** + * Creates a new <i>LegendSection</i>. + * + * @return a new <i>LegendSection</i>. + */ + protected LegendSection buildLegendSection() { + LegendSection legendSection = new LegendSection(); + legendSection.setVisibility(isLegendVisible()); + legendSection.setFontSize(getLegendFontSize()); + legendSection.setAggregationThreshold(10); + return legendSection; + } + + + /** + * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b> + * and <b>HEIGHT=400</b>. + * + * @return a new <i>ExportSection</i>. + */ + protected ExportSection buildExportSection() { + ExportSection exportSection = new ExportSection(); + exportSection.setWidth(600); + exportSection.setHeight(400); + return exportSection; + } + + + /** + * Creates a list of Sections that contains all axes of the chart (including + * X and Y axes). + * + * @return a list of Sections for each axis in this chart. + */ + protected List<AxisSection> buildAxisSections() { + List<AxisSection> axisSections = new ArrayList<AxisSection>(); + + axisSections.addAll(buildXAxisSections()); + axisSections.addAll(buildYAxisSections()); + + return axisSections; + } + + + /** + * Creates a new Section for chart's X axis. + * + * @return a List that contains a Section for the X axis. + */ + protected List<AxisSection> buildXAxisSections() { + List<AxisSection> axisSections = new ArrayList<AxisSection>(); + + String identifier = "X"; + + AxisSection axisSection = new AxisSection(); + axisSection.setIdentifier(identifier); + axisSection.setLabel(getXAxisLabel()); + axisSection.setFontSize(14); + axisSection.setFixed(false); + + // XXX We are able to find better default ranges that [0,0], but the Y + // axes currently have no better ranges set. + axisSection.setUpperRange(0d); + axisSection.setLowerRange(0d); + + axisSections.add(axisSection); + + return axisSections; + } + + + /** + * Returns the <i>settings</i> as <i>ChartSettings</i>. + * + * @return the <i>settings</i> as <i>ChartSettings</i> or null, if + * <i>settings</i> is not an instance of <i>ChartSettings</i>. + */ + public ChartSettings getChartSettings() { + if (settings instanceof ChartSettings) { + return (ChartSettings) settings; + } + + return null; + } + + + /** + * Returns the chart title provided by <i>settings</i>. + * + * @param settings A ChartSettings object. + * + * @return the title provided by <i>settings</i> or null if no + * <i>ChartSection</i> is provided by <i>settings</i>. + * + * @throws NullPointerException if <i>settings</i> is null. + */ + public String getChartTitle(ChartSettings settings) { + ChartSection cs = settings.getChartSection(); + return cs != null ? cs.getTitle() : null; + } + + + /** + * Returns the chart subtitle provided by <i>settings</i>. + * + * @param settings A ChartSettings object. + * + * @return the subtitle provided by <i>settings</i> or null if no + * <i>ChartSection</i> is provided by <i>settings</i>. + * + * @throws NullPointerException if <i>settings</i> is null. + */ + public String getChartSubtitle(ChartSettings settings) { + ChartSection cs = settings.getChartSection(); + return cs != null ? cs.getSubtitle() : null; + } + + + /** + * Returns a boolean object that determines if the chart grid should be + * visible or not. This information needs to be provided by <i>settings</i>, + * otherweise the default is true. + * + * @param settings A ChartSettings object. + * + * @return true, if the chart grid should be visible otherwise false. + * + * @throws NullPointerException if <i>settings</i> is null. + */ + public boolean isGridVisible(ChartSettings settings) { + ChartSection cs = settings.getChartSection(); + Boolean displayGrid = cs.getDisplayGrid(); + + return displayGrid != null ? displayGrid : true; + } + + + /** + * Returns a boolean object that determines if the chart legend should be + * visible or not. This information needs to be provided by <i>settings</i>, + * otherwise the default is true. + * + * @param settings A ChartSettings object. + * + * @return true, if the chart legend should be visible otherwise false. + * + * @throws NullPointerException if <i>settings</i> is null. + */ + public boolean isLegendVisible(ChartSettings settings) { + LegendSection ls = settings.getLegendSection(); + Boolean displayLegend = ls.getVisibility(); + + return displayLegend != null ? displayLegend : true; + } + + + /** + * Returns the legend font size specified in <i>settings</i> or null if no + * <i>LegendSection</i> is provided by <i>settings</i>. + * + * @param settings A ChartSettings object. + * + * @return the legend font size or null. + * + * @throws NullPointerException if <i>settings</i> is null. + */ + public Integer getLegendFontSize(ChartSettings settings) { + LegendSection ls = settings.getLegendSection(); + return ls != null ? ls.getFontSize() : null; + } + + + /** + * Returns the title of a chart. The return value depends on the existence + * of ChartSettings: if there are ChartSettings set, this method returns the + * chart title provided by those settings. Otherwise, this method returns + * getDefaultChartTitle(). + * + * @return the title of a chart. + */ + protected String getChartTitle() { + ChartSettings chartSettings = getChartSettings(); + + if (chartSettings != null) { + return getChartTitle(chartSettings); + } + + return getDefaultChartTitle(); + } + + + /** + * Returns the subtitle of a chart. The return value depends on the + * existence of ChartSettings: if there are ChartSettings set, this method + * returns the chart title provided by those settings. Otherwise, this + * method returns getDefaultChartSubtitle(). + * + * @return the subtitle of a chart. + */ + protected String getChartSubtitle() { + ChartSettings chartSettings = getChartSettings(); + + if (chartSettings != null) { + return getChartSubtitle(chartSettings); + } + + return getDefaultChartSubtitle(); + } + + + /** + * This method always returns null. Override it in subclasses that require + * subtitles. + * + * @return null. + */ + protected String getDefaultChartSubtitle() { + // Override this method in subclasses + return null; + } + + + /** + * This method is used to determine, if the chart's legend is visible or + * not. If a <i>settings</i> instance is set, this instance determines the + * visibility otherwise, this method returns true as default if no + * <i>settings</i> is set. + * + * @return true, if the legend should be visible, otherwise false. + */ + protected boolean isLegendVisible() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings != null) { + return isLegendVisible(chartSettings); + } + + return true; + } + + + /** Where to place the logo. */ + protected String logoHPlace() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings != null) { + ChartSection cs = chartSettings.getChartSection(); + String place = cs.getLogoHPlacement(); + + return place; + } + return "center"; + } + + + /** Where to place the logo. */ + protected String logoVPlace() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings != null) { + ChartSection cs = chartSettings.getChartSection(); + String place = cs.getLogoVPlacement(); + + return place; + } + return "top"; + } + + + /** Return the logo id from settings. */ + protected String showLogo(ChartSettings chartSettings) { + if (chartSettings != null) { + ChartSection cs = chartSettings.getChartSection(); + String logo = cs.getDisplayLogo(); + + return logo; + } + return "none"; + } + + + /** + * This method is used to determine if a logo should be added to the plot. + * + * @return logo name (null if none). + */ + protected String showLogo() { + ChartSettings chartSettings = getChartSettings(); + return showLogo(chartSettings); + } + + + /** + * This method is used to determine the font size of the chart's legend. If + * a <i>settings</i> instance is set, this instance determines the font + * size, otherwise this method returns 12 as default if no <i>settings</i> + * is set or if it doesn't provide a legend font size. + * + * @return a legend font size. + */ + protected int getLegendFontSize() { + Integer fontSize = null; + + ChartSettings chartSettings = getChartSettings(); + if (chartSettings != null) { + fontSize = getLegendFontSize(chartSettings); + } + + return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; + } + + + /** + * This method is used to determine if the resulting chart should display + * grid lines or not. <b>Note: this method always returns true!</b> + * + * @return true, if the chart should display grid lines, otherwise false. + */ + protected boolean isGridVisible() { + return true; + } + + + /** + * Returns the X-Axis label of a chart. + * + * @return the X-Axis label of a chart. + */ + protected String getXAxisLabel() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return getDefaultXAxisLabel(); + } + + AxisSection as = chartSettings.getAxisSection("X"); + if (as != null) { + String label = as.getLabel(); + + if (label != null) { + return label; + } + } + + return getDefaultXAxisLabel(); + } + + + /** + * This method returns the font size for the X axis. If the font size is + * specified in ChartSettings (if <i>chartSettings</i> is set), this size is + * returned. Otherwise the default font size 12 is returned. + * + * @return the font size for the x axis. + */ + protected int getXAxisLabelFontSize() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return DEFAULT_FONT_SIZE; + } + + AxisSection as = chartSettings.getAxisSection("X"); + Integer fontSize = as.getFontSize(); + + return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; + } + + /** + * Glue between axis names and index. + */ + protected abstract String axisIndexToName(int index); + + /** + * This method returns the font size for an Y axis. If the font size is + * specified in ChartSettings (if <i>chartSettings</i> is set), this size is + * returned. Otherwise the default font size 12 is returned. + * + * @return the font size for the x axis. + */ + protected int getYAxisFontSize(int index) { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return DEFAULT_FONT_SIZE; + } + + AxisSection as = chartSettings.getAxisSection(axisIndexToName(index)); + if (as == null) { + return DEFAULT_FONT_SIZE; + } + Integer fontSize = as.getFontSize(); + + return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; + } + + /** + * This method returns the export dimension specified in ChartSettings as + * int array [width,height]. + * + * @return an int array with [width,height]. + */ + protected int[] getExportDimension() { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return new int[] { 600, 400 }; + } + + ExportSection export = chartSettings.getExportSection(); + Integer width = export.getWidth(); + Integer height = export.getHeight(); + + if (width != null && height != null) { + return new int[] { width, height }; + } + + return new int[] { 600, 400 }; + } + + protected abstract String getYAxisLabel(String axisName); + + /** + * This method searches for a specific axis in the <i>settings</i> if + * <i>settings</i> is set. If the axis was found, this method returns the + * specified axis range if the axis range is fixed. Otherwise, this method + * returns null. + * + * @param axisId The identifier of an axis. + * + * @return the specified axis range from <i>settings</i> if the axis is + * fixed, otherwise null. + */ + public Range getRangeForAxisFromSettings(String axisId) { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return null; + } + + AxisSection as = chartSettings.getAxisSection(axisId); + + if (as == null) { + return null; + } + + Boolean fixed = as.isFixed(); + + if (fixed != null && fixed) { + Double upper = as.getUpperRange(); + Double lower = as.getLowerRange(); + + if (upper != null && lower != null) { + return lower < upper + ? new Range(lower, upper) + : new Range(upper, lower); + } + } + + return null; + } + + + /** + * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>. + * + * @param dataset An XYDataset. + * @param idx The axis index. + * @param visible Determines, if the dataset should be visible or not. + */ + public void addAxisDataset(XYDataset dataset, int idx, boolean visible) { + if (dataset == null || idx < 0) { + return; + } + + AxisDataset axisDataset = getAxisDataset(idx); + + Bounds[] xyBounds = ChartHelper.getBounds(dataset); + + if (xyBounds == null) { + logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); + return; + } + + if (visible) { + if (logger.isDebugEnabled()) { + logger.debug("Add new AxisDataset at index: " + idx); + logger.debug("X extent: " + xyBounds[0]); + logger.debug("Y extent: " + xyBounds[1]); + } + + axisDataset.addDataset(dataset); + } + + combineXBounds(xyBounds[0], 0); + combineYBounds(xyBounds[1], idx); + } + + + /** + * This method grants access to the AxisDatasets stored in <i>datasets</i>. + * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is + * created using <i>createAxisDataset()</i>. + * + * @param idx The index of the desired AxisDataset. + * + * @return an existing or new AxisDataset. + */ + public AxisDataset getAxisDataset(int idx) { + AxisDataset axisDataset = datasets.get(idx); + + if (axisDataset == null) { + axisDataset = createAxisDataset(idx); + datasets.put(idx, axisDataset); + } + + return axisDataset; + } + + + /** + * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart + * <i>Settings</i> are applied in this method. + * + * @param plot The XYPlot which is adapted. + */ + protected void adjustPlot(XYPlot plot) { + Stroke gridStroke = new BasicStroke( + DEFAULT_GRID_LINE_WIDTH, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 3.0f, + new float[] { 3.0f }, + 0.0f); + + ChartSettings cs = getChartSettings(); + boolean isGridVisible = cs != null ? isGridVisible(cs) : true; + + plot.setDomainGridlineStroke(gridStroke); + plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); + plot.setDomainGridlinesVisible(isGridVisible); + + plot.setRangeGridlineStroke(gridStroke); + plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); + plot.setRangeGridlinesVisible(isGridVisible); + + plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); + } + + + /** + * This helper mehtod is used to extract the current locale from instance + * vairable <i>context</i>. + * + * @return the current locale. + */ + protected Locale getLocale() { + CallMeta meta = context.getMeta(); + PreferredLocale[] prefs = meta.getLanguages(); + + int len = prefs != null ? prefs.length : 0; + + Locale[] locales = new Locale[len]; + + for (int i = 0; i < len; i++) { + locales[i] = prefs[i].getLocale(); + } + + return meta.getPreferredLocale(locales); + } + + + /** + * Look up \param key in i18n dictionary. + * @param key key for which to find i18nd version. + * @param def default, returned if lookup failed. + * @return value found in i18n dictionary, \param def if no value found. + */ + public String msg(String key, String def) { + return Resources.getMsg(context.getMeta(), key, def); + } + + /** + * Look up \param key in i18n dictionary. + * @param key key for which to find i18nd version. + * @return value found in i18n dictionary, key itself if failed. + */ + public String msg(String key) { + return Resources.getMsg(context.getMeta(), key, key); + } + + public String msg(String key, String def, Object[] args) { + return Resources.getMsg(context.getMeta(), key, def, args); + } + + + protected String getRiverName() { + D4EArtifact flys = (D4EArtifact) master; + + River river = RiverUtils.getRiver(flys); + return (river != null) ? river.getName() : ""; + } + + + protected double[] getRange() { + D4EArtifact flys = (D4EArtifact) master; + + RangeAccess rangeAccess = new RangeAccess(flys); + return rangeAccess.getKmRange(); + } + + + /** + * Returns the size of a chart export as array which has been specified by + * the incoming request document. + * + * @return the size of a chart as [width, height] or null if no width or + * height are given in the request document. + */ + protected int[] getSize() { + int[] size = new int[2]; + + Element sizeEl = (Element)XMLUtils.xpath( + request, + XPATH_CHART_SIZE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (sizeEl != null) { + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + String w = sizeEl.getAttributeNS(uri, "width"); + String h = sizeEl.getAttributeNS(uri, "height"); + + if (w.length() > 0 && h.length() > 0) { + try { + size[0] = Integer.parseInt(w); + size[1] = Integer.parseInt(h); + } + catch (NumberFormatException nfe) { + logger.warn("Wrong values for chart width/height."); + } + } + } + + return size[0] > 0 && size[1] > 0 ? size : null; + } + + + /** + * This method returns the format specified in the <i>request</i> document + * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in + * <i>request</i>. + * + * @return the format used to export this chart. + */ + protected String getFormat() { + String format = (String) XMLUtils.xpath( + request, + XPATH_CHART_FORMAT, + XPathConstants.STRING, + ArtifactNamespaceContext.INSTANCE); + + return format == null || format.length() == 0 + ? DEFAULT_CHART_FORMAT + : format; + } + + + /** + * Returns the X-Axis range as String array from request document. + * If the (x|y)range elements are not found in request document, return + * null (i.e. not zoomed). + * + * @return a String array with [lower, upper], null if not in document. + */ + protected String[] getDomainAxisRangeFromRequest() { + Element xrange = (Element)XMLUtils.xpath( + request, + XPATH_CHART_X_RANGE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (xrange == null) { + return null; + } + + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + String lower = xrange.getAttributeNS(uri, "from"); + String upper = xrange.getAttributeNS(uri, "to"); + + return new String[] { lower, upper }; + } + + + /** Returns null if the (x|y)range-element was not found in request document. + * This usally means that the axis are not manually zoomed, i.e. showing + * full data extent. */ + protected String[] getValueAxisRangeFromRequest() { + Element yrange = (Element)XMLUtils.xpath( + request, + XPATH_CHART_Y_RANGE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (yrange == null) { + return null; + } + + + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + String lower = yrange.getAttributeNS(uri, "from"); + String upper = yrange.getAttributeNS(uri, "to"); + + return new String[] { lower, upper }; + } + + + /** + * Returns the default size of a chart export as array. + * + * @return the default size of a chart as [width, height]. + */ + protected int[] getDefaultSize() { + return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; + } + + + /** + * Add datasets stored in instance variable <i>datasets</i> to plot. + * <i>datasets</i> actually stores instances of AxisDataset, so each of this + * datasets is mapped to a specific axis as well. + * + * @param plot plot to add datasets to. + */ + protected void addDatasets(XYPlot plot) { + logger.debug("addDatasets()"); + + // AxisDatasets are sorted, but some might be empty. + // Thus, generate numbering on the fly. + int axisIndex = 0; + int datasetIndex = 0; + + for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { + if (!entry.getValue().isEmpty()) { + // Add axis and range information. + AxisDataset axisDataset = entry.getValue(); + NumberAxis axis = createYAxis(entry.getKey()); + + plot.setRangeAxis(axisIndex, axis); + + if (axis.getAutoRangeIncludesZero()) { + axisDataset.setRange( + Range.expandToInclude(axisDataset.getRange(), 0d)); + } + + setYBounds(axisIndex, expandPointRange(axisDataset.getRange())); + + // Add contained datasets, mapping to axis. + for (XYDataset dataset: axisDataset.getDatasets()) { + plot.setDataset(datasetIndex, dataset); + plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); + + applyThemes(plot, dataset, + datasetIndex, + axisDataset.isArea(dataset)); + + datasetIndex++; + } + + axisDataset.setPlotAxisIndex(axisIndex); + axisIndex++; + } + } + } + + + /** + * @param idx "index" of dataset/series (first dataset to be drawn has + * index 0), correlates with renderer index. + * @param isArea true if the series describes an area and shall be rendered + * as such. + */ + protected void applyThemes( + XYPlot plot, + XYDataset series, + int idx, + boolean isArea + ) { + if (isArea) { + applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); + } + else { + applyLineTheme(plot, series, idx); + } + } + + + /** + * This method applies the themes defined in the series itself. Therefore, + * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer + * for the series. + * + * @param plot The plot. + * @param dataset The XYDataset which needs to support Series objects. + * @param idx The index of the renderer / dataset. + */ + protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { + logger.debug("Apply LineTheme for dataset at index: " + idx); + + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection anno = plot.getFixedLegendItems(); + + Font legendFont = createLegendLabelFont(); + + XYLineAndShapeRenderer renderer = createRenderer(plot, idx); + + for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { + Series series = getSeriesOf(dataset, s); + + if (series instanceof StyledSeries) { + Style style = ((StyledSeries) series).getStyle(); + style.applyTheme(renderer, s); + } + + // special case: if there is just one single item, we need to enable + // points for this series, otherwise we would not see anything in + // the chart area. + if (series.getItemCount() == 1) { + renderer.setSeriesShapesVisible(s, true); + } + + LegendItem legendItem = renderer.getLegendItem(idx, s); + if (legendItem.getLabel().endsWith(" ") || + legendItem.getLabel().endsWith("interpol")) { + legendItem = null; + } + + if (legendItem != null) { + legendItem.setLabelFont(legendFont); + lic.add(legendItem); + } + else { + logger.warn("Could not get LegentItem for renderer: " + + idx + ", series-idx " + s); + } + } + + if (anno != null) { + lic.addAll(anno); + } + + plot.setFixedLegendItems(lic); + + plot.setRenderer(idx, renderer); + } + + + /** + * @param plot The plot. + * @param area A StyledAreaSeriesCollection object. + * @param idx The index of the dataset. + */ + protected void applyAreaTheme( + XYPlot plot, + StyledAreaSeriesCollection area, + int idx + ) { + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection anno = plot.getFixedLegendItems(); + + Font legendFont = createLegendLabelFont(); + + logger.debug("Registering an 'area'renderer at idx: " + idx); + + StableXYDifferenceRenderer dRenderer = + new StableXYDifferenceRenderer(); + + if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { + dRenderer.setPositivePaint(createTransparentPaint()); + } + + plot.setRenderer(idx, dRenderer); + + area.applyTheme(dRenderer); + + // i18n + dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(context.getMeta(), 2, 4)); + + dRenderer.setAreaLabelTemplate(Resources.getMsg( + context.getMeta(), "area.label.template", "Area=%sm2")); + + LegendItem legendItem = dRenderer.getLegendItem(idx, 0); + if (legendItem != null) { + legendItem.setLabelFont(legendFont); + lic.add(legendItem); + } + else { + logger.warn("Could not get LegentItem for renderer: " + + idx + ", series-idx " + 0); + } + + if (anno != null) { + lic.addAll(anno); + } + + plot.setFixedLegendItems(lic); + } + + + /** + * Expands a given range if it collapses into one point. + * + * @param range Range to be expanded if upper == lower bound. + * + * @return Bounds of point plus 5 percent in each direction. + */ + private Bounds expandPointRange(Range range) { + if (range == null) { + return null; + } + else if (range.getLowerBound() == range.getUpperBound()) { + Range expandedRange = ChartHelper.expandRange(range, 5d); + return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound()); + } + + return new DoubleBounds(range.getLowerBound(), range.getUpperBound()); + } + + + /** + * Creates a new instance of EnhancedLineAndShapeRenderer. + * + * @param plot The plot which is set for the new renderer. + * @param idx This value is not used in the current implementation. + * + * @return a new instance of EnhancedLineAndShapeRenderer. + */ + protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { + logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); + + EnhancedLineAndShapeRenderer r = + new EnhancedLineAndShapeRenderer(true, false); + + r.setPlot(plot); + + return r; + } + + + /** + * Creates a new instance of <i>IdentifiableNumberAxis</i>. + * + * @param idx The index of the new axis. + * @param label The label of the new axis. + * + * @return an instance of IdentifiableNumberAxis. + */ + protected NumberAxis createNumberAxis(int idx, String label) { + return new IdentifiableNumberAxis(axisIndexToName(idx), label); + } + + + /** + * Create Y (range) axis for given index. + * Shall be overriden by subclasses. + */ + protected NumberAxis createYAxis(int index) { + + Font labelFont = new Font( + DEFAULT_FONT_NAME, + Font.BOLD, + getYAxisFontSize(index)); + + String axisName = axisIndexToName(index); + + IdentifiableNumberAxis axis = new IdentifiableNumberAxis( + axisName, getYAxisLabel(axisName)); + + axis.setAutoRangeIncludesZero(false); + axis.setLabelFont(labelFont); + axis.setTickLabelFont(labelFont); + + return axis; + } + + + /** + * Creates a new LegendItem with <i>name</i> and font provided by + * <i>createLegendLabelFont()</i>. + * + * @param theme The theme of the chart line. + * @param name The displayed name of the item. + * + * @return a new LegendItem instance. + */ + public LegendItem createLegendItem(ThemeDocument theme, String name) { + // OPTIMIZE Pass font, parsed Theme items. + + Color color = theme.parseLineColorField(); + if (color == null) { + color = Color.BLACK; + } + + LegendItem legendItem = new LegendItem(name, color); + + legendItem.setLabelFont(createLegendLabelFont()); + return legendItem; + } + + + /** + * Creates Font (Family and size) to use when creating Legend Items. The + * font size depends in the return value of <i>getLegendFontSize()</i>. + * + * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>. + */ + protected Font createLegendLabelFont() { + return new Font( + DEFAULT_FONT_NAME, + Font.PLAIN, + getLegendFontSize() + ); + } + + + /** + * Create new legend entries, dependent on settings. + * @param plot The plot for which to modify the legend. + */ + public void aggregateLegendEntries(XYPlot plot) { + int AGGR_THRESHOLD = 0; + + if (getChartSettings() == null) { + return; + } + Integer threshold = getChartSettings().getLegendSection() + .getAggregationThreshold(); + + AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0; + + LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD); + } + + + /** + * Returns a transparently textured paint. + * + * @return a transparently textured paint. + */ + protected static Paint createTransparentPaint() { + // TODO why not use a transparent color? + BufferedImage texture = new BufferedImage( + 1, 1, BufferedImage.TYPE_4BYTE_ABGR); + + return new TexturePaint( + texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); + } + + + protected void preparePDFContext(CallContext context) { + int[] dimension = getExportDimension(); + + context.putContextValue("chart.width", dimension[0]); + context.putContextValue("chart.height", dimension[1]); + context.putContextValue("chart.marginLeft", 5f); + context.putContextValue("chart.marginRight", 5f); + context.putContextValue("chart.marginTop", 5f); + context.putContextValue("chart.marginBottom", 5f); + context.putContextValue( + "chart.page.format", + ChartExportHelper.DEFAULT_PAGE_SIZE); + } + + + protected void prepareSVGContext(CallContext context) { + int[] dimension = getExportDimension(); + + context.putContextValue("chart.width", dimension[0]); + context.putContextValue("chart.height", dimension[1]); + context.putContextValue( + "chart.encoding", + ChartExportHelper.DEFAULT_ENCODING); + } + + /** + * Retuns the call context. May be null if init hasn't been called yet. + * + * @return the CallContext instance + */ + public CallContext getCallContext() { + return context; + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -69,6 +69,10 @@ this.generator = generator; } + public void setup(Object config) { + logger.debug("ChartInfoGenerator.setup"); + } + /** * Dispatches the operation to the instantiated generator. @@ -77,10 +81,10 @@ * @param out * @param context */ - public void init(Document request, OutputStream out, CallContext context) { + public void init(String outName, Document request, OutputStream out, CallContext context) { this.out = out; - generator.init(request, out, context); + generator.init(outName, request, out, context); } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -19,8 +19,6 @@ import java.text.DateFormat; import java.util.Locale; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; @@ -32,7 +30,6 @@ import org.dive4elements.artifacts.common.utils.Config; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.river.artifacts.D4EArtifact; @@ -82,15 +79,10 @@ protected boolean isCalculated; protected Date validSince; - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("ComputedDischargeCurveExporter.init"); - - super.init(request, out, context); - - this.data = new ArrayList<WQKms>(); + public ComputedDischargeCurveExporter() { + data = new ArrayList<WQKms>(); } - @Override protected void addData(Object d) { if (d instanceof CalculationResult) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -70,7 +70,6 @@ /** Trivial Constructor. */ public CrossSectionGenerator() { - super(); } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/DiagramAttributes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramAttributes.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,374 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import org.dive4elements.river.exports.process.Processor; + +import org.apache.log4j.Logger; + +import org.dive4elements.artifacts.CallContext; + +import org.dive4elements.river.artifacts.resources.Resources; +import org.dive4elements.river.artifacts.D4EArtifact; + +import org.dive4elements.artifacts.common.utils.ElementConverter; + +public class DiagramAttributes +implements ElementConverter +{ + private static Logger log = Logger.getLogger(DiagramAttributes.class); + + public class Instance { + + private List<Processor> processors; + + public Instance() { + processors = createProcessors(); + } + + private List<Processor> createProcessors() { + List<Processor> processors = + new ArrayList<Processor>(axesProcessors.size()); + for (AxisProcessor ap: axesProcessors) { + Processor pr = ap.createProcessor(); + if (pr != null) { + processors.add(pr); + } + } + return processors; + } + + public Processor getProcessorForAxisName(String axisName) { + for (Processor pr: processors) { + String aName = pr.getAxisName(); + if (aName != null && axisName.equals(aName)) { + return pr; + } + } + return null; + } + + public List<Processor> getProcessors() { + return processors; + } + + public Title getTitle() { + return DiagramAttributes.this.getTitle(); + } + + public Title getSubtitle() { + return DiagramAttributes.this.getSubtitle(); + } + + public Title getDomainAxisTitle() { + return DiagramAttributes.this.getDomainAxisTitle(); + } + + public int getAxisIndex(String axisName) { + return DiagramAttributes.this.getAxisIndex(axisName); + } + + public String getAxisName(int index) { + return DiagramAttributes.this.getAxisName(index); + } + + public List<AxisAttributes> getAxesAttributes() { + return DiagramAttributes.this.getAxesAttributes(); + } + } // class Instance + + public static class AxisAttributes { + private String name; + private boolean isLeftAlign; + private boolean forceAlign; + private boolean includeZero; + + public AxisAttributes() { + } + + public AxisAttributes( + String name, + boolean isLeftAlign, + boolean forceAlign, + boolean includeZero + ) { + this.name = name; + this.isLeftAlign = isLeftAlign; + this.forceAlign = forceAlign; + this.includeZero = includeZero; + } + + public String getName() { + return name; + } + + public boolean isLeftAlign() { + return isLeftAlign; + } + + public boolean forceAlign() { + return forceAlign; + } + + public boolean includeZero() { + return includeZero; + } + } // class AxisAttributes + + public static class AxisProcessor { + + private Class<Processor> processorClass; + private String axisName; + + public AxisProcessor(Class<Processor> processorClass, String axisName) { + this.processorClass = processorClass; + this.axisName = axisName; + } + + public Processor createProcessor() { + try { + Processor pr = processorClass.newInstance(); + pr.setAxisName(axisName); + return pr; + } + catch (InstantiationException ie) { + log.error(ie, ie); + } + catch (IllegalAccessException iae) { + log.error(iae, iae); + } + return null; + } + + } // class AxisProcessor + + public static class Argument { + private String expression; + private String type; + + public Argument() { + } + + public Argument(String expression, String type) { + this.expression = expression; + this.type = type; + } + + public Object evaluate(D4EArtifact artifact, CallContext context) { + if (expression.startsWith("artifact.")) { + String value = artifact.getDataAsString( + expression.substring("artifact.".length())); + return convert(value); + } + if (expression.startsWith("context.")) { + return context.getContextValue( + expression.substring("context.".length())); + } + return expression; + } + + private Object convert(String value) { + if (value == null || type == null) { + return value; + } + if ("double".equals(type)) { + return Double.valueOf(value); + } + if ("int".equals(type)) { + return Integer.valueOf(value); + } + // TODO: more types + return value; + } + } // class Argument + + public static class Title { + + private String key; + private String def; + private List<Argument> arguments; + + public Title() { + arguments = new ArrayList<Argument>(5); + } + + public Title(String key) { + this(key, key); + } + + public Title(String key, String def) { + this(); + this.key = key; + this.def = def; + } + + public String getKey() { + return key; + } + + public void addArgument(Argument argument) { + arguments.add(argument); + } + + public String evaluate(D4EArtifact artifact, CallContext context) { + if (key == null || key.isEmpty()) { + return def; + } + Object [] args = new Object[arguments.size()]; + for (int i = 0; i < args.length; ++i) { + args[i] = arguments.get(i).evaluate(artifact, context); + } + return Resources.getMsg(context.getMeta(), key, def, args); + } + } // class Title + + private List<AxisAttributes> axesAttrs; + private List<AxisProcessor> axesProcessors; + + private Title title; + private Title subtitle; + private Title domainAxisTitle; + + public DiagramAttributes() { + axesAttrs = new ArrayList<AxisAttributes>(5); + axesProcessors = new ArrayList<AxisProcessor>(5); + } + + @Override + public Object convert(Element config) { + parseAxis(config); + parseProcessors(config); + parseTitle(config); + parseSubtitle(config); + parseDomainAxisTitle(config); + return this; + } + + public List<AxisAttributes> getAxesAttributes() { + return axesAttrs; + } + + private void parseAxis(Element config) { + NodeList axisNodes = config.getElementsByTagName("axis"); + + for (int i = 0, N = axisNodes.getLength(); i < N; ++i) { + Element axisElement = (Element)axisNodes.item(i); + String name = axisElement.getAttribute("name").trim(); + String align = axisElement.getAttribute("align").trim(); + String includeZero = + axisElement.getAttribute("include-zero").trim(); + if (name.isEmpty()) { + continue; + } + boolean isleftAlign = false; + boolean forceAlign = false; + for (String part: align.split("[\\s,]")) { + part = part.trim(); + if ("left" .equals(part)) isleftAlign = true; + else if ("right".equals(part)) isleftAlign = false; + else if ("force".equals(part)) forceAlign = true; + } + + axesAttrs.add(new AxisAttributes( + name, isleftAlign, forceAlign, + includeZero.equals("true"))); + } + } + + public List<AxisProcessor> getAxesProcessors() { + return axesProcessors; + } + + public Title getTitle() { + return title; + } + + public Title getSubtitle() { + return subtitle; + } + + public Title getDomainAxisTitle() { + return domainAxisTitle; + } + + private void parseProcessors(Element config) { + NodeList processorNodes = config.getElementsByTagName("processor"); + + for (int i = 0, N = processorNodes.getLength(); i < N; ++i) { + Element processorElement = (Element)processorNodes.item(i); + String className = processorElement.getAttribute("class").trim(); + String axisName = processorElement.getAttribute("axis").trim(); + if (className.isEmpty() || axisName.isEmpty()) { + continue; + } + + try { + Class<Processor> processorClass = + (Class<Processor>)Class.forName(className); + axesProcessors.add(new AxisProcessor(processorClass, axisName)); + } + catch (ClassNotFoundException cnfe) { + log.error(cnfe, cnfe); + } + } + } + + private void parseTitle(Element config) { + title = extractTitle(config, "title"); + } + + private void parseSubtitle(Element config) { + subtitle = extractTitle(config, "subtitle"); + } + + private void parseDomainAxisTitle(Element config) { + domainAxisTitle = extractTitle(config, "domain-axis"); + } + + private static Title extractTitle(Element config, String tagName) { + NodeList titleNodes = config.getElementsByTagName(tagName); + if (titleNodes.getLength() < 1) { + return null; + } + Element titleElement = (Element)titleNodes.item(0); + String key = titleElement.getAttribute("key"); + String def = titleElement.getAttribute("default"); + Title title = new Title(key, def); + NodeList argumentNodes = titleElement.getElementsByTagName("arg"); + for (int i = 0, N = argumentNodes.getLength(); i < N; ++i) { + Element argumentElement = (Element)argumentNodes.item(i); + String expression = argumentElement.getAttribute("expr"); + String type = argumentElement.getAttribute("type"); + title.addArgument(new Argument(expression, type)); + } + return title; + } + + public int getAxisIndex(String axisName) { + for (int i = axesAttrs.size()-1; i >= 0; --i) { + if (axesAttrs.get(i).getName().equals(axisName)) { + return i; + } + } + return -1; + } + + public String getAxisName(int index) { + return index < 0 || index >= axesAttrs.size() + ? "" // null? + : axesAttrs.get(index).getName(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,1119 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports; + +import java.awt.Color; +import java.awt.Font; + +import java.text.NumberFormat; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.ImageIcon; + +import org.apache.log4j.Logger; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifactdatabase.state.Facet; + +import org.dive4elements.river.artifacts.D4EArtifact; + +import org.dive4elements.river.exports.process.Processor; + +import org.dive4elements.river.jfree.AnnotationHelper; +import org.dive4elements.river.jfree.AxisDataset; +import org.dive4elements.river.jfree.Bounds; +import org.dive4elements.river.jfree.DoubleBounds; +import org.dive4elements.river.jfree.StyledAreaSeriesCollection; + +import org.dive4elements.river.themes.ThemeDocument; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; + +import org.jfree.chart.annotations.XYAnnotation; +import org.jfree.chart.annotations.XYImageAnnotation; + +import org.jfree.chart.axis.LogarithmicAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; + +import org.jfree.chart.plot.Marker; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; + +import org.jfree.data.Range; + +import org.jfree.data.general.Series; + +import org.jfree.data.xy.XYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + + +/** + * The main diagram creation class. + * + * This class is the glue between output processors and facets. + * The generator creates one diagram and calls the appropiate + * processors for the state and + * + * With respect to datasets, ranges and axis, there are following requirements: + * <ul> + * <li> First in, first drawn: "Early" datasets should be of lower Z-Oder + * than later ones (only works per-axis). </li> + * <li> Visible axis should initially show the range of all datasets that + * show data for this axis (even invisible ones). Motivation: Once + * a dataset (theme) has been activated, it should be on screen. </li> + * <li> There should always be a Y-Axis on the "left". </li> + * </ul> + */ +public class DiagramGenerator extends ChartGenerator2 { + + public static final int AXIS_SPACE = 5; + + /** The logger that is used in this generator. */ + private static Logger logger = Logger.getLogger(DiagramGenerator.class); + + protected List<Marker> domainMarkers = new ArrayList<Marker>(); + + protected List<Marker> valueMarkers = new ArrayList<Marker>(); + + /** The max X range to include all X values of all series for each axis. */ + protected Map<Integer, Bounds> xBounds; + + /** The max Y range to include all Y values of all series for each axis. */ + protected Map<Integer, Bounds> yBounds; + + /** Whether or not the plot is inverted (left-right). */ + private boolean inverted; + + protected DiagramAttributes.Instance diagramAttributes; + + public DiagramGenerator() { + super(); + + xBounds = new HashMap<Integer, Bounds>(); + yBounds = new HashMap<Integer, Bounds>(); + } + + @Override + public void setup(Object config) { + + if (!(config instanceof DiagramAttributes)) { + logger.error("invalid config type"); + return; + } + DiagramAttributes da = (DiagramAttributes)config; + diagramAttributes = da.new Instance(); + } + + /** + * Generate the chart anew (including localized axis and all). + */ + @Override + public JFreeChart generateChart() { + logger.debug("DiagramGenerator.generateChart"); + + JFreeChart chart = ChartFactory.createXYLineChart( + getChartTitle(), + "", + "", + null, + PlotOrientation.VERTICAL, + isLegendVisible(), + false, + false); + + XYPlot plot = (XYPlot) chart.getPlot(); + ValueAxis axis = createXAxis(getXAxisLabel()); + plot.setDomainAxis(axis); + + chart.setBackgroundPaint(Color.WHITE); + plot.setBackgroundPaint(Color.WHITE); + addSubtitles(chart); + adjustPlot(plot); + + //debugAxis(plot); + + addDatasets(plot); + + //debugDatasets(plot); + + addMarkers(plot); + + recoverEmptyPlot(plot); + preparePointRanges(plot); + + //debugAxis(plot); + + localizeAxes(plot); + adjustAxes(plot); + if (!(axis instanceof LogarithmicAxis)) { + // XXX: + // The auto zoom without a range tries + // to include 0 in a logarithmic axis + // which triggers a bug in jfreechart that causes + // the values to be drawn carthesian + autoZoom(plot); + } + + //debugAxis(plot); + + // These have to go after the autozoom. + AnnotationHelper.addAnnotationsToRenderer(annotations, plot, + getChartSettings(), datasets); + + // Add a logo (maybe). + addLogo(plot); + + aggregateLegendEntries(plot); + + return chart; + } + + public String getOutName() { + return outName; + } + + /** + * Return left most data points x value (on first axis). + */ + protected double getLeftX() { + if (inverted) { + return (Double)getXBounds(0).getUpper(); + } + return (Double)getXBounds(0).getLower(); + } + + + /** + * Return right most data points x value (on first axis). + */ + protected double getRightX() { + if (inverted) { + return (Double)getXBounds(0).getLower(); + } + return (Double)getXBounds(0).getUpper(); + } + + + /** Add a logo as background annotation to plot. */ + protected void addLogo(XYPlot plot) { + String logo = showLogo(); + if (logo == null) { + logger.debug("No logo to show chosen"); + return; + } + + ImageIcon imageIcon = null; + if (logo.equals("none")) { + return; + } + /* + If you want to add images, remember to change code in these places: + flys-artifacts: + DiagramGenerator.java + Timeseries*Generator.java and + in the flys-client projects Chart*Propert*Editor.java. + Also, these images have to be put in + flys-artifacts/src/main/resources/images/ + flys-client/src/main/webapp/images/ + */ + java.net.URL imageURL; + if (logo.equals("Intevation")) { + imageURL = DiagramGenerator.class.getResource("/images/intevation.png"); + } + else { // TODO else if ... + imageURL = DiagramGenerator.class.getResource("/images/bfg_logo.gif"); + } + imageIcon = new ImageIcon(imageURL); + + + double xPos = 0d, yPos = 0d; + + String placeh = logoHPlace(); + String placev = logoVPlace(); + + if (placev == null || placev.equals("none")) { + placev = "top"; + } + if (placev.equals("top")) { + yPos = (Double)getYBounds(0).getUpper(); + } + else if (placev.equals("bottom")) { + yPos = (Double)getYBounds(0).getLower(); + } + else if (placev.equals("center")) { + yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d; + } + else { + logger.debug("Unknown place-v value: " + placev); + } + + if (placeh == null || placeh.equals("none")) { + placeh = "center"; + } + if (placeh.equals("left")) { + xPos = getLeftX(); + } + else if (placeh.equals("right")) { + xPos = getRightX(); + } + else if (placeh.equals("center")) { + xPos = ((Double)getXBounds(0).getUpper() + (Double)getXBounds(0).getLower())/2d; + } + else { + logger.debug("Unknown place-h value: " + placeh); + } + + logger.debug("logo position: " + xPos + "/" + yPos); + + org.jfree.ui.RectangleAnchor anchor + = org.jfree.ui.RectangleAnchor.TOP; + if (placev.equals("top")) { + if (placeh.equals("left")) { + anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT; + } + else if (placeh.equals("right")) { + anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT; + } + else if (placeh.equals("center")) { + anchor = org.jfree.ui.RectangleAnchor.TOP; + } + } + else if (placev.equals("bottom")) { + if (placeh.equals("left")) { + anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT; + } + else if (placeh.equals("right")) { + anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT; + } + else if (placeh.equals("center")) { + anchor = org.jfree.ui.RectangleAnchor.BOTTOM; + } + } + else if (placev.equals("center")) { + if (placeh.equals("left")) { + anchor = org.jfree.ui.RectangleAnchor.LEFT; + } + else if (placeh.equals("right")) { + anchor = org.jfree.ui.RectangleAnchor.RIGHT; + } + else if (placeh.equals("center")) { + anchor = org.jfree.ui.RectangleAnchor.CENTER; + } + } + + XYAnnotation xyannotation = + new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor); + plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND); + } + + + protected NumberAxis createXAxis(String label) { + return new NumberAxis(label); + } + + + @Override + protected Series getSeriesOf(XYDataset dataset, int idx) { + return ((XYSeriesCollection) dataset).getSeries(idx); + } + + + @Override + protected AxisDataset createAxisDataset(int idx) { + logger.debug("Create new AxisDataset for index: " + idx); + return new AxisDataset(idx); + } + + + /** + * Put debug output about datasets. + */ + public void debugDatasets(XYPlot plot) { + logger.debug("Number of datasets: " + plot.getDatasetCount()); + for (int i = 0, P = plot.getDatasetCount(); i < P; i++) { + if (plot.getDataset(i) == null) { + logger.debug("Dataset #" + i + " is null"); + continue; + } + logger.debug("Dataset #" + i + ":" + plot.getDataset(i)); + XYSeriesCollection series = (XYSeriesCollection) plot.getDataset(i); + logger.debug("X-Extend of Dataset: " + series.getSeries(0).getMinX() + + " " + series.getSeries(0).getMaxX()); + logger.debug("Y-Extend of Dataset: " + series.getSeries(0).getMinY() + + " " + series.getSeries(0).getMaxY()); + } + } + + + /** + * Put debug output about axes. + */ + public void debugAxis(XYPlot plot) { + logger.debug("..............."); + for (int i = 0, P = plot.getRangeAxisCount(); i < P; i++) { + if (plot.getRangeAxis(i) == null) + logger.debug("Range-Axis #" + i + " == null"); + else { + logger.debug("Range-Axis " + i + " != null [" + + plot.getRangeAxis(i).getRange().getLowerBound() + + " " + plot.getRangeAxis(i).getRange().getUpperBound() + + "]"); + } + } + for (int i = 0, P = plot.getDomainAxisCount(); i < P; i++) { + if (plot.getDomainAxis(i) == null) + logger.debug("Domain-Axis #" + i + " == null"); + else { + logger.debug("Domain-Axis " + i + " != null [" + + plot.getDomainAxis(i).getRange().getLowerBound() + + " " + plot.getDomainAxis(i).getRange().getUpperBound() + + "]"); + } + } + logger.debug("..............."); + } + + /** + * Registers an area to be drawn. + * @param area Area to be drawn. + * @param axisName Name of the axis. + * @param visible Whether or not to be visible (important for range calculations). + */ + public void addAreaSeries(StyledAreaSeriesCollection area, String axisName, boolean visible) { + addAreaSeries(area, diagramAttributes.getAxisIndex(axisName), visible); + } + + /** + * Registers an area to be drawn. + * @param area Area to be drawn. + * @param index 'axis index' + * @param visible Whether or not to be visible (important for range calculations). + */ + public void addAreaSeries(StyledAreaSeriesCollection area, int index, boolean visible) { + if (area == null) { + logger.warn("Cannot yet render above/under curve."); + return; + } + + AxisDataset axisDataset = (AxisDataset) getAxisDataset(index); + + if (visible) { + axisDataset.addArea(area); + } + else { + /* No range merging, for areas extending to infinity this + * causes problems. */ + } + } + + /** + * Add given series if visible, if not visible adjust ranges (such that + * all points in data would be plotted once visible). + * @param series the data series to include in plot. + * @param index index of the axis. + * @param visible whether or not the data should be plotted. + */ + public void addAxisSeries(XYSeries series, int index, boolean visible) { + if (series == null) { + return; + } + + logger.debug("Y Range of XYSeries: " + + series.getMinY() + " | " + series.getMaxY()); + + addAxisDataset(new XYSeriesCollection(series), index, visible); + } + + /** + * Add given series if visible, if not visible adjust ranges (such that + * all points in data would be plotted once visible). + * @param series the data series to include in plot. + * @param axisName name of the axis. + * @param visible whether or not the data should be plotted. + */ + public void addAxisSeries(XYSeries series, String axisName, boolean visible) { + addAxisSeries(series, diagramAttributes.getAxisIndex(axisName), visible); + } + + + /** + * Add the given vertical marker to the chart. + */ + public void addDomainMarker(Marker marker) { + addDomainMarker(marker, true); + } + + + /** + * Add the given vertical marker to the chart.<b>Note:</b> the marker is + * added to the chart only if it is not null and if <i>visible</i> is true. + * @param marker The marker that should be added to the chart. + * @param visible The visibility of the marker. + */ + public void addDomainMarker(Marker marker, boolean visible) { + if (visible && marker != null) { + domainMarkers.add(marker); + } + } + + + /** + * Add the given vertical marker to the chart. + */ + public void addValueMarker(Marker marker) { + addValueMarker(marker, true); + } + + + /** + * Add the given horizontal marker to the chart.<b>Note:</b> the marker is + * added to the chart only if it is not null and if <i>visible</i> is true. + * @param marker The marker that should be added to the chart. + * @param visible The visibility of the marker. + */ + public void addValueMarker(Marker marker, boolean visible) { + if (visible && marker != null) { + valueMarkers.add(marker); + } + } + + + protected void addMarkers(XYPlot plot) { + for(Marker marker : domainMarkers) { + plot.addDomainMarker(marker); + } + for(Marker marker : valueMarkers) { + plot.addRangeMarker(marker); + } + } + + + /** + * Effect: extend range of x axis to include given limits. + * + * @param bounds the given ("minimal") bounds. + * @param index index of axis to be merged. + */ + @Override + protected void combineXBounds(Bounds bounds, int index) { + if (!(bounds instanceof DoubleBounds)) { + logger.warn("Unsupported Bounds type: " + bounds.getClass()); + return; + } + + DoubleBounds dBounds = (DoubleBounds) bounds; + + if (dBounds == null + || Double.isNaN((Double) dBounds.getLower()) + || Double.isNaN((Double) dBounds.getUpper())) { + return; + } + + Bounds old = getXBounds(index); + + if (old != null) { + dBounds = (DoubleBounds) dBounds.combine(old); + } + + setXBounds(index, dBounds); + } + + + @Override + protected void combineYBounds(Bounds bounds, int index) { + if (!(bounds instanceof DoubleBounds)) { + logger.warn("Unsupported Bounds type: " + bounds.getClass()); + return; + } + + DoubleBounds dBounds = (DoubleBounds) bounds; + + if (dBounds == null + || Double.isNaN((Double) dBounds.getLower()) + || Double.isNaN((Double) dBounds.getUpper())) { + return; + } + + Bounds old = getYBounds(index); + + if (old != null) { + dBounds = (DoubleBounds) dBounds.combine(old); + } + + setYBounds(index, dBounds); + } + + + /** + * If no data is visible, draw at least empty axis. + */ + private void recoverEmptyPlot(XYPlot plot) { + if (plot.getRangeAxis() == null) { + logger.debug("debug: No range axis"); + plot.setRangeAxis(createYAxis(0)); + } + } + + + /** + * Expands X axes if only a point is shown. + */ + private void preparePointRanges(XYPlot plot) { + for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { + + Integer key = Integer.valueOf(i); + Bounds b = getXBounds(key); + + + if (b != null && b.getLower().equals(b.getUpper())) { + logger.debug("Check whether to expand a x axis.i ("+b.getLower() + "-" + b.getUpper()+")"); + setXBounds(key, ChartHelper.expandBounds(b, 5)); + } + } + } + + + /** + * This method zooms the plot to the specified ranges in the attribute + * document or to the ranges specified by the min/max values in the + * datasets. <b>Note:</b> We determine the range manually if no zoom ranges + * are given, because JFreeCharts auto-zoom adds a margin to the left and + * right of the data area. + * + * @param plot The XYPlot. + */ + protected void autoZoom(XYPlot plot) { + logger.debug("Zoom to specified ranges."); + + Range xrange = getDomainAxisRange(); + Range yrange = getValueAxisRange(); + + ValueAxis xAxis = plot.getDomainAxis(); + + Range fixedXRange = getRangeForAxisFromSettings("X"); + if (fixedXRange != null) { + xAxis.setRange(fixedXRange); + } + else { + zoomX(plot, xAxis, getXBounds(0), xrange); + } + + for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { + ValueAxis yaxis = plot.getRangeAxis(i); + + if (yaxis instanceof IdentifiableNumberAxis) { + IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis; + + Range fixedRange = getRangeForAxisFromSettings(idAxis.getId()); + if (fixedRange != null) { + yaxis.setRange(fixedRange); + continue; + } + } + + if (yaxis == null) { + logger.debug("Zoom problem: no Y Axis for index: " + i); + continue; + } + + logger.debug("Prepare zoom settings for y axis at index: " + i); + zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange); + } + } + + + protected Range getDomainAxisRange() { + String[] ranges = getDomainAxisRangeFromRequest(); + + if (ranges == null || ranges.length < 2) { + logger.debug("No zoom range for domain axis specified."); + return null; + } + + if (ranges[0].length() > 0 && ranges[1].length() > 0) { + try { + double from = Double.parseDouble(ranges[0]); + double to = Double.parseDouble(ranges[1]); + + if (from == 0 && to == 0) { + logger.debug("No range specified. Lower and upper X == 0"); + return null; + } + + if (from > to) { + double tmp = to; + to = from; + from = tmp; + } + + return new Range(from, to); + } + catch (NumberFormatException nfe) { + logger.warn("Wrong values for domain axis range."); + } + } + + return null; + } + + + protected Range getValueAxisRange() { + String[] ranges = getValueAxisRangeFromRequest(); + + if (ranges == null || ranges.length < 2) { + logger.debug("No range specified. Lower and upper Y == 0"); + return null; + } + + if (ranges[0].length() > 0 && ranges[1].length() > 0) { + try { + double from = Double.parseDouble(ranges[0]); + double to = Double.parseDouble(ranges[1]); + + if (from == 0 && to == 0) { + logger.debug("No range specified. Lower and upper Y == 0"); + return null; + } + + return from > to + ? new Range(to, from) + : new Range(from, to); + } + catch (NumberFormatException nfe) { + logger.warn("Wrong values for value axis range."); + } + } + + return null; + } + + + protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { + return zoom(plot, axis, bounds, x); + } + + + protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { + return zoom(plot, axis, bounds, x); + } + + + /** + * Zooms the x axis to the range specified in the attribute document. + * + * @param plot The XYPlot. + * @param axis The axis the shoud be modified. + * @param bounds The whole range specified by a dataset. + * @param x A user defined range (null permitted). + * + * @return true, if a zoom range was specified, otherwise false. + */ + protected boolean zoom(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { + + if (bounds == null) { + return false; + } + + if (x != null) { + Bounds computed = calculateZoom(bounds, x); + computed.applyBounds(axis, AXIS_SPACE); + + logger.debug("Zoom axis to: " + computed); + + return true; + } + + bounds.applyBounds(axis, AXIS_SPACE); + return false; + } + + /** + * Calculates the start and end km for zoomed charts. + * @param bounds The given total bounds (unzoomed). + * @param range The range specifying the zoom. + * + * @return The start and end km for the zoomed chart. + */ + protected Bounds calculateZoom(Bounds bounds, Range range) { + double min = bounds.getLower().doubleValue(); + double max = bounds.getUpper().doubleValue(); + + if (logger.isDebugEnabled()) { + logger.debug("Minimum is: " + min); + logger.debug("Maximum is: " + max); + logger.debug("Lower zoom is: " + range.getLowerBound()); + logger.debug("Upper zoom is: " + range.getUpperBound()); + } + + double diff = max > min ? max - min : min - max; + + DoubleBounds computed = new DoubleBounds( + min + range.getLowerBound() * diff, + min + range.getUpperBound() * diff); + return computed; + } + + /** + * Extract the minimum and maximum values for x and y axes + * which are stored in <i>xRanges</i> and <i>yRanges</i>. + * + * @param index The index of the y-Axis. + * + * @return a Range[] as follows: [x-Range, y-Range]. + */ + @Override + public Range[] getRangesForAxis(int index) { + logger.debug("getRangesForAxis " + index); + + Bounds rx = getXBounds(Integer.valueOf(0)); + Bounds ry = getYBounds(Integer.valueOf(index)); + + if (rx == null) { + logger.warn("Range for x axis not set." + + " Using default values: 0 - 1."); + rx = new DoubleBounds(0, 1); + } + if (ry == null) { + logger.warn("Range for y" + index + + " axis not set. Using default values: 0 - 1."); + ry = new DoubleBounds(0, 1); + } + + return new Range[] { + new Range(rx.getLower().doubleValue(), rx.getUpper().doubleValue()), + new Range(ry.getLower().doubleValue(), ry.getUpper().doubleValue()) + }; + } + + + /** Get X (usually horizontal) extent for given axis. */ + @Override + public Bounds getXBounds(int axis) { + return xBounds.get(axis); + } + + + /** Set X (usually horizontal) extent for given axis. */ + @Override + protected void setXBounds(int axis, Bounds bounds) { + if (bounds.getLower() == bounds.getUpper()) { + xBounds.put(axis, ChartHelper.expandBounds(bounds, 5d)); + } + else { + xBounds.put(axis, bounds); + } + } + + + /** Get Y (usually vertical) extent for given axis. */ + @Override + public Bounds getYBounds(int axis) { + return yBounds.get(axis); + } + + + /** Set Y (usually vertical) extent for given axis. */ + @Override + protected void setYBounds(int axis, Bounds bounds) { + yBounds.put(axis, bounds); + } + + + /** + * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the + * X axis and applies the inversion if inverted is true. + * + * (Duplicate in TimeseriesChartGenerator) + * + * @param plot The XYPlot of the chart. + */ + protected void adjustAxes(XYPlot plot) { + ValueAxis xaxis = plot.getDomainAxis(); + + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return; + } + + Font labelFont = new Font( + DEFAULT_FONT_NAME, + Font.BOLD, + getXAxisLabelFontSize()); + + xaxis.setLabelFont(labelFont); + xaxis.setTickLabelFont(labelFont); + + logger.debug("Adjusting xAxis. Inverted?: " + inverted); + if (inverted) { + xaxis.setInverted(true); + } + } + + + /** + * This method walks over all axes (domain and range) of <i>plot</i> and + * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for + * range axes. + * + * @param plot The XYPlot. + */ + private void localizeAxes(XYPlot plot) { + for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { + ValueAxis axis = plot.getDomainAxis(i); + + if (axis != null) { + localizeDomainAxis(axis); + } + else { + logger.warn("Domain axis at " + i + " is null."); + } + } + + for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { + ValueAxis axis = plot.getRangeAxis(i); + + if (axis != null) { + localizeRangeAxis(axis); + } + else { + logger.warn("Range axis at " + i + " is null."); + } + } + } + + + /** + * Overrides the NumberFormat with the NumberFormat for the current locale + * that is provided by getLocale(). + * + * @param domainAxis The domain axis that needs localization. + */ + protected void localizeDomainAxis(ValueAxis domainAxis) { + NumberFormat nf = NumberFormat.getInstance(getLocale()); + ((NumberAxis) domainAxis).setNumberFormatOverride(nf); + } + + + /** + * Overrides the NumberFormat with the NumberFormat for the current locale + * that is provided by getLocale(). + * + * @param rangeAxis The domain axis that needs localization. + */ + protected void localizeRangeAxis(ValueAxis rangeAxis) { + NumberFormat nf = NumberFormat.getInstance(getLocale()); + ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); + } + + + /** + * Create a hash from a legenditem. + * This hash can then be used to merge legend items labels. + * @return hash for given legenditem to identify mergeables. + */ + public static String legendItemHash(LegendItem li) { + // TODO Do proper implementation. Ensure that only mergable sets are created. + // getFillPaint() + // getFillPaintTransformer() + // getLabel() + // getLine() + // getLinePaint() + // getLineStroke() + // getOutlinePaint() + // getOutlineStroke() + // Shape getShape() + // String getToolTipText() + // String getURLText() + // boolean isLineVisible() + // boolean isShapeFilled() + // boolean isShapeOutlineVisible() + // boolean isShapeVisible() + String hash = li.getLinePaint().toString(); + String label = li.getLabel(); + if (label.startsWith("W (") || label.startsWith("W(")) { + hash += "-W-"; + } + else if (label.startsWith("Q(") || label.startsWith("Q (")) { + hash += "-Q-"; + } + + // WQ.java holds example of using regex Matcher/Pattern. + + return hash; + } + + /** True if x axis has been inverted. */ + public boolean isInverted() { + return inverted; + } + + + /** Set to true if x axis should be inverted. + * This can not be set to false afterwards. */ + public void setInverted(boolean value) { + /* One request to invert dominates. */ + if (!inverted) { + inverted = value; + } + } + + @Override + public String getDefaultChartTitle() { + DiagramAttributes.Title dTitle = diagramAttributes.getTitle(); + if (dTitle == null) { + return "Title not configured in conf.xml"; + } + + return dTitle.evaluate((D4EArtifact)getMaster(), context); + } + + @Override + public String getDefaultChartSubtitle() { + DiagramAttributes.Title dTitle = diagramAttributes.getSubtitle(); + if (dTitle == null) { + return "Subtitle not configured in conf.xml"; + } + + return dTitle.evaluate((D4EArtifact)getMaster(), context); + } + + /** + * Get internationalized label for the x axis. + */ + @Override + protected String getDefaultXAxisLabel() { + DiagramAttributes.Title dTitle = diagramAttributes.getDomainAxisTitle(); + if (dTitle == null) { + return "Domain Axis Title not configured in conf.xml"; + } + + return dTitle.evaluate((D4EArtifact)getMaster(), context); + } + + @Override + protected String getDefaultYAxisLabel(String axisName) { + Processor pr = diagramAttributes.getProcessorForAxisName(axisName); + return pr == null ? "" : pr.getAxisLabel(this); + } + + + /** + * Creates a list of Section for the chart's Y axes. + * + * @return a list of Y axis sections. + */ + protected List<AxisSection> buildYAxisSections() { + List<AxisSection> axisSections = new ArrayList<AxisSection>(); + + List<DiagramAttributes.AxisAttributes> axesAttrs = diagramAttributes.getAxesAttributes(); + + for (int i = 0, n = axesAttrs.size(); i < n; i++) { + AxisSection ySection = new AxisSection(); + String axisName = diagramAttributes.getAxisName(i); + ySection.setIdentifier(axisName); + ySection.setLabel(getYAxisLabel(axisName)); + ySection.setFontSize(14); + ySection.setFixed(false); + + // XXX We are able to find better default ranges that [0,0], the + // only problem is, that we do NOT have a better range than [0,0] + // for each axis, because the initial chart will not have a dataset + // for each axis set! + ySection.setUpperRange(0d); + ySection.setLowerRange(0d); + + axisSections.add(ySection); + } + + return axisSections; + } + + /** + * Returns the Y-Axis label of a chart at position <i>pos</i>. + * + * @return the Y-Axis label of a chart at position <i>0</i>. + */ + protected String getYAxisLabel(String axisName) { + ChartSettings chartSettings = getChartSettings(); + if (chartSettings == null) { + return getDefaultYAxisLabel(axisName); + } + AxisSection as = chartSettings.getAxisSection(axisName); + if (as != null) { + String label = as.getLabel(); + if (label != null) { + return label; + } + } + + return getDefaultYAxisLabel(axisName); + } + + protected String axisIndexToName(int index) { + return diagramAttributes.getAxisName(index); + } + + /** Add the acutal data to the diagram according to the processors. + * For every outable facets, this function is + * called and handles the data accordingly. */ + @Override + public void doOut( + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible + ) { + String facetName = bundle.getFacetName(); + Facet facet = bundle.getFacet(); + + /* A conservative security check */ + if (facetName == null || facet == null) { + /* Can't happen,.. */ + logger.error("doOut called with null facet."); + return; + } + + logger.debug("DoOut for facet: " + facetName); + + boolean found = false; + List<Processor> prL = diagramAttributes.getProcessors(); + for (Processor pr: prL) { + if (pr.canHandle(facetName)) { + found = true; + pr.doOut(this, bundle, theme, visible); + } + } + if (!found) { + logger.warn("No processor found for: " + facetName); + if (logger.isDebugEnabled()) { + logger.debug("Configured processors for this diagram are:"); + for (Processor pr: prL) { + logger.debug(pr.getClass().getName()); + } + } + } + } + + @Override + protected NumberAxis createYAxis(int index) { + NumberAxis axis = super.createYAxis(index); + + if (diagramAttributes.getAxesAttributes().get(index).includeZero()) { + axis.setAutoRangeIncludesZero(true); + } + return axis; + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * This file is Free Software under the GNU AGPL (>=v3) - * and comes with ABSOLUTELY NO WARRANTY! Check out the - * documentation coming with Dive4Elements River for details. - */ - -package org.dive4elements.river.exports; - -import org.apache.log4j.Logger; - -import org.jfree.data.xy.XYSeries; - -import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; -import org.dive4elements.artifactdatabase.state.Facet; - -import org.dive4elements.river.artifacts.model.FacetTypes; -import org.dive4elements.river.artifacts.model.WQCKms; -import org.dive4elements.river.exports.process.Processor; -import org.dive4elements.river.exports.process.QOutProcessor; -import org.dive4elements.river.exports.process.WOutProcessor; - -import org.dive4elements.river.jfree.RiverAnnotation; -import org.dive4elements.river.jfree.StyledXYSeries; -import org.dive4elements.river.themes.ThemeDocument; - - - -/** - * An OutGenerator that generates discharge longitudinal section curves. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DischargeLongitudinalSectionGenerator -extends LongitudinalSectionGenerator -implements FacetTypes -{ - private static Logger logger = - Logger.getLogger(DischargeLongitudinalSectionGenerator.class); - - - public DischargeLongitudinalSectionGenerator() { - super(); - } - - - @Override - public void doOut( - ArtifactAndFacet artifactFacet, - ThemeDocument attr, - boolean visible - ) { - logger.debug("DischargeLongitudinalSectionGenerator.doOut"); - - String name = artifactFacet.getFacetName(); - - if (name == null) { - return; - } - - Facet facet = artifactFacet.getFacet(); - - if (name.equals(DISCHARGE_LONGITUDINAL_C)) { - doCorrectedWOut( - (WQCKms) artifactFacet.getData(context), - facet, - attr, - visible); - } - else if (name.equals(LONGITUDINAL_ANNOTATION)) { - doAnnotations((RiverAnnotation) artifactFacet.getData(context), - artifactFacet, attr, visible); - } - else if (FacetTypes.IS.MANUALPOINTS(name)) { - doPoints(artifactFacet.getData(context), - artifactFacet, - attr, visible, YAXIS.W.idx); - } - else { - Processor processor = new WOutProcessor(); - Processor qProcessor = new QOutProcessor(); - if (processor.canHandle(name)) { - processor.doOut(this, artifactFacet, attr, visible, YAXIS.W.idx); - } - else if (qProcessor.canHandle(name)) { - qProcessor.doOut(this, artifactFacet, attr, visible, YAXIS.Q.idx); - } - else { - logger.warn("Unknown facet name: " + name); - } - } - } - - - /** - * Adds a new series for the corrected W curve. - * - * @param wqckms The object that contains the corrected W values. - * @param theme The theme that contains styling information. - */ - protected void doCorrectedWOut( - WQCKms wqckms, - Facet facet, - ThemeDocument theme, - boolean visible - ) { - logger.debug("DischargeLongitudinalSectionGenerator.doCorrectedWOut"); - - int size = wqckms.size(); - - if (size > 0) { - XYSeries series = new StyledXYSeries( - facet.getDescription(), - theme); - - for (int i = 0; i < size; i++) { - series.add(wqckms.getKm(i), wqckms.getC(i)); - } - - addAxisSeries(series, YAXIS.W.idx, visible); - } - - if (wqckms.guessWaterIncreasing()) { - setInverted(true); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * This file is Free Software under the GNU AGPL (>=v3) - * and comes with ABSOLUTELY NO WARRANTY! Check out the - * documentation coming with Dive4Elements River for details. - */ - -package org.dive4elements.river.exports; - - -/** - * A ChartInfoGenerator that generates meta information for specific discharge - * longitudinal section curves. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DischargeLongitudinalSectionInfoGenerator -extends ChartInfoGenerator -{ - public DischargeLongitudinalSectionInfoGenerator() { - super(new DischargeLongitudinalSectionGenerator()); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -18,8 +18,6 @@ import java.text.DateFormat; import java.util.Locale; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; @@ -29,7 +27,6 @@ import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JRException; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.artifacts.common.utils.Config; @@ -73,16 +70,10 @@ /** The storage that contains all WQKms objects for the different facets. */ protected List<WQDay> data; - - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("DurationCurveExporter.init"); - - super.init(request, out, context); - - this.data = new ArrayList<WQDay>(); + public DurationCurveExporter() { + data = new ArrayList<WQDay>(); } - @Override protected void addData(Object d) { if (d instanceof CalculationResult) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -13,14 +13,10 @@ import java.util.ArrayList; import java.util.List; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; - import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.FlowVelocityData; @@ -58,13 +54,10 @@ protected List<FlowVelocityData[]> data; - - public void init(Document request, OutputStream out, CallContext cc) { - super.init(request, out, cc); + public FlowVelocityExporter() { data = new ArrayList<FlowVelocityData[]>(); } - @Override protected void addData(Object d) { if (d instanceof CalculationResult) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -18,8 +18,6 @@ import java.util.HashMap; import java.util.Locale; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; @@ -31,7 +29,6 @@ import org.dive4elements.artifacts.common.utils.Config; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.river.artifacts.D4EArtifact; @@ -77,14 +74,10 @@ protected List<WQTimerange[]> data; - - public void init(Document request, OutputStream out, CallContext cc) { - super.init(request, out, cc); - + public HistoricalDischargeCurveExporter() { data = new ArrayList<WQTimerange[]>(); } - @Override protected void addData(Object d) { logger.debug("Add data of class: " + d.getClass()); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java Tue Sep 24 12:29:44 2013 +0200 @@ -48,12 +48,10 @@ protected ChartGenerator generator; - public InfoGeneratorHelper(ChartGenerator generator) { this.generator = generator; } - /** * Triggers the creation of the chart info document. * diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper2.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,409 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; + +import java.util.Date; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.Range; +import org.jfree.data.xy.XYDataset; + +import org.dive4elements.artifacts.common.ArtifactNamespaceContext; +import org.dive4elements.artifacts.common.utils.XMLUtils; +import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator; + +import org.dive4elements.river.jfree.Bounds; + + +/** + * This class helps generating chart info documents. + */ +public class InfoGeneratorHelper2 { + + /** Private logging instance. */ + private static final Logger logger = + Logger.getLogger(InfoGeneratorHelper2.class); + + protected ChartGenerator2 generator; + + public InfoGeneratorHelper2(ChartGenerator2 generator) { + this.generator = generator; + } + + /** + * Triggers the creation of the chart info document. + * + * @param chart The JFreeChart chart. + * @param info An info object that has been created while chart creation. + * + * @return the info document. + */ + public Document createInfoDocument( + JFreeChart chart, + ChartRenderingInfo info) + { + logger.debug("InfoGeneratorHelper.createInfoDocument"); + + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element chartinfo = cr.create("chartinfo"); + + chartinfo.appendChild(createAxesElements(cr, chart)); + chartinfo.appendChild(createTransformationElements(cr, chart, info)); + + doc.appendChild(chartinfo); + + return doc; + } + + + /** + * This method create a axes element that contains all domain and range + * axes of the given chart. + * + * @param cr The ElementCreator. + * @param chart The chart that provides range information of its axes. + * + * @return an element with axes information. + */ + protected Element createAxesElements( + ElementCreator cr, + JFreeChart chart) + { + logger.debug("InfoGeneratorHelper.createRangeElements"); + + Element axes = cr.create("axes"); + + XYPlot plot = (XYPlot) chart.getPlot(); + + int dAxisCount = plot.getDomainAxisCount(); + for (int i = 0; i < dAxisCount; i++) { + ValueAxis axis = plot.getDomainAxis(i); + XYDataset data = plot.getDataset(i); + + if (axis != null) { + Element e = createAxisElement(cr, axis, data, "domain", i); + axes.appendChild(e); + } + } + + int rAxisCount = plot.getRangeAxisCount(); + for (int i = 0; i < rAxisCount; i++) { + ValueAxis axis = plot.getRangeAxis(i); + XYDataset data = plot.getDataset(i); + + if (axis == null || data == null) { + logger.warn("Axis or dataset is empty at pos: " + i); + continue; + } + + Element e = createAxisElement(cr, axis, data, "range", i); + axes.appendChild(e); + } + + return axes; + } + + + /** + * This method create a axis element for a given <i>axis</i> and + * <i>type</i>. Type can be one of 'domain' or 'range'. + * + * @param cr The ElementCreator + * @param axis The axis that provides range information. + * @param dataset The dataset for min/max determination. + * @param type The axis type ('domain' or 'range'). + * @param pos The position in the chart. + * + * @return An element that contains range information of a given axis. + */ + protected Element createAxisElement( + ElementCreator cr, + ValueAxis axis, + XYDataset dataset, + String type, + int pos) + { + logger.debug("createAxisElement " + pos); + logger.debug("Axis is from type: " + axis.getClass()); + + Element e = cr.create(type); + cr.addAttr(e, "pos", String.valueOf(pos), true); + + if (axis instanceof DateAxis) { + prepareDateAxisElement( + e, cr, (DateAxis) axis, dataset, type, pos); + } + else { + prepareNumberAxisElement( + e, cr, (NumberAxis) axis, dataset, type, pos); + } + + return e; + } + + + protected Element prepareNumberAxisElement( + Element e, + ElementCreator cr, + NumberAxis axis, + XYDataset dataset, + String type, + int pos + ) { + Range range = axis.getRange(); + + cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true); + cr.addAttr(e, "to", String.valueOf(range.getUpperBound()), true); + cr.addAttr(e, "axistype", "number", true); + + Range[] rs = generator.getRangesForAxis(pos); + Range r = null; + + if (type.equals("range")) { + r = rs[1]; + } + else { + r = rs[0]; + } + + cr.addAttr(e, "min", String.valueOf(r.getLowerBound()), true); + cr.addAttr(e, "max", String.valueOf(r.getUpperBound()), true); + + return e; + } + + + protected Element prepareDateAxisElement( + Element e, + ElementCreator cr, + DateAxis axis, + XYDataset dataset, + String type, + int pos + ) { + Date from = axis.getMinimumDate(); + Date to = axis.getMaximumDate(); + + Bounds bounds = null; + if (type.equals("range")) { + bounds = generator.getYBounds(pos); + } + else { + bounds = generator.getXBounds(pos); + } + + cr.addAttr(e, "axistype", "date", true); + cr.addAttr(e, "from", String.valueOf(from.getTime()), true); + cr.addAttr(e, "to", String.valueOf(to.getTime()), true); + + cr.addAttr(e, "min", bounds.getLower().toString(), true); + cr.addAttr(e, "max", bounds.getUpper().toString(), true); + + return e; + } + + + /** + * This method appends the values of a transformation matrix to transform + * image pixel coordinates into chart coordinates. + * + * @param cr The ElementCreator. + * @param chart The chart object. + * @param info The ChartRenderingInfo that is filled while chart creation. + * + * @return an element that contains one or more transformation matrix. + */ + protected Element createTransformationElements( + ElementCreator cr, + JFreeChart chart, + ChartRenderingInfo info) + { + logger.debug("InfoGeneratorHelper.createTransformationElements"); + + Element tf = cr.create("transformation-matrix"); + + Rectangle2D dataArea = info.getPlotInfo().getDataArea(); + + XYPlot plot = (XYPlot) chart.getPlot(); + ValueAxis xAxis = plot.getDomainAxis(); + + if (xAxis == null) { + logger.error("There is no x axis in the chart!"); + return null; + } + + for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { + ValueAxis yAxis = plot.getRangeAxis(i); + + if (yAxis == null) { + logger.warn("No y axis at pos " + i + " existing."); + continue; + } + + Element matrix = createTransformationElement( + cr, xAxis, yAxis, dataArea, i); + + tf.appendChild(matrix); + } + + return tf; + } + + + /** + * Creates an element that contains values used to transform coordinates + * of a coordinate system A into a coordinate system B. + * + * @param cr The ElementCreator. + * @param xAxis The x axis of the target coordinate system. + * @param yAxis The y axis of the target coordinate system. + * @param dataArea The pixel coordinates of the chart image. + * @param pos The dataset position. + * + * @return an element that contains transformation matrix values. + */ + protected Element createTransformationElement( + ElementCreator cr, + ValueAxis xAxis, + ValueAxis yAxis, + Rectangle2D dataArea, + int pos) + { + double[] tm = createTransformationMatrix(dataArea, xAxis, yAxis); + + Element matrix = cr.create("matrix"); + + cr.addAttr(matrix, "pos", String.valueOf(pos), true); + cr.addAttr(matrix, "sx", String.valueOf(tm[0]), true); + cr.addAttr(matrix, "sy", String.valueOf(tm[1]), true); + cr.addAttr(matrix, "tx", String.valueOf(tm[2]), true); + cr.addAttr(matrix, "ty", String.valueOf(tm[3]), true); + + if (xAxis instanceof DateAxis) { + cr.addAttr(matrix, "xtype", "date", true); + } + else { + cr.addAttr(matrix, "xtype", "number", true); + } + + if (yAxis instanceof DateAxis) { + cr.addAttr(matrix, "ytype", "date", true); + } + else { + cr.addAttr(matrix, "ytype", "number", true); + } + + return matrix; + } + + + /** + * This method determines a transformation matrix to transform pixel + * coordinates of the chart image into chart coordinates. + * + * @param dataArea The rectangle that contains the data points of the chart. + * @param xAxis The x axis. + * @param yAxis The y axis. + * + * @return a double array as follows: [sx, sy, tx, ty]. + */ + protected static double[] createTransformationMatrix( + Rectangle2D dataArea, + ValueAxis xAxis, + ValueAxis yAxis) + { + logger.debug("InfoGeneratorHelper.createTransformationMatrix"); + + double offsetX = dataArea.getX(); + double width = dataArea.getWidth(); + double offsetY = dataArea.getY(); + double height = dataArea.getHeight(); + + Range xRange = getRangeFromAxis(xAxis); + Range yRange = getRangeFromAxis(yAxis); + + double lowerX = xRange.getLowerBound(); + double upperX = xRange.getUpperBound(); + double lowerY = yRange.getLowerBound(); + double upperY = yRange.getUpperBound(); + + if (xAxis.isInverted()) { + logger.info("X-Axis is inverted!"); + + double tmp = upperX; + upperX = lowerX; + lowerX = tmp; + } + + double dMoveX = upperX - lowerX; + double fMoveX = width * lowerX; + double dMoveY = lowerY - upperY; + double fMoveY = height * upperY; + + AffineTransform t1 = AffineTransform.getTranslateInstance( + offsetX - ( fMoveX / dMoveX ), + offsetY - ( fMoveY / dMoveY ) ); + + AffineTransform t2 = AffineTransform.getScaleInstance( + width / (upperX - lowerX), + height / (lowerY - upperY)); + + t1.concatenate(t2); + + try { + t1.invert(); + + double[] c = new double[6]; + t1.getMatrix(c); + + return new double[] { c[0], c[3], c[4], c[5] }; + } + catch (NoninvertibleTransformException e) { + // do nothing + logger.warn("Matrix is not invertible."); + } + + return new double[] { 1d, 1d, 0d, 0d }; + } + + + protected static Range getRangeFromAxis(ValueAxis axis) { + if (axis instanceof DateAxis) { + DateAxis dAxis = (DateAxis) axis; + Date min = dAxis.getMinimumDate(); + Date max = dAxis.getMaximumDate(); + + return new Range(min.getTime(), max.getTime()); + } + else { + return axis.getRange(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -23,12 +23,11 @@ import org.dive4elements.river.exports.process.BedheightProcessor; import org.dive4elements.river.exports.process.QOutProcessor; import org.dive4elements.river.exports.process.WOutProcessor; +import org.dive4elements.river.exports.process.AnnotationProcessor; -import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; import org.dive4elements.river.utils.RiverUtils; import org.apache.log4j.Logger; import org.jfree.chart.axis.NumberAxis; @@ -340,11 +339,12 @@ return; } - WOutProcessor wProcessor = new WOutProcessor(); - QOutProcessor qProcessor = new QOutProcessor(); + Processor wProcessor = new WOutProcessor(); + Processor qProcessor = new QOutProcessor(); Processor bedp = new BedheightProcessor(); Processor bdyProcessor = new BedDiffYearProcessor(); Processor bdhyProcessor = new BedDiffHeightYearProcessor(); + Processor annotationProcessor = new AnnotationProcessor(); if (wProcessor.canHandle(name)) { wProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx); @@ -361,12 +361,8 @@ else if (bdhyProcessor.canHandle(name)) { bdhyProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx); } - else if (name.equals(LONGITUDINAL_ANNOTATION)) { - doAnnotations( - (RiverAnnotation) artifactAndFacet.getData(context), - artifactAndFacet, - attr, - visible); + else if (annotationProcessor.canHandle(name)) { + annotationProcessor.doOut(this, artifactAndFacet, attr, visible, 0); } else if (name.equals(W_DIFFERENCES)) { doWDifferencesOut( @@ -429,36 +425,6 @@ /** - * This method determines - taking JFreeCharts auto x value ordering into - * account - if the x axis need to be inverted. Waterlines in these charts - * should decrease. - * - * @param wkms The data object that stores the x and y values used for this - * chart. - */ - public boolean needInvertAxis(WKms wkms) { - boolean wsUp = wkms.guessWaterIncreasing(); - boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms()); - boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp); - - int size = wkms.size(); - - if (logger.isDebugEnabled()) { - logger.debug("(Wkms)Values : " + size); - if (size > 0) { - logger.debug("Start km: " + wkms.getKm(0)); - logger.debug("End km: " + wkms.getKm(size-1)); - } - logger.debug("wsUp: " + wsUp); - logger.debug("kmUp: " + kmUp); - logger.debug("inv: " + inv); - } - - return inv; - } - - - /** * Get name of series (displayed in legend). * @return name of the series. */ diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,42 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports; + +import org.apache.log4j.Logger; +import org.jfree.chart.axis.NumberAxis; + + +public class LongitudinalSectionGenerator2 extends DiagramGenerator +{ + public static final String I18N_CHART_SHORT_SUBTITLE = + "chart.longitudinal.section.shortsubtitle"; + + public static final String I18N_CHART_LOCATION_SUBTITLE = + "chart.longitudinal.section.locsubtitle"; + + private static Logger logger = + Logger.getLogger(LongitudinalSectionGenerator.class); + + @Override + public String getDefaultChartSubtitle() { + double[] dist = getRange(); + + Object[] args = null; + if (dist == null || dist.length != 2 || + Double.isNaN(dist[0]) || Double.isNaN(dist[1])) { + args = new Object[] {getRiverName()}; + return msg(I18N_CHART_SHORT_SUBTITLE, "", args); + } else if (Math.abs(dist[0] - dist[1]) < 1E-5) { + args = new Object[] {getRiverName(), dist[1]}; + return msg(I18N_CHART_LOCATION_SUBTITLE, "", args); + } + return super.getDefaultChartSubtitle(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * This file is Free Software under the GNU AGPL (>=v3) - * and comes with ABSOLUTELY NO WARRANTY! Check out the - * documentation coming with Dive4Elements River for details. - */ - -package org.dive4elements.river.exports; - - -/** - * A ChartInfoGenerator that generates meta information for specific - * longitudinal section curves. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class LongitudinalSectionInfoGenerator -extends ChartInfoGenerator -{ - public LongitudinalSectionInfoGenerator() { - super(new LongitudinalSectionGenerator()); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -65,12 +65,18 @@ protected String srid; - + protected String outName; @Override - public void init(Document request, OutputStream out, CallContext context) { + public void setup(Object config) { + logger.debug("MapGenerator.setup"); + } + + @Override + public void init(String outName, Document request, OutputStream out, CallContext context) { logger.debug("MapGenerator.init"); + this.outName = outName; this.request = request; this.out = out; this.context = context; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -13,14 +13,10 @@ import java.util.ArrayList; import java.util.List; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; - import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.MiddleBedHeightData; @@ -64,13 +60,10 @@ protected List<MiddleBedHeightData[]> data; - - public void init(Document request, OutputStream out, CallContext cc) { - super.init(request, out, cc); + public MiddleBedHeightExporter() { data = new ArrayList<MiddleBedHeightData[]>(); } - @Override protected void addData(Object d) { if (d instanceof CalculationResult) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -29,15 +29,21 @@ public interface OutGenerator { /** + * Pre-initialize generator from configuration. + */ + void setup(Object config); + + /** * Initializes the OutGenerator with meta information which are necessary * for the output generation. * + * @param outName The name of the out to serve. * @param request The incomding request document. * @param out The output stream. * @param context The CallContext that provides further information and * objects used for the output generation. */ - void init(Document request, OutputStream out, CallContext context); + void init(String outName, Document request, OutputStream out, CallContext context); /** * This method is used to tell the OutGenerator which artifact is the master diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -10,7 +10,6 @@ import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.artifacts.common.utils.Config; @@ -46,8 +45,6 @@ import org.apache.log4j.Logger; -import org.w3c.dom.Document; - /** * (CSV)Exporter for Reference Curves. */ @@ -91,17 +88,10 @@ protected boolean endAtGauge = false; - - @Override - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("ReferenceCurveExporter.init"); - - super.init(request, out, context); - + public ReferenceCurveExporter() { this.data = new ArrayList<WWQQ[]>(); } - /** * Genereate data in csv format. */ diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -36,13 +36,20 @@ protected Document result; protected OutputStream out; protected CallContext context; + protected String outName; public ReportGenerator() { } @Override - public void init(Document request, OutputStream out, CallContext context) { + public void setup(Object config) { + logger.debug("ReportGenerator.setup"); + } + + @Override + public void init(String outName, Document request, OutputStream out, CallContext context) { logger.debug("init"); + this.outName = outName; this.out = out; this.context = context; result = null; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -29,9 +29,16 @@ private D4EArtifactCollection collection; private String facet; private File dir; + private String outName; @Override - public void init(Document request, OutputStream out, CallContext context) { + public void setup(Object config) { + logger.debug("ShapeExporter.setup"); + } + + @Override + public void init(String outName, Document request, OutputStream out, CallContext context) { + this.outName = outName; this.request = request; this.out = out; this.context = context; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -16,6 +16,7 @@ import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledTimeSeries; import org.dive4elements.river.jfree.TimeBounds; +import org.dive4elements.river.jfree.AxisDataset; import org.dive4elements.river.themes.ThemeDocument; import java.awt.Color; @@ -60,126 +61,6 @@ public abstract class TimeseriesChartGenerator extends ChartGenerator { - /** - * Inner class TimeseriesAxisDataset stores TimeSeriesCollection. - */ - public class TimeseriesAxisDataset implements AxisDataset { - - protected int axisSymbol; - - protected List<TimeSeriesCollection> datasets; - - protected Range range; - - protected int plotAxisIndex; - - public TimeseriesAxisDataset(int axisSymbol) { - this.axisSymbol = axisSymbol; - this.datasets = new ArrayList<TimeSeriesCollection>(); - } - - - @Override - public void addDataset(XYDataset dataset) { - if (!(dataset instanceof TimeSeriesCollection)) { - logger.warn("Skip non TimeSeriesCollection dataset."); - return; - } - - TimeSeriesCollection tsc = (TimeSeriesCollection) dataset; - - datasets.add(tsc); - mergeRanges(tsc); - } - - - @Override - public XYDataset[] getDatasets() { - return datasets.toArray(new XYDataset[datasets.size()]); - } - - - @Override - public boolean isEmpty() { - return datasets.isEmpty(); - } - - - @Override - public void setRange(Range range) { - this.range = range; - } - - - @Override - public Range getRange() { - return range; - } - - - @Override - public void setPlotAxisIndex(int plotAxisIndex) { - this.plotAxisIndex = plotAxisIndex; - } - - - @Override - public int getPlotAxisIndex() { - return plotAxisIndex; - } - - - @Override - public boolean isArea(XYDataset dataset) { - logger.warn("This AxisDataset doesn't support Areas yet!"); - return false; - } - - - protected void mergeRanges(TimeSeriesCollection dataset) { - logger.debug("Range before merging: " + range); - Range subRange = null; - - // Determine min/max of range axis. - for (int i = 0; i < dataset.getSeriesCount(); i++) { - if (dataset.getSeries(i).getItemCount() == 0) { - continue; - } - double min = Double.MAX_VALUE; - double max = -Double.MAX_VALUE; - TimeSeries series = dataset.getSeries(i); - for (int j = 0; j < series.getItemCount(); j++) { - double tmp = series.getValue(j).doubleValue(); - min = tmp < min ? tmp : min; - max = tmp > max ? tmp : max; - } - if (subRange != null) { - subRange = new Range( - min < subRange.getLowerBound() ? - min : subRange.getLowerBound(), - max > subRange.getUpperBound() ? - max : subRange.getUpperBound()); - } - else { - subRange = new Range(min, max); - } - } - - // Avoid merging NaNs, as they take min/max place forever. - if (subRange == null || - Double.isNaN(subRange.getLowerBound()) || - Double.isNaN(subRange.getUpperBound())) { - return; - } - if (range == null) { - range = subRange; - return; - } - range = Range.combine(range, subRange); - } - - } // end of TimeseriesAxisDataset class - protected List<Marker> domainMarker; protected List<Marker> valueMarker; @@ -389,14 +270,14 @@ /** - * This method creates new instances of TimeseriesAxisDataset. + * This method creates new instances of AxisDataset. * - * @param idx The symbol for the new TimeseriesAxisDataset. + * @param idx The symbol for the new AxisDataset. */ @Override protected AxisDataset createAxisDataset(int idx) { logger.debug("Create a new AxisDataset for index: " + idx); - return new TimeseriesAxisDataset(idx); + return new AxisDataset(idx); } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * This file is Free Software under the GNU AGPL (>=v3) - * and comes with ABSOLUTELY NO WARRANTY! Check out the - * documentation coming with Dive4Elements River for details. - */ - -package org.dive4elements.river.exports; - -import org.apache.log4j.Logger; - -import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; -import org.dive4elements.artifactdatabase.state.Facet; - -import org.dive4elements.river.artifacts.model.FacetTypes; -import org.dive4elements.river.artifacts.model.WKms; - -import org.dive4elements.river.exports.process.BedDiffYearProcessor; -import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor; -import org.dive4elements.river.exports.process.BedheightProcessor; -import org.dive4elements.river.exports.process.Processor; -import org.dive4elements.river.themes.ThemeDocument; - -import org.jfree.chart.JFreeChart; -import org.jfree.chart.plot.XYPlot; - - -/** - * An OutGenerator that generates w differences curves. - */ -public class WDifferencesCurveGenerator -extends LongitudinalSectionGenerator -implements FacetTypes -{ - /** The logger that is used in this generator. */ - private static Logger logger = Logger.getLogger(WDifferencesCurveGenerator.class); - - public enum YAXIS { - W(0), - D(1), - Q(2); - protected int idx; - private YAXIS(int c) { - idx = c; - } - } - - /** Key for internationalized title of WDiff charts. */ - public final static String I18N_WDIFF_TITLE = "chart.w_differences.title"; - - /** Default for internationalized title (when no translation found). */ - public final static String I18N_WDIFF_TITLE_DEFAULT = "Differences"; - - public final static String I18N_WDIFF_SUBTITLE = - "chart.w_differences.subtitle"; - - - @Override - protected YAxisWalker getYAxisWalker() { - return new YAxisWalker() { - @Override - public int length() { - return YAXIS.values().length; - } - - @Override - public String getId(int idx) { - YAXIS[] yaxes = YAXIS.values(); - return yaxes[idx].toString(); - } - }; - } - - - /** - * Get internationalized title for chart. - * @return internationalized Chart title. - */ - @Override - public String getDefaultChartTitle() { - return msg(I18N_WDIFF_TITLE, I18N_WDIFF_TITLE_DEFAULT); - } - - - @Override - protected String getDefaultChartSubtitle() { - return getRiverName(); - } - - - /** - * Gets key to look up internationalized String for the charts subtitle. - * @return key to look up translated subtitle. - */ - @Override - protected String getChartSubtitleKey() { - return I18N_WDIFF_SUBTITLE; - } - - - /** Handle additional facets (beddifferences). */ - @Override - public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) { - super.doOut(bundle, attr, visible); - - String name = bundle.getFacetName(); - logger.debug("doOut: " + name); - - if (name == null) { - logger.error("No facet name for doOut(). No output generated!"); - return; - } - - Facet facet = bundle.getFacet(); - - if (facet == null) { - return; - } - - Processor bedp = new BedheightProcessor(); - Processor bdyProcessor = new BedDiffYearProcessor(); - Processor bdhyProcessor = new BedDiffHeightYearProcessor(); - - if (bedp.canHandle(name)) { - bedp.doOut(this, bundle, attr, visible, YAXIS.W.idx); - } - else if (bdyProcessor.canHandle(name)) { - bdyProcessor.doOut(this, bundle, attr, visible, YAXIS.W.idx); - } - else if (bdhyProcessor.canHandle(name)) { - bdhyProcessor.doOut(this, bundle, attr, visible, YAXIS.W.idx); - } - else { - logger.warn("WDifferencesCurveGenerator.doOut: unknown facet type " + name); - } - } - - - /** - * Sets the zero base line visible. - */ - @Override - public JFreeChart generateChart() { - JFreeChart chart = super.generateChart(); - if (chart != null && chart.getPlot() != null) { - XYPlot plot = (XYPlot) chart.getPlot(); - plot.setRangeZeroBaselineVisible(true); - } - return chart; - } - - - /** - * Get name of series (displayed in legend). - * @return name of the series. - */ - protected String getSeriesName(WKms wqkms, String mode) { - String name = wqkms.getName(); - String prefix = (name != null && name.indexOf(mode) >= 0) - ? null - : mode; - - return (prefix != null && prefix.length() > 0) - ? prefix + "(" + name +")" - : name; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * This file is Free Software under the GNU AGPL (>=v3) - * and comes with ABSOLUTELY NO WARRANTY! Check out the - * documentation coming with Dive4Elements River for details. - */ - -package org.dive4elements.river.exports; - - -/** - * A ChartInfoGenerator that generates meta information for specific - * w differences. - */ -public class WDifferencesCurveInfoGenerator -extends ChartInfoGenerator -{ - public WDifferencesCurveInfoGenerator() { - super(new WDifferencesCurveGenerator()); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -19,8 +19,6 @@ import java.text.DateFormat; import java.util.Locale; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; @@ -30,7 +28,6 @@ import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JRException; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.artifacts.common.utils.Config; @@ -68,16 +65,10 @@ /** The storage that contains all WKms objects for the different facets. */ protected List<WKms[]> data; - - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("WDifferencesExporter.init"); - - super.init(request, out, context); - - this.data = new ArrayList<WKms[]>(); + public WDifferencesExporter() { + data = new ArrayList<WKms[]>(); } - /** * Genereate data in csv format. */ diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -21,8 +21,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; @@ -35,7 +33,6 @@ import net.sf.jasperreports.engine.JRException; import org.dive4elements.artifacts.Artifact; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.artifacts.common.utils.Config; @@ -145,15 +142,10 @@ /** The storage that contains official fixings if available.*/ protected List<WQKms> officalFixings; - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("WaterlevelExporter.init"); - - super.init(request, out, context); - + public WaterlevelExporter() { data = new ArrayList<WQKms[]>(); } - @Override public void generate() throws IOException diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -46,6 +46,8 @@ import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledXYSeries; +import org.dive4elements.river.jfree.AxisDataset; +import org.dive4elements.river.jfree.AnnotationHelper; import org.dive4elements.river.themes.ThemeDocument; @@ -66,115 +68,6 @@ */ public abstract class XYChartGenerator extends ChartGenerator { - public static class XYAxisDataset implements AxisDataset { - /** Symbolic integer, but also coding the priority (0 goes first). */ - protected int axisSymbol; - - /** List of assigned datasets (in order). */ - protected List<XYDataset> datasets; - - /** Range to use to include all given datasets. */ - protected Range range; - - /** Index of axis in plot. */ - protected int plotAxisIndex; - - /** Create AxisDataset. */ - public XYAxisDataset(int symb) { - this.axisSymbol = symb; - datasets = new ArrayList<XYDataset>(); - } - - /** Merge (or create given range with range so far (if any). */ - private void mergeRanges(Range subRange) { - // Avoid merging NaNs, as they take min/max place forever. - if (subRange == null || - Double.isNaN(subRange.getLowerBound()) || - Double.isNaN(subRange.getUpperBound())) { - return; - } - if (range == null) { - range = subRange; - return; - } - range = Range.combine(range, subRange); - } - - - /** Add a dataset to internal list for this axis. */ - @Override - public void addDataset(XYDataset dataset) { - datasets.add(dataset); - includeYRange(((XYSeriesCollection) dataset).getSeries(0)); - } - - /** Add a dataset, include its range. */ - public void addDataset(XYSeries series) { - addDataset(new XYSeriesCollection(series)); - } - - - /** Set Range for this axis. */ - @Override - public void setRange(Range range) { - this.range = range; - } - - - /** Get Range for this axis. */ - @Override - public Range getRange() { - return range; - } - - - /** Get Array of Datasets. */ - @Override - public XYDataset[] getDatasets() { - return datasets.toArray(new XYDataset[datasets.size()]); - } - - - /** Add a Dataset that describes an area. */ - public void addArea(StyledAreaSeriesCollection series) { - this.datasets.add(series); - List<?> allSeries = series.getSeries(); - /* We do not include the bounds/ranges, if the area includes - * points at "infinity"/BIG_DOUBLE_VALUE, the charts extents are - * expanded to include these very small/big value. - * This is especially used when showing "area above axis". */ - } - - /** True if to be rendered as area. */ - @Override - public boolean isArea(XYDataset series) { - return (series instanceof StyledAreaSeriesCollection); - } - - /** Adjust range to include given dataset. */ - public void includeYRange(XYSeries dataset) { - mergeRanges(new Range(dataset.getMinY(), dataset.getMaxY())); - } - - /** True if no datasets given. */ - @Override - public boolean isEmpty() { - return this.datasets.isEmpty(); - } - - /** Set the 'real' axis index that this axis is mapped to. */ - @Override - public void setPlotAxisIndex(int axisIndex) { - this.plotAxisIndex = axisIndex; - } - - /** Get the 'real' axis index that this axis is mapped to. */ - @Override - public int getPlotAxisIndex() { - return this.plotAxisIndex; - } - } // class AxisDataset - /** Enumerator over existing axes. */ @Override protected abstract YAxisWalker getYAxisWalker(); @@ -258,7 +151,8 @@ //debugAxis(plot); // These have to go after the autozoom. - addAnnotationsToRenderer(plot); + AnnotationHelper.addAnnotationsToRenderer(annotations, plot, + getChartSettings(), datasets); // Add a logo (maybe). addLogo(plot); @@ -415,8 +309,8 @@ @Override protected AxisDataset createAxisDataset(int idx) { - logger.debug("Create new XYAxisDataset for index: " + idx); - return new XYAxisDataset(idx); + logger.debug("Create new AxisDataset for index: " + idx); + return new AxisDataset(idx); } @@ -481,7 +375,7 @@ return; } - XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index); + AxisDataset axisDataset = (AxisDataset) getAxisDataset(index); if (visible) { axisDataset.addArea(area); @@ -510,13 +404,6 @@ addAxisDataset(new XYSeriesCollection(series), index, visible); - XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index); - - if (!visible) { - // Do this also when not visible to have axis scaled by default such - // that every data-point could be seen (except for annotations). - axisDataset.includeYRange(series); - } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -10,7 +10,6 @@ import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.river.artifacts.model.CalculationResult; @@ -39,8 +38,6 @@ import org.apache.log4j.Logger; -import org.w3c.dom.Document; - /** Exports fixation analysis deltaw(t) computation results to csv. */ public class DeltaWtExporter extends AbstractExporter @@ -101,10 +98,7 @@ protected List<KMIndex<QWD[]>> referenceEvents; - @Override - public void init(Document request, OutputStream out, CallContext context) { - log.debug("DeltaWtExporter.init"); - super.init(request, out, context); + public DeltaWtExporter() { analysisPeriods = new ArrayList<KMIndex<AnalysisPeriod []>>(); referenceEvents = new ArrayList<KMIndex<QWD[]>>(); } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -33,8 +33,8 @@ "/art:action/art:attributes/art:currentKm/@art:km"; @Override - public void init(Document request, OutputStream out, CallContext context) { - super.init(request, out, context); + public void init(String outName, Document request, OutputStream out, CallContext context) { + super.init(outName, request, out, context); Double currentKm = getCurrentKmFromRequest(request); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -462,8 +462,8 @@ @Override - public void init(Document request, OutputStream out, CallContext context) { - super.init(request, out, context); + public void init(String outName, Document request, OutputStream out, CallContext context) { + super.init(outName, request, out, context); Double currentKm = FixChartGenerator.getCurrentKmFromRequest(request); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -15,7 +15,6 @@ import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.model.FacetTypes; -import org.dive4elements.river.artifacts.model.WKms; import org.dive4elements.river.artifacts.model.WQKms; import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriod; import org.dive4elements.river.artifacts.model.fixings.QWD; @@ -28,7 +27,6 @@ import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; import org.dive4elements.river.utils.KMIndex; import org.dive4elements.river.utils.RiverUtils; import org.jfree.chart.plot.Marker; @@ -147,38 +145,7 @@ addAxisSeries(series, YAXIS.Q.idx, visible); - if (needInvertAxis(wqkms)) { - setInverted(true); - } - } - - /** - * This method determines - taking JFreeCharts auto x value ordering into - * account - if the x axis need to be inverted. Waterlines in these charts - * should decrease. - * - * @param wkms The data object that stores the x and y values used for this - * chart. - */ - public boolean needInvertAxis(WKms wkms) { - boolean wsUp = wkms.guessWaterIncreasing(); - boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms()); - boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp); - - int size = wkms.size(); - - if (logger.isDebugEnabled()) { - logger.debug("(Wkms)Values : " + size); - if (size > 0) { - logger.debug("Start km: " + wkms.getKm(0)); - logger.debug("End km: " + wkms.getKm(size-1)); - } - logger.debug("wsUp: " + wsUp); - logger.debug("kmUp: " + kmUp); - logger.debug("inv: " + inv); - } - - return inv; + setInverted(wqkms.guessRTLData()); } @SuppressWarnings("unchecked") diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -10,8 +10,6 @@ import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; - import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.Parameters; @@ -31,8 +29,6 @@ import org.apache.log4j.Logger; -import org.w3c.dom.Document; - public class ParametersExporter extends AbstractExporter { @@ -40,10 +36,7 @@ protected List<Parameters> parametersList; - @Override - public void init(Document request, OutputStream out, CallContext context) { - log.debug("ParametersExporter.init"); - super.init(request, out, context); + public ParametersExporter() { parametersList = new ArrayList<Parameters>(); } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -28,7 +28,6 @@ import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; import org.dive4elements.river.utils.RiverUtils; @@ -257,7 +256,7 @@ StyledSeriesBuilder.addPoints(series, wkms); addAxisSeries(series, YAXIS.D.idx, visible); - if (DataUtil.guessWaterIncreasing(wkms.allWs())) { + if (wkms.guessWaterIncreasing()) { setInverted(true); } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -27,7 +27,6 @@ import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JRException; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.access.BedDifferencesAccess; @@ -40,11 +39,8 @@ import org.dive4elements.river.utils.Formatter; import org.dive4elements.river.utils.RiverUtils; -import org.w3c.dom.Document; - import au.com.bytecode.opencsv.CSVWriter; - public class BedDifferenceExporter extends AbstractExporter { @@ -70,10 +66,7 @@ private BedDifferencesResult[] results; - @Override - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("init"); - super.init(request, out, context); + public BedDifferenceExporter() { results = new BedDifferencesResult[0]; } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -28,7 +28,6 @@ import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; import org.dive4elements.river.utils.RiverUtils; @@ -234,7 +233,7 @@ StyledSeriesBuilder.addPoints(series, wkms); addAxisSeries(series, YAXIS.D.idx, visible); - if (DataUtil.guessWaterIncreasing(wkms.allWs())) { + if (wkms.guessWaterIncreasing()) { setInverted(true); } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -19,10 +19,8 @@ import java.util.List; import org.apache.log4j.Logger; -import org.w3c.dom.Document; import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult; import org.dive4elements.river.artifacts.model.minfo.BedParametersResult; @@ -56,10 +54,7 @@ private BedQualityResult[] results; - @Override - public void init(Document request, OutputStream out, CallContext context) { - logger.debug("BedQualityExporter.init"); - super.init(request, out, context); + public BedQualityExporter() { results = new BedQualityResult[0]; } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -17,12 +17,8 @@ import java.text.NumberFormat; -import org.w3c.dom.Document; - import org.apache.log4j.Logger; -import org.dive4elements.artifacts.CallContext; - import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.minfo.SedimentLoad; import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFraction; @@ -78,17 +74,9 @@ /** Empty constructor. */ public SedimentLoadExporter() { - } - - /** Trivial init. */ - @Override - public void init(Document request, OutputStream out, CallContext context) { - super.init(request, out, context); - logger.debug("init"); results = new SedimentLoadResult[0]; } - /** Process all stored data and write csv. */ @Override protected void writeCSVData(CSVWriter writer) throws IOException { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -33,7 +33,6 @@ import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; /** Generator for Longitudinal Sections of SedimentLoad-Calculations. */ @@ -355,7 +354,7 @@ StyledSeriesBuilder.addPoints(series, wkms); addAxisSeries(series, YAXIS.D.idx, visible); - if (DataUtil.guessWaterIncreasing(wkms.allWs())) { + if (wkms.guessWaterIncreasing()) { setInverted(true); } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,84 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports.process; + +import org.apache.log4j.Logger; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.model.FacetTypes; +import org.dive4elements.river.exports.DiagramGenerator; +import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.jfree.RiverAnnotation; +import org.dive4elements.river.themes.ThemeDocument; + +/** + * Add data to chart/generator. + * + */ +public class AnnotationProcessor extends DefaultProcessor { + + /** Private logger. */ + private static final Logger logger = + Logger.getLogger(AnnotationProcessor.class); + + @Override + public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + if (!visible) { + // Nothing to do + return; + } + CallContext context = generator.getCallContext(); + if (!(bundle.getData(context) instanceof RiverAnnotation)) { + // Just a bit defensive should not happen + logger.error("Incompatible facet in doOut"); + return; + } + RiverAnnotation ra = (RiverAnnotation)bundle.getData(context); + ra.setTheme(theme); + ra.setLabel(bundle.getFacetDescription()); + generator.addAnnotations(ra); + } + + @Override + public void doOut( + XYChartGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible, + int index) + { + if (!visible) { + // Nothing to do + return; + } + CallContext context = generator.getCallContext(); + if (!(bundle.getData(context) instanceof RiverAnnotation)) { + // Just a bit defensive should not happen + logger.error("Incompatible facet in doOut"); + return; + } + RiverAnnotation ra = (RiverAnnotation)bundle.getData(context); + ra.setTheme(theme); + ra.setLabel(bundle.getFacetDescription()); + generator.addAnnotations(ra); + } + + @Override + public boolean canHandle(String facetType) { + if (facetType == null) { + return false; + } + return facetType.equals(FacetTypes.LONGITUDINAL_ANNOTATION); + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,154 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + + +package org.dive4elements.river.exports.process; + +import org.apache.log4j.Logger; + + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifacts.CallContext; + +import org.dive4elements.river.artifacts.geom.Lines; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.model.WQKms; +import org.dive4elements.river.artifacts.model.AreaFacet; +import org.dive4elements.river.artifacts.model.FacetTypes; +import org.dive4elements.river.exports.DiagramGenerator; +import org.dive4elements.river.exports.StyledSeriesBuilder; +import org.dive4elements.river.jfree.StyledAreaSeriesCollection; +import org.dive4elements.river.jfree.StyledXYSeries; +import org.dive4elements.river.themes.ThemeDocument; + +import org.jfree.data.xy.XYSeries; + +public class AreaProcessor extends DefaultProcessor { + + private static final Logger logger = Logger.getLogger(ManualPointsProcessor.class); + + @Override + public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + String seriesName = bundle.getFacetDescription(); + StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme); + + logger.debug("Area Processor processing: " + seriesName); + + AreaFacet.Data data = (AreaFacet.Data) bundle.getData(context); + + XYSeries up = null; + XYSeries down = null; + + if (data.getUpperData() != null) { + up = new StyledXYSeries(seriesName, false, theme); + if (data.getUpperData() instanceof WQKms) { + if (FacetTypes.IS.Q(data.getRootFacetName())) { + StyledSeriesBuilder.addPointsKmQ(up, (WQKms) data.getUpperData()); + } + else { + StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData()); + } + } + else if (data.getUpperData() instanceof double[][]) { + StyledSeriesBuilder.addPoints(up, (double [][]) data.getUpperData(), false); + } + else if (data.getUpperData() instanceof WKms) { + StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData()); + } + else if (data.getUpperData() instanceof Lines.LineData) { + StyledSeriesBuilder.addPoints(up, ((Lines.LineData) data.getUpperData()).points, false); + } + else { + logger.error("Do not know how to deal with (up) area info from: " + + data.getUpperData()); + } + } + + // TODO Depending on style, the area (e.g. 20m^2) should be added as annotation. + + if (data.getLowerData() != null) { + // TODO: Sort this out: when the two series have the same name, + // the renderer (or anything in between) will not work correctly. + down = new StyledXYSeries(seriesName + " ", false, theme); + if (data.getLowerData() instanceof WQKms) { + if (FacetTypes.IS.Q(data.getRootFacetName())) { + StyledSeriesBuilder.addPointsKmQ(down, (WQKms) data.getLowerData()); + } + else { + StyledSeriesBuilder.addPoints(down, (WQKms) data.getLowerData()); + } + } + else if (data.getLowerData() instanceof double[][]) { + StyledSeriesBuilder.addPoints(down, (double[][]) data.getLowerData(), false); + } + else if (data.getLowerData() instanceof WKms) { + StyledSeriesBuilder.addPoints(down, (WKms) data.getLowerData()); + } + else if (data.getLowerData() instanceof Lines.LineData) { + StyledSeriesBuilder.addPoints(down, ((Lines.LineData) data.getLowerData()).points, false); + } + else { + logger.error("Do not know how to deal with (down) area info from: " + + data.getLowerData()); + } + } + + if (up == null && down != null) { + area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE); + down.setKey(seriesName); + area.addSeries(down); + area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(down)); + } + else if (up != null && down == null) { + area.setMode(StyledAreaSeriesCollection.FILL_MODE.UNDER); + area.addSeries(up); + area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(up)); + } + else if (up != null && down != null) { + if (data.doPaintBetween()) { + area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN); + } + else { + area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE); + } + area.addSeries(up); + area.addSeries(down); + } + + /* Decide axis name based on facet name */ + generator.addAreaSeries(area, + axisNameForFacet(data.getRootFacetName()), visible); + } + + /** Look up the axis identifier for a given facet type. */ + private String axisNameForFacet(String facetName) { + if (FacetTypes.IS.W(facetName)) { + return "W"; + } + else if (FacetTypes.IS.Q(facetName)) { + return "Q"; + } + else { + logger.warn("Could not find axis for facet " + facetName); + return "W"; + } + } + + @Override + public boolean canHandle(String facetType) { + if (facetType == null) { + return false; + } + return FacetTypes.IS.AREA(facetType); + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -16,12 +16,14 @@ import org.dive4elements.river.artifacts.model.FacetTypes; import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult; import org.dive4elements.river.exports.StyledSeriesBuilder; +import org.dive4elements.river.exports.DiagramGenerator; import org.dive4elements.river.exports.XYChartGenerator; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -public class BedDiffHeightYearProcessor implements Processor, FacetTypes { +public class BedDiffHeightYearProcessor +extends DefaultProcessor implements FacetTypes { private final static Logger logger = Logger.getLogger(BedDiffHeightYearProcessor.class); @@ -30,21 +32,43 @@ @Override public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + Object data = bundle.getData(context); + if (data instanceof BedDiffYearResult) { + BedDiffYearResult bData = (BedDiffYearResult) data; + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + StyledSeriesBuilder.addPoints(series, bData.getHeightPerYearData(), false, GAP_TOLERANCE); + + generator.addAxisSeries(series, axisName, visible); + return; + } + // Should not happen if canHandle is correct + logger.error("Can't process " + data.getClass().getName() + " objects"); + } + + @Override + public void doOut( XYChartGenerator generator, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index ) { CallContext context = generator.getCallContext(); - Object data = aandf.getData(context); + Object data = bundle.getData(context); if (data instanceof BedDiffYearResult) { - doBedDifferenceYearOut( - generator, - (BedDiffYearResult) data, - aandf, theme, visible, index); + BedDiffYearResult bData = (BedDiffYearResult) data; + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + StyledSeriesBuilder.addPoints(series, bData.getHeightPerYearData(), false, GAP_TOLERANCE); + + generator.addAxisSeries(series, index, visible); return; } + // Should not happen if canHandle is correct logger.error("Can't process " + data.getClass().getName() + " objects"); } @@ -58,12 +82,12 @@ protected void doBedDifferenceYearOut(XYChartGenerator generator, BedDiffYearResult data, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int axidx) { - XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), false, GAP_TOLERANCE); generator.addAxisSeries(series, axidx, visible); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -17,11 +17,13 @@ import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult; import org.dive4elements.river.exports.StyledSeriesBuilder; import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.exports.DiagramGenerator; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -public class BedDiffYearProcessor implements Processor, FacetTypes { +public class BedDiffYearProcessor +extends DefaultProcessor implements FacetTypes { private final static Logger logger = Logger.getLogger(BedDiffYearProcessor.class); @@ -30,23 +32,65 @@ @Override public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + Object data = bundle.getData(context); + if (data instanceof BedDiffYearResult) { + String facetType = bundle.getFacetName(); + BedDiffYearResult bData = (BedDiffYearResult) data; + + double[][] points; + if (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType)) { + points = bData.getHeights1Data(); + } else { + points = bData.getHeights2Data(); + } + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + StyledSeriesBuilder.addPointsFactorY(series, + points, + false, + GAP_TOLERANCE, + 100d); + + generator.addAxisSeries(series, axisName, visible); + + return; + } + logger.error("Can't process " + data.getClass().getName() + " objects"); + } + + @Override + public void doOut( XYChartGenerator generator, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int axidx ) { CallContext context = generator.getCallContext(); - Object data = aandf.getData(context); + Object data = bundle.getData(context); if (data instanceof BedDiffYearResult) { - String facetType = aandf.getFacetName(); - int index = - (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType) - || BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED.equals(facetType))?0:1; - doBedDifferenceHeightsOut( - generator, - (BedDiffYearResult) data, - aandf, theme, visible, index, axidx); + String facetType = bundle.getFacetName(); + BedDiffYearResult bData = (BedDiffYearResult) data; + + double[][] points; + if (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType)) { + points = bData.getHeights1Data(); + } else { + points = bData.getHeights2Data(); + } + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + StyledSeriesBuilder.addPointsFactorY(series, + points, + false, + GAP_TOLERANCE, + 100d); + + generator.addAxisSeries(series, axidx, visible); + return; } logger.error("Can't process " + data.getClass().getName() + " objects"); @@ -60,33 +104,5 @@ || BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED.equals(facetType) || BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED.equals(facetType); } - - private void doBedDifferenceHeightsOut( - XYChartGenerator generator, - BedDiffYearResult data, - ArtifactAndFacet bundle, - ThemeDocument attr, - boolean visible, - int idx, - int axidx) { - logger.debug("doBedDifferenceHeightsOut()"); - - XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr); - double[][] points; - if (idx == 0) { - points = data.getHeights1Data(); - } - else { - points = data.getHeights2Data(); - } - - StyledSeriesBuilder.addPointsFactorY(series, - points, - false, - GAP_TOLERANCE, - 100d); - - generator.addAxisSeries(series, axidx, visible); - } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -17,11 +17,12 @@ import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.model.minfo.BedHeightSingle; import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.exports.DiagramGenerator; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.model.BedHeightSingleValue; import org.dive4elements.river.themes.ThemeDocument; -public class BedheightProcessor implements Processor { +public class BedheightProcessor extends DefaultProcessor { private final static Logger logger = Logger.getLogger(BedheightProcessor.class); @@ -30,34 +31,66 @@ @Override public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + Object data = bundle.getData(context); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), + theme); + if (data instanceof BedHeightSingle) { + BedHeightSingle bData = (BedHeightSingle)data; + double[] width = bData.getMorphWidths(); + double[] stations = bData.getStations().toNativeArray(); + + logger.debug("doBedheightSingleOut"); + + for (int i = 0; i < width.length; i++) { + series.add(stations[i], width[i], false); + } + } else if (data instanceof List<?>) { + List<BedHeightSingleValue> bData = (List<BedHeightSingleValue>)data; + + logger.debug("doBedheightSingleValueOut"); + + for(BedHeightSingleValue bvalue: bData) { + series.add(bvalue.getStation(), bvalue.getSoundingWidth()); + } + } + generator.addAxisSeries(series, axisName, visible); + } + + @Override + public void doOut( XYChartGenerator generator, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index ) { CallContext context = generator.getCallContext(); - Object data = aandf.getData(context); + Object data = bundle.getData(context); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), + theme); if (data instanceof BedHeightSingle) { - doBedheightSingleOut(generator, aandf, theme, index, visible, - (BedHeightSingle)data); - } - else if (data instanceof List<?>) { - doBedheightSingeValuesOut(generator, aandf, theme, index, visible, - (List<BedHeightSingleValue>)data); - } - logger.error("Can't process " + data.getClass().getName() + " objects"); - } + BedHeightSingle bData = (BedHeightSingle)data; + double[] width = bData.getMorphWidths(); + double[] stations = bData.getStations().toNativeArray(); - private void doBedheightSingeValuesOut(XYChartGenerator generator, - ArtifactAndFacet aandf, ThemeDocument theme, int index, boolean visible, - List<BedHeightSingleValue> data) { - logger.debug("doBedheightSingeValuesOut"); + logger.debug("doBedheightSingleOut"); - XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), - theme); - for(BedHeightSingleValue bvalue: data) { - series.add(bvalue.getStation(), bvalue.getSoundingWidth()); + for (int i = 0; i < width.length; i++) { + series.add(stations[i], width[i], false); + } + } else if (data instanceof List<?>) { + List<BedHeightSingleValue> bData = (List<BedHeightSingleValue>)data; + + logger.debug("doBedheightSingleValueOut"); + + for(BedHeightSingleValue bvalue: bData) { + series.add(bvalue.getStation(), bvalue.getSoundingWidth()); + } } generator.addAxisSeries(series, index, visible); } @@ -66,22 +99,4 @@ public boolean canHandle(String facettype) { return BEDHEIGHT.equals(facettype); } - - public void doBedheightSingleOut(XYChartGenerator generator, - ArtifactAndFacet aandf, ThemeDocument theme, int index, boolean visible, - BedHeightSingle data) { - double[] width = data.getMorphWidths(); - double[] stations = data.getStations().toNativeArray(); - - logger.debug("doBedheightSingleOut"); - - XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), - theme); - - for (int i = 0; i < width.length; i++) { - series.add(stations[i], width[i], false); - } - - generator.addAxisSeries(series, index, visible); - } } diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/DefaultProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DefaultProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,75 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports.process; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.exports.DiagramGenerator; +import org.dive4elements.river.themes.ThemeDocument; + +/** Dummy implementation for the Processor interface. + */ +public class DefaultProcessor implements Processor { + + protected String axisName; + + public void setAxisName(String axisName) { + this.axisName = axisName; + } + + public String getAxisName() { + return axisName; + } + + /** + * Processes data to generate e.g. a chart. + * + * @param generator XYChartGenerator to add output on. + * @param bundle The artifact and facet + * @param theme The theme that contains styling information. + * @param visible The visibility of the curve. + * @param index The index of the curve + */ + @Override + public void doOut( + XYChartGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible, + int index) { + return; + } + + @Override + public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + return; + } + + @Override + public String getAxisLabel(DiagramGenerator generator) { + return "Please overwrite me in the implementation"; + } + + + /** + * Returns true if the Processor class is able to generate output for a facet type + * + * @param facettype Name of the facet type + * @return true if the facettype can be processed + */ + @Override + public boolean canHandle(String facettype) + { + return false; + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -31,7 +31,7 @@ /** Helper for data handling in discharge diagrams. */ public class DischargeProcessor -implements Processor, FacetTypes { +extends DefaultProcessor implements FacetTypes { private final static Logger logger = Logger.getLogger(DischargeProcessor.class); @@ -58,32 +58,32 @@ @Override public void doOut( XYChartGenerator generator, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int axisIndex ) { CallContext context = generator.getCallContext(); - Object data = aandf.getData(context); + Object data = bundle.getData(context); if (false && data instanceof WQKms) { doWQKmsPointOut( - generator, (WQKms) data, aandf, theme, visible, axisIndex); + generator, (WQKms) data, bundle, theme, visible, axisIndex); return; } else if (data instanceof RiverAnnotation) { doRiverAnnotationOut( - generator, (RiverAnnotation) data, aandf, theme, visible); + generator, (RiverAnnotation) data, bundle, theme, visible); return; } else if (data instanceof double[][]) { doMarksOut( - generator, (double[][]) data, aandf, theme, visible); + generator, (double[][]) data, bundle, theme, visible); return; } else { logger.error("Can't process " + data.getClass().getName() + " objects of facet " - + aandf.getFacetName()); + + bundle.getFacetName()); } } @@ -109,13 +109,13 @@ /** Handle WQKms data by finding w/q values at given km. */ protected void doWQKmsPointOut(XYChartGenerator generator, WQKms wqkms, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int axidx ) { logger.debug("doWQKmsPointOut"); - String title = aandf.getFacetDescription(); + String title = bundle.getFacetDescription(); XYSeries series = new StyledXYSeries( title, theme); @@ -147,7 +147,7 @@ protected void doRiverAnnotationOut(XYChartGenerator generator, RiverAnnotation annotations, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible ) { @@ -163,7 +163,7 @@ dGenerator.translateRiverAnnotation(annotations); dGenerator.doAnnotations( annotations, - aandf, theme, visible); + bundle, theme, visible); } @@ -173,7 +173,7 @@ */ protected void doMarksOut(XYChartGenerator generator, double[][] data, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible ) { @@ -184,7 +184,7 @@ } // TODO subtract gauge null point if at gauge. - String title = aandf.getFacetDescription(); + String title = bundle.getFacetDescription(); List<StickyAxisAnnotation> yMarks = new ArrayList<StickyAxisAnnotation>(); for (double yPos: data[1]) { @@ -195,7 +195,7 @@ } generator.doAnnotations(new RiverAnnotation(title, yMarks), - aandf, theme, visible); + bundle, theme, visible); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -22,22 +22,22 @@ import org.dive4elements.river.themes.ThemeDocument; import org.dive4elements.river.utils.KMIndex; -public class KMIndexProcessor implements Processor { +public class KMIndexProcessor extends DefaultProcessor { private static final Logger logger = Logger.getLogger(KMIndexProcessor.class); @Override - public void doOut(XYChartGenerator generator, ArtifactAndFacet aandf, + public void doOut(XYChartGenerator generator, ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index) { - String facettype = aandf.getFacetName(); + String facettype = bundle.getFacetName(); if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)) { - doSectorAverageOut(generator, aandf, theme, visible, index); + doSectorAverageOut(generator, bundle, theme, visible, index); } else if (facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)) { - doReferenceEventsOut(generator, aandf, theme, visible, index); + doReferenceEventsOut(generator, bundle, theme, visible, index); } else if (facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)) { - doAnalysisEventsOut(generator, aandf, theme, visible, index); + doAnalysisEventsOut(generator, bundle, theme, visible, index); } } @@ -57,23 +57,23 @@ return false; } - private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet aaf, + private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet bundle, ThemeDocument doc, boolean visible, int idx) { - logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex()); + logger.debug("doSectorAverageOut" + bundle.getFacet().getIndex()); CallContext context = generator.getCallContext(); - int index = aaf.getFacet().getIndex(); + int index = bundle.getFacet().getIndex(); int sectorNdx = index & 3; @SuppressWarnings("unchecked") KMIndex<AnalysisPeriod> kms = - (KMIndex<AnalysisPeriod>)aaf.getData(context); + (KMIndex<AnalysisPeriod>)bundle.getData(context); if(kms == null) { return; } - XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), doc); for (KMIndex.Entry<AnalysisPeriod> entry: kms) { double km = entry.getKm(); @@ -90,14 +90,14 @@ } private void doReferenceEventsOut(XYChartGenerator generator, - ArtifactAndFacet aaf, ThemeDocument doc, boolean visible, int idx) { + ArtifactAndFacet bundle, ThemeDocument doc, boolean visible, int idx) { logger.debug("doReferenceEventOut"); CallContext context = generator.getCallContext(); @SuppressWarnings("unchecked") KMIndex<QWD> kms = - (KMIndex<QWD>)aaf.getData(context); + (KMIndex<QWD>)bundle.getData(context); if(kms == null) { return; @@ -105,7 +105,7 @@ XYSeriesCollection col = new XYSeriesCollection(); - StyledXYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, + StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false, doc); for (KMIndex.Entry<QWD> entry: kms) { @@ -120,14 +120,14 @@ } private void doAnalysisEventsOut(XYChartGenerator generator, - ArtifactAndFacet aaf, ThemeDocument doc, boolean visible, int idx) { + ArtifactAndFacet bundle, ThemeDocument doc, boolean visible, int idx) { logger.debug("doAnalysisEventsOut"); CallContext context = generator.getCallContext(); @SuppressWarnings("unchecked") KMIndex<QWD> kms = - (KMIndex<QWD>)aaf.getData(context); + (KMIndex<QWD>)bundle.getData(context); if(kms == null) { return; @@ -135,7 +135,7 @@ XYSeriesCollection col = new XYSeriesCollection(); - StyledXYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, doc); + StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false, doc); for (KMIndex.Entry<QWD> entry: kms) { double km = entry.getKm(); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,87 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + + +package org.dive4elements.river.exports.process; + +import org.apache.log4j.Logger; + +import java.util.List; +import java.util.ArrayList; + +import org.json.JSONArray; +import org.json.JSONException; + +import org.jfree.data.xy.XYSeries; +import org.jfree.chart.annotations.XYTextAnnotation; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.model.FacetTypes; +import org.dive4elements.river.exports.DiagramGenerator; +import org.dive4elements.river.jfree.RiverAnnotation; +import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; +import org.dive4elements.river.jfree.StyledXYSeries; +import org.dive4elements.river.themes.ThemeDocument; + +public class ManualPointsProcessor extends DefaultProcessor { + + private static final Logger logger = Logger.getLogger(ManualPointsProcessor.class); + + @Override + public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + String seriesName = bundle.getFacetDescription(); + XYSeries series = new StyledXYSeries(seriesName, theme); + String jsonData = (String) bundle.getData(context); + + // Add text annotations for single points. + List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>(); + + try { + JSONArray points = new JSONArray(jsonData); + for (int i = 0, P = points.length(); i < P; i++) { + JSONArray array = points.getJSONArray(i); + double x = array.getDouble(0); + double y = array.getDouble(1); + String name = array.getString(2); + boolean act = array.getBoolean(3); + if (!act) { + continue; + } + //logger.debug(" x " + x + " y " + y ); + series.add(x, y, false); + xy.add(new CollisionFreeXYTextAnnotation(name, x, y)); + } + } + catch(JSONException e){ + logger.error("Could not decode json."); + } + + RiverAnnotation annotation = new RiverAnnotation(null, null, null, theme); + annotation.setTextAnnotations(xy); + + if (visible) { + generator.addAnnotations(annotation); + } + + generator.addAxisSeries(series, axisName, visible); + } + + @Override + public boolean canHandle(String facetType) { + if (facetType == null) { + return false; + } + return FacetTypes.IS.MANUALPOINTS(facetType); + } +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java Tue Sep 24 12:29:44 2013 +0200 @@ -11,6 +11,7 @@ import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; import org.dive4elements.river.exports.XYChartGenerator; import org.dive4elements.river.themes.ThemeDocument; +import org.dive4elements.river.exports.DiagramGenerator; /** * A processor is intended to generate an output e.g. curve in a chart diagramm from @@ -19,19 +20,55 @@ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a> */ public interface Processor { + /** + * Set the axis for this processor. + * This should be done before doOut is called for the first time. + * + * @param axisName The name of the Axis this processor should use. + */ + public void setAxisName(String axisName); + + /** + * Get the axis for this processor. + * + * @return The name of the axis that is used. + */ + public String getAxisName(); + + /** + * Get the axis label for this processor. + * + * @return The label of the axis. + */ + public String getAxisLabel(DiagramGenerator generator); /** * Processes data to generate e.g. a chart. * * @param generator XYChartGenerator to add output on. - * @param aandf The artifact and facet - * @param theme The theme that contains styling information. - * @param visible The visibility of the curve. - * @param index The index of the curve + * @param bundle The artifact and facet + * @param theme The theme that contains styling information. + * @param visible The visibility of the curve. */ public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible); + + /** + * Processes data to generate e.g. a chart. + * + * @param generator DiagramGenerator to add output on. + * @param bundle The artifact and facet + * @param theme The theme that contains styling information. + * @param visible The visibility of the curve. + * @param index The index of the curve + */ + @Deprecated + public void doOut( XYChartGenerator generator, - ArtifactAndFacet aandf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -14,21 +14,26 @@ import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.model.FacetTypes; -import org.dive4elements.river.artifacts.model.WKms; import org.dive4elements.river.artifacts.model.WQKms; import org.dive4elements.river.exports.StyledSeriesBuilder; import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.exports.DiagramGenerator; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; /** * Add data to chart/generator. * * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a> */ -public class QOutProcessor implements Processor { +public class QOutProcessor extends DefaultProcessor { + + public static final String I18N_LONGITUDINAL_LABEL = + "chart.longitudinal.section.yaxis.second.label"; + + public static final String + I18N_LONGITUDINAL_LABEL_DEFAULT = "Q [m\u00b3/s]"; /** Private logger. */ private static final Logger logger = @@ -36,16 +41,48 @@ @Override public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + WQKms wqkms = (WQKms) bundle.getData(context); + + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + + StyledSeriesBuilder.addStepPointsKmQ(series, wqkms); + + generator.addAxisSeries(series, axisName, visible); + + /* Check if the diagram should be inverted*/ + generator.setInverted(wqkms.guessRTLData()); + } + + @Override + public void doOut( XYChartGenerator generator, - ArtifactAndFacet aaf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index) { CallContext context = generator.getCallContext(); - WQKms wqkms = (WQKms) aaf.getData(context); + WQKms wqkms = (WQKms) bundle.getData(context); - doQOut(generator, wqkms, aaf, theme, visible, index); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + + StyledSeriesBuilder.addStepPointsKmQ(series, wqkms); + + generator.addAxisSeries(series, index, visible); + + /* Check if the diagram should be inverted*/ + generator.setInverted(wqkms.guessRTLData()); + } + + @Override + public String getAxisLabel(DiagramGenerator generator) { + return generator.msg(I18N_LONGITUDINAL_LABEL, + I18N_LONGITUDINAL_LABEL_DEFAULT); } /** @@ -59,73 +96,10 @@ if (facetType.equals(FacetTypes.STATIC_WQKMS_Q) || facetType.equals(FacetTypes.LONGITUDINAL_Q) - || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_Q)) { + || facetType.startsWith(FacetTypes.DISCHARGE_LONGITUDINAL_Q)) { return true; } return false; } - - - /** - * Process the output for Q facets in a longitudinal section curve. - * - * @param generator Generator to use. - * @param wqkms An array of WQKms values. - * @param aandf The facet and artifact. This facet does NOT support any data objects. Use - * D4EArtifact.getNativeFacet() instead to retrieve a Facet which supports - * data. - * @param theme The theme that contains styling information. - * @param visible The visibility of the curve. - * @param index Axis index to add data to. - */ - protected void doQOut( - XYChartGenerator generator, - WQKms wqkms, - ArtifactAndFacet aaf, - ThemeDocument theme, - boolean visible, - int index - ) { - logger.debug("QProcessor.doOut"); - - XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); - - StyledSeriesBuilder.addStepPointsKmQ(series, wqkms); - - generator.addAxisSeries(series, index, visible); - - invertAxis(generator, wqkms); - } - - /** - * This method determines - taking JFreeCharts auto x value ordering into - * account - if the x axis need to be inverted. Waterlines in these charts - * should decrease. - * - * @param generator the generator to invert the axis or not. - * @param wkms The data object that stores the x and y values used for this - * chart. - */ - public void invertAxis(XYChartGenerator generator, WKms wkms) { - boolean wsUp = wkms.guessWaterIncreasing(); - boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms()); - int size = wkms.size(); - boolean inv = ((wsUp && kmUp) || (!wsUp && !kmUp)) && size > 1; - - if (logger.isDebugEnabled()) { - logger.debug("(Wkms)Values : " + size); - if (size > 0) { - logger.debug("Start km: " + wkms.getKm(0)); - logger.debug("End km: " + wkms.getKm(size-1)); - } - logger.debug("wsUp: " + wsUp); - logger.debug("kmUp: " + kmUp); - if (size == 1) { - logger.debug("InvertAxis not inverting because we have just one km"); - } - logger.debug("inv: " + inv); - } - generator.setInverted(inv); - } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/WDiffProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WDiffProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,39 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.exports.process; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.river.artifacts.model.FacetTypes; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.themes.ThemeDocument; +import org.dive4elements.river.exports.DiagramGenerator; + +public class WDiffProcessor extends WOutProcessor { + + public final static String I18N_WDIFF_YAXIS_LABEL = + "chart.w_differences.yaxis.label"; + + public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m"; + + @Override + public boolean canHandle(String facetType) { + if (facetType == null) { + return false; + } + return facetType.equals(FacetTypes.W_DIFFERENCES); + } + + + @Override + public String getAxisLabel(DiagramGenerator generator) { + return generator.msg(I18N_WDIFF_YAXIS_LABEL, + I18N_WDIFF_YAXIS_LABEL_DEFAULT); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java Tue Sep 24 12:29:44 2013 +0200 @@ -13,40 +13,102 @@ import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.model.FacetTypes; import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.model.WQCKms; import org.dive4elements.river.exports.StyledSeriesBuilder; import org.dive4elements.river.exports.XYChartGenerator; +import org.dive4elements.river.exports.DiagramGenerator; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.themes.ThemeDocument; -import org.dive4elements.river.utils.DataUtil; +import org.dive4elements.river.utils.RiverUtils; /** * Add data to chart/generator. * * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a> */ -public class WOutProcessor implements Processor { +public class WOutProcessor extends DefaultProcessor { /** Private logger. */ private static final Logger logger = Logger.getLogger(WOutProcessor.class); + public static final String I18N_AXIS_LABEL = + "chart.longitudinal.section.yaxis.label"; + + public static final String I18N_AXIS_LABEL_DEFAULT = "W [NN + m]"; + + @Override + public void doOut( + DiagramGenerator generator, + ArtifactAndFacet bundle, + ThemeDocument theme, + boolean visible) { + CallContext context = generator.getCallContext(); + Object data = bundle.getData(context); + WKms wkms = (WKms) data; + + logger.debug("Processing facet: " + bundle.getFacetName()); + + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); + + if (bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_C)) { + // Add corrected values + WQCKms wqckms = (WQCKms) data; + int size = wqckms.size(); + for (int i = 0; i < size; i++) { + series.add(wqckms.getKm(i), wqckms.getC(i), false); + } + } else { + StyledSeriesBuilder.addPoints(series, wkms); + } + generator.addAxisSeries(series, axisName, visible); + + // If a "band around the curve shall be drawn, add according area. + double bandWidth = theme.parseBandWidth(); + if (bandWidth > 0 ) { + XYSeries seriesDown = new StyledXYSeries( + "band " + bundle.getFacetDescription(), false, theme); + XYSeries seriesUp = new StyledXYSeries( + bundle.getFacetDescription()+"+/-"+bandWidth, false, theme); + StyledSeriesBuilder.addUpperBand(seriesUp, wkms, bandWidth); + StyledSeriesBuilder.addLowerBand(seriesDown, wkms, bandWidth); + + StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme); + area.addSeries(seriesUp); + area.addSeries(seriesDown); + area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN); + generator.addAreaSeries(area, axisName, visible); + } + + if (bundle.getFacetName().equals(FacetTypes.LONGITUDINAL_W) || + bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) || + bundle.getFacetName().equals(FacetTypes.STATIC_WQKMS_W) || + bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_C)) { + /* Only use W values to check if the diagram should be inverted + * see flys/issue1290 for details */ + logger.debug("Check for RTL data: "+ wkms.guessRTLData()); + generator.setInverted(wkms.guessRTLData()); + } + } + @Override public void doOut( XYChartGenerator generator, - ArtifactAndFacet aaf, + ArtifactAndFacet bundle, ThemeDocument theme, boolean visible, int index) { CallContext context = generator.getCallContext(); - WKms wkms = (WKms) aaf.getData(context); + WKms wkms = (WKms) bundle.getData(context); logger.debug("doOut"); - XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); + XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme); StyledSeriesBuilder.addPoints(series, wkms); generator.addAxisSeries(series, index, visible); @@ -55,9 +117,9 @@ double bandWidth = theme.parseBandWidth(); if (bandWidth > 0 ) { XYSeries seriesDown = new StyledXYSeries( - "band " + aaf.getFacetDescription(), false, theme); + "band " + bundle.getFacetDescription(), false, theme); XYSeries seriesUp = new StyledXYSeries( - aaf.getFacetDescription()+"+/-"+bandWidth, false, theme); + bundle.getFacetDescription()+"+/-"+bandWidth, false, theme); StyledSeriesBuilder.addUpperBand(seriesUp, wkms, bandWidth); StyledSeriesBuilder.addLowerBand(seriesDown, wkms, bandWidth); @@ -68,15 +130,27 @@ generator.addAreaSeries(area, index, visible); } - if (aaf.getFacetName().equals(FacetTypes.LONGITUDINAL_W) || - aaf.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) || - aaf.getFacetName().equals(FacetTypes.STATIC_WQKMS_W)) { + if (bundle.getFacetName().equals(FacetTypes.LONGITUDINAL_W) || + bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) || + bundle.getFacetName().equals(FacetTypes.STATIC_WQKMS_W)) { /* Only use W values to check if the diagram should be inverted * see flys/issue1290 for details */ - invertAxis(generator, wkms); + generator.setInverted(wkms.guessRTLData()); } } + @Override + public String getAxisLabel(DiagramGenerator generator) { + D4EArtifact flys = (D4EArtifact) generator.getMaster(); + + String unit = RiverUtils.getRiver(flys).getWstUnit().getName(); + + return generator.msg( + I18N_AXIS_LABEL, + I18N_AXIS_LABEL_DEFAULT, + new Object[] { unit }); + } + /** * Returns true if facettype is longitutinal_section.w . */ @@ -91,41 +165,12 @@ || facetType.equals(FacetTypes.HEIGHTMARKS_POINTS) || facetType.equals(FacetTypes.STATIC_WQKMS) || facetType.equals(FacetTypes.STATIC_WQKMS_W) - || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W)) + || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) + || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_C)) { return true; } return false; } - - /** - * This method determines - taking JFreeCharts auto x value ordering into - * account - if the x axis need to be inverted. Waterlines in these charts - * should decrease. - * - * @param wkms The data object that stores the x and y values used for this - * chart. - */ - public void invertAxis(XYChartGenerator generator, WKms wkms) { - boolean wsUp = wkms.guessWaterIncreasing(); - boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms()); - int size = wkms.size(); - boolean inv = ((wsUp && kmUp) || (!wsUp && !kmUp)) && size > 1; - - if (logger.isDebugEnabled()) { - logger.debug("(Wkms)Values : " + size); - if (size > 0) { - logger.debug("Start km: " + wkms.getKm(0)); - logger.debug("End km: " + wkms.getKm(size-1)); - } - logger.debug("wsUp: " + wsUp); - logger.debug("kmUp: " + kmUp); - if (size == 1) { - logger.debug("InvertAxis not inverting because we have just one km"); - } - logger.debug("inv: " + inv); - } - generator.setInverted(inv); - } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java Tue Sep 24 12:29:44 2013 +0200 @@ -62,6 +62,13 @@ protected List<JFreeChart> charts; + protected String outName; + + @Override + public void setup(Object config) { + logger.debug("SQOverviewGenerator.setup"); + } + /** * Produce output. * @param artifactAndFacet current facet and artifact. @@ -75,6 +82,8 @@ ) { logger.debug("doOut()"); + // TODO: Why not using outName for this. + String name = artifactAndFacet.getData(context).toString(); if(name != null) { logger.debug("name: " + name); @@ -94,7 +103,7 @@ try { Document cAttr = getAttribute(context, collectionAttribute, name); - g.init(request, out, context); + g.init(name, request, out, context); helper.doOut(g, name, name, cAttr, context); JFreeChart chart = g.generateChart(); @@ -111,7 +120,8 @@ } @Override - public void init(Document request, OutputStream out, CallContext context) { + public void init(String outName, Document request, OutputStream out, CallContext context) { + this.outName = outName; this.request = request; this.out = out; this.context = context; @@ -153,6 +163,7 @@ if (i > 1) { vertPos = (size[1] / 3) * (i / 2); } + // TODO: Dispose Graphics object! result.createGraphics().drawImage(img, horPos, vertPos, null); } ImageIO.write(result, "png", out); diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java Tue Sep 24 12:29:44 2013 +0200 @@ -18,8 +18,6 @@ import java.text.DateFormat; import java.text.NumberFormat; -import org.w3c.dom.Document; - import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; @@ -27,7 +25,6 @@ import au.com.bytecode.opencsv.CSVWriter; -import org.dive4elements.artifacts.CallContext; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.river.artifacts.model.CalculationResult; @@ -117,13 +114,10 @@ protected List<SQResult []> data; - - public void init(Document request, OutputStream out, CallContext cc) { - super.init(request, out, cc); + public SQRelationExporter() { data = new ArrayList<SQResult []>(); } - @Override protected void addData(Object d) { if (d instanceof CalculationResult) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,365 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ +package org.dive4elements.river.jfree; + +import org.dive4elements.river.themes.ThemeDocument; + +import java.util.List; +import java.util.Map; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; + +import org.jfree.ui.TextAnchor; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.annotations.XYLineAnnotation; + +import org.dive4elements.river.themes.LineStyle; +import org.dive4elements.river.themes.TextStyle; +import org.dive4elements.river.exports.ChartSettings; +import org.dive4elements.river.exports.LegendSection; +import org.dive4elements.river.exports.ChartArea; + +import org.apache.log4j.Logger; + +/** Annotation helper class, handles plotting of annotations. */ +public class AnnotationHelper { + private static final Logger logger = Logger.getLogger(AnnotationHelper.class); + + protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; + + /* arr this would be better in chartsettings */ + public static final int DEFAULT_FONT_SIZE = 12; + public static final String DEFAULT_FONT_NAME = "Tahoma"; + + /** + * Add annotations (Sticky, Text and hyk zones) to a plot. + * @param annotations Annotations to add + * @param plot Plot to add annotations to. + * @param settings ChartSettings object for settings. + */ + public static void addAnnotationsToRenderer(List<RiverAnnotation> annotations, + XYPlot plot, ChartSettings settings, Map<Integer, AxisDataset> datasets) { + logger.debug("addAnnotationsToRenderer"); + + if (annotations == null || annotations.isEmpty()) { + logger.debug("addAnnotationsToRenderer: no annotations."); + return; + } + + // OPTMIMIZE: Pre-calculate positions + ChartArea area = new ChartArea( + plot.getDomainAxis(0).getRange(), + plot.getRangeAxis().getRange()); + + // Walk over all Annotation sets. + for (RiverAnnotation fa: annotations) { + + // Access text styling, if any. + ThemeDocument theme = fa.getTheme(); + TextStyle textStyle = null; + LineStyle lineStyle = null; + + // Get Themeing information and add legend item. + if (theme != null) { + textStyle = theme.parseComplexTextStyle(); + lineStyle = theme.parseComplexLineStyle(); + if (fa.getLabel() != null) { + // Legend handling, maybe misplaced? + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection old = plot.getFixedLegendItems(); + + Color color = theme.parseLineColorField(); + if (color == null) { + color = Color.BLACK; + } + + LegendItem newItem = new LegendItem(fa.getLabel(), color); + LegendSection ls = (settings != null ? + settings.getLegendSection() : null); + newItem.setLabelFont (new Font( + DEFAULT_FONT_NAME, + Font.PLAIN, + ls != null ? ls.getFontSize() : null) + ); + + lic.add(newItem); + // (Re-)Add prior legend entries. + if (old != null) { + old.addAll(lic); + } + else { + old = lic; + } + plot.setFixedLegendItems(old); + } + } + + // The 'Sticky' Annotations (at axis, with line and text). + for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { + addStickyAnnotation( + sta, plot, area, lineStyle, textStyle, theme, + datasets.get(new Integer(sta.getAxisSymbol()))); + } + + // Other Text Annotations (e.g. labels of (manual) points). + for (XYTextAnnotation ta: fa.getTextAnnotations()) { + // Style the text. + if (textStyle != null) { + textStyle.apply(ta); + } + ta.setY(area.above(0.05d, ta.getY())); + plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); + } + } + } + + /** + * Add a text and a line annotation. + * @param area convenience to determine positions in plot. + * @param theme (optional) theme document + */ + public static void addStickyAnnotation( + StickyAxisAnnotation annotation, + XYPlot plot, + ChartArea area, + LineStyle lineStyle, + TextStyle textStyle, + ThemeDocument theme, + AxisDataset dataset + ) { + // OPTIMIZE pre-calculate area-related values + final float TEXT_OFF = 0.03f; + + XYLineAnnotation lineAnnotation = null; + XYTextAnnotation textAnnotation = null; + + int rendererIndex = 0; + + if (annotation.atX()) { + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF)); + // OPTIMIZE externalize the calculation involving PI. + //textAnnotation.setRotationAngle(270f*Math.PI/180f); + lineAnnotation = createGroundStickAnnotation( + area, annotation.getPos(), lineStyle); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); + } + else { + // Do the more complicated case where we stick to the Y-Axis. + // There is one nasty case (duration curves, where annotations + // might stick to the second y-axis). + if (dataset == null) { + logger.warn("Annotation should stick to unfindable y-axis: " + + annotation.getAxisSymbol()); + rendererIndex = 0; + } + else { + rendererIndex = dataset.getPlotAxisIndex(); + } + + // Stick to the "right" (opposed to left) Y-Axis. + if (rendererIndex != 0) { + // OPTIMIZE: Pass a different area to this function, + // do the adding to renderer outside (let this + // function return the annotations). + // Note that this path is travelled rarely. + ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex)); + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos()); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT); + lineAnnotation = createRightStickAnnotation( + area2, annotation.getPos(), lineStyle); + if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { + // New line annotation to hit curve. + if (theme.parseShowVerticalLine()) { + XYLineAnnotation hitLineAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.X_AXIS, + annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(), + area2, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + if (theme.parseShowHorizontalLine()) { + XYLineAnnotation lineBackAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.Y_AXIS2, + annotation.getPos(), annotation.getHitPoint(), + area2, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + } + } + else { // Stick to the left y-axis. + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos()); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); + lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle); + if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { + // New line annotation to hit curve. + if (theme.parseShowHorizontalLine()) { + XYLineAnnotation hitLineAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.Y_AXIS, + annotation.getPos(), annotation.getHitPoint(), + area, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + if (theme.parseShowVerticalLine()) { + XYLineAnnotation lineBackAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.X_AXIS, + annotation.getHitPoint(), annotation.getPos(), + area, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + } + } + } + + // Style the text. + if (textStyle != null) { + textStyle.apply(textAnnotation); + } + + // Add the Annotations to renderer. + plot.getRenderer(rendererIndex).addAnnotation(textAnnotation, + org.jfree.ui.Layer.FOREGROUND); + plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation, + org.jfree.ui.Layer.FOREGROUND); + } + + /** + * Create annotation that sticks to "ground" (X) axis. + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createGroundStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + pos, area.atGround(), + pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), + new BasicStroke(lineStyle.getWidth()),lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + pos, area.atGround(), + pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET)); + } + } + + + /** + * Create annotation that sticks to the second Y axis ("right"). + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createRightStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, + area.atRight(), pos, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + area.atRight(), pos, + area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos); + } + } + /** + * Create annotation that sticks to the first Y axis ("left"). + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createLeftStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + area.atLeft(), pos, + area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + area.atLeft(), pos, + area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos); + } + } + + + /** + * Create a line from a axis to a given point. + * @param axis The "simple" axis. + * @param fromD1 from-location in first dimension. + * @param toD2 to-location in second dimension. + * @param area helper to calculate offsets. + * @param lineStyle optional line style. + */ + public static XYLineAnnotation createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2, + ChartArea area, LineStyle lineStyle + ) { + double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d; + switch(axis) { + case X_AXIS: + anchorX1 = fromD1; + anchorX2 = fromD1; + anchorY1 = area.atGround(); + anchorY2 = toD2; + break; + case Y_AXIS: + anchorX1 = area.atLeft(); + anchorX2 = toD2; + anchorY1 = fromD1; + anchorY2 = fromD1; + break; + case Y_AXIS2: + anchorX1 = area.atRight(); + anchorX2 = toD2; + anchorY1 = fromD1; + anchorY2 = fromD1; + break; + } + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + anchorX1, anchorY1, + anchorX2, anchorY2, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + anchorX1, anchorY1, + anchorX2, anchorY2); + } + } + +}; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/jfree/AxisDataset.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AxisDataset.java Tue Sep 24 12:29:44 2013 +0200 @@ -0,0 +1,127 @@ +/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * XYDatasethis file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUXYDatasetELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ + +package org.dive4elements.river.jfree; + +import java.util.List; +import java.util.ArrayList; + +import org.jfree.data.Range; +import org.jfree.data.RangeInfo; +import org.jfree.data.xy.XYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.data.time.TimeSeriesCollection; + +import org.apache.log4j.Logger; + +/** + * Axis datasets. + */ +public class AxisDataset +{ + private static Logger logger = Logger.getLogger(AxisDataset.class); + + /** Symbolic integer, but also coding the priority (0 goes first). */ + protected int axisSymbol; + + /** List of assigned datasets (in order). */ + protected List<XYDataset> datasets; + + /** Range to use to include all given datasets. */ + protected Range range; + + /** Index of axis in plot. */ + protected int plotAxisIndex; + + protected boolean rangeDirty; + + /** Create AxisDataset. */ + public AxisDataset(int symb) { + axisSymbol = symb; + datasets = new ArrayList<XYDataset>(); + } + + /** Add a dataset to internal list for this axis. */ + public void addDataset(XYDataset dataset) { + datasets.add(dataset); + rangeDirty = true; + } + + /** Add a dataset. */ + public void addDataset(XYSeries series) { + addDataset(new XYSeriesCollection(series)); + } + + public void setRange(Range val) { + range = val; + } + + /** Get Range for the range axis of this dataset. */ + public Range getRange() { + if (range != null && !rangeDirty) { + return range; + } + /* Calculate the min / max of all series */ + for (XYDataset dataset: datasets) { + Range newRange = null; + if (dataset instanceof StyledAreaSeriesCollection) { + /* We do not include areas in the range calculation because + * they are used with very large / small values to draw areas + * with axis boundaries */ + continue; + } else if (dataset instanceof RangeInfo) { + /* The usual case for most series */ + newRange = ((RangeInfo) dataset).getRangeBounds(false); + } else if (dataset instanceof TimeSeriesCollection) { + /* Lalala <3 Jfreechart's class hirarchy */ + newRange = ((TimeSeriesCollection) dataset).getRangeBounds(false); + } + + /* Now we only expand as we also only add new data */ + if (range == null) { + range = newRange; + } else { + range = Range.combine(range, newRange); + } + } + rangeDirty = false; + return range; + } + + /** Get Array of Datasets. */ + public XYDataset[] getDatasets() { + return datasets.toArray(new XYDataset[datasets.size()]); + } + + /** True if to be rendered as area. */ + public boolean isArea(XYDataset series) { + return (series instanceof StyledAreaSeriesCollection); + } + + /** True if no datasets given. */ + public boolean isEmpty() { + return datasets.isEmpty(); + } + + /** Set the 'real' axis index that this axis is mapped to. */ + public void setPlotAxisIndex(int axisIndex) { + plotAxisIndex = axisIndex; + } + + /** Get the 'real' axis index that this axis is mapped to. */ + public int getPlotAxisIndex() { + return plotAxisIndex; + } + + /** Add a Dataset that describes an area. */ + public void addArea(StyledAreaSeriesCollection series) { + addDataset(series); + } + +} diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java --- a/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java Tue Sep 24 12:29:44 2013 +0200 @@ -14,11 +14,20 @@ public class DataUtil { - public static boolean guessWaterIncreasing(TDoubleArrayList data) { - return guessWaterIncreasing(data, 0.05f); + public static boolean guessDataIncreasing(TDoubleArrayList data) { + return guessDataIncreasing(data, 0.05f); } - public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) { + /** Guess if data1 and data2 both grow in the same direction */ + public static boolean guessSameDirectionData(TDoubleArrayList data1, + TDoubleArrayList data2) { + boolean d1dir = DataUtil.guessDataIncreasing(data1, 0.05f); + boolean d2dir = DataUtil.guessDataIncreasing(data2, 0.05f); + int size = data1.size(); + return ((d1dir && d2dir) || (!d1dir && !d2dir)) && size > 1; + } + + public static boolean guessDataIncreasing(TDoubleArrayList data, float factor) { int N = data.size(); if (N < 2) return false; diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/java/org/dive4elements/river/utils/Pair.java --- a/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java Tue Sep 24 12:29:44 2013 +0200 @@ -21,8 +21,7 @@ private A a; private B b; - @SuppressWarnings("unused") - private Pair() { + public Pair() { } public Pair(A a, B b) { diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/resources/messages.properties --- a/artifacts/src/main/resources/messages.properties Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/resources/messages.properties Tue Sep 24 12:29:44 2013 +0200 @@ -152,6 +152,7 @@ chart.longitudinal.section.title = W-Longitudinal Section chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} chart.longitudinal.section.shortsubtitle = {0} +chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###} chart.longitudinal.section.xaxis.label = {0}-km chart.longitudinal.section.yaxis.label = W [{0}] chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] @@ -197,7 +198,7 @@ chart.reference.curve.y.axis.in.m = Target Station(s) [NN + m] chart.w_differences.title = Differences -chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.subtitle = {0} chart.w_differences.yaxis.label = m chart.w_differences.yaxis.second.label = W [NN + m] diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/resources/messages_de.properties --- a/artifacts/src/main/resources/messages_de.properties Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/resources/messages_de.properties Tue Sep 24 12:29:44 2013 +0200 @@ -158,6 +158,7 @@ chart.longitudinal.section.title = W-L\u00e4ngsschnitt chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###} chart.longitudinal.section.shortsubtitle = {0} +chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###} chart.longitudinal.section.xaxis.label = {0}-km chart.longitudinal.section.yaxis.label = W [{0}] chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] @@ -207,7 +208,7 @@ chart.normalized.reference.curve.title = Reduzierte Bezugslinie chart.w_differences.title = Differenzen -chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.subtitle = {0} chart.w_differences.yaxis.label = m chart.w_differences.yaxis.second.label = W [NN + m] diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/resources/messages_de_DE.properties --- a/artifacts/src/main/resources/messages_de_DE.properties Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/resources/messages_de_DE.properties Tue Sep 24 12:29:44 2013 +0200 @@ -157,6 +157,7 @@ chart.longitudinal.section.title = W-L\u00e4ngsschnitt chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###} chart.longitudinal.section.shortsubtitle = {0} +chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###} chart.longitudinal.section.xaxis.label = {0}-km chart.longitudinal.section.yaxis.label = W [{0}] chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] @@ -205,7 +206,7 @@ chart.normalized.reference.curve.title = Reduzierte Bezugslinie chart.w_differences.title = Differenzen -chart.w_differences.subtitle = Strecke: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.subtitle = {0} chart.w_differences.yaxis.label = m chart.w_differences.yaxis.second.label = W [NN + m] diff -r bdadffad35b1 -r 3c7471b929d1 artifacts/src/main/resources/messages_en.properties --- a/artifacts/src/main/resources/messages_en.properties Mon Sep 23 11:37:13 2013 +0200 +++ b/artifacts/src/main/resources/messages_en.properties Tue Sep 24 12:29:44 2013 +0200 @@ -157,6 +157,7 @@ chart.longitudinal.section.title = W-Longitudinal Section chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} chart.longitudinal.section.shortsubtitle = {0} +chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###} chart.longitudinal.section.xaxis.label = {0}-km chart.longitudinal.section.yaxis.label = W [{0}] chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s] @@ -210,7 +211,7 @@ chart.normalized.reference.curve.title = Reduced Reference Curve chart.w_differences.title = Differences -chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.subtitle = {0} chart.w_differences.yaxis.label = m chart.w_differences.yaxis.second.label = W [NN + m]