Mercurial > dive4elements > river
changeset 3806:881fcd01e056
merged flys-artifacts/pre2.6-2011-11-04
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:50 +0200 |
parents | e831dc29e572 (current diff) c35ef7f4b88f (diff) |
children | d73c43798a99 |
files | |
diffstat | 237 files changed, 47502 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/ChangeLog Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,8089 @@ +2011-11-08 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue406 (Themestyle-editor: themes for "other.wkms" and + "other.wqkms" missing) + + * doc/conf/themes.xml: Fixed typos in WKms and WQKms theme names. + +2011-11-08 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue405 (Datacage: Recommendations get loaded twice). + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java + (setup): Do not try/catch exception. + (spawn_state): Generate just one "general" output. + +2011-11-08 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data.xml: Removed orphaned datacage configuration which is + no longer loadable. + +2011-11-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Create + line and polygon layers for barriers only if they are really existing. + +2011-11-04 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Disable mainvalue-recommendations for discharge curves as they trigger + a yet-to-be understood NPE. + + * doc/conf/meta-data.xml: Comment mainvalue-recommendations for + discharge curves. + +2011-11-04 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added new matching condition for theme-mappings: the name of + the output. + + * src/main/java/de/intevation/flys/themes/ThemeMapping.java: + Added output field and function to match it against a given + output name. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Createing ThemeMapping with output attribute from configuration. + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: + (getTheme(FLYSContext, string)): Removed, never called. + (getTheme): Added outputName argument, match it. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Pass outputs name until it can be matched against mapping. + +2011-11-04 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/themes.xml: Added default themes for other.w(q)kms. + +2011-11-04 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Cosmetics, reduce logging noise. + +2011-11-03 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added access to static W_Q_Kms - data in much the same way then static + WKms. + + * src/main/java/de/intevation/flys/artifacts/model/StaticWQKmsCacheKey.java: + Cache Key for static wqkms data. + + * src/main/java/de/intevation/flys/artifacts/model/WQKmsFacet.java: + Facet for WQKms. + + * src/main/java/de/intevation/flys/artifacts/model/WQKmsFactory.java: + Factory to access WQKms. + + * src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java: + Artifact that provides 'static' WQKms. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added STATIC_WQKMS type. + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/jfree/XYDatasetToZeroMapper.java: + New. Maps series to zero to be compatible with XYDifferenceRenderer. + It returns an iterator over XYDatasets to enable splitting by NaNs, + which still needs to be implemented. + + * src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java: + Uses a XYDatasetToZeroMapper now. Fixed package name. + +2011-11-03 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added new matching options for theme-mappings to allow more + overspecification of defaults (e.g. now name,description-pattern and + master-artifacts attributes are matched). The first full match from + the configuration file is done. + New matching option in the masterAttr- field of a mapping are + super-basic until new use-cases come up. + Concrete new themes are point-styles of Ws when locations where chosen + to calculate. + + * doc/conf/themes.xml: Added newly defined Themes. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Put master-artifact in flys-context. + + * src/main/java/de/intevation/flys/themes/ThemeMapping.java: + Accept masterAttr in constructor. + (masterAttrMatches): New, check masterAttr-condition against + artifact. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + When creating ThemeMappings, pass in masterAttr. + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: + (getTheme): Evaluate masterAttr-condition, always return first full + match. + +2011-11-03 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Added ARTIFACT key, documentation. + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java: + New. At the moment a pure copy of JFreeChart's XYDifferenceRenderer. + Needs to be refactored to cope with its limitations: + + - Series numbers need to be zero based. We have more than two series + in our diagrams. + + - Cannot handle definition holes indicated by NaNs. We have these + cases e.g. more than one "Fliessbereich". + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Reenabled dumping data to disk. + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Removed superfluous import. + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java: + Use new pair in stack of results and connections because they are + always used in pairs. Maintaining two separate stacks is not + needed any longer. + +2011-11-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/utils/Pair.java: New. A generic pair. + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Rewritten to useful as test bed for "Raum/Flaeche" operations. + +2011-11-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Added + setter methods for all parameters and removed the constructors. There is + just an empty constructor - all parameters need to be set via setter + methods. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Modified the + creation of LayerInfo objects. + + * src/main/java/de/intevation/flys/artifacts/model/DBLayerInfo.java: + Removed, because the internal structure and constructors of LayerInfo have + changed, so that we gonna use LayerInfo for all layers now. + + * doc/conf/themes.xml: Modified the color definitions of + 'floodmap.riveraxis' and 'floodmap.kms' themes and added a 'symbol' field + to 'floodmap.kms'. + + * doc/conf/mapserver/symbols.sym, + doc/conf/mapserver/fontset.txt: New. Required by Mapserver. + + * doc/conf/mapserver/db_layer.vm: Added an 'EXTENT' field that is filled + using LayerInfo.getExtent(). + + * doc/conf/mapserver/mapfile.vm: Modified FONTSET directory and added a + SYMBOLSET. + + * src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java: Added an + abstract method getGeometryType(). + + * src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: Override + getGeometryType() of WMSDBArtifact. This Artifact provides "POINT"s. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Create new WMSDBLayerFacets with geometry type "LINE". + + * src/main/java/de/intevation/flys/artifacts/model/WMSDBLayerFacet.java: + Added a 'geometryType' attribute and getter/setter methods. This attribute + determines the type of geometry provided by this database wms layer. Types + could be "POLYGON", "POINT", "LINE" and so on. + + * src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java: + Added a 'symbol' attribute to the inner class Clazz. + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: Added a function to + parse the symbol field of a theme. This symbol is used for + MapserverStyle.Clazz. + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Apply point theme to heightmarks when imported in longitudinal + section diagram. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added new FacetType HEIGHTMARK_POINTS. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java: + Allow name to be given in constructor. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java: + If heightmarks were loaded, give respective name in WKmsFacet + generation. + + * doc/conf/themes.xml: Added virtual "Points" and concrete + heightmark_points - theme. + + * doc/conf/artifacts/winfo.xml: Made longitudinal_section output + compatible with heightmarks_points. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Render heightmarks like other wkms. + + * doc/conf/meta-data.xml: Changed heightmark ids such that it can be + identified in StaticWKmsFacet . + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: + Documentation added. + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java: + Fix bug where (wrong) active-attribute was set, doc. + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Refactoring to allow mainvalues in both discharge and computed + discharge curve diagrams. + + * src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java + (addPointsQW): New helper function. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Moved doMainValueQAnnotations, doMainValueWAnnotations from child- + to parent-class, extracetd doDischargeOut. Use + StyledSeriesBuilder.addPointsQW . + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Removed logger/debugging noise. + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + 1) Pass outputs name to artifacts getInitialFacetActivity(). + 2) Do not allow "gaps" in positions of facets in outputs in attributes + of collection (prevent e.g. positions 1,3,5; will become 1,2,3 + instead). + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Pass outputname to artifacts getInitialFacetActivity(), prevent + gaps in facets positions in outputs (1,3,10 become 1,2,3). + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + 1) Give Artifacts information about the out when they have to decide + whether a given facet is initially in/active by adding parameter + to getInitialFacetActivity(+outputName). + 2) Generate separate Set of MainValueFacets for discharge curves. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + (getInitialFacetActivity): Adjusted, new parameter outputName. + Added MAINVALUES_{Q,W} definition. + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java, + src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java: + Accept name in constructor. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Create second pair of MainValuesFacets, give distinguishable names. + +2011-11-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added compatibility of (computed)discharge-curve diagrams with + mainvalues. + + * doc/conf/artifacts/winfo.xml: Added mainvalues to compatibility list + of (computed) discharge curve outputs. These facet-definitionss can + differ in names because we can can have up to 4 mainvalue facets in + one state. + +2011-11-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: Determine + the extent of such WMS layer based on the list of RiverAxisKm objects + returned by the backend. + +2011-11-01 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data.xml: Added a datacage node that represents a + kilometer WMS layer. + + * doc/conf/conf.xml: Added a new Artifact WMSKmArtifact. + + * doc/conf/themes.xml: Added a style for 'floodmap.kms' facets. + + * doc/conf/artifacts/winfo.xml: Defined 'floodmap.kms' as valid floodmap + facet. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added + a 'floodmap.kms' facet. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method to + determine the srid of a river based on its name. + + * src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java: New. This + Artifact should act as base Artifact for WMS layers that represent data + from database datastore. + + * src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java: New. This + Artifact is used to generate facets for kilometer WMS layers. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added default descriptions + for 'floodmap.kms' facets. + +2011-11-01 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix incompilability. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java: + Adjust to changed CrossSectionFacet. + +2011-11-01 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Minor preparations to handle multiple cross sections in one + diagram/artifact, faking certain aspects (e.g. ability to display + multiple cross sections, but let these fetch the exactly same data + for now). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Parameterize but fake access to cross-section (always take first + one). + (getCrossSectionName,getCrossSectionNames): Renamed, access names + of all cross-sections, so that at least facets with different names + are created (they will still deliver the same data). + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java: + Allow indexing. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Index created cross-sections. + +2011-11-01 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Show multiple water lines and facets in cross-section diagram if + multiple waterlevel values had been entered. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java + (appendBackgroundActivity): Made static. + (getWaterLines): Add 'idx' argument to specify index of queried + waterlevel. + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java: + Add index. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Add one Facet for each of the computed waterlevels. + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java: + Added code to calculate intersection points. + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + Added polygons for trivial cases. WIP + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + Made it compilable again. + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java: + Made X() and Y() access macros public. + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + More code. WIP. + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + Handle start points when building polygons. Work in progress. + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java: + New. Vector operations on Point2D. + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + Moved vector operations to VectorUtils. + +2011-10-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java: + Do not call size() in for loop again and again. + +2011-10-30 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: + Interim check in. Work in progress. + +2011-10-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java: New. + Polygon class to help creating "Raum/Flaeche" renderers with gaps in + their definitions. WORK IN PROGRESS! + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: Added a default theme for the riveraxis used in the + floodmap. + + * src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java: + New. This class is used by ThemeUtil to create a style which is + compatible for Mapserver- + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: Added a method to + retrieve a Mapserver compatible style (as string) based on a given + Document (that comes from CollectionItem's attribute). + + * src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: + Implemented the setStyle() and getStyle() methods. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added a + new parameter 'style' to createDatabaseLayer(). This parameter is set on + LayerInfo. + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: Create + Mapserver compatible styles and call createDatabaseLayer() with this + style. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Defined "floodmap.riveraxis" and + "floodmap.wmsbackground" as compatible layers for the floodmap output. + + * doc/conf/mapserver/db_layer.vm: New. This layer template is used for + Mapserver layers with database datastore. + + * doc/conf/mapserver/dbconnection.include: New. The database configuration + used in the db_layer template. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method + getUserWMSUrl() that returns the URL to the user specific WMS server. + This method requires a UUID of an Artifact to identify the owner of the + Artifact. + + * src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: Use + FLYSUtils.getUserWMSUrl() to create the URL to the user WMS for + WMSLayerFacets creation. + + * src/main/java/de/intevation/flys/artifacts/model/WMSDBLayerFacet.java: + New. Subclasses WMSLayerFacet to save data and filter parameters used + for database storage in Mapfiles. + + * src/main/java/de/intevation/flys/artifacts/model/DBLayerInfo.java: New. + Subclasses LayerInfo to save database relevant parameters. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Creates new WMSDBLayerFacets, so that the riveraxis layer data is + fetched from database. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added a + public method that allows creating layers (type LINE) based on + WMSDBLayerFacets. + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: Enabled + support for Facets other than "floodmap.wsplgen" and "floodmap.barriers". + Those other Facets are supposed to be WMSDBLayerFacets. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Refactored, added StyledSeriesBuilder to unify adding points to + XYSeries. + + * src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java: + New class to help with adding points to XYSeries. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Use StyledSeriesBuilder to add points to series. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added DC-conf, such that static data can be loaded from w-difference + diagrams datacage. + + * doc/conf/meta-data.xml: Minor "refactoring" (definition of two + macros, allow certain static data to be loaded via datacage to + w-difference diagrams, too. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Load and display annotations in w-differences, minor polishing. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Use FacetType 'instead' of string. Allow Annotations in + WDifferences- diagram. + + * doc/conf/meta-data.xml: Recommend annotations in w-differences case. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Survive case where a given output doesnt exist in compatibility + matrix. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/StaticState.java: + Added simpler constructor. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Adjusted construction of StaticStates. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/artifacts/winfo.xml: Allow Annotations in longitudinal and + w-differences diagrams. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Use artifacts configuration (e.g. winfo.xml) to define which facets + can be used in which output. Hide no-matches. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added use of "compatibility matrix". Only include facets in in + collections description document that are marked compatible in the + masterartifacts configuration (e.g. winfo.xml). + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java + (mergeAttributes, getMasterArtifact): Extraced, updated caller. + Cosmetics to reduce indentation one step. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java + (getStateHistoryIds): New, return list of current and all previous + state ids. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Trigger the re-creation of FLYS mapfile if endOfLife() of this state is + called. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/mapserver/mapfile.vm: The "layers" injected by VelocityEngine is + now used to include layers. A single string in this list represents the + path to a file which contains a LAYER section for Mapserver' Mapfile. + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Removed the + update() call of MapfileGenerator. Mapfiles are generated by MapGenerator + only which requires a FLYSArtifactCollection.doOut()! + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: Call update() + of MapfileGenerator to trigger the re-creation of mapfile(s). + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Collect all + LAYER snippets from filesystem and inject the filepath for each snippet + into the Mapfile template. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java, + src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Cosmetics, doc. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Let OutputParser and AttributeParser collect all facets on the way. + + * src/main/java/de/intevation/flys/collections/OutputParser.java, + src/main/java/de/intevation/flys/collections/AttributeParser.java: + Collect all facets while iterating over Outputs and Attributes, + documentation added. + +2011-10-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Extracted getFlysContext from FLYSArtifacts into FLYSUtils. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java + (getFlysContext): Added, extracted from FLYSArtifact. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + (getFlysContext): Moved to FLYSUtils, updated callers. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Update callers to getFlysContext. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: Bugfix: + Catch IOException - flys-artifacts compiles again. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Defined "floodmap.barriers" as valid facet + for floodmaps. + + * doc/conf/mapserver/shapefile_layer.vm: New. Currently a copy of + layer.vm. This template will evolve to a special mapserver layer + template with a shapefile data source. + + * src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: Fixed broken + facet name of barriers. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Added + public methods for creating wsplgen and barriers layer files for + mapserver. + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: Use + MapfileGenerator to create new layer files for wsplgen and barriers. + +2011-10-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: Added new + method gerRiverBoundary() which returns an Envelope object (which + represents the bounding box of a Geometry) of a riveraxis specified by its + rivername. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: Use + Geometry.getRiverBoundary() to determine the max extent of a river. + + * src/main/java/de/intevation/flys/wsplgen/FacetCreator.java, + src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: Use + JTS Envelope to save the bounding boxes of WMS layers. + + * src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java: + Adapted the code to apply the changes in GeometryUtils (use Envelope to + determine the max extent of the river axis). + + * src/main/java/de/intevation/flys/exports/MapGenerator.java: New (work in + progress). This Generator will currently return a map configuration in XML + which consists of parameters required by OpenLayers to create a map. + + * doc/conf/conf.xml: Registered the new MapGenerator. + +2011-10-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Do not recommend historical data to load + when having computational discharge curves. + +2011-10-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Reduce noise, given "error" message was more of "debug" nature. + +2011-10-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Restore mapping of state id to facets (essentially revert, revision + 3083 and 3088). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + (getInitialFacetActivity): Be more explicit on which facets to + introduce inactivated. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Minor refactoring, declare a string final static. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java, + src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java, + src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Restore association from state id to facets. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/collections/OutputParser.java: + Cosmetics, documentation. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Prepare rendering of "other/static wkms" (functional) and + w-differences (not yet fully functional). + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Cosmetics. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: + Do not store facets in a map from stateId to list of facets, but in + a pure list instead. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/artifacts/winfo.xml: Adjusted to newer semantics, + minor cleanups. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Resolve association of facets to states in artifacts. This eases + merging of outputs and facets and inhibition of unwanted outputs + substiantially (at the price of slightly more expensive merging). + Also, the semantics of artifacts configuration files (e.g. winfo.xml) + is changed (facet elements within an output elements are used for + merging). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java, + src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Do not store facets in a map from stateId to list of facets, but in + a pure list instead. + +2011-10-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Added dc configuration for some data + that can be loaded from longitudinal section diagrams. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Cosmetics, docs. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Let Qs in Longitudinal Diagram be inactive, initally. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + (getInitialFacetActivity): Do not let Facets ending with a 'q' + enter in active state. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Fix broken datacage config. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Configured to include correct id to + clone artifact that produces w-diff. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Revert correct behaviour of Annotations (the small axis tick shall + always be drawn). Minor refactoring. + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Added configuration to include differences + in datacage when longitudinal sections are shown (yet not + functional). + +2011-10-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: Cosmetics. + +2011-10-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/themes.xml: Added theme for w_differences facets. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java + (createSecondAxisRange): Survive parameter-nullness for now. + Added documentation. + +2011-10-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/themes/ThemeAccess.java: + New. Caching wrapper around an XML document theme. It uses ThemeUtil + to access the values and stores them in instance variable. + Background: ThemeUtil use XPath a lot which is expensive. + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java, + src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Use ThemeAccess to style the annotations. + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: + Removed some XPath strings. They are in ThemeUtil. + +2011-10-21 Raimund Renkert <raimund.renkert@intevation.de> + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + Added methods to parse further attributes. + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: + Apply the theme attributes and use ThemeUtils to get the attribute values. + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Removed the spamy debug output. + +2011-10-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + To obtain the size of a diagram it is rendered twice. The + second time the generated image is omitted so the concrete + rendered image is not needed. To save CPU cycles in this pass + the image is rendered to to /dev/null Graphics2D object. + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: + Added boolean system property "info.rendering.nop.graphics" (default: false). + With this property set the info rendering is done via a new + NOPGraphics2D opbject which does not render the image. + + * src/main/java/de/intevation/flys/java2d/NOPGraphics2D.java: + New. Implements java.awt.Graphics2D trivial empty methods. + This prevents rendering. + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Commented out spamy debug output + +2011-10-21 Raimund Renkert <raimund.renkert@intevation.de> + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Draw the text background and use orientation attribute. + +2011-10-21 Raimund Renkert <raimund.renkert@intevation.de> + + * doc/conf/themes.xml: + Renamed 'textbackground' to 'backgroundcolor' to have 'color' in the + attribute name. + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + Process text attributes correctly. + +2011-10-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java: + New. Service to lookup the Nth nearest neighbors for a set of given + cross section ids and kms. + + * doc/conf/conf.xml: Registered service. + + * doc/conf/cache.xml: Cache config. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Removed superfluous imports. + +2011-10-20 Raimund Renkert <raimund.renkert@intevation.de> + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + Added methods to extract further attributes from theme. + +2011-10-20 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: Added main value themes for longitudinal section + charts. + + * src/main/java/de/intevation/flys/themes/ThemeMapping.java: New. This + class stores the name of a facet, the related theme and a pattern + string. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Read the pattern string and store a list of ThemeMapping objects in the + FLYSContext. + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: Modified + getTheme() which now takes the FLYSContext, the name of a facet and an + optional pattern string. Now, we can have specialized Themes for each + chart type. E.g. the facet "longitudinal_section.w" maps the default + Theme for W lines in longitudinal section charts. If the optional + pattern string matches the pattern ".*(HQ1000)(\D.*)*", the ThemeFactory + will return the Theme "LongitudinalSectionW_HQ1000". + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Use the description of a facet as pattern string to get the relevant + Theme from ThemeFactory. + +2011-10-20 Raimund Renkert <raimund.renkert@intevation.de> + + * doc/conf/themes.xml: + Added new theme attributes. + +2011-10-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix WDifference plots where masterartifact has no range set. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + (addSubtitles): Overridden, the master artifact has no + range. + +2011-10-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix masterartifact in collections in cases where the original + masterartifacts facets do not come first in certain list. Query + 'backend'/db instead. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Set 'real' master artifact, defined to be oldest belonging to this + collection. + +2011-10-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java, + src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Modified the method signature of createStaticData() which now also + requires a FLYSArtifact. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Override createStaticData() to create titles for Qs manually - we want + to display the named main values if existing for the selected Qs. + +2011-10-19 Raimund Renkert <raimund.renkert@intevation.de> + + * doc/conf/themes.xml: + ComputedDischargeCurveW and ComputedDischargeCurveQ inherit attributes from + theme 'Text'. + +2011-10-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue316 + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: Added + constructor to not sort the data. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Use the not sorting constructor of StyledXYSeries. + +2011-10-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + moved the code to create WSP W and Q facet names to FLYSUtils. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: New functions for + creating WSP W and Q facet names and for querying a named main value + based on a given gauge and value. The names of W and Q facets will now + depend on the selected Q and Q mode: if the mode is "q at gauge" and a + named value is found for the given value, the facet's name contains the + named value instead of the value itself. + +2011-10-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix datacage configuration to let client load ZUS and flood + protections. + + * doc/conf/meta-data.xml: Use 'ids' instead of 'id' to help client. + +2011-10-19 Raimund Renkert <raimund.renkert@intevation.de> + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + Added methods to parse text attributes from theme document. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java, + src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Apply a theme to axis annotations. + +2011-10-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Create (data) Label from data string (client will be adjusted to send + the name). + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Create label from input data string, documentation added, junk + removed. + +2011-10-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java(zoom): + Do not crash if no axis is given. + +2011-10-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added + getFormat() which extracts the format string from XML request document. + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: Adapted + method signatures of exportImage(), exportSVG() and exportPDF(). All + methods now take a CallContext object which stores extra chart export + parameters. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Enabled PDF and SVG chart exports based on the "format" string given in + the XML request document. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Simplify rendereing W(Q)Kms in WDifferencesCurveGenerator. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + (doWOut, doWOut): Simplified. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Render zus and flood-protections in WDifferences-diagrams. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Added basic respect of "other.wkms"- facets. + + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Adjusted Datacage-Configuration to fetch "extra longitudinal...." + (.zus) - waterlevels in certain case (in system-part). + + * doc/conf/meta-data.xml: Adjusted to present extra-kms with + staticwkms factory in certain case. + +2011-10-17 Raimund Renkert <raimund.renkert@intevation.de> + + * doc/conf/themes.xml: + Added new virtual theme for text with the attributes 'font', 'textcolor' + and 'textsize'. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Give StaticWKmsArtifacts proper names, and pre-deselect them. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java: + (getInitialFacetActivity): Overridden to let facets enter plot + inactively. + (setup): Give Facets the name of the Wst. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java: + Refactored to expose getWKmsName separately. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Adjusted Datacage-Configuration to fetch flood-protections when + waterlevels are requested (in system-part). + + * doc/conf/meta-data.xml: Adjusted to present flood-protections with + staticwkms factory in certain case. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Register staticwkms factory. + + * doc/conf/conf.xml: Register staticwkms factory to spawn StaticWKms- + Artifacts. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fetch name of static WKms. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java: + Removed dependence on "kind", but fetch name for created WKms. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java: + Remove dependence on Kind. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Store parameterization in data, not in Artifact. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java: + Resolve col_pos and wst_id field, use data instead. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add convenience-method to add defaultdata (string). + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + (addStringData): Add Default (String) Data . + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add a new Artifact and Facet (StaticWKmsArtifact, WKmsFacet) to + access WKms obtainable with the WKmsFactory. + + * src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java: + New, artifact with single state to get WKms from WKmsFactory. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java: + New Facet to display W over km. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added new type name. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add WKMsFactory to access 'static' wst-data. + + * src/main/java/de/intevation/flys/artifacts/model/StaticWKmsCacheKey.java: + Cache Key for the static WKms data. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java: + New, creates WKms from wst-id and column. Does not yet fetch the + name. + + * doc/conf/cache.xml: Added cache configuration for static wkms data. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, docs. + + * src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java, + src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: + Cosmetics, docs. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java: + Added constructor that takes name, docs. + +2011-10-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix issue that Annotations do not come with theme/style. + + * doc/conf/themes.xml: Added "Annotations" default style. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Set style of annotations, minor cosmetics. + +2011-10-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Made inner class ThemeList static. + s/new Integer(small)/Integer.valueOf(small)/ + +2011-10-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + The inner class ThemeList makes now use of ManagedDomFacet to read the + attributes of Facets saved in the Collection's attribute. + +2011-10-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue314: Variables in datacage are now case insensitive. + (H2 returns meta data variables uppercase) + + * doc/conf/meta-data.xml: + Made a statement more precise. Added some debug output. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java: + Use uppercase variable names. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java, + src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java, + src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java, + src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java: + Variables are now treated as uppercase. + + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + Input variables are now treated uppercase. + +2011-10-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java: + Prepared Facets to support a "visible" attribute. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: Adapted + the constructor call of ManagedFacetAdapter. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + The inner class ThemeList now supports the "visible" attribute of + ManagedFacets. + +2011-10-17 Raimund Renkert <raimund.renkert@intevation.de> + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: + Added method to apply line type. + + * doc/conf/themes.xml: + Changed initial default value for line type. + +2011-10-17 Ingo Weinzierl <ingo@intevation.de> + + flys/issue226 (W-INFO: Dauerlinienberechung /Abbbildung x-Achse) + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + Set the upper bound of these charts to 364. + +2011-10-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java: + Added a function that returns a Gauge based on its name. + + * src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java: + Create proper descriptions for facets. + + * src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: Added a + setter for labels. + + * src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Use the string returned by Facet.getDescription() as series names. The + ThemePanel and the Legend will always display the same titles for curves + now. + + +2011-10-17 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue363 (W-INFO/ Abflusskurve, Extremwert-Rendering). + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + (parseLineWidth): New. Get line width from Document. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + When adding annotations, parse line width from theme, set it. + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + When painting, set Paint and stroke early enough. + +2011-10-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Put the + config directory into the Velocity context. It is available as + '$CONFIGDIR' in templates. + + * doc/conf/mapserver/mapfile.vm: Set the debug file to + "$CONFIGDIR/flys-user-wms.log" and added a LEGEND section. + + * doc/conf/mapserver/wsplgen_class.vm: Adapted the class names. Those + names are displayed in the image served by GetLegendGraphic. + +2011-10-14 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add possibility of programmatic configuration of initial "activity" + state (active or inactive) of (Managed)Facets by introducing + FLYSArtifact.getInitialFacetActivity. This method shall be overriden + by subclasses where Facets are wanted to come to live inactive. + Artifacts will be asked only once how the MangedFacet should come to live, + namely when AttributeWriter finds a genuinely new Facet. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java, + src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java + (getInitialFacetActivity): + New function to let Artifact decide whether a ManagedFacet shall + initially be set to active or inactive. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Accept database in constructor. For genuinely new Facets, spawn its + mother artifact and ask whether the (Managed)Facet shall be active + or inactive (initially). + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java + (buildOutAttributes): Pass database to AttributeWrite (which needs it + to spawn artifacts), rename items parameter to reflect content. + +2011-10-13 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, removed obsolete imports. + + * src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Removed obsolete imports. + +2011-10-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Added a further attribute "mark.selected" to the barriers feature type. + This attribute is used in the client, where we are not able to remove it + properly. Reading the GeoJSON string without this attribute is no longer + possible... strange! + +2011-10-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added i18n for label of wdiff "pair select" states data. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n for + state.winfo.waterlevel_pair_select . + +2011-10-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Addressed "Wrong labels" [6] part of flys/issue371 (W-INFO / Differenzen: + Anmerkungen zur Umsetzung) . + + * src/main/java/de/intevation/flys/exports/WDifferencesExporter.java: + Adjusted variable names to avoid conflict in subclasses, adjusted + default value for i18n string. + +2011-10-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, docs. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Cosmetics: Space after full stop in commments, slightly improved + documentation, added one debug message. + +2011-10-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix remainder of flys/issue304 (Erweiterte Funktionen W-Differenzen) . + + * doc/conf/artifacts/winfo.xml: Removed obsolete data of + WDifferencesState. + + * src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java: + Re-enable facet-filtering, but adjust filters before that happens + (former longitudinal_section output is now w_differences output). + +2011-10-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added wdiff-chart translations. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n for wdiff. + +2011-10-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Expose translateable Strings as constants. + +2011-10-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix most labels in w-differences charts. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Remove most static final i18n-variables in favor of direct String + usage or usage of methods. By this, allow easier adoption of labels + in subclasses. + +2011-10-11 Ingo Weinzierl <ingo@intevation.de> + + flys/issue383 (Zweite Y-Achse wird beim Zoomen/Verschieben nicht angepasst.) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Call adjustAxes() before applying zoom settings with autoZoom(). We need + to add new y-axes first before we adjust their ranges. + +2011-10-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + s/new Integer(small)/Integer.valueOf(small)/ + +2011-10-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Do not fire change events for each and every data point added. + +2011-10-10 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix WDifferences with more than one Pair (crashed due to incorrect index for + facets.) + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Fix wrong index for difference facets (allows for more than one + difference facet per artifact without crashes). + +2011-10-10 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Use slightly modified doWOut-implementation from + LongitudinalSectionGenerator (here need to add Ws to different axis). + +2011-10-10 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Fix import/reference. + +2011-10-10 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Let WaterlevelPairSelectState include diffids-data (if any) to enable + future work on repopulation of Grid in GUI when jumping back + (reparameterization). + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + (createItems): Override to include old data. + +2011-10-10 Ingo Weinzierl <ingo@intevation.de> + + flys/issue150 (Diagramm: Anzeige von W bergauf) + flys/issue345 (W-INFO / Wasserspiegellagenberechnung, Diagrammausgabe) + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Corrected the determination to invert the x axis, so that waterlines + will start with their highest value at the left and end with their + lowest values at the right. + +2011-10-10 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java: + Cosmetics, added an (@Override) annotation. + + * src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Cosmetics, added documentation. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Cosmetics, whitespace after full stops in comments, added + (@Override) annotation. + +2011-10-10 Ingo Weinzierl <ingo@intevation.de> + + flys/issue220 (Diagramm: Achsenbeschriftungen an verschiedenen Achsen müssen gleich aussehen) + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Adjusted the label font of the 2nd y-axis - now, both axes labels look + equal. + +2011-10-10 Ingo Weinzierl <ingo@intevation.de> + + flys/issue189 (WINFO/Dauerlinie: Sortierung der Berechnungsausgabe nach Dauerzahlen aufsteigend) + + * src/main/java/de/intevation/flys/artifacts/model/WQDay.java: Added a + method that determines if the items (days) in this object are increasing + or not. + + * src/main/java/de/intevation/flys/exports/DurationCurveExporter.java: + Changed the order of the CSV export - the highest day is at the top of + the export; the lowest day is at the bottom. + +2011-10-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Worked on flys/issue150 (Diagramm: Anzeige von W bergauf). + Still does not work in all cases. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java (generateChart()): + Reordered calls to ensure that the inversion of the x axis is + not eliminated by other chart generation steps as a side effect. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Assuming that JFreeChart is inverting axis automatically if the + KMs are reversed ordered only do invert only in some situations. + + Do not invert axis for Q (@Ingo: This is wrong! We must do this + if we are only displaying the Qs and the Ws are deactived). + +2011-10-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Take care on empty ranges while preparing ranges for single points. + +2011-10-07 Ingo Weinzierl <ingo@intevation.de> + + flys/issue114 (W-INFO: Wasserspiegellagenberechnung / Ort (Spezialfall: Generierung eines Diagramms bei punkthafter Berechnung)) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Expand + ranges for x and y axes if there is just a single point in a series - + JFreeChart requires a range where lower <> upper. + +2011-10-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java, + src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/RangeState.java, + src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Add whitespace after full stop in comments, minor doc improvements. + +2011-10-07 Ingo Weinzierl <ingo@intevation.de> + + flys/issue353 (W-INFO / Wasserspiegellagenberechnung, Diagramm) + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the process of collecting outs for Artifacts/Facets. We will now + call OutGenerator.doOut() for each Artifact and Facet - never mind if + the facet is activated (visible) or not. The OutGenerator should decide + on its own whtat to do with facets which are "marked" as _not_ visible. + + * src/main/java/de/intevation/flys/exports/OutGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/ReportGenerator.java, + src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/AbstractExporter.java, + src/main/java/de/intevation/flys/exports/ATExporter.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: + Adapted the signature of OutGenerator.doOut(). There will be a new + boolean parameter "visible" that determines if the facet specified in + this method is visible for this output or not. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Now, + that we walk over every Artifact/Facet, we are able to collect min/max + data for all axes. We store these information and use them to set the + ranges of x and y axes. The result of this: a chart can have proper axes + set without any data in it. + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: Use + min/max ranges stored while calling doOut() for each Artifact/Facet + instead of fetching those information from chart's Datasets (which could + be null). + +2011-10-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/Formatter.java: Changed the max + number of digits for AT exports from 0 to 2. + +2011-10-06 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ATWriter.java: Get rid + of buggy first line code. + +2011-10-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + fixed flys/issue201 + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Replace >= with > in km index lookup because last km was not found. + +2011-10-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + fixed flys/issue177 + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Revert rev2245. Code works fine now! :-) + +2011-10-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: Stores a + list of XYTextAnnotations instead of FLYS specific Annotations. This + makes this class suitable for other annotation types as well. + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java, + src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java: + Both facets' getData() will now return an instance of FLYSAnnotation. + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java: + Create XYTextAnnotations used to instantiate an object of + FLYSAnnotation. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Removed the code to add annotations to the plot. This task is general + enough to move this code to parent class. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Instances of this class are now able to store - besides first and second + axes datasets - a list of annotations. This annotations are added to the + plot after the datasets have been added. To support LegendItems for + those annotions, it was necessary to create a the LegendItemCollection + by ourself. This work is done while applying the themes for each series + in the chart. + +2011-10-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Removed code duplication of guessWaterIncreasing() + + * src/main/java/de/intevation/flys/artifacts/model/WKms.java(allKms, allWs): + Added methods to fetch all kms and all ws. + + * src/main/java/de/intevation/flys/utils/DataUtil.java: Generalized to + get WKms as arguments. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java, + src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java, + src/main/java/de/intevation/flys/artifacts/model/WQ.java: Implements + the extended WKms interface. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Uses the generalized DataUtil.guessWaterIncreasing(). + +2011-10-05 Ingo Weinzierl <ingo@intevation.de> + + flys/issue347 (W-INFO / Wasserspiegellagenberechnung, Längsschnittdiagramm) + flys/issue303 (Keine Streckenfavoriten, wenn nur Q im Längsschnittdiagram ausgewählt) + flys/issue353 (W-INFO / Wasserspiegellagenberechnung, Diagramm) + + * src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java: New. A + wrapper for Annotations which allows us to provide a description for a + set of annotations. + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java: + The getData() will now return an instance of FLYSAnnotation that wraps + the Annotations returned by the AnnotationArtifact. The lebel of + FLYSAnnotation is the description of this Facet. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Modified the way to add Annotations. We will no longer create an empty + series to support a LegendItem for a set of Annotations, but we will add + a LegendItem manually to the LegendItemCollection of the plot. In + addition, we are now able to display annotations if one of the two + y-axes are missing. If there are no y-axes existing, we are not able to + display annotations yet. + +2011-10-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Lifted the wrong point. Now all backjump corrections look fine. :-) + +2011-10-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Fixed bugs and make use of a cache for annotations now. + +2011-10-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Worked on flys/issue31 + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Simplified the code a lot. Needs testing. Maybe flys/issue31 is gone + +2011-10-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Fetch the WstUnit value from river - the Wst itself no longer supports a + Unit iself. + +2011-10-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java, + src/main/java/de/intevation/flys/utils/DoubleUtil.java: Moved some generic + double array code to DoubleUtil. + +2011-10-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/utils/DoubleUtil.java (interpolateSorted): + Added code to linear interpolate double values in a sorted array. + Keys and values are given as double arrays. Keys need to be sorted. + +2011-10-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Removed superfluous imports. + +2011-10-04 Ingo Weinzierl <ingo@intevation.de> + + flys/issue330 (Dauerlinie kann nicht berechnet werden) + + * src/main/java/de/intevation/flys/artifacts/model/Calculation3.java: + Add a problem if no data was found for duration curves. + +2011-10-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/themes/Theme.java, + src/main/java/de/intevation/flys/themes/DefaultTheme.java: Added + getter/setter methods to provide a facet (string) and index (int). Both + values are written as attribute to the Theme's XML representation. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Set the "facet" and "index" values of themes. + +2011-10-04 Ingo Weinzierl <ingo@intevation.de> + + flys/issue346 (W-INFO / Anzeige der Höheninformation) + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Modified x and y axis title. The x axis title is now "RIVERNAME-km"; the + y axis title is now "W[WST_UNIT]" where WST_UNIT depends on the unit of + the WST. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Modified i18n expressions for + x and y axis of longitudinal section charts. + +2011-09-30 Ingo Weinzierl <ingo@intevation.de> + + flys/issue351 (W-INFO / Wasserspiegellagenberechnungen) + + * src/main/java/de/intevation/flys/utils/DoubleUtil.java: Modified the + explode() function that returns a list of values specified by min, max + and an interval. If the last value, determined by the interval, is + bigger than the max value, it is not included in the result list. + +2011-09-30 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + flys/issue334 (Querprofil-Diagramm: Ausgabe dieses Diagrammtyps + möglich, obwohl WSP Berechnung keine Ergebnisse liefert) + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + (compute): Add Facets regarding CrossSections only if data available. + +2011-09-29 Ingo Weinzierl <ingo@intevation.de> + + flys/issue176 (Diagramm: Benennung eines Abflusses bei gewählter Höhe am Pegel) + flys/issue349 (W-INFO / Wasserspiegellagenberechnung, Längsschnittdiagramm) + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Create titles for W and Q waterlevel facets with proper fractions. + +2011-09-30 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + (createStaticData, getLabels): Create proper labels for differences. + +2011-09-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue244 (WINFO: Export von AT-Dateien im ersten Teil unterschiedlich) + flys/issue332 (W-INFO / Berechnung Abflusskurve, Export, FLYS 2.5) + + * src/main/java/de/intevation/flys/exports/ATWriter.java: Due to a rounding + issue the w's of the first line underun the minimal w of the curve at times. + An extra test was introduced to suppress the output of the q's of the wrong w's. + +2011-09-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Use StringUtil.wWrap , fix wrong loop, minor refac and cosmetics. + +2011-09-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + Extracted/use StringUtil.wWrap . + +2011-09-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java: + Log the gauge which has been determined. + +2011-09-28 Ingo Weinzierl <ingo@intevation.de> + + flys/issue332 (W-INFO / Berechnung Abflusskurve, Export, FLYS 2.5) + + * src/main/java/de/intevation/flys/exports/ATExporter.java: Store the + master Artifact which is set via setMasterArtifact(). This is required + for meta information used while preparing the header row of AT exports. + + * src/main/java/de/intevation/flys/exports/ATWriter.java: Print a header + row into the AT export for being compatible with desktop FLYS. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added header row for AT + export files. + +2011-09-28 Ingo Weinzierl <ingo@intevation.de> + + flys/issue328 (W-INFO / ÜSK: Auswahl der Wasserspiegellage / Auswahlunterstützung) + + * doc/conf/meta-data.xml: Added an out 'waterlevels' that might be used to + fetch user specific waterlevels (same as longitudinal sections, but + without Q facet). + In addition, the system specific datacage stuff is now fetched, when: + a) no user-id is given + b) a user-id is given and there is a parameter 'load-system' + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + If the label of the WQKms object specified by the waterlevel selection + begins with a "Q", the label is wrapped into a "W()", e.g. "W(Q=1200)". + +2011-09-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Made + feed() able to remove existing data items from Artifact's data pool. + Therefore, the value for the item which should be removed needs to an + empty string. + +2011-09-28 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Extracted StringUtil.unbracket from WaterlevelSelectState.strip. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + Extract and use StringUtil.unbracket, minor doc. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Changed access of getCurrentState() from protected to public. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: We + use the configured Outputs instead of the actuel Outputs of an Artifact + to make recommendations, now. This has the bad side effect of giving + recommendations for Outputs that we might _NOT_ be able to produce. But + otherwise, we would not be able to give recommendations for states with + long calculation times that start background threads for calculation (as + WSPLGEN caluclations does). + +2011-09-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Enable "auto-scaling" axis for waterlevels in WDifference-plots. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + (createSecondaryAxis, zoomY): Refactored to allow modification in + siblings. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + (createSecondaryAxisRange): Override to achieve expected behavior. + Also adjusted label. + +2011-09-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + In W-Difference Calculation, respect indices of selected facets. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + (computeAdvance): Respect index of selected facets. + +2011-09-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue317: (Querprofil-Diagramm: Referenzen auf CrossSectionApp entfernen) + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Removed dependency to demo app. + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Refactored to use logic from the models. + + * src/main/java/de/intevation/flys/artifacts/geom/Lines.java: + Moved some logic from the demo app to this model. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/wsplgen/FacetCreator.java: New. Code + from FloodMapState moved to its own class with the intent, to use it in + classes different from FloodMapState. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Removed the inner class FacetCreator. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Stores + an instance of FacetCreator. + NOTE: Maybe we should move the WSPLGEN parameters into an own class + which might be serializable. + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Use the + FacetCreator instance stored in the WSPLGENJob to create a new WSPLGEN + facet if the calculation was successfully (without errors). Finally, the + facets of FacetCreator are added to the Facet list of the FLYSArtifacts. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Put Artifact in background mode first before starting WSPLGEN, otherwise + a very fast errors (call CallContext.afterBackground() before Artifact is + in Background mode) might lead to an inconsistent state. + + * src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Repaired + broken error num parsing. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Track + critical errors as well (improved regular expression for errors). + +2011-09-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * ChangeLog: Fixed whitespace usage. + +2011-09-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/RiverService.java, + src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java + src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Removed superfluous imports. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + flys/issue68 (Diagramm: Werte an der Y-Achse benötigen i18n) + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added a + method to retrieve the current/preferred locale specified by CallMeta. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Introduced two methods localizeDomainAxis() and localizeRangeAxis(). + Both methods of this class override the NumberFormat used to format axes + numbers. Those methods are called by localizeAxes() - which has private + access - for each domain and range axis of the current XYPlot. + +2011-09-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/cache.xml: Number of cached annotations was much + to low. + +2011-09-27 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Improved w-differences diagram generation where multiple differences + can be shown. + + * src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java: + New facet type. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Employ new DifferenceCurveFacet, return CalculationResult that can + store more than one WKms. + +2011-09-27 Ingo Weinzierl <ingo@intevation.de> + + flys/issue320 (ÜSK:Mapserver hat Probleme beim Shapefilepath mit "../" im Pfad) + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Use + File.getCanonicalPath() to substitute "../" in shapefile directories. + +2011-09-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + The extent of the WSPLGEN result layer is now specified by the extent of + the CrossSectionTracks that matches the start and end kilometer of the + WSPLGEN calculation. + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: New function + that creates the OpenLayers bounding box based on two Geometries. + +2011-09-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Improved w-differences diagram generation with included "absolute" + values. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Fixed one (of two) incorrect labels. + (doWaterlevelOut): Survive non-found gauge, fetch kilometer and w + instead of values for w and q. + +2011-09-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add positional-conflict-awareness when merging Facets for a + FLYSArtifactCollection. First come first serve. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + (writeFacets): First, sort incoming facets into 2 groups: + "genuinely new" and "already there", then for each new check + whether the position is already taken. If so, push "up" (position++) + until no conflict exists anymore. + +2011-09-26 Ingo Weinzierl <ingo@intevation.de> + + flys/issue296 (Karte: Bezeichnungen verbessern) + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + I18N of the WSPLGEN and barriers facets (WMS layers). + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added strings for the wsplgen + and barriers WMS layers. + +2011-09-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix (revert) access to position in ManagedDomFacet. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java: + (getPosition): Do not add prefix when querying position ("pos") + attribute, add a logger for faster future debugging. + +2011-09-23 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Squash positional conflict-bug. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + (mergeFacets): Removed, replaced in parts by pickFacet. + (pickFacet): New, return facet to be added to document. + Documentation added. + +2011-09-23 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Squash a bug about wrongly named "art:pos" attribute in ManagedDomFacet (was + "pos"). Added documentation from commit message. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java: + Added documentation (commit message with minor adjustments). + (getPosition, setPosition): Include PREFIX in attribute name. + +2011-09-23 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Refactoring, doc. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + (mergeFacets): Removed, replaced in parts by pickFacet. + (pickFacet): New, return facet to be added to document. + Documentation added. + +2011-09-23 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, docs. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Documentation added. + +2011-09-22 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Subclasses the FLYSService now - this should improve the database + connection handling. + +2011-09-22 Bjoern Schilberg <bjoern.schilberg@intevation.de> + + * doc/mapserver/mosel-mapfile.map: + Full blown mosel wms mapfile. + +2011-09-22 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/FLYSService.java: + New. A subclass of DefaultService which is used in FLYS to init and + shutdown database connections. + + * src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java, + src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Centralized the initialization and shutdown of database connections. + These services now subclass FLYSService which handles the database stuff. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: The + current database connection is not closed here - this is done in a + Service or in the CallContext. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: It's + not necessary to create new database connections here. We already have + an existing connection which is initialized by CallContext. + +2011-09-22 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java: + Override validate() of parent classes to suppress "step" validation + which is not present in this state. + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java: Added + new method validateBounds() which really just validates a boundary + without "step" parameter. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added WaterlevelArtifact to accompany WINFOArtifacts in WDifferencesPlots. + + * src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java: + New WaterlevelArtifact. + + * doc/conf/conf.xml: + Added configuration for WaterlevelArtifact configuration (path to state-xml) + waterlevel-factory. + + * doc/conf/artifacts/waterlevel.xml: + New, trivial state description for Waterlevelartifact. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java: + New, only state for WaterlevelArtifact. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, docs. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java, + src/main/java/de/intevation/flys/collections/AttributeWriter.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Cosmetics, documentation. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added WaterlevelOut-processing ability to WDifferencesCurveGenerator. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Process LONGITUDINAL_W facets. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Improved WDifferenceState in preparation to be able to deal with multiple + pairs for differences. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Prepare multiple-pairs-case. + +2011-09-21 Ingo Weinzierl <ingo@intevation.de> + + flys/issue315 (Überschwemmungsfläche: String bei Streckenauswahl) + + * doc/conf/artifacts/winfo.xml: Added a new state for floodmap's range + input. This state will accept a km range only, there is no step width. + + * src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java: + New. The state which is used to enter a km range with step width. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, improved debug output, doc. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Improved debug output. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: + Added documentation. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java, + src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Removed commented code. + + * src/main/java/de/intevation/flys/collections/AttributeParser.java: + Whitespace cosmetics. + +2011-09-21 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Avoid NullPointerException when drawing XYChart without data. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Guard calls to dataset to avoid NullPointerException. + +2011-09-21 Ingo Weinzierl <ingo@intevation.de> + + flys/issue325 (FLYS Client: Auswahl des DGM zeigt numerischen Wert an) + + * src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java: Write + a better label for the selected DEM into the static DESCRIBE. Use the + name of the DEM file as label instead of the database id. + +2011-09-16 Bjoern Schilberg <bjoern.schilberg@intevation.de> + + * doc/mapserver/fontset.txt: + Added initial font set for km_annotation layer. + * doc/mapserver/symbols/symbols.sym: + Added square symbol for km and fixpoint layer. + * doc/mapserver/saar-mapfile.map: + Adjust styling in the flys karte-archiv way. + +2011-09-19 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 2.5 + + * Changes: Prepared changes for release. + +2011-09-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Removed + needless imports. + +2011-09-16 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data.xml: Improved datacage configuration for DEMs. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Query DGMs by the given ID in the FLYS data pool - not by given range + values. + +2011-09-16 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data.xml: Added a section for DEMs to the floodmap + section. + +2011-09-16 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix build. + + * src/main/java/de/intevation/flys/artifacts/state/WDifferencesState.java: + Fix build. + +2011-09-16 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Take a given WaterlevelPair-String, load artifact and plot the diff. + + * src/main/java/de/intevation/flys/artifacts/state/WDifferencesState.java: + Load correct artifacts. + +2011-09-16 Bjoern Schilberg <bjoern.schilberg@intevation.de> + + * doc/mapserver/*: Added inital mapserver configuration files. + +2011-09-16 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data.xml: Moved the system specific configuration into a + macro to the top of the configuration document. Call this macro at the + end of the user specific part and in the part that should contain the + system specific stuff only! + +2011-09-16 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Defined createItem() here and adapted some method signatures, because I + need the CallContext deeper in code than expected. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + Write the name of the selected waterlevel into the static DESCRIBE. + + * src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java: + Adapted method signatures that have been changed in DefaultState. + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Removed method createItem() which is now defined in the upper class + DefaultState. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added string for selected + waterlevel that is displayed in static UI. + +2011-09-16 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added state label for + waterlevel selection. + +2011-09-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + Validate incoming data string and strip brackets. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Fetch waterlevel data from external Artifact if existing. If no external + Artifact is specified that provides waterlevel data, we gonna try to + fetch it from the current Artifact. + +2011-09-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + Removed needless import which caused compile errors. + +2011-09-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a transform() method. This method should be used to transform + input data in form of a string into a better data structure. This state + provides a simple implementation which just returns a StateData object + that contains exactly the input string. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Transform + input strings using DefaultState.transform() before they are added to + its data pool. + +2011-09-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Made collection solid for the case if it has no Artifacts. + +2011-09-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: Fetch Artifact + from ArtifactDatabase properly. Write better error logs if that process + fails. + +2011-09-14 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Extend WDifferences branch to have calculations in dedicated, new state. + + * doc/conf/artifacts/winfo.xml: Added new State and Transition in + WDifferences-branch. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java: + New state. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Specify to not take input, prepare getting other facets. + +2011-09-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java: This + state now desires the UIProvider 'dem_datacage_panel'. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java: + New. This state is used to define the desired UIProvider + 'wsp_datacage_panel'. + + * doc/conf/artifacts/winfo.xml: Added a new way to start a WSPLGEN + calculation when choosing the calculation type 'floodmap'. + +2011-09-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: Merge + facets only if their name AND their owner artifact are equal. + +2011-09-14 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Partial Fix flys/issue304 (3) (Baseline). + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Show Baseline in WDifferencesPlot. + +2011-09-14 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue310 (cross-section theme). + + * doc/conf/themes.xml: CrossSection-Themes: profile thin, water blue. + +2011-09-14 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue310 (cross-section theme). + + * doc/conf/themes.xml: CrossSection-Themes: profile thin, water blue. + +2011-09-13 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Add an quick and simple error to the report if an error occured while + WSPLGENJob creation. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added error messages for + WSPLGEN job creation errors. + +2011-09-13 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix crash when drawing StickyAxisAnnotation when no range was given. + User-directed issues remain ( flys/issue303 ). + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java + (draw): + Guard access to domainAxis, rangeAxis and the corresponding ranges. + Warn and exit if any was null. + +2011-09-13 Ingo Weinzierl <ingo@intevation.de> + + flys/issue290 (Karte: Eingabe von Differenzen zw. WSP und Gelände findet + keine Ausprägung in der Karte) + + * doc/conf/mapserver/wsplgen_class.vm: Added styles for DIFF attribute. + + * doc/conf/mapserver/mapfile.vm: Set debug default to '5'. + +2011-09-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Add a first WSPLGEN status message (notifies the user about a + queued job) after the job has been added to the Scheduler. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added english and german + status message text. + +2011-09-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: Write + shapefiles only if there are features for it existing. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + A WSPLGEN facet is only created, if the calculation was successfully + added to the Scheduler. A barrier facet is only created if the WSPLGEN + calculation has been added to the scheduler AND if there are digitized + geometries existing. + +2011-09-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Copied and slightly modified implementation of guessWaterIncreasing from + WQKms to (new) DataUtils. Accidentally commited usage in last commit, to + correct orientation of diagram (invert x axis). + + * src/main/java/de/intevation/flys/utils/DataUtils.java: + New file with guessWaterIncreasing implementation from WQKms, + slightly adjusted. + +2011-09-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java, + src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java: + Cosmetics. + +2011-09-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added CSV Export for W-Differences. + + * doc/conf/conf.xml: Added Exporter. + + * doc/conf/artifacts/winfo.xml: Removed transition over distances + state, added export outputmode and facet. + + * src/main/java/de/intevation/flys/exports/WDifferencesExporter.java: + New, CSV-Exporter for WDifferences. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Add export facet. + +2011-09-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java: + Cosmetics, docs. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java, + src/main/java/de/intevation/flys/exports/AbstractExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java: + Cosmetics. + +2011-09-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Sourced the code to append a concrete data item to the static DESCRIBE + part out to an own method (appendStaticData()). + + * src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java: + Override appendStaticData() to suppress the GeoJSON string to be + included in the static DESCRIBE. + +2011-09-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java, + src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java, + src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java, + src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java, + src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Removed needless imports. + +2011-09-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/CalculationMessage.java: + New. A subclass of Message (in the Artifacts system). It stores a + string message and a progress (in form of step x/y). + + * src/main/java/de/intevation/flys/wsplgen/JobObserver.java: The observer + now writes background messages into the artifact system using the + CallContext.addBackgroundMessage(). We use instances of + CalculationMessage here, that makes the WINFOArtifact able to put + progress information into the Artifact's DESCRIBE as well. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Write + status message and progress information into the DESCRIBE if the + Artifact has started a background process. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue280 . + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Do not draw boxes around annotations. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix flys/issue279 . + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Fix behaviour in various thinkable malconditions. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Made one case of cross section fetching more robust. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix Facet name and Legend of W-Differences, also insert state to specify + distance. + + * doc/conf/artifacts/winfo.xml: Add additional transitions to walk over + distances state. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + Set facets description. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + Take facets description as legend. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added configuration to kick-in w-differences branch of winfo. + + * doc/conf/conf.xml: Register new (w-differences) OutputGenerators. + + * doc/conf/artifacts/winfo.xml: Register new state and transitions. + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Added new (w-differences) calculation mode. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveInfoGenerator.java: + New file, implementing naive approach to display w-differences + (accidentially omitted in last commit). + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added very stubby WDifferences State/OutGenerator for WINFOArtifact. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added w_differences facet type. + + * src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java: + New file, implements naive approach to calculate w-differences (of + calculations identified by hardcoded uuids!) and register respective facet. + + * src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java: + New file, implementing naive approach to display w-differences. + +2011-09-09 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Implement getArtifact(uuid,context) in FLYSUtils. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java (getArtifact): + Added implementation, partially resolving a TODO. Added logger instance. + +2011-09-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENCalculation.java: + New. This sublcass of Calculation saves warnings and errors that occur + while WSPLGEN is running. + Note, that the interface of this class doesn't exactly apply the interface + of Calculation. Maybe, we should generalize this interface! + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Stores + an instance of WSPLGENCalculation now. We use this instance to save + warnings and errors. + + * src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: Use the + WSPLGENCalculation to save errors and warnings. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENReportFacet.java: + New. This facet is used for WSPLGEN reports. It stores an instance of + WSPLGENCalculation which saves ERRORS and WARNINGS that occur while + WSPLGEN execution. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Create a WSPLGENReportFacet for WSPLGEN reports. + + * doc/conf/conf.xml: Added an OutputGenerator 'report' for WSPLGEN + reports. + + * doc/conf/artifacts/winfo.xml: Added an output 'report' for WSPLGEN + reports. + +2011-09-08 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Cosmetics, docs. + +2011-09-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/wsplgen/JobObserver.java: Small + adjustments that makes it easier to subclass this observer. + + * src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java: New. A + sublcass of JobObserver which analyses the WSPLGEN output for errors and + warnings. + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Use + JobObserver and ProblemObserver to track the whole WSPLGEN output and + print number of errors/warnings to log output. + +2011-09-08 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added translation of w_differences. + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages.properties: + Added "w_differnces" translation. + +2011-09-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Appended + a new attribute 'background-processing' to the DESCRIBE of this + Artifact. Its value is 'true' if this Artifact has started a background + thread which has not finished yet - otherwise it is 'false. + +2011-09-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java, + src/main/java/de/intevation/flys/utils/GeometryUtils.java: Moved the + code to determine the extent of a river based on its axis to + GeometryUtils. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Determine the extent of the selected river and set the WMSLayerFacet's + extent attribute. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added Themeing support for CrossSection Diagrams. + + * doc/conf/themes.xml: + Added new themes and mappings, slightly modified longitudinalsection + theme. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + In CrossSection Diagram fix subtitle to display the km of which the data is + actually displayed (maybe contrasting users wish). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + (getCrossSectionSnapKm): New method to fetch the actual km of crosssection. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Add correct km to charts subtitle. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix various display-issues like i18n in cross-section diagram. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Set description of facets to expected values. + + * src/main/java/de/intevation/flys/artifacts/exports/CrossSectionGenerator.java: + Fix i18n of chart title. Set subtitle to expected value, pass facets + description to StyledSeries to see expected legend. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics, resolved refactoring todo. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java + (compute, computeAdvance, computeFeed): Refactored, extracted method, + resolving duplicate code and TODO. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix waterline "calculation" at given km. Chosen approach is + "head-through-wall". + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Some refactoring to do the same calculation twice easier and be able + to "cross" waterline against correct profile data. + + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix setting of kilometer for profile (not yet waterlevel) of cross section + diagram. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + (getCrossSectionData): Respect corss_section.km data; do naive linear + search for profile data for this km. + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java: + Declare a ComputeType. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Implement computeFeed. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java, + src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Minor cosmetics. + +2011-09-07 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java, + src/main/java/de/intevation/flys/artifacts/model/DataFacet.java, + src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/state/DefaultState.java: + Cosmetics, docs. + +2011-09-06 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/mapserver/barrier_polygons_class.vm, + doc/conf/mapserver/barrier_lines_class.vm, + doc/conf/mapserver/wsplgen_class.vm: Default Mapserver styles for + barriers and WSPLGEN results. Those styles are only used as long as we + don't have map specific themes (as already used in charts). + + * doc/conf/mapserver/layer.vm: Implements a fallback mechanism for styling + barrier lines/polygons and WSPLGEN results. + + * src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Added a + getStyle() method that currently returns "null". This method needs to be + implemented when map themes are introduced. + +2011-09-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: Renamed + some attributes to make their job in the mapfile more obvious. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added + new facet type for barriers. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: Add + a facet for the barriers layer. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: Parse + barriers (lines and polygons) and create two new layers for each type - + those layers are grouped. + + * doc/conf/mapserver/layer.vm: Renamed attribute based on changes in + LayerInfo and added support for Group-Layers. + +2011-09-06 Raimund Renkert <raimund.renkert@intevation.de> + + * doc/conf/conf.xml: + Added driver to database configuration for use with postgresql. + +2011-09-06 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/mapserver/mapfile.vm: Removed FONTSET attribute and set quotes + for SHAPEPATH. + +2011-09-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java: + Removed. We gonna use the WMSLayerFacet until now. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Add a WMSLayerFacet after we triggered the Scheduler to start a WSPLGEN + calculation. + +2011-09-05 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/mapserver/layer.vm, + doc/conf/mapserver/mapfile.vm: Small bugfixes and style improvements. + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Trigger the + MapfileGenerator after a WSPLGEN job has finished regardless if it has + been finished successfully or not. + +2011-09-05 Ingo Weinzierl <ingo@intevation.de> + + * pom.xml: Added Apache Velocity 1.7 for templating support. + + * doc/conf/conf.xml: Added config options for mapserver/template relevant + stuff. + + * doc/conf/mapserver/mapfile.vm, + doc/conf/mapserver/layer.vm: New. A default mapfile template and a + template used for layers. + + * src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: New. + This class is used while reading WMS layer relevant information from + filesystem. + + * src/main/java/de/intevation/flys/utils/MapfileGenerator.java: New. This + thread is used for creating mapfiles for Mapserver. The MapfileGenerator + runs in daemon mode (own thread) and creates mapfiles based on WMS + layer relevant information read from filesystem. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java, + src/main/java/de/intevation/flys/utils/FLYSUtils.java: Moved shapefile + specific XPath expressions from FloodMapState to FLYSUtils which is a + better place to use it in other classes (as MapfileGenerator). + +2011-09-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: + Bugfixed broken attribute assignment. + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: Call + CallContext.afterBackground() after a WSPLGEN job has finished to remove + the background lock from Artifact. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Call CallContext.afterCall(BACKGROUND) to lock the Artifact for + background processing. + +2011-09-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/wsplgen/JobExecutor.java: New. This + class is used to start WSPLGEN for a specific WSPLGENJob. The System + property "wsplgen.bin.path" tells the JobExecutor where the WSPLGEN + binary is placed (which means in general, the property points to the + 'wsplgen.exe'). + + * src/main/java/de/intevation/flys/wsplgen/Scheduler.java: New. This + scheduler currently allows to start just a single WSPLGEN Thread. All + WSPLGEN calculations should be started using Scheduler.addJob(). + + * src/main/java/de/intevation/flys/wsplgen/JobObserver.java: New. This + thread reads log messages from WSPLGEN and listens for specific + messages. It should be used to update status messages of the WSPLGEN + calculation that is currently running. + There is a System property that tells the JobObserver to log all WSPLGEN + output to log4j: enable WSPLGEN output with "-Dwsplgen.log.output=true". + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Added + the FLYSArtifact, the current working directory and the CallContext. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Use the Scheduler to start new WSPLGEN calculations. + +2011-09-02 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add CrossSectionInfoGenerator. + + * src/main/java/de/intevation/flys/exports/CrossSectionInfoGenerator.java: + New, trivial implementation of CrossSectionInfoGenerator. + + * doc/conf/conf.xml: + Register CrossSectionInfoGenerator. + +2011-09-01 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cleanups of CrossSection*. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + New methods to retrieve name of utilized CrossSection. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Cleanup, get rid of copied unused method, documentation and more sensible + translations. + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages.properties: + Added cross_section* translations, also cleanups. + +2011-09-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Set the "typ" attribute of lines and polygons in barrier shapefiles. + +2011-09-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Set the Z values of line and polygon barrier geometries. Both barrier + shapefiles will contain 3D geometries now. + +2011-09-01 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Get real data to display in CrossSection (although ignorant of + parameterization), making use of the showcase code of the CrossSectionApp- + Standalone application. + + * doc/conf/artifacts/winfo.xml: Add new facet (~waterline) to state/out. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + New methods to get relevant data. So far just takes the first value of + everything and assuming a waterlevel at 130m. + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java: + New Facet responsible of water level in cross section. + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Made some functionality publicly and statically available. + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java: + Update call. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added new + Facet type. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Added new Facet to out. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Respect new facet and facets data. + +2011-09-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: Close an open + shapefile transaction and catch exceptions which are thrown while + shapefile creation here. If there occured an error, this functions + returns FALSE, otherwise TRUE. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Removed exception handling while shapefile creation - this is done in + GeometryUtils now. + +2011-09-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Renamed the file for WSPLGEN required waterlevels to "waterlevels.wst". + WSPLGEN did not work with the former "waterlevels.txt" file. + +2011-09-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: New static + function that builds new SimpleFeatureTypes with additional attributes. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Write attributes "ELEVATION" and "KILOMETER" into the crosssection + tracks shapefiles. + +2011-08-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a method + stub that should return a FLYSArtifact based on a given UUID. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Write the selected WST file for WSPLGEN. Note, that this is the WST file + of the current WINFO artifact. Furthermore, there is currently no way + for the user to select a column from WST file, so we currently use the + column that is written to WST file at first. + +2011-08-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed + the parameter order of a compute(...) method. This makes me able to call + this without a hash value. + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java, + src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java: + Adapted the parameter order of the compute() call (see above). + +2011-08-31 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + More bones to CrossSection sceleton. + + * src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java, + src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java: + New, yet trivial implementations of Factory and Facet for CrossSections. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Add new CrossSection- (instead of Default-)Facet. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Adjusted to use (touch) new Factory and Facet. + +2011-08-31 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Cosmetics. + +2011-08-31 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Cosmetics. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java, + src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + Removed obselete imports. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Removed obselete imports, whitespaces. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Whitespaces, docs. + +2011-08-31 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Fix translations of Main Values Facets. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Do acrobatics to keep state transient but get translated title. + + * src/main/java/de/intevation/flys/artifacts/MainValuesQFacet.java, + src/main/java/de/intevation/flys/artifacts/MainValuesWFacet.java: + Changed constructor to get description (which is then already be translated) + dynamically, adjust deepCopy. + + * src/main/java/de/intevation/flys/states/StaticState.java: + Adjust constructor accordingly. + +2011-08-31 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Add sceleton for CrossSection outs. + + * doc/conf/artifacts/winfo.xml: + Added new output mode to respective state of winfo artifact configuration. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Add new Dummy-Facet in state. + + * doc/conf/conf.xml: + Registered new OutputGenerator. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added new CROSS_SECTION type. + + * src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java: + New, stubby skeleton for an CrossSectionGenerator. + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages.properties: + Added cross_section translation, also cleanups (e.g. main values). + +2011-08-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Marked + required parameters with a comment. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Export floodplains (german 'Talaue') to shapefile and write its file + path into the WSPLGEN job. + +2011-08-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Search for a DGM that fits to the current river and km range and write + its file path into the WSPLGEN job. + +2011-08-30 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: Improved + exception handling: exceptions are catched in GeometryUtils now. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Write river axis and crosssections to shapefiles and save shapefile + pathes in WSPLGENJob. + +2011-08-30 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Use the coorect SRID for reading GeoJSON and writing line/polygon + shapefiles. + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: Use a + concrete coordinate system while feature type creation. + +2011-08-30 Ingo Weinzierl <ingo@intevation.de> + + * pom.xml: Added GeoTools 2.7.2 dependencies for Shapefile, GeoJSON and + EPSG support. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: Store + 'LIN' parameter in a list now. A WSPLGEN parameter might contain many + LINs. + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: New functions + to create FeatureTypes and to write shapefiles. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Write user specified barriers into a shapefile placed in the artifact + directory. + +2011-08-30 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: Added a function + that extracts the SRID defined in the global configuration file for a + given river. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Removed the code that extracts the river SRID - use FLYSUtils instead. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: + Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Use FLYSUtils.getRiver instead of WINFOArtifact.getRiver. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Commit accidentally omitted result of refactoring (WINFO/FLYSUtils/getRiver). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Removed implementations of getRiver, update calls to use FLYSUtils. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Interpolate Q main values, generate interpolated W main values on the fly from + Q main values. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + (getGaugeDatum): removed, obsolete + (getLocation): new, gets location + Use WstValueTable to look up interpolated Qs of MainValues. In absence of + the same functionality for Ws, generate W Main Values from Q Main Values. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Use new helper class FLYSUtils, minor refactorization. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Use new functionality of helper class, convenience of FLYSArtifact. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Refactored to use new Helper class FLYSUtils, moved getRiver-functionality + in there. + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java (getRiver): + New function to retrieve river of an artifact, slightly modified from + WINFOArtifact. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Removed implementations of getRiver, update calls to use FLYSUtils. + +2011-08-29 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Minor cosmetics. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Resolved a TODO. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Added some documentation. + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: + Minor cosmetic. + +2011-08-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data.xml: Made it Oracle compatible. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java: + If running in debug mode log executed statements. + Helps debugging Oracle connections. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Set some WSPLGENJob parameters which are stored at FLYSArtifact. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/FLYSUtils.java: New. This helper + class should provide some basic FLYS stuff. Currently, there are functions + that return the km range/location. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed + the methods that return the km range/locations. This is implemented in + FLYSUtils now. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/exports/ChartGenerator.java: Adapted + the WINFO method calls to retrieve the km range/locations - call + FLYSUtils now. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New method + to destroy a single state. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Cosmetic, remove debug output and comments, minor style adjustments. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added limited themeing-support for MainValues. + + * doc/conf/themes.xml: + Added Q/W-MainValues themes. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Added limited theming support, add legend entry for main values. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added a configuration node that points to the directory + where shapefiles should be stored in. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: New. + This class is used to save/write the parameter for a WSPLGEN calculation. + WSPLGEN's *.par files are written using the toFile() method. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + A directory for all WSPLGEN stuff is created in computeAdvance() - those + directory and all its contained files are removed in endOfLife(). + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/utils/ThemeUtil.java: + New Util to work with theme-related stuff. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Use NamedDoubles instead of MainValues, try to adjust scale of Ws. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Use NamedDoubles instead of MainValues, generalize annotation handling, to + allow easier reusability and themeing. + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Added convenience constructor, exemplary switch on bordered text. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/meta-data.xml: + Recommend MainValues for Computed discharge curves. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + New NamedDouble class which implements a <String,double>-pair. + + * src/main/java/de/intevation/flys/artifacts/model/NamedDouble.java: + New, implementation of a double with a string or vice versa. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call + State.endOfLife() for all States when endOfLife() of the Artifact is + called. + +2011-08-26 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Also plot "W"-MainValues (on vertical axis), take correct parameters, but + do not convert to correct scale (cm vs NN+m). + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Naive attempt at allowing the vertical axis to be sticked at. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Store Q and W MainValues separately, add them to plot as annotations. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Serve the MainValues, parameterized on river and location, Q and W. + Removed Facet-implementation. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Updated Facet Types. + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java: + src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java: + New, trivial facets, extracted from MainValuesArtifact. + +2011-08-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call + State.endOfLife() of each State that is no longer in the queue of the + artifact when this artifact steps back to a previous state. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Plot MainValues delivered by MainValuesFacet in much the same ways than + Annotations in LongitudinalSection plots. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Prepare further differentiation between Annotations that stick to X or Y- + Axis, copied some positioning logic into StickyAxisAnnotation + implementation. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Minor cosmetics. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added MainValue-Fetching-Capabilities to MainValuesArtifact. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java + (initialize, getMinValues): + Let MainValuesArtifact return "real" MainValues, although ignorant of all + parameterization. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java + (MainValueFacet): + Improved and straightened implementation, added code-Annotations and Todos. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java + (MainValueFacet): + Improved and straightened implementation, added code-Annotations and Todos. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java + (getState): + Resolved multiple creation of state (yet not very clean). + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Cosmetics, docs. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Cosmetics. + +2011-08-25 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added new (MainValues) Facet-Type. + +2011-08-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Added a method that returns a River object based on its database id. + + * src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: The + data that is required for this artifact is fetched from database instead + from a Master-Artifact. The creation of static artifacts should use + database ids instead of cloning a Master-Artifact. + +2011-08-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Removed needless imports. + +2011-08-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added configuration for factory and rule for MainValueArtifacts. + + * doc/conf.xml: + Add a mainvalue factory to serve MainValueArtifacts. + + * doc/conf/meta-data.xml: + Recomment mainvalue artifact when computed_discharge_curve can be put out. + +2011-08-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Let MainValuesArtifact have a state, outputmode and facet. + + * src/main/java/de/intevation/flys/artifacts/states/StaticState.java: + New, a non-abstract DefaultState. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Progressed with implementation, use StaticState to hook output modes and + facet in; use (Static)FLYSArtifact implementation. + +2011-08-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Smaller cosmetics. + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Cosmetics in comments. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Added @Override annotations. + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java: + Use brackets to improve readability of ternary operator. + +2011-08-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data.xml: Use "ids" in user specific part, too. + +2011-08-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data.xml: s/db-ids/ids/g to unify system and user specific + loading. + +2011-08-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Extracted access to state and states in order to have fewer places to + modify when going for single/trivial state- artifacts. + +2011-08-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java: + Append the river's srid to the WMSLayerFacet. + +2011-08-24 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Minor fixes, ressurect Facet implementation as inner class. + +2011-08-24 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Moved the input of barriers one state + earlier where the scenario is selected as well. + + * src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java: + This state now desires the "map_digitize" UI provider and returns both + items "scenario" and "uesk.barriers" in the dynamic describe part. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Removed the UI provider and the computeFeed() which is no longer needed, + because there is no more user input in this state. + +2011-08-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java, + src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Repaired broken xpath expressions (the config changed in one of the last + commits) and make use of the variable support in xpath expressions to + replace the rivername. + + * src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java: + Save the rivername while initializing this artifact. This is now + necessary, because each river can have its own background wms + configured. + +2011-08-23 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Repaired broken river-wms initialization. + +2011-08-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java, + src/main/java/de/intevation/flys/artifacts/model/DataFacet.java, + src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java, + src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java, + src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java, + src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java: + Made facets cloneable with the right type. + + * src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Removed superfluous imports. + +2011-08-23 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Declare the first artifact of an output as master artifact (artifacts + are ordered by their creation time). + +2011-08-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + Removed facet interface. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Make artifacts cloneable. TODO: Override deepCopy() in subclassed + states and facets. + +2011-08-23 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added the MapInfoService and adapted the floodmap + configuration. Now, each river can have its own background wms layer + defined. + + * src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java: + New. This service returns some basic information used to create maps for + a specific river. The name of the desired river needs to be defined at + "/mapinfo/river/text()". + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Adapted an xpath expression that points to the srid of a river in the + floodmap configuration (which changed). + +2011-08-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Build out/facet filter from XML document passed at creation time. + +2011-08-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Added some code to filter outs/facets by an optional positive list. + This is needed to only expose parts of the facets. This + is needed for artifacts which are loaded into a collection. + TODO: create the filter from the XML document passed at creation + time. + +2011-08-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Moved all + WINFO specific code to WINFOArtifact. FLYSArtifact is now only revolving + about state affairs. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Re-inserted + the specific stuff here. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ChartGenerator.java: + Adjusted the casts. + +2011-08-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data.xml: Filter by outs in user template part. + +2011-08-22 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Defined an input value for the GeoJSON + string to save user defined barriers in the map. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + Added a computeFeed() method. In addition, this state now prefers the + "noinput" UI provider. + +2011-08-19 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Each river requires a SRID definition. This + definition is used to transform the river's geometries into the desired + projection. + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: + This facets are able to save a SRID. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + The extent that is written into the WMSLayerFacet is determined by the + boundary of the geometry. + + * src/main/java/de/intevation/flys/utils/GeometryUtils.java: New. A + utility class that provides helper functions for geometries. Currently, + one function is defined, that creates a boundary string for OpenLayers. + +2011-08-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/RangeState.java: + Removed superfluous imports. + +2011-08-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Removed CallContext from state validation. It is not needed and hindered + the extraction of all out of an artifact if you don't have a + call context (like initial scan of datacage database). + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Extract all outs now. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java, + src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java, + src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/RangeState.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java, + src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Adjusted calls. + +2011-08-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: + Added lacalization of "Streckenfavoriten". + +2011-08-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Picky white-space cosmetics. + +2011-08-19 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added stub implementation of new MainValuesArtifact. + + * src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java: + New. Stub implementation for new MainValuesArtifact. + +2011-08-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fixed flys/issue262 + + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + Unified user and system tempate. + Looks for "/artifact-database/metadata/template/text()" + in conf.xml. Defaults to "${artifacts.config.dir}/meta-data.xml". If user id is + given its the default connection for contexts. + + * doc/conf/conf.xml: Adjusted + * doc/conf/meta-data-system.xml, doc/conf/meta-data-user.xml: Deleted. + * doc/conf/meta-data.xml: Unified version of user and system template. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java: + Adjusted the test program. + +2011-08-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fixed flys/issue260 + + * doc/conf/meta-data-user.xml: Uses master_artifacts view now. + +2011-08-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Added view master_artifacts to select + the master artifacts of the collections more easily. + To upgrade existing database: + + CREATE VIEW master_artifacts AS + SELECT a2.id AS id, + a2.gid AS gid, + a2.state AS state, + a2.creation AS creation, + ci2.collection_id AS collection_id + FROM collection_items ci2 + JOIN artifacts a2 + ON ci2.artifact_id = a2.id + JOIN (SELECT ci.collection_id AS c_id, + MIN(a.creation) AS oldest_a + FROM collection_items ci + JOIN artifacts a + ON ci.artifact_id = a.id + GROUP BY ci.collection_id) o + ON o.c_id = ci2.collection_id + WHERE a2.creation = o.oldest_a; + + TODO: Use the view in the templates. + +2011-08-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data-user.xml: Removed state filter because it was broken. + Simplified by joining two contexts. + +2011-08-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + Fix issues with lines of annotation when zoomed (wrong scale used). + +2011-08-18 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added legend for annotations to LongitudinalSectionDiagram. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Added a pseudo-dataseries/collection to employ existing infrastructure for + displaying localized label for Annotations (yet unthemed). + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: + Made label-string available for localization. + +2011-08-17 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Moved StickyAxisAnnotation into new package de.intevation.flys.jfree . + + * src/main/java/de/intevation/flys/exports/StickyAxisAnnotation.java: + Deleted/moved to src/main/java/de/intevation/flys/jfree/ . + + * src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java: + New/moved from src/main/java/de/intevation/flys/export/ , adjusted + package statement, made class public. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Added import statement for de.intevation.flys.jfree.StickyAxisAnnotation . + +2011-08-17 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Extracted and renamed CustomAnnotation to StickyAxisAnnotation. Also removed + needless imports. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/StickyAxisAnnotation.java: + Extracted class implementation CustomAnnotation and renamed to + StickyAxisAnnotation. + +2011-08-17 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Improved CustomAnnotations and rendering thereof, now including an + "axis mark" (little line at axis), also prepared possibility to put + annotations on Y-axis. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Improved CustomAnnotation to include marks on the axis and better spacing + from it. + +2011-08-17 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Implemented proof-of-concept collision-detection when drawing + CustomAnnotations (text only). + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + Pass a fresh ChartRenderingInfo-Object to createBufferedImage, such that + information can be collected while rendering. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Exploit the fact that XYTextAnnotation already registers drawn shape in the + ChartRenderingInfo if it exists and either an URL or tooltip is set. + Before drawing, calculate own shape and compare against already registered + shapes. + +2011-08-16 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Implemented (yet dummy) custom Annotation class. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Added implementation of yet dummy CustomAnnotation class. + +2011-08-16 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Slightly improved rendering of annotations. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Slightly improved rendering of annotations. Still no valid collision + detection. Annotations are drawn every 2 km; first come first serve. + +2011-08-15 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue191 + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java(longestIncreasingWRangeIndices): + Added a method to find the longest index range with increasing w values. + + * src/main/java/de/intevation/flys/exports/ATWriter.java: Export the longest + range of monotone increasing w values instead of the first one. + TODO: The first line of the export is still broken. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Removed superfluous import. + +2011-08-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java: + Resolved two TODOs: get Annotations of selected River, get "point" + Annotations only. + +2011-08-12 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added functionality to query range and point-annotations only to + AnnotationFactory. + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java + (getPointAnnotations, getAnnotationsBreadth): + New functions to query breadth and point-only annotations. + +2011-08-11 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + Added a development mode for recommendations. Enabled with + stetting system property 'flys.datacage.recommendations.development' to true. + When set the XML template are re-read if the timestamps of the + files have changed so you do not have to restart the server again and again. + + * doc/conf/meta-data-user.xml: Sort collections by creation time in descending order. + +2011-08-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Rather picky cosmetics only. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java, + src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java, + src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java, + src/main/java/de/intevation/flys/artifacts/model/WQKms.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + End comments on a full stop, separate from closing '*/' by whitespace, + adjusted javadoc comments. + +2011-08-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Made Annotations visible in LongitudinalSection diagrams. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Register new Facet Type, let LongitudinalSectionGenerator include + Annotations in diagram (yet unfiltered and independent of river). + +2011-08-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/conf.xml, + doc/conf/artifacts/annotation.xml, + doc/conf/meta-data-system.xml: + Added configuration for AnnotationArtifacts. + +2011-08-11 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + Added demo- implementation of a AnnotationArtifact and its Facet. + + * src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java, + src/main/java/de/intevation/flys/artifacts/states/AnnotationRiverState.java: + New. Initial version of an AnnotationArtifact and its State and Facet. + +2011-08-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Extended schema for artifacts and collections + to have creation times, too. + + To update existing databases: + + ALTER TABLE artifacts ADD COLUMN creation TIMESTAMP NOT NULL DEFAULT current_timestamp; + ALTER TABLE collections ADD COLUMN creation TIMESTAMP NOT NULL DEFAULT current_timestamp; + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Store creation times for artifacts and collections, too. + + * src/main/resources/datacage-sql/org-h2-driver.properties, + src/main/resources/datacage-sql/org-postgresql-driver.properties: + Adjusted SQL statements. + +2011-08-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Make it compilable again (BackendListener interface changed). + +2011-08-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data-user.xml: Added grouping element around w/q of each + longitudinal section artifact. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java, + src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java: + Added some debugging capabilities. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java: + Added isEmpty() method. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java: + Now it is possible to nest <dc:elements> into other elements in the <dc:context> + body. This is useful and needed for grouping and repeating results. + +2011-08-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + User connection was cached, system was not. Lead to incorrect results. + +2011-08-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data-user.xml: Added <old_calculations> element + around old calculations. + +2011-08-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: + Cosmetic: Replaced usage of legacy java.util.Stack with java.util.Deque. + +2011-08-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data-user.xml: Use 'CAST(x AS uuid)' instead of 'x::uuid' + to be more compatible. + +2011-08-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + If given an artifact place its identifier into parameters passed to template. + Fixed swapped user/system connections if using the user template. + + * doc/conf/meta-data-user.xml: Recommend w/q facet from old calculations + if an artifact was given that represents a longitudinal section + "Laengsschnitt". + + TODO I: The template uses PostgreSQL specific UUID casts. + TODO II: We need to find a way only to recommend the master artifacts. + +2011-08-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java: + Added a <dc:comment> tag to place comments in the meta data templates. + <!-- ... --> comments are copied through. + + * doc/conf/meta-data-user.xml: Added a simple test. + +2011-08-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Be a bit more tolerant about empty strings for UUIDs of artifact and user. + +2011-08-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Bring user specific meta data service to life. + + * doc/conf/conf.xml: There are now two templates to configure: + The system template (only the data from the backend) and the + user template (the datcage db and the backend db), + + * doc/conf/meta-data-template.xml: Deleted. + * doc/conf/meta-data-user.xml: New. The user specific template. TODO: Write it! + * doc/conf/meta-data-system.xml: New. The system template. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java: + Deleted. + * src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java: + New. The logic to fill the templates. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: + Adjusted to follow the new call signatures. + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Refactored. Removed the old code and only leave the new service. Following + XPaths are evaluated on the incomming document: + + "/art:meta/art:artifact-id/@value" The UUID of the artifact. Optional. + Used to fill the template enviroment. + "/art:meta/art:user-id/@value" The UUID of the user. Optional. + If given the user specific template is filled. + "/art:meta/art:outs/@value" The list of outs used to recommend for the + various outputs. + "/art:meta/art:parameters/@value" A list of key/value pairs to inject more + filters to the templating. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java: + Change to follow the new recommendations semantics. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java: + Added symbolic constants to distinguish "user" and "system" db connections. + +2011-08-03 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * doc/conf/conf.xml: + Cosmetics, let comments start with a capital and end on a full stop, + removed incorrect comment. + +2011-08-03 Felix Wolfsteller <felix.wolfsteller@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Cosmetics, let comments start with a capital and end on a full stop. + +2011-08-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java(extractOutputNames): + Fixed potential NPE. + +2011-08-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Added support for more than one db connection in datacage templating. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java: + Now you can pass a list of named db connections to the build process. + The purpose is to mix more then one database (e.g. the backend db and + the user specific one). + + To use this feature you can add an "connection" attribute + to <dc:context> with the name of the connection to use. + If no connection name is given the last used is used again. + Initially the first connection in the given list is used. + If the context is left the connection that was active before + will be active again in a stacking manner. + + When creating NamedConnection objects you can set a boolean flag + if the results coming from the connection should be cached. This + is useful e.g. for the user specific database which runs in-memory + so caching would introduce some superfluous overhead. + + * src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java: + When executing the queries explicitly pass if caching should be used. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Forgot to fetch dialect dependent SQL statement for deleting + artifacts by uuid. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Set the name of the collections at initial scan, too. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Fixed wrong SQL references. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Fixed two NPEs. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java, + src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Forwarded kill collections and artifacts events to datacage. + + * src/main/resources/datacage-sql/org-h2-driver.properties, + src/main/resources/datacage-sql/org-postgresql-driver.properties: + Added statement to delete artifact by uuid. + +2011-08-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java: + Made it compilable again. The signature of BackendListener has changed. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Completed the backend listener stuff. + + TODO I: Added some cleanup for orphaned artifacts. + TODO II: Figure out a way to delete collections/artifacts + which are delete from backend without the + backend API. + + * src/main/resources/datacage-sql/org-h2-driver.properties, + src/main/resources/datacage-sql/org-postgresql-driver.properties: + Added needed statements. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Update collection names on change. Remove artifacts from collections. + + * src/main/resources/datacage-sql/org-h2-driver.properties, + src/main/resources/datacage-sql/org-postgresql-driver.properties: + Added needed statements. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java: + Call datacage with the global context. This is needed to access the state engine. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: Changed + signatures to take the global context, too. Create artifacts via backend listener + interface. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java: + Fixed recursion bug. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java: + Added debug output. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Added backend listener for datacage. + + * src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java: + New. Proxies backend listener calls to datacage. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Implements backend listener. TODO: Update the datacage database + according the change calls. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta, + src/main/java/de/intevation/flys/artifacts/datacage/templating: + Moved/renamed package to better fit the common semantics. + DataCage.java is now call NoneUserSpecific.java to reflect the + fact that it is the template for the user independent db + analysis. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java, + src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Ajusted imports and calls. + +2011-08-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: For documentation purposes added a out-commented + section with the default configuration of the datacage. + +2011-07-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Delete the artifacts at before initial scan, too. They are independent + from users. Fixed problem when writing state data content. + + * src/main/resources/datacage-sql/org-h2-driver.properties: Added + statement to delete the artifacts at initial scan, too. + + * src/main/resources/datacage-sql/org-postgresql-driver.properties: + New. PostgreSQL version of the statements. The database scheme + is the same as H2. Very useful for debugging. + +2011-07-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Added kludge for the types of artifact data. They seem to be null + in some circumstances. Needs to be debugged! + +2011-07-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java: + Fixed default connection URL to use a namend in-mermory database. + +2011-07-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Fixed constraint. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Added some debug output. + +2011-07-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Removed artifact_id from facet because there + is a link via out_id -> outs.id: artifact_id -> artifacts to + find the corresponding artifact. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: Store + facets of outs into datacage db at initial scan. + +2011-07-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: + Store outs of artifacts into datacage db at initial scan. + TODO: store facets. + +2011-07-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Added 'type' column in artifacts data. + Maybe useful for filtering. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: + Store artifact data into db at initial scan. + TODO: store outs and facets. + +2011-07-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: + Add artifacts into datacage db at initial scan. + TODO: Store data, outs and facets. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Fixed spelling in sequence name. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: Simply add + collection item at initial scan if artifact was stored before. + TODO: Store new artifacts. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: Add + collections at initial scan. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: Add users + at initial scan. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Using sequences for id generation now + to make schema more compatible. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java, + src/main/resources/datacage-sql/org-h2-driver.properties: Clear database + before initial scan. + +2011-07-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/DBConnection.java: + Deleted. This stuff comes from the artifact database now. + + * src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java: + New. The db config of the datacage database. + + * src/main/resources/datacage-sql/org-h2-driver.properties: New. + The SQL statements needed for the datacage. + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + Make use of the db config. + +2011-07-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java: + New. A artifact database lifetime listener to build the initial + index of the artifacts in database. + + * doc/conf/conf.xml: Added the datacage to the list of lifetime + listeners. + +2011-07-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Added an explicit table for the outs + of an artifact. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Make the current outs of an artifact accessible only with + the global context. + +2011-07-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: Added ON DELETE CASCADE constraints. + Added state in facet. + +2011-07-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/datacage.sql: New. H2 Schema for the datacage database. + Uses special features like IDENTITY (autoincrement) typed columns. + + * src/main/java/de/intevation/flys/artifacts/datacage/DBConnection.java: + Pooled connection. + + * pom.xml: Added dependencies to H2 and Apache DBCP. + +2011-07-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/test/java/de/intevation/flys/artifacts/AppTest.java, + src/main/java/de/intevation/flys/artifacts/App.java: Removed. + This the stupid "Hello, World!" app initially created by the + maven archetype. It was never used. + +2011-07-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Argh! Usage the DOM was not thread safe (discovered with ab). + +2011-07-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/meta-data-template.xml: '$recommended' lead to XPath + errors. Using "dc:contains($parameters, 'recommended')" helps. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java, + src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java: + Added some debugging. + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + Added parameters as 'parameters' to parameters. Usefull to check + for containment of variables. + +2011-07-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Added 'if log.isDebugEnabled() {}'. + +2011-07-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Made Artifact UUID optional and accept extra parameters, too. This enables + the service to be used without an arttifact and test all filters. + + <art:meta xmlns:art="http://www.intevation.de/2009/artifacts"> + <art:outs value="computed_discharge_curve,floodmap"/> + <art:parameters value="river:Elbe"/> + <art:filters value="recommended"/> + </art:meta> + + * src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java: + 'contain' accept Maps and collection, too. + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + Made artifact option (= null) in recommendations. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + Moved connection specific into inner class. The enables the reuse of the + compiled statement. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: The + SQL statements are now only compiled once at creation time of the builder. + Each connection now reuses them. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/cache.xml: Added configuration for static datacage db access. + + * src/main/java/de/intevation/flys/artifacts/services/meta/App.java: Using + caches seems to need an explicit System.exit(). + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + Added support for caching the SQL statements and there results. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: Some + clean up. Reordered code for performance. Strip SQL statements more + aggressively. + + * src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java: + Made it Serializable. + + * src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java: + Introduced system property 'flys.artifacts.cache.config.file' to make + the caching configurable without pulling up the whole stack. + +2011-07-22 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Prepared the FLYSArtifactCollection to support the storage of already + loaded recommendations in its attribute document. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Adjusted to use the DataCage recommendations. The incomming document + can pass the artifacts UUID by '/art:outs/@value' the outs as a + comma separated list in '/art:outs/@value' and optional a set of + filters comma separated in '/art:filters/@value'. + + If UUID and OUTS are not given the old service is used. This + should be removed as soon as the client uses the new service. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + Allow '-' in variable names. + +2011-07-21 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data-template.xml: Now, the river-id is really added to + the factory node of the wmsbackground layer. + +2011-07-21 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data-template.xml: Added the river-id to the factory node + of the riveraxis and wmsbackground layer. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java, + src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java, + src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Adjusted to implement changed Service interface. + +2011-07-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Refactored + the method that retrieves the Outputs for the Artifact. Now, we are able + to query the Outputs for the current state, and all outputs separately. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: The + recommendations provided by this monitor will take the Outputs of the + current state only into account. + +2011-07-21 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/meta-data-template.xml: Added conditions for each output type. + Splitted the "floodmap" output into two parts: a recommended one and a + complete one. The recommended part will only build the document tree for + the recommended artifacts; the complete part will build the whole document + tree that is available for a floodmap. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: + This Hook now uses the DataCage to generate the recommended artifacts. + The output-defaults configurtion is needless now. + + * doc/conf/output-defaults.xml: Removed. The configuration of recommended + artifacts takes place in meta-data-template.xml. + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + Bugfix: the DataCage didn't start working if its builder was NOT null, + but it shouldn't start if the builder IS null. + +2011-07-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Now it is possible to directly pass a Node as a root to the builder. + The owning document if fetch by Node.getOwnerDocument(). This is + useful if you want to generate the recommendation directly into + an already existing document under a given node. + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + Changed the signature of recommend() to accept a node where to + append the recommendations. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Added a macro mechanism: + + <dc:macro name="keine-fuenf"> + <dc:text>'5' ist nicht in der Liste der Outs.</dc:text> + </dc:macro> + <dc:call-macro name="keine-fuenf"/> + <dc:call-macro name="keine-fuenf"/> + <dc:call-macro name="keine-fuenf"/> + + Macros can be defined everywhere in the template + with 'macro'. There bodies can contain all valid elements + including other 'macro's and 'call-macro's. They are + called with their 'name' with 'call-macro'. The control flow + is continued inside the body of the called macro and + will continue right after the calling 'call-macro' when + the macro body is finished. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Added a new 'if' construct similiar to XSLT: + + <dc:if test="not(dc:contains($outs, '5'))"> + <dc:text>'5' ist nicht in der Liste der Outs.</dc:text> + </dc:if> + + The control flow is continued inside the 'if' if the 'test' attribute + as an XPath expression on an empty document evalutes to true. + Else the inside is skipped. There is no 'else'. Use 'choose'/'otherwise' + if you need this. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + Added a recommend() method to generate recommendations for + a given artifact, outs and extra parameters. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added + method to extract all data at once. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java: + New. Custom XPath function provider. Provides + 'dc:contains(Object [] haystack, Object needle)' by now. Should be + useful to check containments in 'out' lists later. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Register the FunctionResolver to the evaluated XPaths. + + * src/main/java/de/intevation/flys/artifacts/services/meta/App.java: + Added code to parse + "param:a,b,c" to "param" -> new String [] { "a", "b", "c" } to + help testing the 'dc:contains' XPath function. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added wms configurations for Saar, Mosel and Elbe. + Each river supported by FLYS requires such a WMS configuration. A WMS + should contain layers for the river axis, buildings, kilometer labels + and maybe a background layer as well. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Added key that is used to store a map of WMS URLs - for each river a + WMS URL. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Parse the river WMS from global configuration. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Create WMSLayerFacets with URLs based on the river and the river wms + configuration stored in the FLYSContext. + + * src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java, + src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: + Adapted the initialize() signature and the method call of computeInit() + which requires a FLYSContext to retrieve the river WMS configurations. + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added the context object parameter to the computeInit() method. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Call + initialize() with the context object - which is a FLYSContext or a + CallContext. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/App.java: + Check if builder was created properly before using it. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + Implements now variable provider for XPath expressions. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Added new choose/when/otherwise construct similiar to XSLT + + <dc:choose> + <dc:when test="$river = 'Mosel'"> + <dc:text>Es ist die Mosel.</dc:text> + </dc:when> + <dc:when test="$river = 'Saar'"> + <dc:text>Es ist die Saar.</dc:text> + </dc:when> + <dc:otherwise> + <dc:text>Es ist weder Mosel noch Saar.</dc:text> + </dc:otherwise> + </dc:choose> + + A 'choose' block can contain a list of 'when's and an optional + 'otherwise'. For each 'when' the test attribute is evaluated + as an XPath expression on an empty document. The result of + the evaluation is taken as a boolean value. If its value is + true the control flow is continued inside the corresponding + 'when' and the other choose elements are not tested. + If the value is values the testing continues with the next + 'when'. If no 'test' expression is evaluated to true, the + control flow continues inside the 'otherwise'. If no 'otherwise' + is given nothing happens at all. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added a config section for floodmaps. Currently, the + background layer's url and layername is defined here. + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java: + Read the background layer configuration from conf.xml. Those values are + used to create the WMSLayerFacet. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java: + The background layer facet will no longer have an extent set. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java, + src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java: + Set the extent of the created WMSLayerFacets and i18n its descriptions. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added I18N strings for the + background an river axis layer. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: + This facet type has a method to set the extent of a WMS layer. The + extent is written to the facets XML node in toXML() as well. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adapted + the signature of setup() which requires a CallMeta parameter now. + + * src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java, + src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: Use + the CallMeta object retrieved in setup() to call initialize(). It is now + able to i18n things. + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/App.java: New. + Standalone app to debug the datacage template. To use in a maven environment: + + -Dmeta.data.template=PATH_TO_META_DATA-TEMPLATE.XML \ + -Dmeta.data.parameters=river:Mosel \ + -Dmeta.data.output=OUTPUT.XML \ + -Dflys.backend.user=DB_USER \ + -Dflys.backend.password=DB_PASSWORD \ + -Dflys.backend.url=DB_CONNECTION_URL \ + -Dexec.mainClass=de.intevation.flys.artifacts.services.meta.App + +2011-07-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java: + New. First step to factor out the "Datenkorb" logic into a service independent + singleton. + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Uses the "Datenkorb" singleton now. + +2011-07-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: + Removed "index" property, because it is already existing in parent + class. + +2011-07-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Improved error handling. + +2011-07-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: Deleted. + + * doc/conf/meta-data-template.xml: New. Was template.xml + + * doc/conf/conf.xml: Made meta data template configurable. + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Load template from configuration not from resources. + +2011-07-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: s/[a-z]+-id/db-id/ + Make database ids identifiable with unique name "db-id". + +2011-07-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: + Override the toXML() method. Subclasses can now write their own XML + representation. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java: + New. This ManagedFacet uses an Element (DOM) to store the information + about a facet. The intent of this facet type is to represent a facet + stored in an Collection attribute. Different facets can have different + attributes that we need to parse, but the only thing ManagedFacets need + to do, is to adjust the attributes "active" and "position". So, those + values are set directly on the Element, the other attributes aren't + touched. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java: + New. This facet is a wrapper for another facet. This subclass of a + ManagedFacet overrides the toXML() method. The XML representation is + defined by the inner facet that is stored as member variable. The + ManagedFacet specific attributes "artifact", "facet", "pos" and "active" + are added manually. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: Uses + the toXML() method to write a facet node into the attribute document. + + * src/main/java/de/intevation/flys/collections/AttributeParser.java: Uses + the ManagedDomFacet to save the information of a facet which is + contained in the attribute part of a Collection's DESCRIBE document. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: Uses the + ManagedFacetAdapter to save a facet, because we want to keep the + specific facet to be able to write its specific XML representation into + the Collection's DESCRIBE document. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the XPath of facets stored in the attribute part of the + DESCRIBE. + +2011-07-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method that returns the Outputs for the Artifact. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapeted the call of OutputParser. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: + Simplified the code to read the Outputs of Artifacts. This parser will + now longer parse the DESCRIBE documents of the Artifacts, but query the + Outputs via FLYSArtifact.getOutputs() directly. + +2011-07-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java: + Adapted the function call of ProtocolUtils.appendOutputModes(). + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: + Override toXML() to add the URL and layernames to the XML representation + of this facet. + +2011-07-18 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/wmsbackground.xml, + doc/conf/artifacts/riveraxis.xml: New configurations for an Artifact + that is used as background layer in floodmaps, and an artifact that is + used as layer showing the river axis in a floodmap. + + * doc/conf/output-defaults.xml: New file to configure default artifacts + for specific output states. E.g. the floodmap state recommends a + background layer and a layer displaying the river axis. In suche case, + the floodmap state recommends two artifacts for the two layers. + + * doc/conf/conf.xml: Added new artifacts/artifact-factories and a Hook to + monitor artifacts (-> CollectionMonitor.java). + + * src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java: + New. This facet is used to represent a layer in a map. So, this facet + stores information about a WMS URL and the layer names provided by this + WMS. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added + facet types for the wmsbackground and riveraxis. + + * src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java, + src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java: + New. An artifact and its default state. The intent of these classes is + to generate WMSLayerFacets which represent background layers in maps. + + * src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java, + src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java: New. + An artifact and its default state. The intent of these classes is to + generate WMSLayerFacets which represent layers that display a river + axis. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added the INIT ComputeType. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + case for the INIT ComputeType while computing data. + +2011-07-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a method computeInit() which is called to initialize data/facets + after an artifact has been created. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Repaired + broken XPath. + +2011-07-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: + Removed the code to generate new artifacts. Instead of creating new + artifacts automatically, we decided to suggest creating new artifacts + from specific types. Therefore, the DESCRIBE document of the artifacts + is extended with a node that contains recommended artifact types. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + FLYSArtifacts might be setup with the identifier of an other + FLYSArtifact. Subclasses are able to override a method called + initialize(Artifact, GlobalContext). This might be helpful to extract + required values or clone artifacts. + +2011-07-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Adapted the signature of createArtifactContext() - it returns an + instance of GlobalContext now. + +2011-07-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Lines.java: + Debugged the water fill algorithm. Added a lot of logging. + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Added a text field to give a water level to fill in. + +2011-07-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Moved code + to create the output modes based on the given facets to FLYSArtifact. In + addition, FLYSArtifact got a new method that returns a specific input + value as string. + + * src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java: New. + This hook monitors the "post-feed" and "post-advance". If the monitored + Artifact's state has configured recommended artifacts, this hook will + create new Artifacts. + + TODO: We have to add the UUIDs of the new Artifacts to the DESCRIBE + document of the artifact to let the client know, that there are new + recommended Artifacts. + + * src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java: New. + This Artifact is the base class for Artifacts, that represent static + data. E.g. this could be a decoration theme in a chart or a background + layer in the map. + + * src/main/java/de/intevation/flys/artifacts/states/OutputState.java: New. + This state might be used as base class for states, that doesn't require + any user input, but only provide static Facets added by a computeFeed() + operation. So, subclasses need to implement computeFeed() only. + +2011-07-13 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + New. Standalone Swing-App to test cross sections from database without the + hassles of our complete software stack. Runnable from a maven environment: + + $ mvn -e \ + -Dflys.backend.user=DB_USER \ + -Dflys.backend.password=DB_PASSWD \ + -Dflys.backend.url=DB_CONNECTION_URL \ + -Dexec.mainClass=de.intevation.flys.artifacts.charts.CrossSectionApp \ + exec:java + + You can set the river to be used with the system property 'river'. + Defaults to 'Mosel'. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java: + Removed superfluous imports. + +2011-07-13 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: The FloodMapState has a new Outputmode + called "floodmap" now. + + * src/main/java/de/intevation/flys/artifacts/model/WSPLGENFacet.java: New. + This facet is used to generate WSPLGEN results. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Added + the WSPLGENFacet. + + * src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java: + This state will now generate WSPLGENFacets. + +2011-07-12 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Lines.java: + Fixed corner case. + +2011-07-11 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/geom/Lines.java: New. + fillWater() generates a list of wet lines for a given profile and a + given water level. + +2011-07-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + Outputs created while generating the DESCRIBE document will now have the + 'type' member set. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: Read the + 'type' member from DESCRIBE document. + +2011-07-06 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Removed a typo. + +2011-07-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java: + DataFacet are now able to store the ID of the state which has created + this Facet. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Initialize DataFacets with the ID of this state. This is necessary to + renew the waterlevel data if it is no longer existing in the cache. + +2011-07-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java: A + report facet can now store the state's id and the artifact's hash value + when it has been created. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Initialize the ReportFacet and WaterlevelFacet with state id and hash + information. This has been necessary to retrieve reports and waterlevels + in states after this one - in states that we need to enter for floodmap + parameterization. + +2011-07-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java: + Changed the desired UI provider. + +2011-07-05 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added the option to continue the + waterlevel parameterization with the intent to create flood maps. + + * src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + This state is no longer a final state. The user has the option to + continue with the parameterization for flood maps based on the current + waterlevel. Therefore, this states desires the "continue" UI provider. + Clients should recognice this to just step to the next state or display + a button that lets the user step to the next state manually. + + * src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java, + src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java, + src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java, + src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java: + New. These states are used to parameterize a further calculation type: + flood map. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Required strings for the + flood maps states. + +2011-07-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix(?) for flys/issue114 + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java: + Make guessing a bit more robust. + +2011-07-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Added the math needed to calculate "W-Differenzen" in "Laengsschnitten". + Needs testing! + + * src/main/java/de/intevation/flys/artifacts/model/NamedObject.java: + Made it an interface to be usable in more than one inheritance chain. + + * src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java: + Implements the NamedObject interface and is the new base class of + WQ and WKmsImpl. + + * src/main/java/de/intevation/flys/artifacts/model/WKms.java: + New. Interface to associate kms with ws. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Changed the base class to NamedObjectImpl. Renamed getKms(int) + to getKm(int) to make clear it return a single scalar value + and fullfil the WKms interface. + + * src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java: + New. Implements the WKms interface. Intended to be a lightweight + datastore for "zusaetzliche Laengsschnitte" and as results + of the WKmsOperations. + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java: + Changed base class to NamedObjectImpl. + + * src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java: + New. Operations on WKms data. + Currently only the SUBTRACTION operation is implemented. This + one is needed to calculate the "W-Differenzen". The operation + is insensitive about the km directions of the datasets. Missing + values are interpolated linear. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Adjusted to satisfy the signature change of WQKMs. + +2011-07-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/StyledXYSeries.java: New. This + XYSeries stores the style information that should be used to render this + series. These information are stored as raw XML documents. A public + method can be used to apply those style information to a + XYLineAndShapeRenderer. + + Note: The only two attributes currently supported by StyledXYSeries + items are "linesize" and "linecolor". + + * doc/conf/themes.xml: Added some more basic themes for the four + calculation methods. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: This + generator now tries to apply themes for all series contained in the + chart. If a series is no instance of StyledXYSeries, the default + renderer is used. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Removed the code that had been introduced to adapt renderers statically. + Now, each of these concrete ChartGenerators instantiates StyledXYSeries + items to put the curves into the chart. Those items contain style + information now! + +2011-07-01 Ingo Weinzierl <ingo@intevation.de> + + flys/issue135 (Diagramm: Trotz abgeschalteter Themen bleiben Beschriftungen bestehen) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: The way + to store datasets has changed. Until this revision, the concrete + generators managed their own datasets. E.g. the + DischargeLongitudinalSectionGenerator had three datasets: w, q and + corrected w. Now, there are just two datasets, managed by this base + generator - one dataset for the first Y axis and one dataset for the + second Y axis. This makes it easier to remove axes, that have no data to + be displayed. All concrete chart generators have to add their XYSeries + using two methods: addFirstAxisSeries() and addSecondAxisSeries(). + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Those concrete chart generators no longer manage datasets themself but + they use the two methods described above, to plot the data to the first + or second Y axis. + +2011-07-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: + Added the option to use a filter to reduce the number of items returned + by this service. + +2011-06-30 Ingo Weinzierl <ingo@intevation.de> + + flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen) + + * doc/conf/artifacts/winfo.xml: Changed the kilometer range input for + calculation 4. This calculation type requires a kilometer range. So, + after choosing the calculation 4, the transition model leads to a state + that just allows the input of a kilometer range with no option to + enter locations. + + * src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java: + New. A base state for the kilometer selection for calculations. The + target of this state is to provide facets for the duration curves. + + * src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java: + New. This state is used to enter a kilometer range. The difference to + the LocationDistanceSelect state is, that there is no option to enter + locations. + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java: + Improved this state to be the base state for calculation ranges. + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: This + state no longer inherits from RangeState which now is used as base + state for kilometer ranges. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added new i18n strings for + the DistanceSelect state. + +2011-06-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Downgraded Trove to 1.1-beta-5, because the new + later ones are removed from the maven repos. + + The functionality we need is in 1.1 so this downgrade should + cause no problems. + + Would be nice if we would support the maintainers of trove to + bring there new versions back into the main maven repos. + +2011-06-28 Ingo Weinzierl <ingo@intevation.de> + + * pom.xml: Repaired the JBoss repository which place has changed. + +2011-06-28 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 2.4 + +2011-06-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/WstWriter.java: + Append the corrected W column (if existing) to the WST export. + +2011-06-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java: + Guessing increaing w is not based on direct neighbors any more. + The second to be compared with is choosen by random of + the values before the first one. This makes the guessing + more robust against 'plateaus' of equal w values. + +2011-06-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ATWriter.java: Array + for constructing the spline was too large leading to non-increasing + values. + +2011-06-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue150 + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Invert the x axis if its guessed that water is increasing. + +2011-06-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java(guessWaterIncreasing): + Added a method to guess based on a given factor of the size (default 0.05) + if the water levels are increasing. Needed to determine in which direction + the water level curve should be orientated. Based on random to avoid + running over large dataset each time a diagram is generated. + +2011-06-27 Ingo Weinzierl <ingo@intevation.de> + + flys/issue177 (WINFO: Abflusskurven am Pegel verursachen ein Hängen des Servers) + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Commented out code to generate time ranges for series names. There + seems to be a problem while loading the discharge tables of a gauge or + while determining the start and/or end time of such discharge tables. + +2011-06-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/**/*.java: Removed trailing whitespace. + +2011-06-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Removed dead code. + +2011-06-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue173 + + * src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + Fixed the way the gauge was found for a given interval. + The old way does not work because it was just tested if + the station point was inside the segments which is not + necessarily true. The obvious solution to simply check + the overlapping intervals does not work either because + the gauge ranges touch each other and so more than + one gauge are returned in these cases. The River.maxOverlap() + is now used to find the gauge with the max overlapping + range. + +2011-06-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue147 + + * src/main/java/de/intevation/flys/artifacts/model/Calculation1.java: + Removed the 'kmUp' flag. It was an left over from former + WSP calculations (w/o ref km) leading to wrong results now. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Don't pass the kmUp flag to the calculation. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Added debug output to see the value of 'wq_free'. + +2011-06-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue86 + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + The reference gauge for calculations "am Pegel" was determined + wrong. + +2011-06-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Conversion w->q was broken. This should fix a number of issues + around "W am Pegel" calculations. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Issue an error report if a w->q conversion fails. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + The gauge to convert w->q with its discharge table was determined wrong. + +2011-06-24 Ingo Weinzierl <ingo@intevation.de> + + flys/issue174 (Diagramm: Q-Linie wird bei initialem Laden des Diagramms + anders dargestellt als bei Ansicht auf gesamten Wertebereich) + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + The chart will now have upper margins again. + +2011-06-24 Ingo Weinzierl <ingo@intevation.de> + + flys/issue172 (Diagramm: Ursprung der Diagramme bei Dauerzahlen) + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + Charts of this type will have the lower X value set to "0". + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ATWriter.java: + Results are now in cm. Made it more robust against corner cases. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Removed superfluous import. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/artifacts/winfo.xml, doc/conf/conf.xml: + Added facet config for AT exports. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added 'at' facet type. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java: + Generate AT facets. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ATExporter.java: New. + Exporter for AT facets. Needs testing. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ATWriter.java: New. + Writer for AT files. New code because our data model differs + from Desktop-FLYS. Needs testing. + +2011-06-23 Ingo Weinzierl <ingo@intevation.de> + + flys/issue157 (Diagramm: Ursprung berechnete Abflusskurve) + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Moved the method that adjusts the X-axis to include the "0" value from + ComputedDischargeCurveGenerator to DischargeCurveGenerator. Now, both + charts will include the "0" on the X-axis. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQDay.java: + Based on WQ now to make it exportable as AT. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQ.java: + New. Base class for WQKms. + + TODO 1: make it a base class for WQDay, too, + TODO 2: Generate instances of WQ instead of WQKms in "Abflusskurven" + calculations. This will save memory. + + AT exporter will get instances of this class as data model + + * src/main/java/de/intevation/flys/artifacts/model/NamedObject.java: + Add default constructor to ease inheritance. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Based on WQ now. Removed code allready defined in base class. + +2011-06-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/artifacts/winfo.xml: Add TODOs for error reports + of "Abflusskurven am Pegel". Maybe we don't need them? + + * doc/conf/conf.xml: Configure report for each calculated output type. + +2011-06-22 Ingo Weinzierl <ingo@intevation.de> + + flys/issue164 (Berechnung 4: Umgekehrtes Berechnungsintervall führt zur) + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Create the items with range information for W and Q which allows the + client to validate the user inserted values. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Arguments in filling datastructure were flip. Now + the "Abflusskurve am Pegel" looks correct again. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Removed superfluous imports. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Improved situtation on rendering "Abflusskurve am Pegel". Not + fully working, yet. + + * doc/conf/artifacts/winfo.xml: Generate facets for the location path, too. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Misspelled + the facet which prevented the facets from being to the outputs. + + * src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java: + Extended to store the hash and the state id of the producing artifact/state + else it results in NPEs because the data is calculated on later (wrong) states. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Store the state id and the hash in the facet, too. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Add two + method to do calculations for a state the artifact is currently not in + and fetching the current state id. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/artifacts/winfo.xml: Generate facets for "Abflusskurven am Pegel" + Not working by now. :-/ + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: New facet type + for "Abflusskurven am Pegel" + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Do calculation + in the artifact not in the output generator. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Generate the new facets. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Fetch data from facet. + +2011-06-22 Ingo Weinzierl <ingo@intevation.de> + + flys/issue161 (Diagramm: Q-Achse in W-Längsschnitten immer bei Q=0) + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + The Q axis (which is the second y axis) initially contains the 0 value. + After a zoom action has taken place, this behaviour is no longer + supported. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Draw correction curve again. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java: + Use correction curve to map plot to axes. Not doing so prevented + the correction curve from being drawn! + Smaller code cleanups and simplifications. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java, + src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: + Added methods to directly access the components w, q and c + at a given index. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: + Attribute access via DOM instead of XPath. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Code simplification. + +2011-06-21 Ingo Weinzierl <ingo@intevation.de> + + flys/issue157 (Diagramm: Ursprung berechnete Abflusskurve) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Splitted + up zooming for x and y axes to be able to override specific axis + zoom behaviour. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + The lower bound of the x axis (which is the Q axis in such chart) is + always 0. + +2011-06-21 Ingo Weinzierl <ingo@intevation.de> + + flys/issue90 (Diagramm: Trennung derDiagrammfläche und Achsenaufheben) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Added a margin between chart data and chart axes. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue158 + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java: + Checks for right class now. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Fix for flys/issue154 + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Checks for right class now. + +2011-06-21 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Only generate 'outs' if they have facets. + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + 'boolean ? true : false' <=> 'boolean' + +2011-06-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added the time-to-live to the DESCRIBE document. + +2011-06-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/artifacts/winfo.xml: Configured states to generate report facets. + +2011-06-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java: + Return the report. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: + Generate report facets if there are problems with the calculations. + TODO: Adjust winfo.xml to configure the facets. + +2011-06-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/ReportGenerator.java: + New. Generator for calculation reports. + + * doc/conf/conf.xml: Added ReportGenerator. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + Added type 'report'. + + * src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java: + Specialized facet for serving reports. TODO: Added them to the + calculation states. + + * src/main/java/de/intevation/flys/artifacts/model/Calculation.java: + Looped through CallMeta for i18n purposes. TODO: do i18n + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Removed superfluous import. + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: + Added some override annotations. + +2011-06-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java: + New. Used to transport the data and the error report. + + * src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation1.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation2.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation3.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation4.java, + src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation.java, + src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java: + Use the CalculationResult now. + +2011-06-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation1.java: + Allow an explicit reference km to enable calculation "am Pegel". + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + If a calculation "am Pegel" is done, take start km of the + calculation range and find the gauge in which range it is located. + Take the station of the gauge as the reference km. If no gauge + is found the calcualtion falls back to calculation "auf freier Strecke". + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Replaced another inefficient attribute extraction via XPath + with direct DOM access. + +2011-06-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Removed dead code. + +2011-06-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java + src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java, + src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java, + src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Only generate facets when needed. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: + Removed more XPath for simply accessing attributes of an element. + + * doc/conf/cache.xml: 200 elements in memory for "computed.values" are enough, + LRU as eviction strategy is sufficent. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java, + src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Use + more 'standard' Java naming conventions. + +2011-06-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Merged in the facet-slt branch to bring in the 'facet' feature. + + * doc/conf/artifacts/winfo.xml: Fixed some facets. + + * doc/conf/cache.xml: Added a "computed.values" cache to store the + results of the WINFO calculations. + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: + Add support for index per facet to make them unique and identifiable. + + * src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: + New. Inteface to be used to access the facet names of the configuration. + + * src/main/java/de/intevation/flys/artifacts/model/DataFacet.java: New. + A facet to be used to have raw access to the computed data of an artifact. + Useful to export things like CSV and WST. + + * src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java: New. + Specialized facet to access the water level data stored in WQKms arrays. + + * src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java: New. + Specialized facet to access the duration data stored in WQDay data structures. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Facets are + now generated dynamically from the current available ones stored with + the artifact. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: Added + methods computeAdvance() and computeFeed() called if artifact is fed or + adance. This overwritten in subclasses to do the state depending calculations. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: These + states overwrites the computeAdvance() and computeFeed() methods to do + the corresponding WINFO calculations. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Calls + computeAdvance() and computeFeed() if artifact is fed or advanced. Centralized + the caching mechanism. + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java, + src/main/java/de/intevation/flys/collections/AttributeParser.java, + src/main/java/de/intevation/flys/collections/OutputParser.java, + src/main/java/de/intevation/flys/collections/AttributeWriter.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adjusted the code to cope with the indices of the facets. Used + DOM to access the attributes instead of XPath. Removed smaller bugs + concerning position generation. + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java, + src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/OutGenerator.java: + Forwarded facet references. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: + Uses facets to fetch data and generate output now. + +2011-06-17 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a new parameter "wq_free" that + determines the mode of calculation 1. If it is "false" (default), + the calculation should be bound to a gauge. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method to retrieve the information about the "wq_free" parameter. + +2011-06-17 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a facet for corrected W in + computation 4. + +2011-06-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: + Got rid of namespace in result document. + +2011-06-14 Ingo Weinzierl <ingo@intevation.de> + + flys/issue77 (Diagramm: Beschriftung der Kurven bei Dauerlinien) + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + Give the curves in the chart names. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added titles for duration + chart curves. + +2011-06-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: + Write top 'Oberkante' and bottom 'Unterkante' to out going XML + if they exist. + +2011-06-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Write the min/max W/Q ranges as art:range elements into the DESCRIBE. + +2011-06-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java: + This state that is used to retrieve locations will now write the + kilometer range of the selected river into the DESCRIBE document. + +2011-06-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation1.java: + New. Factored out version of "Wasserspiegellage" calculation. + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + + Removed some dead code. + + * src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: + Added Override annotation and used quick access method. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Looped through error reporting use by interpolate. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Used factored out version of calculation 1. Removed dead code. + +2011-06-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation2.java: + New. Factored out version of "Abflusskurve". + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Loop errors through w/q at km interpolation. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use factored out version of calculation 2. + +2011-06-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation3.java: + New. Factored out version of "Dauerzahlen". + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Loop errors through for q->w interpolations. + + * src/main/java/de/intevation/flys/artifacts/model/WQDay.java: + Added constructor to directly create with calculated results. + + * src/main/java/de/intevation/flys/artifacts/model/Calculation.java: + Added method to return the number of problems. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use factored out version of calculation 3. + +2011-06-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java, + src/main/java/de/intevation/flys/artifacts/model/WQCKms.java, + src/main/java/de/intevation/flys/artifacts/model/WQDay.java: + Added methods to remove NaN values. + +2011-06-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation.java: + New. Base class for calculations. Used to collect problems occuring + during calculation. + + * src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + Extends Calculation now. Looped through the problem reports to + base class. + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Looped through the problem reports. + +2011-06-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: + Append the min/max range and a transformation matrix for each axis. + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: + Instantiate the InfoGeneratorHelper with a XYChartGenerator instance. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Changed the zoom operation. The zoom values defined in the chart request + document are no longer absolute values for a specific axis. Those values + represent percental values for the start and end point of x and y axes. + E.g. a chart has three axes with the following ranges: + - x axis : 0 - 10 + - y axis 1: 20 - 40 + - y axis 2: 40 - 90 + - zoom values for x: 0.1 - 0.9 (10% - 90%) + - zoom values for y: 0.2 - 0.8 (20% - 80%) + The produced chart will have the following ranges: + - x axis : 1 - 9 + - y axis 1: 24 - 36 + y axis 2: 50 - 80 + +2011-06-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + Map datasets to axes correctly. + +2011-06-08 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + Determine the gauges by their station positions. This hopfully + fixes the problem with wrong assigned gauges and invalid segments. + +2011-06-08 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Segment.java, + src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + Added more debug output. + +2011-06-08 Ingo Weinzierl <ingo@intevation.de> + + flys/issue103 PART 1 (WINFO: Wasserspiegellagenberechnung / Layout-Inkonsistenz) + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Selected values are formatted with the current locale. The static part + of the DESCRIBE document will now contain i18n formatted numbers. + +2011-06-08 Ingo Weinzierl <ingo@intevation.de> + + flys/issue93 (WINFO: Benennung der Berechnungsart korrigieren) + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties: Changed the name of + calculation 4. + +2011-06-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Made the range determination more robust against NaN values. + +2011-06-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + The second y axis is set to position "1". It was set to "2" before, but + in that case, there was no position "1". + +2011-06-08 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + s@m³/s@m\\u00b3/s@ + +2011-06-08 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: + Only generate an axis element if a axis really exists. + +2011-06-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Simpified array swapping. + +2011-06-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Use java.util.List instead of java.util.Vector + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Removed superfluous imports. + +2011-06-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + createItem() is not abstract any longer to avoid code repetitionin sub classes. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java, + src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Removed duplicated code, inserted default constructors and Override annotations. + +2011-06-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: + Append axes range information to the info document. + +2011-06-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + If feed() fails do not store invalid values in database. + +2011-06-06 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: + Removed asymmetrical "- 1" from width calculation. + +2011-06-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + Fixed problem when more than one value per segment are given. + +2011-06-05 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Refactored version of "Berechnung 4" + + * src/main/java/de/intevation/flys/artifacts/model/Segment.java: + Added instance fields for a reference point (= location of gauge) + and backup of values (needed for naming). + + * src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: + Added a constructor to be created from a WQKms. This is helpful + if a WQKms is replaced by a back jump correction. + + * src/main/java/de/intevation/flys/artifacts/model/Calculation4.java: + New. Outfactored version of "W bei ungleichmaessigen Abflusslaengsschnitt". + Much cleaner now and it should have a better handling of the corner + cases. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Removed the linear interpolation stuff. It is now in Linear. Removed + the LinearRemap interpolation method because it is not needed any + longer. Added a method to interpolate a given km with a given + function. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Removed the old calc 4 and used the new one. + + * src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java: + Deleted. Not needed any longer. + + * src/main/java/de/intevation/flys/artifacts/math/Function.java: + New. Interface for a uni-variate real function. + + * src/main/java/de/intevation/flys/artifacts/math/Identity.java: + New. Implements Function with f(x) = x + + * src/main/java/de/intevation/flys/artifacts/math/Linear.java: + New. Implements Function with f(x) = m*x + b + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Factored some stuff out to DoubleUtil. Removed some dead code. + Does some rounding correct. + + * src/main/java/de/intevation/flys/utils/DoubleUtil.java: New. + Centralized utils surrounding common double operations. + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: + Removed superfluous imports. + +2011-06-03 Ingo Weinzierl <ingo@intevation.de> + + flys/issue90(Diagramm: Trennung der Diagrammfläche und Achsen aufheben) + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Determine the ranges of x and y axes. If no zoom ranges are given, we + will determine the min and max xy values in the dataset manually, + because JFreeCharts adds a margin to the left and right of the data + area automatically.. + +2011-06-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Moved + the chart creation into an own public method. This lets the + ChartInfoGenerator create charts without duplicating code. + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: + Limited the possible class for generators to XYChartGenerator. This + enables the ChartInfoGenerator class to do the whole chart creation + stuff itself without outsourcing the code to concrete subclasses. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java: + Removed the code to generate charts - this is done in ChartInfoGenerator + now. + +2011-06-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added new + methods to extract the x and y ranges from request document. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Added a + method that zooms the chart to the specified x and y ranges. + +2011-06-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Set collection ttl to 6 hours. + +2011-06-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/Segment.java: + New. Parse segments only once. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Expose new parser to artifacts. TODO: Use it. + +2011-06-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Changed interpolation methods to interpolate to an arbitrary position + in a given double result array as a preparation for segment independent + calculation. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed superfluous import. + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Fixed cause for crashing: Run back too far in some siutations. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Refactored range code a bit. Needs more work. + +2011-06-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: + Interchange the lower and upper x value of the chart if the x-axis is + inverted before the matrix values are computed. Now, the matrix is able + to work with charts that have an inverted x-axis. + +2011-06-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: New. A + chart info generator generates a document that contains meta information + for a specific chart. Concrete instances of this abstract class need to + instantiate concrete ChartGenerators and dispatch nearly all methods of + an OutGenerator (init(), doOut(), setMaster()) to this instance. The + generate() method is implemented in the ChartInfoGenerator itself. It + creates a chart with help of the ChartGenerator instance and builds a + document that contains meta information of this chart. + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: New. + This helper is used to create the chart info document. At the moment, + the only information that is included in this document is a + transformation matrix to transform image coordinates into chart + coordinates. + + NOTE: The transformation matrix creation needs some work to support + charts with inverted X axis. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java: + Concrete instances of ChartInfoGenerator that create the chart info for + the currently supported chart types. + + * doc/conf/conf.xml: Registered new OutGenerators. + +2011-05-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added support for the 'type' paramter of the collection's out() + operation. + +2011-05-31 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Use an explicit reference km for interpolation now. + +2011-05-30 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/cache.xml: Introduced cache to store the distance info per river. + This boosts performance in following ab setup from 3.61 to 39.91 requests/secs. + + $ ab -c 20 -n 1000 -p distances.xml http://127.0.0.1:8181/service/distanceinfo + + $ cat distances.xml + <?xml version="1.0" encoding="UTF-8"?> + <art:river xmlns:art="http://www.intevation.de/2009/artifacts">Elbe</art:river> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: Added + an iterator result to avoid construction expensive interim lists. + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: Use + the cache if configured. + +2011-05-30 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue82 + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Only successful interpolations are named. + +2011-05-27 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Make it work independent of river flow direction. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Fixed bug in ordering segments + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Re-enabled + calculation of the back jump correction. Fixed more flow direction issues. + +2011-05-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed call of XMLDebug class which is not in the version control. + +2011-05-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Moved the code part that skips themes right after the part that sets the + master artifact for the OutGenerator. We need this master artifact to + display empty charts - master artifact is used to create titles and + axes. + +2011-05-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Deactivated themes are not put into the chart. + +2011-05-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeParser.java: + Repaired broken XPath expressions to find the output modes in an + attribute document of a collection. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: This + writer will now create a document that has a root node art:attribute. + Before these changes, the document's root node was art:outputs which is + part of the attribute document but not the right root node. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted some XPath expressions and corrected the the process to create + attribute documents. + +2011-05-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java: + Made it work independent of from/to order. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Added method to extract the ranges correctly from data. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use the correct ranges. Comment out backjump detection temporarily. + +2011-05-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Generate fields for w/q input depend on flow direction. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Moved km up question out of loop. +2011-05-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Take the flow direction into account. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Prevent NPE. + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java: + Allow to be 'from' greater than 'to' in ranges. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Negate + step if 'from' is greater than 'to'. + +2011-05-26 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Round exploded values to a precision of 1e-6. + +2011-05-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Qs are now stored in ranges for each column. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java: + Deleted. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java: + Was WstValueTableFactory2. + + * src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: Fixed + node linking bug. Removed dead code. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Q values are now stored in range trees by each column. The qs of the rows + are removed and the calculations are adjusted. Removed dead code. + +2011-05-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java: + Moved cache name to WstValueTableCacheKey. Do not cache null references. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java: + Moved cache name into this class. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Store QRangeTree for each column of value table. TODO: Use them! + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java: + Intended as a replacement for WstValueTableFactory, but is work in progress. + + * src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: Fixed + index errors and added methods to dump as graphviz graph. + +2011-05-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Improved the validation of WQ values. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Use the + correct input data object to determine the selected WQ mode (range or + single input). + +2011-05-24 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-40 (part I/II) + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Write default values (values already selected by the user before) of the + input data items into DESCRIBE. + +2011-05-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Removed needless imports. + +2011-05-24 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-85 (part III/III) + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Added a static function that returns the kilometer values (double[]) + from locations input (whitespace separated double values). + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adapted + the getKms() method. It will now return a computed array of kilometers + if we had inserted a range, or it will return the inserted kilometers if + we had inserted locations. + +2011-05-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java: + Moved cache key to separate class. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java: + New. The new cache key class. + +2011-05-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: + Model to store the q values of a WST column efficiently. First + building block not to store the q values directly aside the + w values. + +2011-05-24 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-85 (part I/III) + + * doc/conf/artifacts/winfo.xml: Added two further field 'ld_mode' and + 'ld_locations' to the range/locations state to track the selected mode + and locations. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Added methods to validate the user inserted locations. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method to determine of a range or locations have been inserted. + +2011-05-23 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-62 (part II/II) + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Dump the + artifacts state/data in DEBUG mode in describe(). + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java, + src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + States will no longer store data. The only reason for states storing + StateData is to know about the necessary data for this state. If a State + needs to access the user input for a specific StateData object, it needs + to query the FLYSArtifact which stores the data. + +2011-05-23 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method to dump the artifacts state(s)/data. + +2011-05-23 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue84 + + * src/main/java/de/intevation/flys/artifacts/model/WstFactory.java: + Forget to select wst kind. + +2011-05-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + flys/issue81 + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + weights for kms were swapped. + +2011-05-20 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 2.3.1 + +2011-05-20 Ingo Weinzierl <ingo@intevation.de> + + * Changes: Prepared changes for the upcoming release. + +2011-05-20 Hans Plum <hans@intevation.de> + + * NEWS: + Hint to Release 2.3.1. For further information look into module + flys-client/NEWS + +2011-05-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java (getExplodedValues): + Increment kms array size by one to take the end of range, too. + +2011-05-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java: + Sort by rows (should not be necessary). + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Use unsharp km lookup (epsilon = 0.0001). This fixes the problem + that some kms were not found. + +2011-05-19 Ingo Weinzierl <ingo@intevation.de> + + flys/issue66 + + * src/main/resources/messages_en.properties: Fixed broken template. + +2011-05-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Set the name of the computed discharge curve objects. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + The curves of this chart will now have names that consist of the word + 'Discharge Curve', the river name and the kilometer that has been used + for the computation. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + computed discharge curves. + +2011-05-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Added convenience method isQ() to determine if we are + doing Q calculations. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Reintroduced titles for the "W for unausgeglichene Abfluesse". + +2011-05-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + The curves will now have names that consist of the gauge name and its + valid time range. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + discharge curves. + +2011-05-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Removed dead code. + +2011-05-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use the correct method to generate kms. + + * src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java: + Added some logging to test the map in debug mode. + +2011-05-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Made getExplodedValues static. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use new logic to calculate "W für ungleichwertige Abfluesse". + Not working, yet. + + * ChangeLog: Fixed former entry. + +2011-05-18 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Registered the WST export for discharge + longitudinal sections. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Set the + names of the discharge longitudinal section computation results. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java: + The W/Q curves in the chart will now have names. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: + The WstWriter is filled with column names in an own method. So, we are + able to override this process in subclasses. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java: + Adapted the column names for the WST export. + +2011-05-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + Work on flys/issue69 + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use new logic to calculate "Wasserstand/Wasserspiegellage". + Compared to desktop FLYS are the results are structurally right + but a bit off in the positions after the decimal points. + Maybe a result of the interpolation? Need to debug this. + +2011-05-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + W and Q curves will now have names based on the defined W or Q values + for the waterlevel computation. + +2011-05-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/NamedObject.java: + New. This object is used to give objects a name. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Inherit + from NamedObject now. Because we need to display names for those objects + in different places. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The WQKms + objects returned by a waterlevel computation will now have names. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: + Insert the column names for the WSTs into the WstWriter. + + * src/main/java/de/intevation/flys/exports/WstWriter.java: The column + names are written into the head of the WSTs now. + +2011-05-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstLine.java: New. This + class is used to store the W/Q values of a specific kilometer of a WST. + + * src/main/java/de/intevation/flys/exports/WstWriter.java: New. A writer + that creates WSTs. + + TODO: The header of the WSTs is not finished. The Q descriptions are + missing. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: + Enabled WST exports. + + * doc/conf/artifacts/winfo.xml: Registered the WST export for waterlevels. + +2011-05-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/utils/Formatter.java: New. This class + supports functions to retrieve formatters for specific types of data + used in FLYS. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java, + src/main/java/de/intevation/flys/exports/AbstractExporter.java: + Removed the formatter declaration - the whole formatter stuff is done in + Formatter now. + +2011-05-17 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-72 + + * src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java: + Repaired broken XPath expressions to extract start and end kilometer. + +2011-05-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Removed the Hibernate loading stuff. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java: + New. The Hibernate loading. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Use the WstValueTableFactory for loading now. + +2011-05-17 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 0.1 aka Version 2.3.0 + +2011-05-16 Hans Plum <hans@intevation.de> + + * NEWS: + New. Giving some user specific perspective to new functionality and + changes. This file references releases dates only; details can be find + in the client module at flys-client. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + First step to calculate "W fuer ungleichwertige Abfluesse" correctly. + flys/issue55 + + * src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java: + New. Remaps "gleichwertige" Q values to the corresponding + "ungleichwertige" Q values depending on km. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Remap the Q values "ungleichwertig" depending on the + "gleichwertige" ones. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + First step to fix flys/issue69 + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + New code path to implement the calculation of "Wasserstand/Wasspiegellage" + correctly. TODO 1: Use new path in UI. TODO 2: Remove unused old code. + +2011-05-13 Ingo Weinzierl <ingo@intevation.de> + + * Changes: Prepared Changes for the upcoming release 2.3 - see Changes + file to get to know about the changes of the version numbers. + +2011-05-13 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-37 + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: This + state would be happy if there is a UI provider called "river_panel". + +2011-05-11 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added new + methods that return the requested chart size as integer array [width, + height]. The requested size is read from the incomding request document. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: The size + of a chart is no longer static. The requested size is fetched using + ChartGenerator.getSize(). + +2011-05-11 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-52 + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + The X-Axis of such a chart is inverted, if the head of the river is not + at kilometer 0. This type of charts always have the head of the river at + the left side. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Make incoming XML symmetric to DistanceInfoService. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-47 + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Adjusted temporarily the color of the W, Q and corrected W curves to + distinguish each other. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Adjusted the plot of xy charts - the gridlines are displayed now. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + ISSUE-53 + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + Added a method that returns the preferred locale based on the available + locales of the server and the desired locales of the request (CallMeta). + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: Added a + method that creates a number formatter with minimum and maximum digits. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Formatted the number values of the CSV exports. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java: + New. A data structure that enables us to save a data triple: a range + that consist of lower and upper double value and a set of values that + belong to this range. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Implemented the validation of W/Q values. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: New + method to retrieve i18n messages based on keys. + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java, + src/main/java/de/intevation/flys/exports/WaterlevelExporter.java, + src/main/java/de/intevation/flys/exports/DurationCurveExporter.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Added headers for CSV exports. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added new i18n strings for + CSV headers. + +2011-05-10 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Fetches river name from incoming XML document. If no river is given all + infos about all rivers are listed. + + * src/main/resources/metadata/template.xml: Templates honors the 'river' + parameter. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Extended to pass parameters to the templating. Added support for + type conversion. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + Take parameters as an initial stack frame. + + * src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java: + New. Converts types off stacked variables. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Added a + method to add subtitles to charts. The implementation in this class does + not add any subtitle. Concrete subclasses may override this method to + add some. + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Add subtitles to charts. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n templates for + compound messages (chart subtitles). + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + Added new methods to retrieve translated compound messages. + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: Added a + method to set the master artifact that should be used for some special + operations. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/AbstractExporter.java: + Implement the setMasterArtifact() method of the interface. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Call OutGenerator.setMasterArtifact(). + + NOTE: The determination of the master artifact needs to be implemented! + +2011-05-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Removed + hard coded dev code that defined a WQ mode. + +2011-05-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + Stripped ugly extra whitespace from output introduced by + templating. + +2011-05-09 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: Added forgotten + columns of fixation WSTs. + +2011-05-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added a + method to retrieve i18n strings. + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Enabled i18n support for chart title and axes labels. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + chart types above. + +2011-05-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java: + New. This OutGenerator exports the data of a discharge longitudinal + section computation. + + * doc/conf/conf.xml: Added the DischargeLongitudinalSectionExporter. + + * doc/conf/artifacts/winfo.xml: Added the exporter with CSV facet to the + discharge_longitudinal_section state. + +2011-05-07 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + Added code to make back jump correction work with both + potential flow directions. + +2011-05-06 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/resources/metadata/template.xml: + Added data cage configuration for 'Längsschnitt'. + +2011-05-06 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added the missing Q facet for discharge + longitudinal sections. + + * src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: New. A + derived dataset to store W/Q values with corrected Ws for a kilometer + range. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Some new + methods and a new constructor to initialize this data object with a + predefined set of values. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New + methods to retrieve the W/Q values for the 'discharge longitudinal + section' computation. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: New + methods to retrieve and compute data used for the 'discharge + longitudinal section' computation. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: + Bugfix: just write the ranges of gauges into the DESCRIBE if the + 'wq_values' data item is required. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n messages used in + the DESCRIBE of the WQAdapted state. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Changed the title of the y-axis (now 'W [NN+m]'). + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Improved the transition model to reach the + output state for creating 'discharge longitudinal section' charts. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a + method that returns all gauges of the selected river based on a the + given kilometer range. + + * src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: New. + This state creates a set of elements for the DESCRIBE that consist of a + tuple of kilometer values. The number of elements depend on the number + of gauges intersected by the given kilometer range. + + * src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java: + New. This state is the output state that is reached after the 'discharge + longitudinal section' computation has been chosen. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Default + step width between two kilometers added - if no step width is given, + this default width is used. + +2011-05-05 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Fixed a potential NullPointerException if there is just a single + kilometer given to create a longitudinal section. + +2011-05-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added http://repository.jboss.org/maven2 repo + to fix flys/issue30 + +2011-05-04 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + To make diagram generation possible ws are now generated from qs + because they are many ws having different qs. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + Fetch the WQ data from WINFO artifact and write those values into the + CSV export. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data for discharge curves (computed). + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + Fetch the computed discharge curve data from WINFOArtifact and add the + values into the JFreeChart dataset. + +2011-05-04 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added new transitions and states to enable + the WINFO artifact for computing discharge curves. + + * doc/conf/conf.xml: Added OutGenerators that generate computed discharge + curves and exports for its data. + + * src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java: + New. This state is reached if the user chose the computed discharge + curve. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java: + New. This is only a stub implementation at the moment. This + OutGenerator should create computed discharge curves later. It extends + the DischargeCurveGenerator which should do the same stuff for discharge + curves for gauges. + + * src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java: + New. This is only a stub implementation at the moment. This OutGenerator + should create the exports of the discharge curve computation. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a new output mode for the duration + curve state (CSV export). + + * doc/conf/conf.xml: Added a new OutGenerator to export duration curve + computations. + + * src/main/java/de/intevation/flys/exports/AbstractExporter.java: New. + This abstract OutGenerator represents the base class for exporting + computed data. Currently, the CSV export is supported. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: Moved + the most code to export to CSV into the AbstractExporter. + + * src/main/java/de/intevation/flys/exports/DurationCurveExporter.java: + New. This exporter exports the computed data of a duration computation. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Modified a wrong debug statement which would confuse the user. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added a new output mode for the waterlevel + state (CSV export). + + * doc/conf/conf.xml: Added a new OutGenerator to export waterlevels. + + * src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: New. + This OutGenerator exports the data of a waterlevel computation. Note: + It is necessary to specify the desired facet (e.g. + 'waterlevel_export.csv'). + + * pom.xml: Added a dependency to OpenCSV. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Introduced an "export" output target. If the target is "export", a + facet (read from the incoming xml document) is a necessary parameter + that determines which facets are written to the output. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_en.properties: Bugfix: replaced german + string (copy & paste mistake). + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added new i18n strings for + for the location selection. + +2011-05-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java: + New. An OutGenerator for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/model/WQDay.java: New. A + model class to store necessary data for creating W and Q facets of a + duration curve. This model stores W, Q and Days. + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java: + Added a function to retrieve tuples of (day, q) based on a given gauge - + these tuples are necessary for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data necessary for creating duration + curves. + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java: + Bugfix: improved the access to the location array (avoid + NullPointerException). + + * doc/conf/conf.xml: Registered the new OutGenerator for duration curves. + +2011-05-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * contrib/visualize-transitions.xsl: State quoting was done wrong. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the transition model to reach the + final state for creating duration curves. + + * src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java: + New. This state should be reached to just insert an array of locations. + + * src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java: + New. This state is reached if the duration curve calculation is + selected. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adjusted + getDistance() so that it takes care on inserted locations - not just + inserted ranges. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java: + Flush/persist caches at program exists. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Commented out too eloquent debug output. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Uses the methods of FLYSArtifact to retrieve the necessary information + rivername and selected distance. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Added interpolateW method to take reference to result ws array + as an argument to avoid expensive array allocations in km iterating + loops. + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Added a constructor to create backing trove datastructure + with the right capacity. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Move allocation of result ws out of km loop. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added two further data items in the WQ + selection state. Those items are necessary to store the information + about the selected mode (range/single selection) and the values of the + single selection. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: If + there is no value for a data item, this item is not written into the + static DESCRIBE part. + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java, + src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Renamed the method to validate upper and lower values. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: This + state can now handle values inserted in the single selection. Therefore, + new validate methods has been added. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + methods getWs() and getWs() take care on the values inserted in the + single insert mode of the client which enables the user to insert single + W and Q. + +2011-05-02 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Repaired getQForW() by calculating indices on right dimension. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New + methods for retrieving selected W values (getWs()). + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + method for retrieving waterlevel data takes care on selected Ws, now. + The selected Ws are transformed using the DischargeTables.getQForW() + into Q values. + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Removed an unused parameter 'result' of getQForW(). + +2011-05-02 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Removed + the getDataset() method and replaced it with a addDataset() method. + On this way, concrete subclasses of this OutGenerator can have multiple + datasets (e.g. different datasets for W and Q). This abstract method is + called after the chart generation is finished. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Both classes implement the addDataset() method. The + LongitudinalSectionGenerator has already multiple datasets for W and Q. + Both are added to the chart - both have an own range axis. + +2011-05-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Use the cache for the wst value table if configured. + + * doc/conf/cache.xml: Choose a more precise name for the + wst value table cache. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to Ehcache. Apache 2.0 license. + + * doc/conf/conf.xml: Added configuration of ehcache. + + * doc/conf/cache.xml: New. Cache configurations. + + * src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java: + New. Factory to access caches. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Forgot to add. + + * src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: + New. Given a database connection and a XML template it generates + an output with meta data about the database. + + * src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java: + New. Holds prepared statements optimized to be run in the stack of + contextes. + + * src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java: + New. Model to hold a hierarchical scope of variables. + + * src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java: + New. Stores data set fetched from a sql select to be iterated in + a context. + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java, + src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed superfluous imports. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Added meta data service. + + * src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java: + Stub for the meta data service. + + * src/main/resources/metadata/template.xml: Initial template for + the meta data service. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Bugfix: Now, curves for Q values are drawn into a longitudinal section + chart as well. Therefore, it was necessary to change the datastructure + of the inner class ThemeList that stores all themes included in a chart + in an ordered list (stored in a java.util.Vector now). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: New. An + abstract base class for ChartGenerators that create XY charts. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Sourced the generate() method out to the XYChartGenerator. + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + Implemented the methods to add W and Q facets to the chart. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Improved + the calculation of the step with for ranges. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Added a + method that returns the number of elements stored in the data pool. + +2011-04-29 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: + Added a get() method which takes destination array as an + argument. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Changed the + data structure to store w, q and kms values from List<Double> to + TDoubleArrayList which stores native double values instead of big + Double values. + + * pom.xml: Added the GNU Trove dependency. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: Added the + name a the requested facet to doOut(). Concrete generators should just + create output for this facet now. + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java, + src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java, + src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + Adapted the method signature of doOut(). + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the method call of OutGenerator.doOut(). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: Added mappings for the facets + longitudinal.section.w and longitudinal.section.q. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added some + methods to retrieve necessary information for computing the data of a + waterlevel. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + methods to compute and retrieve the data of a waterlevel computation. + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WQKms.java: New. This + model class represents a pool of data triples that consist of W, Q and + Kms information. This class might be used to compute data for creating + longitudinal section curves (which are based on those W, Q and Kms + values). + +2011-04-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Bugfix: Removed endless loop and a bug while iterating over Hibernate + results. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java: + New. An OutGenerator that creates longitudinal section curves. + + NOTE: This is just the stub - the out creation needs to be implemented! + + * doc/conf/conf.xml: Added the LongitudinalSectionGenerator. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Bugfix: Added missing <output-generators> section. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WaterlevelState.java: New. + This state should be reached if the 'calc.surface.curve' calculation + method has been chosen. + + * doc/conf/artifacts/winfo.xml: Modified a transition and added the + WaterlevelState. This state is reached if the 'calc.surface.curve' + calculation method has been chosen. It currently has 1 output - a + longitudinal section that is not implemented yet! + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java: + New. This transition is valid if the a data object of the current + artifact equals/notequals a configured value in the transition model. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Bugfixes: + - Adapted the parameters of the isStateReachable() call - added the + artifact and the current state. + - Append the outputs of a current state if the state is filled with + valid data. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java: + Added the missing init() method that has been introduced in the + interface some commits ealier. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + Call init() after a Transition has been created. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: + Bugfix: Introduced a <art:outputs> node in the attribute document of a + Collection that contains further <art:output> nodes - instead of having + multiple <art:output> nodes at toplevel of the document. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Adapted the XPath to retrieve the outputs in the attribute document of a + Collection. + +2011-04-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * contrib/visualize-transitions.xsl: Added to create a + Graphviz digraph out of the config.xml. Usage: + + $ xsltproc --stringparam base-dir ../doc/conf/ \ + contrib/visualize-transitions.xsl \ + doc/conf/conf.xml > transitions.dot + + $ dot -Tsvg -o transitions.svg transitions.dot + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + If no data has been inserted so far, an IllegalArgumentException is + thrown. + +2011-04-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:: + A lot of new methods to retrieve the theme of a facet - used while + creating the output of a facet/artifact. If a facet has no theme yet, it + is initialized. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/AttributeParser.java, + src/main/java/de/intevation/flys/collections/OutputParser.java: Removed + useless imports. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartGenerator.java: New. An + abstract OutGenerator that might be used to create chart output. Some + basic things that are equal in all charts should be done here! + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + This OutGenerator no longer implements the OutGenerator directly, but it + extends the ChartGenerator now. + +2011-04-27 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Restructured the out() operation of a collection: Now, we collect a list + of artifacts/facets and use this for the OutGenerator. Its doOut() + method gets the attribute of an artifact - the position and the active + state is managed by the Collection itself. + +2011-04-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: New. + a specialized facet that stores information about its position and its + state (active/inactive) in an output of a collection. + + * src/main/java/de/intevation/flys/collections/AttributeParser.java: New. + This parser takes the attributes (XML) of a collection and extracts the + contained outputs with its facets. The result is a Map<String, Output>. + + * src/main/java/de/intevation/flys/collections/OutputParser.java: New. + This parser is used to query the artifact's DESCRIBE and to extract the + supported outputs. The result is a Map<String, Output>. + + * src/main/java/de/intevation/flys/collections/AttributeWriter.java: New. + This writer merges the outputs contained in an attribute of a collection + with the outputs of a collection's artifacts. + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + The attributes of a collection are written into its DESCRIBE document + now. The OutputParser and AttributeParser are used to read the supported + attributes by the collection and its artifacts - the AttributeWriter is + used to merge both attributes and create a final attribute document. + +2011-04-26 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java, + src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The facet-2-theme mappings are initialized at startup and stored in the + FLYSContext. + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: Added a + function that retrieves a theme from FLYSContext based on its name. + +2011-04-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Corrected a silly c&p mistake. + +2011-04-22 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java: + + Implementation of the "Ruecksprungkorrektur" to be done in + "W fuer angepassten Abflusslaengschnitt". + + All tests show the expected results. In some corner cases the + algorithm described in the "Anwenderhandbuch" chapter 3.3.4.3 "Korrektur" + has some definition shortcomings: + + a - What should happend when you cannot find point 2 because + you cannot step back one quarter from point 3 because there + is no data there any more (river too short in this direction)? + The implemented algorithm raises point 3' only to an + according factor. E.g. If you can step back the whole quarter + distance the elevation is the full quarter. If you can + step back only the half of the quarter the elevation is + only an eighth. + + b - If the water heights between point 2 and 3 are constant then + the algorithm will produce a spline interpolation that + lowers those values. Is this intended? + + For real data the back jumps are expected to be more in the middle + of the distance ranges so the corner cases are maybe not so + important. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Removed superfluous import. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: Removed debug + code that has been commited by accident. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/themes.xml: New. A first small theme configuration. + + * doc/conf/conf.xml: Added a link to the theme configuration. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Defined a key that is used to store a themes map in the FLYSContext. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The theme configuration is read at startup and the themes are stores in + the FLYSContext. + +2011-04-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/themes/Theme.java, + src/main/java/de/intevation/flys/themes/DefaultTheme.java: + New. The interface and its default implementation that represents themes + used to style charts and maps. + + * src/main/java/de/intevation/flys/themes/ThemeField.java, + src/main/java/de/intevation/flys/themes/DefaultThemeField.java: + New. The interface and its default implementation that represents fields + in themes. A theme might be "Lines" and one of its field might be + "Color" or "Size". + + * src/main/java/de/intevation/flys/themes/ThemeFactory.java: A factory + that creates new themes based on a theme configuration. + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Implementation of "Abflusskurve/Abflusstafel" calculation. + + Added method interpolateWQ() which takes an km and results in a + tuple of two double arrays containing the w/q values interpolated + between the surrounding w/q values of the table. + w values are interpolated linear, q values with a cubic spline. + + Drawing w over q gives you the discharge table at the given km. + + !!! This code needs testing !!! + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to Apache Commons Math 2.2 (Apache License 2.0) + +2011-04-20 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Fix problem when sorting by q (copied w instead of q). + +2011-04-20 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Fixed broken HQL statement. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + add a method interpolateW() which takes an array of + q values and returns an equal sized array of w values. + This is essentially the "Wasserstand/Wasserspiegellagen" calculation + of desktop FLYS. + + If you want to do a calculation with given w values you have + to convert the w values with DischargeTables.getQForW() first. + + !!! This code needs heavy testing !!! + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + We need a getQForW() method and not getWForQ() because when + doing a "Wasserstand/Wasserspiegellagen" calculation with given + w values these values need to be translated to q values with + the master discharge table. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Sorting of q values was done wrong. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + Ordered the list of annotations returned by this factory based on its + range. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java: + New. This service returns an XML document that includes the main values + of a gauge based on a river name, a start and an end point. + + * doc/conf/conf.xml: Registered the MainValuesService. + +2011-04-19 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java: + A factory that provides methods to return MainValues. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Added static method getWForQ() to interpolate a w value for + a given q value based on a given discharge table. + +2011-04-19 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Added convenience constructors/methods to ease the access to the master + discharge table of a gauge. + +2011-04-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Moved the query complexity into view 'wst_value_table' and + used this instead. + +2011-04-18 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java: + Fetches w/q value tables from the backend. TODO: Move this + to the backend and use a view. + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed + the error key that is thrown if no input data was found so that the key + is usable for GWT's i18n mechanism. + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + The inserted river is validated now (overrides validate() of + DefaultState). + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + The inserted calculation method is validated now (overrides validate() + of DefaultState). + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + The input data of feed() are validated using the DefaultStates + validate() method. + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + New method validate() that needs to be overidden by concrete subclasses. + It should return true, if the data of the State is fine, otherwise it + should raise an exception. + + NOTE: The exceptions are not translated in the server but in the client! + + * src/main/java/de/intevation/flys/artifacts/states/RangeState.java: New. + This abstract class exists to provide some methods for handling ranges. + Currently, there is a method that validates a given range based on + min/max values. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Implemented input data validation for ranges. + + NOTE: The input validation of concrete values has not been implemented + yet! + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Removed debug code that has been commited by accident :-/ + +2011-04-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + getGauge() method returns the first gauge based on the given start and + end point of the river. + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + Fixed potential bugs: if no gauge could be determined, the default + values for W and Q are the minimum and maximum double values. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: Fills + the DESCRIBE with default values for W and Q. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/WstFactory.java: + New. Returns Wst object - based on a river. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Now + provides some methods that return some basic objects inserted while + parameterization: River, Gauge and so on. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Bugfix: Repaired broken imports of the SessionHolder. + +2011-04-15 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + Moved this class to flys-backend. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java, + src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java, + src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Adapted imports of the SessionHolder. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * trunk/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java, + trunk/src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Acquire/release sessions in services to avoid db connection leaks. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Changed some method signatures - added a reference to the owner + Artifact. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/WQSelect.java, + src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Modified method signatures based on the changes in DefaultState. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + Added default values to the dynamic part of DESCRIBE. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Added a function that returns a River object based on a given river + name. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java, + src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java, + src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + Static methods are using the SessionHolder, too. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Added the setup() method that has been added to the interface + description in the last commits. + + * doc/conf/conf.xml: Registered the SessionCallContextListener as + CallContext.Listener. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + Bugfix: Call correct method to retrieve an instance of + SessionFactoryProvider. + +2011-04-14 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java: + New. Contains thread local session holder for hibernate sessions. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Uses session from SessionHolder. + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Interacts with SessionHolder now. + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + Removed superfluous import. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java: + Implementation of a CallContext.Listener to open/close Hibernate + Sessions for each request. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Some modifications related to the last commit - modification of the + describe() signature of a State. + +2011-04-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: + Removed. We do not need a state to select a gauge - the selection takes + place by choosing a start and an end point. + +2011-04-13 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Bugfix: added missing label node to root node and a namespace to the + data node. + +2011-04-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + The name of the artifact 'winfo' is written into the DESCRIBE document + now. We need this to have a proper way to distinguish between different + artifacts in the UI. + +2011-04-12 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Bugfix: + States are filled with data before they describe themself. + +2011-04-11 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: + New. A factory that returns the annotations of a specific river. + + * src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: + New. This service provides a document that contains information about + distances of a river. + + * doc/conf/conf.xml: Registered the DistanceInfoService. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + This collection overrides the out() operation now. The incoming request + document is read and the related OutGenerator is used to create the + output. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed + the code to create discharge curves. It has moved to the + DischargeCurveGenerator which now does this work. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: The + generate() method throws an IOException now. + + * src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java: + New. This OutGenerator creates discharge curves. + +2011-04-06 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed + the visibility of the getData() method. It's now public, because the + OutGenerator needs an artifact's data. + +2011-04-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/**/*.java: Removed trailing whitespace. + +2011-04-03 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Make project compilable again by + commenting out a not existing XMLDebug reference. + +2011-03-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Added code to parse the configured OutGenerators and to save them (in a + map) in the FLYSContext. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: + Added a key that is used to save the OutGenerators Map in the context. + +2011-03-31 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/OutGenerator.java: + New. This interface is used to generator different types of output. + ArtifactCollections will make use of this interface to create a + collected output of all its artifacts. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Removed the Backend from FLYSArtifactCollection - used ArtifactDatabase + operations instead. + + * TODO: Removed 'remove Backend reference' TODO. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the configuration of the + discharge curve output mode. This output now provides three facets - W, + Q and the curve itself. + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + Tagged RELEASE 0.1 + +2011-03-30 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Renamed an output mode in the WINFO + artifact configuration. + +2011-03-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de_DE.properties, + src/main/resources/messages_de.properties: Changed a german string. + +2011-03-29 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Modified the available calculation modes and its order in the DESCRIBE + document. + + * src/main/resources/messages.properties, + src/main/resources/messages_de_DE.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Adapted the names of + calculation modes. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added the hash code of an artifact to the artifact part of the + collection's DESCRIBE document. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Changed + the background color of discharge curves to white. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Cache the scale, too. Otherwise two calls to getValues() with + different arguments will result in the same output. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Introduced a 'scale' parameter in the getValues() method. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Filled + the out() operation with code that draws a discharge table of one or + more gauges specified by the given range in entered in a previous state. + +2011-03-28 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/exports/ChartExportHelper.java: + New. A helper class to exports charts. + + * pom.xml: Added dependencies to iText, Batik and JFreeChart. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/importer/PegelGltParser.java: + Fixed swap of operands. + +2011-03-28 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java: + New. Load gauges for a river and filter them for given + ranges. + +2011-03-25 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + Store data in a double [][] instead of interleaved double [] + to be compatible with org.jfree.data.xy.DefaultXYDataset. + +2011-03-25 Ingo Weinzierl <ingo@intevation.de> + + * TODO: Removed 'i18n' and 'step-back' TODOs and added an issue to remove + the Backend reference from FLYSArtifactCollection. + +2011-03-25 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + Added missing artifact namespace of an attribute in the DESCRIBE + document. + +2011-03-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java: + New. This ArtifactCollection overrides the DefaultArtifactCollection to + implement FLYS specific describe() and out() operations. + + * doc/conf/conf.xml: Use the FLYSArtifactCollection instead of the + DefaultArtifactCollection for this application. + +2011-03-24 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java: + New. Fetches values of discharge tables in form of packed + w/q double arrays for given gauges. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Removed needless import. + +2011-03-24 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de_DE.properties: Added a german resource + bundle to avoid exceptions in the flys artifacts. Sometimes, the + Resources class is not able to find a 'de_DE' bundle and throws an + exception. This is really strange, because it should use the 'de' bundle + in that case, but it doesn't. + +2011-03-23 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added + some code to append the output modes of previous states to the DESCRIBE + document. + + TODO: Determine if the current state is already filled with data and + append its output modes as well! + +2011-03-22 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Enhanced the location_distance state with + an output mode 'discharge_table'. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Write human readable strings as label attribute into the DESCRIBE + output. Those labels are used to be displayed in the GUI. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + Added some further calculation types. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + calculation types + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented the step-back part of the advance() operation. + +2011-03-21 Ingo Weinzierl <ingo@intevation.de> + + * src/main/resources/messages_de.properties: Fixed a german umlaut. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Added new states for entering a + location/distance and w/q. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: New string for the + location/distance and w/q input states. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: + New. A state for the W/Q input of the WINFO parameterization. + +2011-03-18 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Append an attribute 'uiprovider' to the dynamic UI node. + + * src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java: + New. A state for the location/distance selection of the WINFO + parameterization. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a new method getUIProvider() that might be overriden by concreted + subclasses that should be rendered with a specific UIProvider. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + The static UI part is created by the previous states now. This makes it + possible to group the data objects (which is necessary to group the + objects in the ui). + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Added a describeStatic() method that creates a node that contains the + data of that state. + +2011-03-17 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + Removed TODO: the document contains the rivers provided by the backend + now. + +2011-03-17 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * pom.xml: Added dependency to flys backend. + + * src/main/java/de/intevation/flys/artifacts/model/River.java: + Removed. We are using the backend model now. + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java: + Fetches the rivers from the backend. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Import fixes. + +2011-03-15 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * doc/conf/conf.xml: Added section for database backend configuration. + +2011-03-15 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + Fixed build error coming from different import of XMLUtils. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + Appended the missing label node that contains the human readable name of + the data item. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, + src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + I18N of strings for the DESCRIBE document. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: I18N strings for the + calculcation mode state. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Modified the winfo states. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Adapted + the artifact regarding the changes of the last commit. The states + describe() method creates the dynamic UI node - the artifact needs to + apply this node. + + * src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java: + New. The state for choosing the calculation mode. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Added i18n strings for the + calculation mode state. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: + New. This is the base state for the FLYS application. It provides a + method that creates the dynamic ui node for the DESCRIBE. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: Both + classes extend the abstract base class DefaultState. + +2011-03-14 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/resources/Resources.java: + New. This class retrieves the i18n strings from a ResourceBundle. + + * src/main/resources/messages.properties, + src/main/resources/messages_en.properties, + src/main/resources/messages_de.properties: Resource files for german and + english translation. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Appended + the data that have been inserted in former states into the static node + of the DESCRIBE. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The + operations feed() and advance() return the description of the artifact + using the describe() operation. This avoids additional server round trips + in the client - the clients gets to know about the new state of the + artifact immediately. + +2011-03-10 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented a part (step forward) of the advance operation. + + * TODO: Implement Step-Back in advance operation. + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: + Implemented the abstract method getName(). It returns the constant + 'winfo' string. + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: + Implemented the feed action. The data of an incoming feed() operation is + stored in StateData objects that are saved in a map in the artifact. + + NOTE: There is no input validation and no i18n of error messages (see + TODO). + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * TODO: This file contains some open points that need to be done. + +2011-03-09 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New. This + artifact serves as the default artifact for the FLYS application. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: This + artifact now inherits from FLYSArtifact. Furthermore, there is one big + change: we don't store the State objects itself in the artifact, but + just the identifier of those. This makes the artifact smaller and more + compatible agains previous versions of the software. + +2011-03-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/services/RiverService.java: + New. This service will retrieve a list of provided rivers. + + * doc/conf/conf.xml: Added a configuration for the RiverService. + +2011-03-07 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: Added a section user-factory and collection-factory in + the factories part of the configuration. + +2011-03-01 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Replaced the 'special' attribute from DESCRIBE with a 'uiprovider' + attribute. + +2011-02-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + RiverSelect state is called to create the UI part of the describe + document. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: + Implemented the dynamic UI part of describe(). The static part is not + inserted into the describe document at the moment. We need a reference to + the previous states for this. + +2011-02-08 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java, + src/main/java/de/intevation/flys/artifacts/model/River.java: New. A model + class that represents a river and its factory to create concrete river + instances. + NOTE: Currently, this is just a mockup. The factory just returns two + static rivers "Mosel" and "Saar" without a connection to a backend. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: The + input data of a state is initialized with empty StateData objects after + the State has been created. + + * doc/conf/artifacts/winfo.xml: Renamed the input data nodes of the states + which now fits better to the class name of the implementation. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The + describe document returned by this artifact now contains the current state + and the reachable states. + +2011-02-07 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + The transitions are put into the TransitionEngine with the ID of the state + - not longer with the artifact name. On this way, we are able to fetch + just the transitions for a specific state, instead of all the transitions + of an artifact. + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Describe() + returns the artifact's uuid and hash value. The whole implementation of + describe() is still outstanding. + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/artifacts/winfo.xml: Removed useless config stuff. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed + useless methods, and improved the init process - the first state is set as + the current state for this artifact. + + * src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java, + src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: New. + The states are used in the first two steps of the WINFOArtifact. + Currently, they just implement stubs of the necessary methods setup() and + describe(). + +2011-02-04 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/State.java, + src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java, + src/main/java/de/intevation/flys/artifacts/transitions/Transition.java: + Removed. These classes are placed in the artifact-database now. + + * src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java, + src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + Adapted imports of Transition. + + * src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: New. + This factory should be used to create concrete State objects. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: Added + a constant key to store the StateEngine in the context. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + New method that initializes the states at application start. + +2011-02-03 Ingo Weinzierl <ingo@intevation.de> + + * src/main/java/de/intevation/flys/artifacts/states/State.java: New. The + interface description of a state. + + * src/main/java/de/intevation/flys/artifacts/transitions/Transition.java, + src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java: + New. The interface description and a default implementation of a + transition. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java: + New. The TransitionEngine stores all transitions for each artifact and + should be used to determine, if an artifact can advance from one state to + another. + + * src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java: + New. Transitions should be created by using this class. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: New. + The Flys context. It currently defines keys to store important components + in the context. + + * src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java: + New. The context factory initializes the basic components of the + application. Currently, the TransitionEngine is created and all artifacts + with its transitions are read from the global configuration and stored in + the FLYSContext. + + * pom.xml: Added a dependency to the 'artifacts-common' package. + + * doc/conf/artifacts/winfo.xml: Corrected the classname of the + DefaultTransition. + + * doc/conf/conf.xml: Added FLYSContextFactory as context-factory. + +2011-02-02 Ingo Weinzierl <ingo@intevation.de> + + * doc/conf/conf.xml: An initial configuration file for the FLYS artifact + server. + + * doc/conf/artifacts/winfo.xml: An initial transition configuration of an + WINFO artifact. + + * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: A stub of + an artifact for a WINFO parameterization. + + * pom.xml: Set the source code version to 1.5. + +2011-02-01 Sascha L. Teichmann <sascha.teichmann@intevation.de> + + * src/**, pom.xml: Added initial maven project. + * ChangeLog: new.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/Changes Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,310 @@ +2011-09-19 RELEASE 2.5 + + NEW: + + * Introduced the concept of a "datacage": the datacage is a service that + provides a list of chart themes that fit to a given chart type. The + service accepts a user uuid, the uuid of a master Artifact, the name + of the output type and a set of further string parameters. In general, + we distinguish between user-specific datacage and system-specific + datacage: + The user-specific datacage returns themes provided by old + calculations computed by the user. + The system-specific datacage returns themes that might be generated + using the data stored in the flys-backend. + + * Introduced a database used by the datacage that stores information + about Artifacts, their outputs and their facets. This database + requires synchronization with the Artifact database. The intent of + this database is to have a fast access to data stored in Artifacts. + + * Introduced a mechanism to clone existing Artifacts (with or without + restrictions). For cloning an Artifact, it is necessary to specify the + UUID of a "model Artifact". The clone will be based on that model by + extracting all required data from it. + + * Introduced "recommendations": recommendations are themes in charts or + maps (Artifacts/Facets) that should be added automatically to an + existing chart/map. They are provided by the datacage. + + * Introduced new Artifact types to provide further chart themes: + - Annotations + - Mainvalues + + * Introduced new Facet type WMSLayerFacet. This type stores all required + information (server url, layer names, layer extent, layer srid) to + display a WMS layer in a WMS client. + + * Introduced a new chart output "Querprofildiagramm". This output + calculates a waterlevel and displays it as single line together with + one or more cross section lines ("Querprofilspuren"). + + * Introduced a new chart output "W-Differenzen". This output calculates + waterlevel differences based on at least two waterlevels (a + "W-Differenzen" output can consist of more than a single waterlevel + difference calculation). Each waterlevel difference calculation + results in three chart themes: two W curves and a W-Differences curve. + + * Introduced a new output "Überschwemmungskarte". The visualization of + this output is a map. One of the map's layers is a WMS layer that + displays the calculation result of WSPLGEN (external C++ tool). + + * Improved the rendering process of chart themes: the styles configured + for themes are now used. + + * Improved the DistanceInfoService: it supports filters to filter the + type of items returned by this service (reduces the number of returned + items of course). + + * Introduced a CSV export for "W-Differenzen". + + * Downgraded GNU Trove to 1.1-beta-5 (later versions have been removed + from maven repositories). + + + FIXES: + + * flys/issue135 (Diagramm: Trotz abgeschalteter Themen bleiben Beschriftungen bestehen) + + * flys/issue159 (WINFO: Radiobutton - Ortsauswahl bei "W für ungleichwertigen Abflusslängsschnitt" entfernen) + + * flys/issue160 (WINFO: Auswahltabelle Orte bei Modus Strecke nach Step-Back) + + * flys/issue176 (Diagramm: Benennung eines Abflusses bei gewählter Höhe am Pegel) + + * flys/issue180 (WINFO: Zeilen der Tabelle können nach der Markierung in die Zwischenablage kopiert werden.) + + * flys/issue181 (Erstes Thema in der Themenliste wählt sich automatisch wieder an) + + * flys/issue191 (AT-Export: Längsten monoton steigenden Bereich exportieren.) + + * flys/issue219 (W-INFO: Abflusskurvenberechnung / keine Themen im Diagramm) + + * flys/issue254 (Datenkorb: Klonen von Artefakten in anderen Collections + Facettenfilter zum Ausblenden) + + * flys/issue256 (Datenkorb: XXX Issue festhalten) + + * flys/issue258 (Datenkorb: Outs statt States führen) + + * flys/issue259 (Daten aus Datenkorb in Diagramm einladen) + + * flys/issue260 (Datenkorb: Masterartefakt in View aufführen) + + * flys/issue262 (Datenkorb: Vereinigung der beiden Konfigurations-Templates) + + * flys/issue279 (WINFO: Elbe Wasserspiegellage - Index Out of Bounds) + + * flys/issue280 (BoundingBoxen von Streckenfavoriten und Haupt- und Extremwerten unsichtbar machen) + + * flys/issue281 (Karte: Auswahl der berechnten Wasserspiegellage über Inline-Datenkorb) + + * flys/issue282 (Karte: Abstand interpolierte Profile - Default wert) + + * flys/issue290 (Karte: Eingabe von Differenzen zw. WSP und Gelände findet keine Ausprägung in der Karte) + + * flys/issue303 (Keine Streckenfavoriten, wenn nur Q im Längsschnittdiagram ausgewählt) + + * flys/issue309 (Querprofil: Manuelle Eingabe funktioniert nicht nach Return (nur nach Tab)) + + * flys/issue310 (Querprofil: Farben der Themen) + + * flys/issue311 (Querprofil: i18n) + + + +2011-06-27 RELEASE 2.4 + + NEW: + + * Finalized the Facet concept: output modes and the output generation + are now based on facets. Facets are created dynamically by an Artifact + based on the results of a calculation. + + * Introduced a report mechanism that gives feedback of calculation + problems. + + * Introduced output modes that generate XML document with calculation + report information. + + * Introduced deactivated themes in charts: such themes are not rendered. + + * Introduced a cache to store distance info per river. + + * Introduced output modes that generate XML documents which contain meta + information of charts as axes ranges, data ranges and a transformation + matrix that allows to transform image coordinates into chart + coordinates. + + * Added support for zoom values in Chart output modes. + + * Added support for min/max values in DESCRIBE documents. + + * Added "Oberkante" and "Unterkante" columns to distance info service. + + * Added a new export mode to save data in AT format. + + * Improved performance while storing/loading Q values of WST columns. + + * Improved the WQ values validation for calculation 1 & 4. + + * Improved calculations to work independent of "from"/"to" order of + kilometer ranges. + + * Improved the input of WQ values for calculation 1. We distinguish + between a selected Q at a given gauge or a selected Q that doesn't + base on a given gauge. + + * Allow "from" to be greater than "to" in kilometer ranges. + + * Write default values of the user into the Artifact's DESCRIBE + document (flys/issue40). + + + FIXES: + + * flys/issue62 Artifacts no longer share their data with each other. + + * flys/issue77 Added titles for themes in duration curve charts. + + * flys/issue81 + + * flys/issue82 Fixed NPE after a calculation has taken place. + + * flys/issue84 + + * flys/issue85 Fixed location input for calculation 1 & 4. + + * flys/issue86 Fixed Q determination based on a given W. + + * flys/issue90 Removed space between chart axes and chart area. + + * flys/issue93 Renamed calculation 4. + + * flys/issue103 Append values selected by the user in the correct format + to the DESCRIBE document of Artifacts (uses i18n). + + * flys/issue147 + + * flys/issue150 Invert the X axis correctly for charts of type + calculation 1 & 4. + + * flys/issue154 Repaired computed discharge curve that broke after the + facets had been finalized. + + * flys/issue157 Discharge curve charts (computed an static) will now + have a lower x value set to "1". + + * flys/issue161 Longitudinal section chart's second Y axis will + initially start at Q=0. + + * flys/issue164 Improved input validation for WQ input of calculation 4. + + * flys/issue172 Duration curve charts will now have a lower x value set + to "0". + + * flys/issue173 Fixed broken gauge determination in calculation 4. + + * flys/issue174 Repaired broken upper margin between chart data and + chart border in longitudinal section charts. + + * Added missing "Corrected W" facet for results of calculation 4. + + * Map datasets in duration curve charts to the correct axes. + + * Fixed broken XPath to detect output modes in an attribute document of + a Collection. + + * Feed operation will no longer save data if the validation of the given + values failed. + + + +2011-05-19 RELEASE 2.3.1 + + NEW: + + * New export format for waterlevels: WST. + + * Added descriptions for the curves of the following charts: + - discharge curves (dt. 'Abflusskurven am Pegel') + - computed discharge curves (dt. 'berechnete Abflusskurven') + - longitudinal section curves (dt. 'Längsschnitt') + - discharge longitudinal section curves (dt. 'Abflusslängsschnitt') + + * Number formatting is done in a central place/class. + + FIXES: + + * flys/issue47 (Diagramm: Farbliche Unterscheidung von Abfluß und Wasserstand) + + * flys/issue52 (WINFO: W-Längsschnitt - Wasser jeweils von links nach rechts laufen lassen) + + * flys/issue53 (WINFO/Berechnungsausgabe: Kilometerierung und Wasserstände werden zum Teil mit vielen Nachkommastellen angezeigt) + + * flys/issue66: (i18n: Untertitel bei Längsschnitten - Bereich der Strecke enthält "double") + + * flys/issue67 (WINFO: Längsschnitt - Wasser fließt bergauf) + + * flys/issue72: (WINFO: Q/W/D-Info liefert selten eine Antwort) + + + +2011-05-13 RELEASE 2.3 + + NEW: + + * Initial release of the artifacts for FLYS. Currently there is a single + WINFO artifact for the following computations: + - waterlevels + - discharge curves + - duration curves + - discharge longitudinal section curves + + * Configuration is placed in doc/conf/conf.xml + + * WINFO Artifact specific configuration is placed in + doc/conf/artifacts/winfo.xml + + * So called 'OutGenerators' produce different types of output. + Currently, the flys-artifacts are able to produce charts and exports. + Each output type has to be configured in conf.xml. + + * New chart types: + - discharge curves (dt. 'Abflusskurven am Pegel') + - computed discharge curves (dt. 'Abflusskurve') + - longitudinal section curves (dt. 'Längsschnitte') + - duration curves (dt. 'Dauerlinie') + - discharge longitudinal section curve (dt. 'W bei + ungleichmäßigem Abflusslängsschnitt') + + * New exports: + - csv of waterlevels + - csv of duration curves + - csv of computed discharge curves + - csv of discharge longitudinal section + + * New services that provides: + - supported rivers + - main values of a gauge + - range information of a river + - meta information of a river + + * Caching of computation relevant values + + * Initial model to support chart specific themes (theme.xml) + + + LIMITATIONS: + + * Charts are not rendered using the themes in theme.xml + + + !!! + + The version number of this release depends on an existing desktop variant of + this software that is in version 2.1. + + !!! + + +2011-03-30 RELEASE 0.1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/NEWS Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,13 @@ +NEWS + +for enduser-specific information, c.f. the NEWS -file in the module flys-client + +2011-05-19 Release V 2.3.1 + * Bugfixing Release for WINFO + +2011-05-16 Release V 2.3.0 + * Enhanced functionality for WINFO, diagram and Data-Manager (Datenkorb) + +2011-03-27 Release V 2.2 + +This file starts with V. 2.2. Earlier versions are based on a Desktop-version until V 2.1.3.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/TODO Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1 @@ +- Validation of the input values of an incoming feed() call
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/contrib/visualize-transitions.xsl Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Copyright (c) 2010 by Intevation GmbH + + This program is free software under the LGPL (>=v2.1) + Read the file LGPL.txt coming with the software for details + or visit http://www.gnu.org/licenses/ if it does not exist. + + Author: Sascha L. Teichmann (sascha.teichmann@intevation.de) +--> + +<xsl:stylesheet + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.0"> + + <xsl:output method="text" encoding="UTF-8"/> + + <xsl:param name="base-dir">.</xsl:param> + + <xsl:template match="/"> + <xsl:text>digraph transition_model {
</xsl:text> + <xsl:apply-templates /> + <xsl:text>}
</xsl:text> + </xsl:template> + + <xsl:template match="artifact"> + <xsl:choose> + <xsl:when test="@xlink:href != ''"> + <!-- handle external artifacts --> + <xsl:variable name="path"> + <xsl:call-template name="string-replace-all"> + <xsl:with-param name="text" select="@xlink:href" /> + <xsl:with-param name="replace">${artifacts.config.dir}</xsl:with-param> + <xsl:with-param name="by" select="$base-dir" /> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="document($path)"> + <xsl:apply-templates select="/artifact"/> + </xsl:for-each> + </xsl:when> + <xsl:otherwise> + <!-- handle internal artifacts --> + <xsl:text>subgraph </xsl:text><xsl:value-of select="@name"/> + <xsl:text> {
</xsl:text> + <xsl:text> label = "Artefakt: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:text>";
</xsl:text> + <xsl:apply-templates mode="inside-artifact" select="./states/state"/> + <xsl:apply-templates mode="inside-artifact" select="./states/transition"/> + <xsl:text>}
</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="state" mode="inside-artifact"> + <xsl:text> "</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text disable-output-escaping="yes" + >" [ shape = "record" label=<<table border="0" cellborder="0" cellpadding="3"> + <tr><td align="center" colspan="2" bgcolor="black"><font color="white"></xsl:text> + <xsl:value-of select="@id"/> + <xsl:text disable-output-escaping="yes" + ></font></td></tr></xsl:text> + <xsl:apply-templates mode="inside-artifact" /> + <xsl:text disable-output-escaping="yes" + ></table>>]</xsl:text> + <xsl:text>;
</xsl:text> + </xsl:template> + + <xsl:template match="data" mode="inside-artifact"> + <xsl:text disable-output-escaping="yes" + ><tr><td align="right"></xsl:text> + <xsl:value-of select="@name"/> + <xsl:text disable-output-escaping="yes" + ></td><td align="left"></xsl:text> + <xsl:value-of select="@type"/> + <xsl:text disable-output-escaping="yes" + ></td></tr></xsl:text> + </xsl:template> + + <xsl:template match="transition" mode="inside-artifact"> + <xsl:text> "</xsl:text> + <xsl:value-of select="from/@state"/> + <xsl:text disable-output-escaping="yes">" -> "</xsl:text> + <xsl:value-of select="to/@state"/> + <xsl:text>"</xsl:text> + <xsl:apply-templates mode="inside-artifact"/> + <xsl:text>;
</xsl:text> + </xsl:template> + + <xsl:template match="condition" mode="inside-artifact"> + <xsl:text> [ label="</xsl:text> + <xsl:value-of select="@inputvalue"/> + <xsl:text> </xsl:text> + <xsl:value-of select="@operator"/> + <xsl:text> </xsl:text> + <xsl:value-of select="@value"/> + <xsl:text>" ]</xsl:text> + </xsl:template> + + <xsl:template match="text()" mode="inside-artifact"/> + <xsl:template match="text()"/> + + <xsl:template name="string-replace-all"> + <xsl:param name="text" /> + <xsl:param name="replace" /> + <xsl:param name="by" /> + <xsl:choose> + <xsl:when test="contains($text, $replace)"> + <xsl:value-of select="substring-before($text,$replace)" /> + <xsl:value-of select="$by" /> + <xsl:call-template name="string-replace-all"> + <xsl:with-param name="text" + select="substring-after($text,$replace)" /> + <xsl:with-param name="replace" select="$replace" /> + <xsl:with-param name="by" select="$by" /> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$text" /> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + +</xsl:stylesheet> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/annotation.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="annotation"> + <states> + <state id="state.annotation.river" + description="state.annotation.river" + state="de.intevation.flys.artifacts.states.AnnotationRiverState"> + <outputmodes> + <outputmode name="longitudinal_section" description="output.longitudinal_section" mime-type="image/png" type="chart"> + <facets> + <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations" /> + </facets> + </outputmode> + </outputmodes> + </state> + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/riveraxis.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="riveraxis"> + <states> + <state id="state.riveraxis.layer" + description="state.riveraxis.layer.description" + state="de.intevation.flys.artifacts.states.RiverAxisState"> + <outputmodes> + <outputmode name="floodmap" description="output.uesk.map.description" type="map"> + <facets> + <facet name="floodmap.riveraxis"/> + </facets> + </outputmode> + </outputmodes> + </state> + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/waterlevel.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="waterlevel"> + <states> + <state id="state.waterlevel.done" + description="state.waterlevel.done" + state="de.intevation.flys.artifacts.states.WaterlevelInfoState"> + <outputmodes> + <outputmode name="w_differences" description="output.w_differences" mime-type="image/png" type="chart"> + <facets> + <facet name="longitudinal_section.w" description="facet.longitudinal_section.w" /> + </facets> + </outputmode> + </outputmodes> + </state> + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/winfo.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,380 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="winfo"> + <states> + + <state id="state.winfo.river" description="state.winfo.river" state="de.intevation.flys.artifacts.states.RiverSelect"> + <data name="river" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.river"/> + <to state="state.winfo.calculation_mode"/> + </transition> + + <state id="state.winfo.calculation_mode" description="state.winfo.calculation_mode" state="de.intevation.flys.artifacts.states.CalculationSelect"> + <data name="calculation_mode" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location_distance"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.distance_only"/> + <condition data="calculation_mode" value="calc.flood.map" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location"/> + <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.location"/> + <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.distance"/> + <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.calculation_mode"/> + <to state="state.winfo.waterlevel_pair_select"/> + <condition data="calculation_mode" value="calc.w.differences" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.waterlevel_pair_select"/> + <to state="state.winfo.w_differences"/> + <condition data="calculation_mode" value="calc.w.differences" operator="equal"/> + </transition> + + <state id="state.winfo.location" description="state.winfo.location" state="de.intevation.flys.artifacts.states.LocationSelect"> + <data name="ld_locations" type="Double[]" /> + + <outputmodes> + <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png" type="chart"> + <facets> + <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/> + <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/> + <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/> + </facets> + </outputmode> + <!-- TODO: Do we want an error report? --> + </outputmodes> + </state> + + <state id="state.winfo.distance_only" description="state.winfo.distance_only" state="de.intevation.flys.artifacts.states.DistanceOnlySelect"> + <data name="ld_from" type="Double" /> + <data name="ld_to" type="Double" /> + </state> + + <state id="state.winfo.distance" description="state.winfo.distance" state="de.intevation.flys.artifacts.states.DistanceSelect"> + <data name="ld_from" type="Double" /> + <data name="ld_to" type="Double" /> + <data name="ld_step" type="Double" /> + + <outputmodes> + <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png" type="chart"> + <facets> + <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/> + <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/> + <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.distance_only"/> + <to state="state.winfo.uesk.wsp"/> + <condition data="calculation_mode" value="calc.flood.map" operator="equal"/> + </transition> + + <state id="state.winfo.location_distance" description="state.winfo.location_distance" state="de.intevation.flys.artifacts.states.LocationDistanceSelect"> + <data name="ld_mode" type="String" /> + <data name="ld_locations" type="Double[]" /> + <data name="ld_from" type="Double" /> + <data name="ld_to" type="Double" /> + <data name="ld_step" type="Double" /> + + <outputmodes> + <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png" type="chart"> + <facets> + <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/> + <facet name="mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/> + <facet name="mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/> + </facets> + </outputmode> + <!-- TODO: Do we want an error report? --> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.distance"/> + <to state="state.winfo.wq_adapted"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location_distance"/> + <to state="state.winfo.wq"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location"/> + <to state="state.winfo.durationcurve"/> + <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.location"/> + <to state="state.winfo.computeddischargecurve"/> + <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/> + </transition> + + <state id="state.winfo.wq" description="state.winfo.wq" state="de.intevation.flys.artifacts.states.WQSelect"> + <data name="wq_mode" type="String" /> + <data name="wq_free" type="Boolean" /> + <data name="wq_selection" type="String" /> + <data name="wq_from" type="Double" /> + <data name="wq_to" type="Double" /> + <data name="wq_step" type="Double" /> + <data name="wq_single" type="Double[]" /> + </state> + + <state id="state.winfo.wq_adapted" description="state.winfo.wq_adapted" state="de.intevation.flys.artifacts.states.WQAdapted"> + <!-- TODO Add data objects --> + <data name="wq_mode" type="String" /> + <data name="wq_values" type="WQTriple" /> + </state> + + <state id="state.winfo.waterlevel_pair_select" description="state.winfo.waterlevel_pair_select" state="de.intevation.flys.artifacts.states.WaterlevelPairSelectState"> + <data name="diffids" type="String" /> + </state> + + <state id="state.winfo.w_differences" description="state.winfo.w_differences" state="de.intevation.flys.artifacts.states.WDifferencesState"> + + <outputmodes> + <outputmode name="w_differences" description="output.w_differences" mime-type="image/png" type="chart"> + <facets> + <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/> + <facet name="w_differences" description="facet.w_differences"/> + <facet name="other.wkms" description="facet.other.wkms"/> + <facet name="heightmarks_points" description="facet.other.wkms.heightmarks_points"/> + <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/> + </facets> + </outputmode> + <outputmode name="w_differences_export" description="output.w_differences.export" mime-type="text/plain" type="export"> + <facets> + <facet name="csv" description="facet.w_differences.csv" /> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.durationcurve" description="state.winfo.durationcurve" state="de.intevation.flys.artifacts.states.DurationCurveState"> + <outputmodes> + <outputmode name="duration_curve" description="output.duration_curve" mime-type="image/png" type="chart"> + <facets> + <facet name="duration_curve.w" description="facet.duration_curve.w"/> + <facet name="duration_curve.q" description="facet.duration_curve.q"/> + </facets> + </outputmode> + <outputmode name="durationcurve_export" description="output.durationcurve_export" mime-type="text/plain" type="export"> + <facets> + <facet name="csv" description="facet.durationcurve_export.csv" /> + </facets> + </outputmode> + <outputmode name="durationcurve_report" description="output.durationcurve_report" mime-type="text/xml" type="report"> + <facets> + <facet name="report" description="facet.durationcurve_export.report" /> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.computeddischargecurve" description="state.winfo.computeddischargecurve" state="de.intevation.flys.artifacts.states.ComputedDischargeCurveState"> + <outputmodes> + <outputmode name="computed_discharge_curve" description="output.computed_discharge_curve" mime-type="image/png" type="chart"> + <facets> + <facet name="computed_discharge_curve.q" description="facet.computed_discharge_curve.q"/> + <facet name="computed_discharge_curve.mainvalues.q" description="facet.computed_discharge_curve.mainvalues.q"/> + <facet name="computed_discharge_curve.mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/> + </facets> + </outputmode> + <outputmode name="computed_dischargecurve_export" description="output.computed_dischargecurve_export" mime-type="text/plain" type="export"> + <facets> + <facet name="csv" description="facet.computed_dischargecurve_export.csv" /> + </facets> + </outputmode> + <outputmode name="computed_dischargecurve_report" description="output.computed_dischargecurve_report" mime-type="text/xml" type="report"> + <facets> + <facet name="report" description="facet.computed_dischargecurve_export.report"/> + </facets> + </outputmode> + <outputmode name="computed_dischargecurve_at_export" description="output.computed_dischargecurve_at_export" mime-type="text/plain" type="export"> + <facets> + <facet name="at" description="facet.computed_dischargecurve_export.at"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition"> + <from state="state.winfo.wq"/> + <to state="state.winfo.waterlevel"/> + <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/> + </transition> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.wq_adapted"/> + <to state="state.winfo.discharge_longitudinal_section"/> + </transition> + + <state id="state.winfo.waterlevel" description="state.winfo.waterlevel" state="de.intevation.flys.artifacts.states.WaterlevelState"> + <outputmodes> + <outputmode name="longitudinal_section" description="output.longitudinal_section" mime-type="image/png" type="chart"> + <facets> + <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/> + <facet name="longitudinal_section.q" description="facet.longitudinal_section.q"/> + <facet name="w_differences" description="facet.w_differences"/> + <facet name="other.wkms" description="facet.other.wkms"/> + <facet name="heightmarks_points" description="facet.other.wkms.heightmarks_points"/> + <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/> + </facets> + </outputmode> + <outputmode name="waterlevel_export" description="output.waterlevel_export" mime-type="text/plain" type="export"> + <facets> + <facet name="csv" description="facet.waterlevel_export.csv" /> + <facet name="wst" description="facet.waterlevel_export.wst" /> + </facets> + </outputmode> + <outputmode name="waterlevel_report" description="output.waterlevel_report" mime-type="text/xml" type="report"> + <facets> + <facet name="report" description="facet.waterlevel_export.report"/> + </facets> + </outputmode> + <outputmode name="cross_section" description="output.cross_section" mime-type="image/png" type="chart"> + <facets> + <facet name="cross_section_water_line" description="facet.cross_section_water_line"/> + <facet name="cross_section" description="facet.cross_section"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <state id="state.winfo.discharge_longitudinal_section" description="state.winfo.discharge_longitudinal_section" state="de.intevation.flys.artifacts.states.DischargeLongitudinalSection"> + <outputmodes> + <outputmode name="discharge_longitudinal_section" description="output.discharge_longitudinal_section" mime-type="image/png" type="chart"> + <facets> + <facet name="discharge_longitudinal_section.w"/> + <facet name="discharge_longitudinal_section.q"/> + <facet name="discharge_longitudinal_section.c"/> + </facets> + </outputmode> + <outputmode name="discharge_longitudinal_section_export" description="output.discharge_longitudinal_section_export" mime-type="text/plain" type="export"> + <facets> + <facet name="csv" description="facet.discharge_longitudinal_section_export.csv" /> + <facet name="wst" description="facet.discharge_longitudinal_section_export.wst" /> + </facets> + </outputmode> + <outputmode name="discharge_longitudinal_section_report" description="output.discharge_longitudinal_section_report" mime-type="text/xml" type="report"> + <facets> + <facet name="report" description="facet.discharge_longitudinal_section_export.report"/> + </facets> + </outputmode> + </outputmodes> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.waterlevel"/> + <to state="state.winfo.uesk.dgm"/> + </transition> + + <state id="state.winfo.uesk.wsp" description="state.winfo.uesk.wsp" state="de.intevation.flys.artifacts.states.WaterlevelSelectState"> + <data name="wsp" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.wsp"/> + <to state="state.winfo.uesk.dgm"/> + </transition> + + <state id="state.winfo.uesk.dgm" description="state.winfo.uesk.dgm" state="de.intevation.flys.artifacts.states.DGMSelect"> + <data name="dgm" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.dgm"/> + <to state="state.winfo.uesk.profiles"/> + </transition> + + <state id="state.winfo.uesk.profiles" description="state.winfo.uesk.profiles" state="de.intevation.flys.artifacts.states.ProfileDistanceSelect"> + <data name="profile_distance" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.profiles"/> + <to state="state.winfo.uesk.floodplain"/> + </transition> + + <state id="state.winfo.uesk.floodplain" description="state.winfo.uesk.floodplain.description" state="de.intevation.flys.artifacts.states.FloodplainChoice"> + <data name="use_floodplain" type="Boolean" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.floodplain"/> + <to state="state.winfo.uesk.differences"/> + </transition> + + <state id="state.winfo.uesk.differences" description="state.winfo.uesk.differences" state="de.intevation.flys.artifacts.states.WaterlevelGroundDifferences"> + <data name="diff_from" type="Double" /> + <data name="diff_to" type="Double" /> + <data name="diff_diff" type="Double" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.differences"/> + <to state="state.winfo.uesk.scenario"/> + </transition> + + <state id="state.winfo.uesk.scenario" description="state.winfo.uesk.scenario" state="de.intevation.flys.artifacts.states.ScenarioSelect"> + <data name="scenario" type="String" /> + <data name="uesk.barriers" type="String" /> + </state> + + <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition"> + <from state="state.winfo.uesk.scenario"/> + <to state="state.winfo.uesk.uesk"/> + </transition> + + <state id="state.winfo.uesk.uesk" description="state.winfo.uesk.uesk" state="de.intevation.flys.artifacts.states.FloodMapState"> + <outputmodes> + <outputmode name="floodmap" description="output.uesk.map.description" type="map"> + <facets> + <facet name="floodmap.wsplgen"/> + <facet name="floodmap.barriers"/> + <facet name="floodmap.riveraxis"/> + <facet name="floodmap.wmsbackground"/> + <facet name="floodmap.kms"/> + </facets> + </outputmode> + <outputmode name="wsplgen_report" description="output.wsplgen_report" mime-type="text/xml" type="report"> + <facets> + <facet name="report" description="facet.wsplgen_export.report"/> + </facets> + </outputmode> + </outputmodes> + </state> + + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/artifacts/wmsbackground.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<artifact name="wmsbackground"> + <states> + <state id="state.wmsbackground.layer" + description="state.wmsbackground.layer.description" + state="de.intevation.flys.artifacts.states.WMSBackgroundState"> + <outputmodes> + <outputmode name="floodmap" description="output.uesk.map.description" type="map"> + <facets> + <facet name="floodmap.wmsbackground"/> + </facets> + </outputmode> + </outputmodes> + </state> + </states> +</artifact>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/cache.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ehcache> + + <diskStore path="java.io.tmpdir"/> + + <defaultCache + maxElementsInMemory="1000" + eternal="false" + timeToIdleSeconds="120" + timeToLiveSeconds="3600" + overflowToDisk="true" + maxElementsOnDisk="100000" + diskPersistent="false" + diskExpiryThreadIntervalSeconds="120" + memoryStoreEvictionPolicy="LRU" + /> + + <!-- This one is used for the WST value tables --> + + <cache name="wst-value-table" + maxElementsInMemory="20" + maxElementsOnDisk="100" + eternal="false" + timeToIdleSeconds="360" + overflowToDisk="true" + timeToLiveSeconds="14400" + diskPersistent="true" + memoryStoreEvictionPolicy="LRU" + /> + + <!-- This one is used to cache the distance infos per river as Documents --> + <cache name="service-distanceinfo" + maxElementsInMemory="20" + eternal="false" + timeToIdleSeconds="360" + timeToLiveSeconds="86400" + memoryStoreEvictionPolicy="LFU" + /> + + <!-- This one is used to cache the distance infos per river as Lists --> + <cache name="annotations" + maxElementsInMemory="2000" + eternal="false" + timeToIdleSeconds="360" + timeToLiveSeconds="86400" + memoryStoreEvictionPolicy="LFU" + /> + + <!-- This one is used to cache the computed values.--> + <cache name="computed.values" + maxElementsInMemory="200" + eternal="false" + timeToLiveSeconds="172800" + overflowToDisk="true" + diskPersistent="true" + memoryStoreEvictionPolicy="LRU" + /> + + <!-- This one is used to cache the non-computed wst-values.--> + <cache name="wst-value-table-static" + maxElementsInMemory="200" + eternal="false" + timeToLiveSeconds="172800" + overflowToDisk="true" + diskPersistent="true" + memoryStoreEvictionPolicy="LRU" + /> + + <!-- This one is used for the SQL statements used by the static datacage --> + <cache name="datacage.db" + maxElementsInMemory="2000" + eternal="false" + timeToLiveSeconds="7200" + memoryStoreEvictionPolicy="LFU" + /> + + <!-- This one is used for the cross section next neighbor lookup --> + <cache name="cross-section-kms" + maxElementsInMemory="50" + eternal="false" + timeToLiveSeconds="7200" + memoryStoreEvictionPolicy="LRU" + /> +</ehcache>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/conf.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,218 @@ +<artifact-database> + <export-secret>YOUR_SECRET</export-secret> + <factories> + <context-factory>de.intevation.flys.artifacts.context.FLYSContextFactory</context-factory> + + <collection-factory + name="DefaultArtifactCollectionFactory" + description="The default artifact collection factory" + ttl="21600000" + artifact-collection="de.intevation.flys.collections.FLYSArtifactCollection">de.intevation.artifactdatabase.DefaultArtifactCollectionFactory</collection-factory> + + <artifact-factories> + <!-- All Artifactfactories which are available in this Database. --> + <artifact-factory name="winfo" description="Factory to create an artifact to be used in WINFO" + ttl="3600000" + artifact="de.intevation.flys.artifacts.WINFOArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="riveraxis" description="Factory to create an artifact to be used in WINFO" + ttl="3600000" + artifact="de.intevation.flys.artifacts.RiverAxisArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="wmskmfactory" description="Factory to create an artifact that generates WMS facets for KMs." + ttl="3600000" + artifact="de.intevation.flys.artifacts.WMSKmArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="wmsbackground" description="Factory to create an artifact to be used in WINFO" + ttl="3600000" + artifact="de.intevation.flys.artifacts.WMSBackgroundArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="annotations" description="Factory to create an artifact to access Annotations for Points at rivers" + ttl="3600000" + artifact="de.intevation.flys.artifacts.AnnotationArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="waterlevel" description="Factory to create an artifact to access waterlevel data" + ttl="3600000" + artifact="de.intevation.flys.artifacts.WaterlevelArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="mainvalue" description="Factory to create an artifact to access Main Values for discharge curve diagrams" + ttl="3600000" + artifact="de.intevation.flys.artifacts.MainValuesArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + <artifact-factory name="staticwkms" description="Factory to create an artifact to access 'other' WKms data" + ttl="3600000" + artifact="de.intevation.flys.artifacts.StaticWKmsArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory> + </artifact-factories> + + <user-factory name="default" description="Factory to create new users">de.intevation.artifactdatabase.DefaultUserFactory</user-factory> + + <service-factories> + <service-factory + name="rivers" + service="de.intevation.flys.artifacts.services.RiverService" + description="This service returns a list of provided rivers by the artifact server.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="distanceinfo" + service="de.intevation.flys.artifacts.services.DistanceInfoService" + description="Returns a list of distances supported by a specific river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="mainvalues" + service="de.intevation.flys.artifacts.services.MainValuesService" + description="Returns the main values of a river's gauge based on a start and end point of the river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="metadata" + service="de.intevation.flys.artifacts.services.MetaDataService" + description="The service provides some introspection into the database content.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="mapinfo" + service="de.intevation.flys.artifacts.services.MapInfoService" + description="The service provides some basic information to create a WMS for a specific river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + <service-factory + name="cross-section-km" + service="de.intevation.flys.artifacts.services.CrossSectionKMService" + description="The service provides the N next neighbored kms and ids of cross section lines for given cross section id, km and N.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory> + </service-factories> + + </factories> + + <lifetime-listeners> + <listener>de.intevation.flys.artifacts.datacage.Datacage</listener> + </lifetime-listeners> + + <backend-listeners> + <listener>de.intevation.flys.artifacts.datacage.DatacageBackendListener</listener> + </backend-listeners> + + <callcontext-listener + name="SessionCallContextListener" + description="A CallContext.Listener to open and close Hibernatesessions">de.intevation.flys.artifacts.context.SessionCallContextListener</callcontext-listener> + + <artifacts> + <artifact name="winfo" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/winfo.xml" /> + <artifact name="waterlevel" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/waterlevel.xml" /> + <artifact name="riveraxis" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/riveraxis.xml" /> + <artifact name="annotation" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/annotation.xml" /> + <artifact name="wmsbackground" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/wmsbackground.xml" /> + </artifacts> + + + <hooks> + <hook + class="de.intevation.flys.artifacts.CollectionMonitor" + applies="post-feed,post-advance" + xmlns:xlink="http://www.w3.org/1999/xlink" + xlink:href="${artifacts.config.dir}/output-defaults.xml"> + </hook> + </hooks> + + <output-generators> + <output-generator name="discharge_curve">de.intevation.flys.exports.DischargeCurveGenerator</output-generator> + <output-generator name="discharge_curve_chartinfo">de.intevation.flys.exports.DischargeCurveInfoGenerator</output-generator> + <output-generator name="cross_section">de.intevation.flys.exports.CrossSectionGenerator</output-generator> + <output-generator name="cross_section_chartinfo">de.intevation.flys.exports.CrossSectionInfoGenerator</output-generator> + <output-generator name="computed_discharge_curve">de.intevation.flys.exports.ComputedDischargeCurveGenerator</output-generator> + <output-generator name="computed_discharge_curve_chartinfo">de.intevation.flys.exports.ComputedDischargeCurveInfoGenerator</output-generator> + <output-generator name="longitudinal_section">de.intevation.flys.exports.LongitudinalSectionGenerator</output-generator> + <output-generator name="longitudinal_section_chartinfo">de.intevation.flys.exports.LongitudinalSectionInfoGenerator</output-generator> + <output-generator name="duration_curve">de.intevation.flys.exports.DurationCurveGenerator</output-generator> + <output-generator name="duration_curve_chartinfo">de.intevation.flys.exports.DurationCurveInfoGenerator</output-generator> + <output-generator name="discharge_longitudinal_section">de.intevation.flys.exports.DischargeLongitudinalSectionGenerator</output-generator> + <output-generator name="discharge_longitudinal_section_chartinfo">de.intevation.flys.exports.DischargeLongitudinalSectionInfoGenerator</output-generator> + <output-generator name="waterlevel_export">de.intevation.flys.exports.WaterlevelExporter</output-generator> + <output-generator name="durationcurve_export">de.intevation.flys.exports.DurationCurveExporter</output-generator> + <output-generator name="computed_dischargecurve_export">de.intevation.flys.exports.ComputedDischargeCurveExporter</output-generator> + <output-generator name="discharge_longitudinal_section_export">de.intevation.flys.exports.DischargeLongitudinalSectionExporter</output-generator> + <output-generator name="w_differences">de.intevation.flys.exports.WDifferencesCurveGenerator</output-generator> + <output-generator name="w_differences_chartinfo">de.intevation.flys.exports.WDifferencesCurveInfoGenerator</output-generator> + <output-generator name="w_differences_export">de.intevation.flys.exports.WDifferencesExporter</output-generator> + <output-generator name="floodmap">de.intevation.flys.exports.MapGenerator</output-generator> + <!-- Error report generators. --> + <output-generator name="discharge_longitudinal_section_report">de.intevation.flys.exports.ReportGenerator</output-generator> + <output-generator name="waterlevel_report">de.intevation.flys.exports.ReportGenerator</output-generator> + <output-generator name="computed_dischargecurve_report">de.intevation.flys.exports.ReportGenerator</output-generator> + <output-generator name="durationcurve_report">de.intevation.flys.exports.ReportGenerator</output-generator> + <output-generator name="wsplgen_report">de.intevation.flys.exports.ReportGenerator</output-generator> + <!-- AT exporter. --> + <output-generator name="computed_dischargecurve_at_export">de.intevation.flys.exports.ATExporter</output-generator> + </output-generators> + + <!-- Path to the template file of the meta data. --> + <metadata> + <template>${artifacts.config.dir}/meta-data.xml</template> + </metadata> + + <!-- The floodmap configuration for each supported river. Each element + requires a srid, wms and background-wms. Those information are used for + creating a Map view.--> + <floodmap> + <shapefile-path value="${artifacts.config.dir}/../shapefiles"/> + <mapserver> + <server path="http://flys-devel.intevation.de/cgi-bin/"/> + <mapfile path="${artifacts.config.dir}/../flys.map"/> + <templates path="${artifacts.config.dir}/mapserver/"/> + <map-template path="mapfile.vm"/> + </mapserver> + + <velocity> + <logfile path="${artifacts.config.dir}/../velocity_log.log"/> + </velocity> + + <river name="Saar"> + <srid value="31466"/> + <river-wms url="http://flys-devel.intevation.de/cgi-bin/saar-wms"/> + <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/> + </river> + <river name="Mosel"> + <srid value="31466"/> + <river-wms url="http://flys-devel.intevation.de/cgi-bin/mosel-wms"/> + <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/> + </river> + <river name="Elbe"> + <srid value="31466"/> + <river-wms url="http://flys-devel.intevation.de/cgi-bin/elbe-wms"/> + <background-wms url="http://vmap0.tiles.osgeo.org/wms/vmap0" layers="basic"/> + </river> + </floodmap> + + <rest-server> + <!-- The port which the ArtifactDatabase (ArtifactServer) will bind to. --> + <port>8181</port> + <listen>localhost</listen> + </rest-server> + <!-- Garbage collection of outdated artifacts. --> + <cleaner> + <sleep-time>60000</sleep-time> + </cleaner> + + <cache> + <config-file>${artifacts.config.dir}/cache.xml</config-file> + </cache> + + <!-- This is the default configuration of the datacage db: + <datacage> + <user/> + <password/> + <driver>org.h2.Driver</driver> + <url>jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'</url> + </datacage> + --> + + <database> + <!-- This Section configures the Settings for connecting to the + Artifact-Database instance. e.g. SQLite --> + <user>SA</user> + <password></password> + <!-- For use with a postgresql database use the appropriate driver--> + <!--driver>org.postgresql.Driver</driver--> + <url>jdbc:h2:${artifacts.config.dir}/../artifactdb/artifacts.db</url> + </database> + <!-- This is the default backend db configuration. --> + <!-- + <backend-database> + <user>flys</user> + <password>flys</password> + <dialect>org.hibernate.dialect.PostgreSQLDialect</dialect> + <driver>org.postgresql.Driver</driver> + <url>jdbc:postgresql://localhost:5432/flys</url> + </backend-database> + --> + + <flys> + <themes> + <configuration>${artifacts.config.dir}/themes.xml</configuration> + </themes> + </flys> +</artifact-database>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/datacage.sql Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,104 @@ +BEGIN; + +CREATE SEQUENCE USERS_ID_SEQ; + +CREATE TABLE users ( + id INT PRIMARY KEY NOT NULL, + gid UUID NOT NULL UNIQUE +); + +CREATE SEQUENCE COLLECTIONS_ID_SEQ; + +CREATE TABLE collections ( + id INT PRIMARY KEY NOT NULL, + gid UUID NOT NULL UNIQUE, + user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + name VARCHAR(256) NOT NULL, + creation TIMESTAMP NOT NULL +); + +CREATE SEQUENCE ARTIFACTS_ID_SEQ; + +CREATE TABLE artifacts ( + id INT PRIMARY KEY NOT NULL, + gid UUID NOT NULL UNIQUE, + state VARCHAR(256) NOT NULL, + creation TIMESTAMP NOT NULL +); + +CREATE SEQUENCE COLLECTION_ITEMS_ID_SEQ; + +CREATE TABLE collection_items ( + id INT PRIMARY KEY NOT NULL, + collection_id INT NOT NULL REFERENCES collections(id) ON DELETE CASCADE, + artifact_id INT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE +); + +CREATE SEQUENCE ARTIFACT_DATA_ID_SEQ; + +CREATE TABLE artifact_data ( + id INT PRIMARY KEY NOT NULL, + artifact_id INT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE, + kind VARCHAR(256) NOT NULL, + k VARCHAR(256) NOT NULL, + v VARCHAR(256), -- Maybe too short + UNIQUE (artifact_id, k) +); + +CREATE SEQUENCE OUTS_ID_SEQ; + +CREATE TABLE outs ( + id INT PRIMARY KEY NOT NULL, + artifact_id INT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE, + name VARCHAR(256) NOT NULL, + description VARCHAR(256), + out_type VARCHAR(256) +); + +CREATE SEQUENCE FACETS_ID_SEQ; + +CREATE TABLE facets ( + id INT PRIMARY KEY NOT NULL, + out_id INT NOT NULL REFERENCES outs(id) ON DELETE CASCADE, + name VARCHAR(256) NOT NULL, + num INT NOT NULL, + state VARCHAR(256) NOT NULL, + description VARCHAR(256), + UNIQUE (out_id, num, name) +); + +CREATE VIEW master_artifacts AS + SELECT a2.id AS id, + a2.gid AS gid, + a2.state AS state, + a2.creation AS creation, + ci2.collection_id AS collection_id + FROM collection_items ci2 + JOIN artifacts a2 + ON ci2.artifact_id = a2.id + JOIN (SELECT ci.collection_id AS c_id, + MIN(a.creation) AS oldest_a + FROM collection_items ci + JOIN artifacts a + ON ci.artifact_id = a.id + GROUP BY ci.collection_id) o + ON o.c_id = ci2.collection_id + WHERE a2.creation = o.oldest_a; + +-- DROP VIEW master_artifacts; +-- DROP SEQUENCE USERS_ID_SEQ; +-- DROP SEQUENCE COLLECTIONS_ID_SEQ; +-- DROP SEQUENCE ARTIFACTS_ID_SEQ; +-- DROP SEQUENCE COLLECTION_ITEMS_ID_SEQ; +-- DROP SEQUENCE ARTIFACT_DATA_ID_SEQ; +-- DROP SEQUENCE OUTS_ID_SEQ; +-- DROP SEQUENCE FACETS_ID_SEQ; +-- DROP TABLE facets; +-- DROP TABLE outs; +-- DROP TABLE artifact_data; +-- DROP TABLE collection_items; +-- DROP TABLE collections; +-- DROP TABLE artifacts; +-- DROP TABLE users; + +COMMIT;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/barrier_lines_class.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,32 @@ +CLASS + NAME "Damm" + EXPRESSION ("[TYP]"="Damm") + STYLE + SIZE 5 + OUTLINECOLOR "#008000" + END +END +CLASS + NAME "Rohr 1" + EXPRESSION ("[TYP]"="Rohr 1") + STYLE + SIZE 5 + OUTLINECOLOR "#800080" + END +END +CLASS + NAME "Rohr 2" + EXPRESSION ("[TYP]"="Rohr 2") + STYLE + SIZE 5 + OUTLINECOLOR "#808080" + END +END +CLASS + NAME "Graben" + EXPRESSION ("[TYP]"="Graben") + STYLE + SIZE 5 + OUTLINECOLOR "#800000" + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/barrier_polygons_class.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,7 @@ +CLASS + NAME "POLYGON_BARRIERS" + STYLE + SIZE 5 + OUTLINECOLOR "#FF8000" + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/db_layer.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,37 @@ +LAYER + NAME "$LAYER.getName()" + TYPE $LAYER.getType() + + INCLUDE "$CONFIGDIR/mapserver/dbconnection.include" + DATA "$LAYER.getData()" + FILTER '$LAYER.getFilter()' + EXTENT $LAYER.getExtent() + + STATUS ON + TEMPLATE map.html + TOLERANCE 10 + DUMP TRUE + #if( $LAYER.getGroup() ) + GROUP "$LAYER.getGroup()" + #end + + METADATA + "wms_title" "$LAYER.getTitle()" + "gml_include_items" "all" + #if ( $LAYER.getGroupTitle() ) + "wms_group_title" "$LAYER.getGroupTitle()" + #end + END + + #if ( $LAYER.getStyle() ) + $LAYER.getStyle() + #else + CLASS + NAME "" + STYLE + SIZE 5 + OUTLINECOLOR "#000000" + END + END + #end +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/dbconnection.include Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,2 @@ +CONNECTIONTYPE postgis +CONNECTION "dbname='flys3' host='czech-republic.atlas.intevation.de' port=5432 user='flys' password='flys' sslmode=disable"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/fontset.txt Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1 @@ +FreeSans /usr/share/fonts/truetype/freefont/FreeSans.ttf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/layer.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,34 @@ +LAYER + NAME "$LAYER.getName()" + TYPE $LAYER.getType() + DATA "$LAYER.getDirectory()/$LAYER.getData()" + STATUS ON + TEMPLATE map.html + TOLERANCE 10 + DUMP TRUE + #if( $LAYER.getGroup() ) + GROUP "$LAYER.getGroup()" + #end + + METADATA + "wms_title" "$LAYER.getTitle()" + "gml_include_items" "all" + #if ( $LAYER.getGroupTitle() ) + "wms_group_title" "$LAYER.getGroupTitle()" + #end + END + + #if ( !$LAYER.getStyle() ) + #if ( $LAYER.getGroupTitle() ) + #if ( $LAYER.getType() == "POLYGON" ) + #include("barrier_polygons_class.vm") + #else + #include("barrier_lines_class.vm") + #end + #else + #include("wsplgen_class.vm") + #end + #else + $LAYER.getStyle() + #end +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/mapfile.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,60 @@ +MAP + NAME "FLYS-Map" + STATUS ON + SIZE 600 400 + MAXSIZE 4000 + EXTENT -90 -180 90 180 + UNITS DD + SHAPEPATH "$SHAPEFILEPATH" + FONTSET "$CONFIGDIR/mapserver/fontset.txt" + SYMBOLSET "$CONFIGDIR/mapserver/symbols.sym" + IMAGECOLOR 255 255 255 + PROJECTION + "init=epsg:31466" + END + + DEBUG 5 + CONFIG "MS_ERRORFILE" "/tmp/flys-user-wms.log" + + WEB + METADATA + "wms_title" "FLYS Web Map Service" + "wms_onlineresource" "$MAPSERVERURL" + "wms_accessconstraints" "none" + "wms_fees" "none" + "wms_addresstype" "postal" + "wms_address" "Any Street" + "wms_city" "Any City" + "wms_stateorprovince" "Any state" + "wms_postcode" "My Postalcode" + "wms_country" "Any Country" + "wms_contactperson" "Any Person" + "wms_contactorganization" "Any Orga" + "wms_contactelectronicmailaddress" "any-email@example.com" + "wms_contactvoicetelephone" "Any's telephone number" + "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467" + "wms_feature_info_mime_type" "text/html" + "ows_enable_request" "*" + END + END + + LEGEND + KEYSIZE 20 20 + STATUS ON + TRANSPARENT ON + + LABEL + COLOR 150 150 150 + OUTLINECOLOR 255 255 255 + TYPE truetype + FONT "FreeSans" + SIZE 12 + POSITION AUTO + END + END + + ## Don't change the following lines. + #foreach ($LAYER in $LAYERS) + include "$LAYER" + #end +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/shapefile_layer.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,34 @@ +LAYER + NAME "$LAYER.getName()" + TYPE $LAYER.getType() + DATA "$LAYER.getDirectory()/$LAYER.getData()" + STATUS ON + TEMPLATE map.html + TOLERANCE 10 + DUMP TRUE + #if( $LAYER.getGroup() ) + GROUP "$LAYER.getGroup()" + #end + + METADATA + "wms_title" "$LAYER.getTitle()" + "gml_include_items" "all" + #if ( $LAYER.getGroupTitle() ) + "wms_group_title" "$LAYER.getGroupTitle()" + #end + END + + #if ( !$LAYER.getStyle() ) + #if ( $LAYER.getGroupTitle() ) + #if ( $LAYER.getType() == "POLYGON" ) + #include("barrier_polygons_class.vm") + #else + #include("barrier_lines_class.vm") + #end + #else + #include("wsplgen_class.vm") + #end + #else + $LAYER.getStyle() + #end +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/symbols.sym Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,22 @@ +SYMBOLSET +SYMBOL + NAME 'point' + TYPE ELLIPSE + POINTS + 1 1 + END + FILLED TRUE +END +SYMBOL + NAME "square" + TYPE VECTOR + POINTS + 0 0 + 0 1 + 1 1 + 1 0 + 0 0 + END + FILLED TRUE +END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/mapserver/wsplgen_class.vm Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,44 @@ +CLASS + NAME "0.0 <= DIFF < 1" + EXPRESSION ([DIFF] < 1) + STYLE + SIZE 5 + COLOR "#B2C9D7" + END +END + +CLASS + NAME "1.0 <= DIFF < 2" + EXPRESSION ([DIFF] >= 1 AND [DIFF] < 2) + STYLE + SIZE 5 + COLOR "#6F93AA" + END +END + +CLASS + NAME "2.0 <= DIFF < 3" + EXPRESSION ([DIFF] >= 2 AND [DIFF] < 3) + STYLE + SIZE 5 + COLOR "#426F8B" + END +END + +CLASS + NAME "3.0 <= DIFF < 4" + EXPRESSION ([DIFF] >= 3 AND [DIFF] < 4) + STYLE + SIZE 5 + COLOR "#214F6C" + END +END + +CLASS + NAME "Sonstiges" + EXPRESSION ([DIFF] >= 4) + STYLE + SIZE 5 + COLOR "#021B2A" + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/meta-data.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,701 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage"> +<datacage> + <dc:macro name="load-system"> + <dc:context connection="system"> + <dc:statement> + SELECT id AS river_id, name as river_name FROM rivers + WHERE lower(name) LIKE lower(${river}) + </dc:statement> + <dc:elements> + <dc:comment> + Base-data macros (mostly data imported from wst-files) + </dc:comment> + <dc:macro name="basedata_0"> + <dc:comment comment=" BASEDATA ---------------------------"/> + <basedata> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 0 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <basedata> + <dc:attribute name="name" value="${prot_description}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name, + position AS prot_rel_pos + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="ids" value="base_data-wstv-${prot_rel_pos}-${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </basedata> + </dc:elements> + </dc:context> + </basedata> + </dc:macro> + <dc:macro name="basedata_1_additionals"> + <dc:comment comment=".ZUS -------------------------------"/> + <addtionals> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 1 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <additional> + <dc:attribute name="name" value="${prot_description}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name, + position AS prot_rel_pos + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="ids" value="additionals-wstv-${prot_rel_pos}-${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </additional> + </dc:elements> + </dc:context> + </addtionals> + </dc:macro> + <river> + <dc:attribute name="name" value="${river_name}"/> + <dc:if test="dc:contains($artifact-outs, 'w_differences')"> + <annotation> + <dc:attribute name="factory" value="annotations"/> + <dc:attribute name="ids" value="${river_id}"/> + </annotation> + </dc:if> + <dc:if test="dc:contains($artifact-outs, 'longitudinal_section') or (dc:contains($artifact-outs, 'w_differences'))"> + <dc:choose> + <dc:when test="dc:contains($parameters, 'recommended')"> + </dc:when> + <dc:otherwise> + <dc:call-macro name="basedata_0"/> + <dc:call-macro name="basedata_1_additionals"/> + <dc:comment comment=" FIXATIONS ---------------------------"/> + <fixations> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 2 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <fixation> + <dc:attribute name="name" value="${prot_description}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name, + position AS prot_rel_pos + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="ids" value="fixations-wstv-${prot_rel_pos}-${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </fixation> + </dc:elements> + </dc:context> + </fixations> + <dc:comment comment=" HOEHENMARKEN ---------------------------"/> + <heightmarks> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 4 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <heightmark> + <dc:attribute name="name" value="${prot_description}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name, + position AS prot_rel_pos + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="ids" value="heightmarks_points-wstv-${prot_rel_pos}-${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </heightmark> + </dc:elements> + </dc:context> + </heightmarks> + </dc:otherwise> + </dc:choose> + </dc:if> + + <dc:if test="dc:contains($artifact-outs, 'waterlevels')"> + <flood-protections> + <dc:attribute name="id" value="flood-protections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 5 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <flood-protection> + <dc:attribute name="name" value="${prot_description}"/> + <dc:attribute name="db-id" value="${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name, + position AS prot_rel_pos + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="ids" value="flood_protection-wstv-${prot_rel_pos}-${prot_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </columns> + </flood-protection> + </dc:elements> + </dc:context> + </flood-protections> + <extra-longitudinal-sections> + <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS els_id, + description AS els_description + FROM wsts WHERE kind = 1 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <extra-longitudinal-section> + <dc:attribute name="name" value="${els_description}"/> + <dc:attribute name="db-id" value="${els_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS els_column_id, + name AS els_column_name, + position AS els_rel_pos + FROM wst_columns WHERE wst_id = ${els_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${els_column_name}"/> + <dc:attribute name="ids" value="flood_protection-wstv-${els_rel_pos}-${els_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </columns> + </extra-longitudinal-section> + </dc:elements> + </dc:context> + </extra-longitudinal-sections> + </dc:if> + <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve') and not (dc:contains($parameters, 'recommended'))"> + <discharge-table-nn> + <discharge-table-gauge> + <dc:context> + <dc:statement> + SELECT id AS gauge_id, + name AS gauge_name + FROM gauges WHERE river_id = ${river_id} + </dc:statement> + <dc:elements> + <gauge> + <dc:attribute name="name" value="${gauge_name}"/> + <dc:attribute name="db-id" value="${gauge_id}"/> + <dc:context> + <dc:statement> + SELECT description AS gauge_desc, + d.id AS discharge_id, + ti.start_time AS g_start, + ti.stop_time AS g_stop + FROM discharge_tables d JOIN time_intervals ti + ON d.time_interval_id = ti.id + WHERE d.gauge_id = ${gauge_id} AND d.kind = 1 + </dc:statement> + <dc:elements> + <historical> + <dc:attribute name="name" value="${gauge_desc}"/> + <dc:attribute name="from" value="${g_start}"/> + <dc:attribute name="to" value="${g_stop}"/> + <dc:attribute name="db-id" value="${discharge_id}"/></historical> + </dc:elements> + </dc:context> + </gauge> + </dc:elements> + </dc:context> + </discharge-table-gauge> + <fixations> + <dc:attribute name="id" value="fixations-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS fix_id, + description AS fix_description + FROM wsts WHERE kind = 2 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <fixation> + <dc:attribute name="name" value="${fix_description}"/> + <dc:attribute name="db-id" value="${fix_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS fix_column_id, + name AS fix_column_name + FROM wst_columns WHERE wst_id = ${fix_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${fix_column_name}"/> + <dc:attribute name="db-id" value="${fix_column_id}"/> + </column> + </dc:elements> + </dc:context> + </columns> + </fixation> + </dc:elements> + </dc:context> + </fixations> + <flood-protections> + <dc:attribute name="id" value="flood-protections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS prot_id, + description AS prot_description + FROM wsts WHERE kind = 5 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <flood-protection> + <dc:attribute name="name" value="${prot_description}"/> + <dc:attribute name="db-id" value="${prot_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS prot_column_id, + name AS prot_column_name + FROM wst_columns WHERE wst_id = ${prot_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${prot_column_name}"/> + <dc:attribute name="db-id" value="${prot_column_id}"/> + <dc:attribute name="factory" value="staticwkms"/> + </column> + </dc:elements> + </dc:context> + </columns> + </flood-protection> + </dc:elements> + </dc:context> + </flood-protections> + <flood-water-marks> + <dc:attribute name="id" value="flood-water-marks-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS fw_id, + description AS fw_description + FROM wsts WHERE kind = 4 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <flood-water-mark> + <dc:attribute name="name" value="${fw_description}"/> + <dc:attribute name="db-id" value="${fw_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS fw_column_id, + name AS fw_column_name + FROM wst_columns WHERE wst_id = ${fw_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${fw_column_name}"/> + <dc:attribute name="db-id" value="${fw_column_id}"/></column> + </dc:elements> + </dc:context> + </columns> + </flood-water-mark> + </dc:elements> + </dc:context> + </flood-water-marks> + <water-levels> + <dc:attribute name="id" value="water-levels-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS wl_id, + description AS wl_description + FROM wsts WHERE kind = 0 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <water-level> + <dc:attribute name="name" value="${wl_description}"/> + <dc:attribute name="db-id" value="${wl_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS wl_column_id, + name AS wl_column_name + FROM wst_columns WHERE wst_id = ${wl_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${wl_column_name}"/> + <dc:attribute name="db-id" value="${wl_column_id}"/> + </column> + </dc:elements> + </dc:context> + </columns> + </water-level> + </dc:elements> + </dc:context> + </water-levels> + <extra-longitudinal-sections> + <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/> + <dc:context> + <dc:statement> + SELECT id AS els_id, + description AS els_description + FROM wsts WHERE kind = 1 AND river_id = ${river_id} + </dc:statement> + <dc:elements> + <extra-longitudinal-section> + <dc:attribute name="name" value="${els_description}"/> + <dc:attribute name="db-id" value="${els_id}"/> + <columns> + <dc:context> + <dc:statement> + SELECT id AS els_column_id, + name AS els_column_name + FROM wst_columns WHERE wst_id = ${els_id} + ORDER by position + </dc:statement> + <dc:elements> + <column> + <dc:attribute name="name" value="${els_column_name}"/> + <dc:attribute name="db-id" value="${els_column_id}"/> + </column> + </dc:elements> + </dc:context> + </columns> + </extra-longitudinal-section> + </dc:elements> + </dc:context> + </extra-longitudinal-sections> + </discharge-table-nn> + </dc:if> + <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve')"> + <computed-discharge-curve> + <!--mainvalue> + <dc:attribute name="factory" value="mainvalue"/> + <dc:attribute name="db-ids" value="${river_id}"/> + </mainvalue--> + </computed-discharge-curve> + </dc:if> + <dc:if test="dc:contains($artifact-outs, 'longitudinal_section')"> + <longitudinal-section> + <dc:call-macro name="longitudinal_section-recommended"/> + </longitudinal-section> + <dc:macro name="longitudinal_section-recommended"> + <annotation> + <dc:attribute name="factory" value="annotations"/> + <dc:attribute name="ids" value="${river_id}"/> + </annotation> + </dc:macro> + </dc:if> + <dc:if test="dc:contains($artifact-outs, 'floodmap')"> + <floodmap> + <dc:choose> + <dc:when test="dc:contains($parameters, 'recommended')"> + <dc:call-macro name="flood-map-recommended"/> + </dc:when> + <dc:when test="dc:contains($parameters, 'dem')"> + <dc:call-macro name="flood-map-dem"/> + </dc:when> + <dc:otherwise> + <dc:call-macro name="flood-map-complete"/> + </dc:otherwise> + </dc:choose> + </floodmap> + <dc:macro name="flood-map-recommended"> + <dc:comment> + FIXME: Following two macros look identical to me. + </dc:comment> + <kilometrage> + <riveraxis> + <dc:attribute name="factory" value="riveraxis"/> + <dc:attribute name="ids" value="${river_id}"/> + </riveraxis> + </kilometrage> + <rastermap> + <background> + <dc:attribute name="factory" value="wmsbackground"/> + <dc:attribute name="ids" value="${river_id}"/> + </background> + </rastermap> + </dc:macro> + <dc:macro name="flood-map-dem"> + <dems> + <dc:context> + <dc:statement> + SELECT id AS dem_id, + lower AS dem_lower, + upper AS dem_upper + FROM dem WHERE river_id = ${river_id} + </dc:statement> + <dc:elements> + <dem> + <dc:attribute name="factory" value="demfactory"/> + <dc:attribute name="ids" value="${dem_id}"/> + <dc:attribute name="description" value="${dem_lower}-${dem_upper}"/> + </dem> + </dc:elements> + </dc:context> + </dems> + </dc:macro> + <dc:macro name="flood-map-km"> + <dc:context> + <dc:statement> + SELECT count(*) as km_exists + FROM river_axes_km WHERE river_id = ${river_id} + </dc:statement> + <dc:elements> + <dc:if test="$km_exists>0"> + <km> + <dc:attribute name="factory" value="wmskmfactory"/> + <dc:attribute name="ids" value="${river_id}"/> + </km> + </dc:if> + </dc:elements> + </dc:context> + </dc:macro> + <dc:macro name="flood-map-complete"> + <kilometrage> + <riveraxis> + <dc:attribute name="factory" value="riveraxis"/> + <dc:attribute name="ids" value="${river_id}"/> + </riveraxis> + <dc:call-macro name="flood-map-km"/> + </kilometrage> + <rastermap> + <background> + <dc:attribute name="factory" value="wmsbackground"/> + <dc:attribute name="ids" value="${river_id}"/> + </background> + </rastermap> + <dc:call-macro name="flood-map-dem"/> + </dc:macro> + </dc:if> + </river> + </dc:elements> + </dc:context> + </dc:macro> + + <dc:choose> + <dc:comment> + User specific part + ------------------ + </dc:comment> + <dc:when test="dc:contains($parameters, 'user-id')"> + <dc:comment> + Show diffferences if longitudinal sections are there. + ----------------------------------------------------- + </dc:comment> + <dc:choose> + <dc:when test="dc:contains($artifact-outs, 'longitudinal_section')"> + <dc:context> + <dc:statement> + SELECT a.gid as aid, f.id AS fid, f.name AS facet_name, f.num AS facet_num, f.description as facet_description + FROM outs as o, facets as f, artifacts as a + WHERE f.name = 'w_differences' and f.out_id = o.id and o.artifact_id = a.id; + </dc:statement> + <dc:elements> + <differences> + <dc:element name="${facet_name}"> + <dc:attribute name="description" value="${facet_description}"/> + <dc:attribute name="factory" value="winfo"/> + <dc:attribute name="artifact-id" value="${aid}"/> + <dc:attribute name="ids" value="${aid}"/> + <dc:attribute name="out" value="w_differences"/> + </dc:element> + </differences> + </dc:elements> + </dc:context> + </dc:when> + </dc:choose> + <old_calculations> + <dc:context connection="user"> + <dc:statement>SELECT u.id AS user_id, c.id AS collection_id, c.name as collection_name + FROM collections c JOIN users u ON c.user_id = u.id + WHERE u.gid = CAST(${user-id} AS uuid) + ORDER BY c.creation DESC + </dc:statement> + <dc:elements> + <dc:comment><!-- + <hello_collection><dc:attribute name="description" value="Collection: id=${collection_id} name='${collection_name}'"/></hello_collection> + --></dc:comment> + <dc:context> + <dc:statement>SELECT m.id AS a_id, m.state AS a_state, m.gid AS a_gid, m.creation AS a_creation + FROM master_artifacts m + WHERE m.collection_id = ${collection_id} AND m.gid <> CAST(${artifact-id} AS uuid) + AND EXISTS ( + SELECT id FROM artifact_data ad WHERE ad.artifact_id = m.id AND k = 'river' AND v = ${river}) + </dc:statement> + <dc:elements> + <dc:comment><!-- + <hello_artifact><dc:attribute name="description" value="Artifact UUID: ${a_gid}"/></hello_artifact> + --></dc:comment> + <dc:choose> + + + <dc:comment> + Import longitudinal sections only if the ${artifact-out} is defined as 'longitudinal_section' + --------------------------------------------------------------------------------------------- + </dc:comment> + <dc:when test="dc:contains($artifact-outs, 'longitudinal_section')"> + <dc:context> + <dc:statement> + SELECT id AS out_id + FROM outs + WHERE artifact_id = ${a_id} AND name = 'longitudinal_section' + </dc:statement> + <dc:elements> + <dc:context> + <dc:statement> + SELECT name AS facet_name, num as facet_num, description AS facet_description + FROM facets + WHERE out_id = ${out_id} + ORDER BY num ASC, name DESC + </dc:statement> + <longitudinal_section_columns> + <dc:attribute name="description" value="${river} ${a_creation}"/> + <dc:elements> + <dc:element name="${facet_name}"> + <dc:attribute name="description" value="${facet_description}"/> + <dc:attribute name="ids" value="${facet_num}"/> + <dc:attribute name="factory" value="winfo"/> + <dc:attribute name="artifact-id" value="${a_gid}"/> + <dc:attribute name="out" value="longitudinal_section"/> + </dc:element> + </dc:elements> + </longitudinal_section_columns> + </dc:context> + </dc:elements> + </dc:context> + </dc:when> + <dc:comment> + Import of longitudinal sections finished + </dc:comment> + + + <dc:comment> + Import Waterlevels only if the ${artifact-out} is defined as 'waterlevels' + -------------------------------------------------------------------------- + </dc:comment> + <dc:when test="dc:contains($artifact-outs, 'waterlevels')"> + <dc:context> + <dc:statement> + SELECT id AS out_id + FROM outs + WHERE artifact_id = ${a_id} AND name = 'longitudinal_section' + </dc:statement> + <dc:elements> + <dc:context> + <dc:statement> + SELECT name AS facet_name, num as facet_num, description AS facet_description + FROM facets + WHERE out_id = ${out_id} and name = 'longitudinal_section.w' + ORDER BY num ASC, name DESC + </dc:statement> + <waterlevels> + <dc:attribute name="description" value="${river} ${a_creation} ${collection_name}"/> + <dc:elements> + <dc:element name="${facet_name}"> + <dc:attribute name="description" value="${facet_description}"/> + <dc:attribute name="ids" value="${facet_num}"/> + <dc:attribute name="factory" value="winfo"/> + <dc:attribute name="artifact-id" value="${a_gid}"/> + <dc:attribute name="out" value="longitudinal_section"/> + </dc:element> + </dc:elements> + </waterlevels> + </dc:context> + </dc:elements> + </dc:context> + </dc:when> + <dc:comment> + Import of waterlevels finished + </dc:comment> + + </dc:choose> + </dc:elements> + </dc:context> + </dc:elements> + </dc:context> + </old_calculations> + + + <dc:comment> + Include System specific part when 'load-system' is in parameters. + ----------------------------------------------------------------- + </dc:comment> + <dc:choose> + <dc:when test="dc:contains($parameters,'load-system')"> + <dc:call-macro name="load-system"/> + </dc:when> + </dc:choose> + </dc:when> + + + <dc:comment> + Include System specific part only if no user ID is given. + --------------------------------------------------------- + </dc:comment> + <dc:otherwise> + <dc:call-macro name="load-system"/> + </dc:otherwise> + </dc:choose> +</datacage> +</dc:template>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/conf/themes.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,821 @@ +<themes> + + <!-- Concrete themes are following now! --> + <theme name="DischargeCurve"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/> + <field name="linesize" type="int" display="Liniendicke" default="2" hints="h"/> + </fields> + </theme> + + <!-- + Discharge Longitudinal Section + --> + <theme name="LongitudinalSectionW"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ1"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ2"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ5"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ10"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ20"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ25"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ50"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ100"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ200"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ500"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ1000"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQRZ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HSQ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MHQ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MNQ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MQ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_NQ"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQExtrem"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionPoints"> + <inherits> + <inherit from="LongitudinalSectionW"/> + </inherits> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="false"/> + <field name="showpoints" type="boolean" display="Punkte anzeigen" default="true"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ1_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ2_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ5_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ10_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ20_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ25_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ50_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ100_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ200_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ500_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQ1000_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQRZ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HSQ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MHQ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MNQ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_MQ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_NQ_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionW_HQExtrem_Points"> + <inherits><inherit from="LongitudinalSectionPoints"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <!-- + Longitudinal Section Q's + --> + + <theme name="LongitudinalSectionQ"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ1"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ2"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ5"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 153, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ10"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 204, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ20"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ25"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 51, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ50"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 153, 153"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ100"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 51"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ200"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ500"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQ1000"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQRZ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 0, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HSQ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="253, 153, 0"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_MHQ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="102, 255, 102"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_MNQ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 255, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_MQ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 51, 204"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_NQ"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="153, 204, 255"/> + </fields> + </theme> + + <theme name="LongitudinalSectionQ_HQExtrem"> + <inherits> + <inherit from="LongitudinalSectionQ"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + + <!-- + Computed Discharge Curves + --> + <theme name="ComputedDischargeCurve"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/> + <field name="linesize" type="int" display="Liniendicke" default="2" hints="h"/> + </fields> + </theme> + + <theme name="ComputedDischargeCurveQ"> + <inherits> + <inherit from="HiddenColorLines"/> + <inherit from="Text"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Farbe" default="200, 0, 15"/> + </fields> + </theme> + + <theme name="ComputedDischargeCurveW"> + <inherits> + <inherit from="HiddenColorLines"/> + <inherit from="Text"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Farbe" default="0, 215, 0"/> + </fields> + </theme> + + <!-- + Cross Sections + --> + <theme name="CrossSection"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0,0,0"/> + <field name="linesize" type="int" display="Liniendicke" default="1" hints="h"/> + </fields> + </theme> + + <theme name="CrossSectionWaterLine"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0,0,153"/> + <field name="linesize" type="int" display="Liniendicke" default="1" hints="h"/> + </fields> + </theme> + + + <!-- + Duration Curves + --> + <theme name="DurationCurveW"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0,51,204"/> + <field name="linesize" type="int" display="Liniendicke" default="2" hints="h"/> + </fields> + </theme> + + <theme name="DurationCurveQ"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0,204,0"/> + <field name="linesize" type="int" display="Liniendicke" default="2" hints="h"/> + </fields> + </theme> + + <!-- Differences --> + <theme name="Differences"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + + <!-- General --> + <theme name="WKms"> + <inherits><inherit from="HiddenColorLines"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + <theme name="WQKms"> + <inherits><inherit from="HiddenColorLines"/></inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + <!-- + Discharge Longitudinal Section + --> + <theme name="DischargeLongitudinalSectionW"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + + <theme name="DischargeLongitudinalSectionC"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0 , 0"/> + </fields> + </theme> + + <theme name="DischargeLongitudinalSectionQ"> + <inherits> + <inherit from="HiddenColorLines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/> + </fields> + </theme> + + + <!-- Annotations --> + <theme name="Annotations"> + <inherits> + <inherit from="HiddenColorLines"/> + <inherit from="Text"/> + <inherit from="AnnotationText"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + <!-- Height Marks --> + <theme name="heightmarks_points"> + <inherits> + <inherit from="Points"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 0"/> + </fields> + </theme> + + + <!-- Virtual themes are following now! --> + <theme name="Lines" type="virtual"> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="true"/> + <field name="linesize" type="int" display="Liniendicke" default="1"/> + <field name="linetype" type="Dash" display="Linienart" default="10"/> + </fields> + </theme> + + <theme name="Points" type="virtual"> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="false"/> + <field name="linesize" type="int" display="Liniendicke" default="1"/> + <field name="linetype" type="Dash" display="Linienart" default="10"/> + <field name="showpoints" type="boolean" display="Punkte anzeigen" default="true"/> + </fields> + </theme> + + <theme name="ColorLines" type="virtual"> + <inherits> + <inherit from="Lines"/> + </inherits> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="Color.BLACK"/> + </fields> + </theme> + + <theme name="HiddenColorLines" type="virtual"> + <inherits> + <inherit from="ColorLines"/> + </inherits> + <fields> + <field name="showlines" type="boolean" display="Linie anzeigen" default="true" hints="h"/> + <field name="linesize" type="int" display="Liniendicke" default="1" hints="h"/> + <field name="linetype" type="Dash" display="Linienart" default="10" hints="h"/> + <field name="showpoints" type="boolean" display="Datenpunkte anzeigen" default="false" hints="h"/> + </fields> + </theme> + + <theme name="Text" type="virtual"> + <fields> + <field name="font" type="Font" display="Schriftart" default="arial"/> + <field name="textcolor" type="Color" display="Schriftfarbe" default="0, 0, 0"/> + <field name="textsize" type="int" display="Schriftgröße" default="10"/> + <field name="textstyle" type="Style" display="Schriftstil" default="standard"/> + </fields> + </theme> + + <theme name="AnnotationText" type="virtual"> + <fields> + <field name="backgroundcolor" type="Color" display="Texthintergrund" default="255, 255, 255"/> + <field name="textorientation" type="boolean" display="Textausrichtung" default="false"/> + <field name="showbackground" type="boolean" display="Hintergrund anzeigen" default="false"/> + </fields> + </theme> + + + <!-- MAP relevant themes --> + <theme name="RiverAxis"> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 205"/> + <field name="linesize" type="int" display="Liniendicke" default="5" hints="h"/> + </fields> + </theme> + + <theme name="Kms"> + <fields> + <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0, 0"/> + <field name="linesize" type="int" display="Liniendicke" default="5" hints="h"/> + <field name="symbol" type="Symbol" display="Symbol" default="square"/> + </fields> + </theme> + + + <!-- + Mappings are following now. A mapping maps between a name of a facet + and a theme. + Always the first matching mapping is taken, so consider putting most + specific mappings on top of the list. + --> + <mappings> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ1)(\D.*)*" to="LongitudinalSectionW_HQ1_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ2)(\D.*)*" to="LongitudinalSectionW_HQ2_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ5)(\D.*)*" to="LongitudinalSectionW_HQ5_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ10)(\D.*)*" to="LongitudinalSectionW_HQ10_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ20)(\D.*)*" to="LongitudinalSectionW_HQ20_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ25)(\D.*)*" to="LongitudinalSectionW_HQ25_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ50)(\D.*)*" to="LongitudinalSectionW_HQ50_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ100)(\D.*)*" to="LongitudinalSectionW_HQ100_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ200)(\D.*)*" to="LongitudinalSectionW_HQ200_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ500)(\D.*)*" to="LongitudinalSectionW_HQ500_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQ1000)(\D.*)*" to="LongitudinalSectionW_HQ1000_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQExtrem)(\D.*)*" to="LongitudinalSectionW_HQExtrem_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HQRZ)(\D.*)*" to="LongitudinalSectionW_HQRZ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(HSQ)(\D.*)*" to="LongitudinalSectionW_HSQ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MHQ)(\D.*)*" to="LongitudinalSectionW_MHQ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MNQ)(\D.*)*" to="LongitudinalSectionW_MNQ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(MQ)(\D.*)*" to="LongitudinalSectionW_MQ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" pattern=".*(NQ)(\D.*)*" to="LongitudinalSectionW_NQ_Points"/> + <mapping from="longitudinal_section.w" masterAttr="ld_mode==location" to="LongitudinalSectionPoints"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ1)(\D.*)*" to="LongitudinalSectionW_HQ1"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ2)(\D.*)*" to="LongitudinalSectionW_HQ2"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ5)(\D.*)*" to="LongitudinalSectionW_HQ5"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ10)(\D.*)*" to="LongitudinalSectionW_HQ10"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ20)(\D.*)*" to="LongitudinalSectionW_HQ20"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ25)(\D.*)*" to="LongitudinalSectionW_HQ25"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ50)(\D.*)*" to="LongitudinalSectionW_HQ50"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ100)(\D.*)*" to="LongitudinalSectionW_HQ100"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ200)(\D.*)*" to="LongitudinalSectionW_HQ200"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ500)(\D.*)*" to="LongitudinalSectionW_HQ500"/> + <mapping from="longitudinal_section.w" pattern=".*(HQ1000)(\D.*)*" to="LongitudinalSectionW_HQ1000"/> + <mapping from="longitudinal_section.w" pattern=".*(HQExtrem)(\D.*)*" to="LongitudinalSectionW_HQExtrem"/> + <mapping from="longitudinal_section.w" pattern=".*(HQRZ)(\D.*)*" to="LongitudinalSectionW_HQRZ"/> + <mapping from="longitudinal_section.w" pattern=".*(HSQ)(\D.*)*" to="LongitudinalSectionW_HSQ"/> + <mapping from="longitudinal_section.w" pattern=".*(MHQ)(\D.*)*" to="LongitudinalSectionW_MHQ"/> + <mapping from="longitudinal_section.w" pattern=".*(MNQ)(\D.*)*" to="LongitudinalSectionW_MNQ"/> + <mapping from="longitudinal_section.w" pattern=".*(MQ)(\D.*)*" to="LongitudinalSectionW_MQ"/> + <mapping from="longitudinal_section.w" pattern=".*(NQ)(\D.*)*" to="LongitudinalSectionW_NQ"/> + <mapping from="longitudinal_section.w" to="LongitudinalSectionW"/> + + <mapping from="longitudinal_section.q" pattern="(HQ1)(\D.*)*" to="LongitudinalSectionQ_HQ1"/> + <mapping from="longitudinal_section.q" pattern="(HQ2)(\D.*)*" to="LongitudinalSectionQ_HQ2"/> + <mapping from="longitudinal_section.q" pattern="(HQ5)(\D.*)*" to="LongitudinalSectionQ_HQ5"/> + <mapping from="longitudinal_section.q" pattern="(HQ10)(\D.*)*" to="LongitudinalSectionQ_HQ10"/> + <mapping from="longitudinal_section.q" pattern="(HQ20)(\D.*)*" to="LongitudinalSectionQ_HQ20"/> + <mapping from="longitudinal_section.q" pattern="(HQ25)(\D.*)*" to="LongitudinalSectionQ_HQ25"/> + <mapping from="longitudinal_section.q" pattern="(HQ50)(\D.*)*" to="LongitudinalSectionQ_HQ50"/> + <mapping from="longitudinal_section.q" pattern="(HQ100)(\D.*)*" to="LongitudinalSectionQ_HQ100"/> + <mapping from="longitudinal_section.q" pattern="(HQ200)(\D.*)*" to="LongitudinalSectionQ_HQ200"/> + <mapping from="longitudinal_section.q" pattern="(HQ500)(\D.*)*" to="LongitudinalSectionQ_HQ500"/> + <mapping from="longitudinal_section.q" pattern="(HQ1000)(\D.*)*" to="LongitudinalSectionQ_HQ1000"/> + <mapping from="longitudinal_section.q" pattern="(HQExtrem)(\D.*)*" to="LongitudinalSectionQ_HQExtrem"/> + <mapping from="longitudinal_section.q" pattern="(HQRZ)(\D.*)*" to="LongitudinalSectionQ_HQRZ"/> + <mapping from="longitudinal_section.q" pattern="(HSQ)(\D.*)*" to="LongitudinalSectionQ_HSQ"/> + <mapping from="longitudinal_section.q" pattern="(MHQ)(\D.*)*" to="LongitudinalSectionQ_MHQ"/> + <mapping from="longitudinal_section.q" pattern="(MNQ)(\D.*)*" to="LongitudinalSectionQ_MNQ"/> + <mapping from="longitudinal_section.q" pattern="(MQ)(\D.*)*" to="LongitudinalSectionQ_MQ"/> + <mapping from="longitudinal_section.q" pattern="(NQ)(\D.*)*" to="LongitudinalSectionQ_NQ"/> + <mapping from="longitudinal_section.q" to="LongitudinalSectionQ"/> + + <mapping from="discharge_curve.curve" to="DischargeCurve"/> + <mapping from="cross_section" to="CrossSection"/> + <mapping from="cross_section_water_line" to="CrossSectionWaterLine"/> + <mapping from="computed_discharge_curve.q" to="ComputedDischargeCurve"/> + <mapping from="duration_curve.w" to="DurationCurveW"/> + <mapping from="duration_curve.q" to="DurationCurveQ"/> + <mapping from="discharge_longitudinal_section.w" to="DischargeLongitudinalSectionW"/> + <mapping from="discharge_longitudinal_section.c" to="DischargeLongitudinalSectionC"/> + <mapping from="discharge_longitudinal_section.q" to="DischargeLongitudinalSectionQ"/> + <mapping from="computed_discharge_curve.mainvalues.q" to="ComputedDischargeCurveQ"/> + <mapping from="computed_discharge_curve.mainvalues.w" to="ComputedDischargeCurveW"/> + <mapping from="longitudinal_section.annotations" to="Annotations"/> + <mapping from="w_differences" to="Differences"/> + <mapping from="floodmap.riveraxis" to="RiverAxis"/> + <mapping from="floodmap.kms" to="Kms"/> + + <mapping from="other.wkms" to="WKms"/> + <mapping from="other.wqkms" to="WQKms"/> + <mapping from="heightmarks_points" to="heightmarks_points"/> + </mappings> +</themes>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/dbconnection.include Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,2 @@ +CONNECTIONTYPE postgis +CONNECTION "dbname='flys3' host=127.0.0.1 port=5432 user='flys' password='flys' sslmode=disable"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/elbe-mapfile.map Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,73 @@ +MAP + NAME "Elbe" + STATUS ON + SIZE 600 400 + MAXSIZE 4000 + EXTENT 3590790.616790 5648985.748203 3868521.325854 5922665.254118 + UNITS DD + #FONTSET "fontset.txt" + IMAGECOLOR 255 255 255 + PROJECTION + "init=epsg:31466" + END + + + OUTPUTFORMAT + NAME agg + DRIVER AGG/PNG + IMAGEMODE RGB + END + + CONFIG "MS_ERRORFILE" "logs/flys-elbe-wms.log" + DEBUG 5 + + WEB + METADATA + "wms_title" "FLYS-3.0 WMS (ELBE)" + "wms_onlineresource" "http://czech-republic.atlas.intevation.de/cgi-bin/elbe-wms" + "wms_accessconstraints" "none" + "wms_fees" "none" + "wms_addresstype" "postal" + "wms_address" "Any Street" + "wms_city" "Any City" + "wms_stateorprovince" "Any state" + "wms_postcode" "My Postalcode" + "wms_country" "Any Country" + "wms_contactperson" "Any Person" + "wms_contactorganization" "Any Orga" + "wms_contactelectronicmailaddress" "any-email@example.com" + "wms_contactvoicetelephone" "Any's telephone number" + "wms_srs" "EPSG:31466 EPSG:4326" + "wms_feature_info_mime_type" "text/html" + "ows_enable_request" "*" + END + END + + LAYER + NAME riveraxis + EXTENT 3590790.616790 5648985.748203 3868521.325854 5922665.254118 + DEBUG 5 + + METADATA + "wms_title" "River Axis" + END + + TYPE LINE + STATUS ON + INCLUDE "dbconnection.include" + DATA 'geom FROM "river_axes" USING UNIQUE id USING srid=31466' + FILTER "river_id='6'" + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "riveraxis" + STYLE + SIZE 5 + COLOR "#000000" + END + END + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/fontset.txt Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,5 @@ +# LiberationsSans weist eine metrische Identitaet zu Arial auf und ist unter +# einer freien Lizenz (modifizierte GPL 2) verfuegbar. +# Quelle: http://de.wikipedia.org/wiki/Arial + +LiberationSans-Italic /usr/share/fonts/truetype/LiberationSans-Italic.ttf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/mosel-mapfile.map Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,301 @@ +MAP + NAME "Mosel" + STATUS ON + SIZE 600 400 + MAXSIZE 4000 + EXTENT 2457864.326387 5297459.306295 2634771.191263 5586961.449130 + UNITS DD + FONTSET "fontset.txt" + SYMBOLSET './symbols/symbols.sym' + IMAGECOLOR 255 255 255 + PROJECTION + "init=epsg:31466" + END + + + OUTPUTFORMAT + NAME agg + DRIVER AGG/PNG + IMAGEMODE RGB + END + + CONFIG "MS_ERRORFILE" "logs/flys-mosel-wms.log" + DEBUG 5 + + WEB + METADATA + "wms_title" "FLYS-3.0 WMS (SAAR)" + "wms_onlineresource" "http://czech-republic.atlas.intevation.de/cgi-bin/saar-wms" + "wms_accessconstraints" "none" + "wms_fees" "none" + "wms_addresstype" "postal" + "wms_address" "Any Street" + "wms_city" "Any City" + "wms_stateorprovince" "Any state" + "wms_postcode" "My Postalcode" + "wms_country" "Any Country" + "wms_contactperson" "Any Person" + "wms_contactorganization" "Any Orga" + "wms_contactelectronicmailaddress" "any-email@example.com" + "wms_contactvoicetelephone" "Any's telephone number" + "wms_srs" "EPSG:31466 EPSG:4326" + "wms_feature_info_mime_type" "text/html" + "ows_enable_request" "*" + END + END + + LAYER + NAME catchment + EXTENT 2457864.326387 5297459.306295 2634771.191263 5586961.449130 + DEBUG 0 + + METADATA + "wms_title" "catchment" + END + + TYPE POLYGON + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM catchment USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "catchment" + STYLE + COLOR "#000080" + OUTLINECOLOR "#000000" + END + END + END + LAYER + NAME km + GROUP km + EXTENT 2525910.000000 5481666.000000 2614362.250000 5582403.000000 + DEBUG 0 + DUMP TRUE + + METADATA + "wms_title" "km" + END + + TYPE POINT + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes_km USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "km" + STYLE + COLOR "#ff0000" + SYMBOL 'square' + SIZE 5 + END + END + END + + LAYER + NAME km_annotation + GROUP km + EXTENT 2525910.000000 5481666.000000 2614362.250000 5582403.000000 + DEBUG 5 + DUMP TRUE + + METADATA + "wms_title" "km_annotation" + END + + TYPE ANNOTATION + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes_km USING SRID 31466" + FILTER 'river_id=2' + LABELITEM km + + MAXSCALE 25000 + + CLASS + LABEL + ANGLE auto + SIZE 10 + COLOR "#000000" + TYPE truetype + FONT LiberationSans-Italic + POSITION ur + OFFSET 2 2 + END + END + END + + LAYER + NAME buildings + Extent 2526389.654387 5492305.031511 2612653.500000 5582427.500000 + DEBUG 0 + + METADATA + "wms_title" "buildings (Bauwerke/Wehre)" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM buildings USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "buildings" + STYLE + COLOR "#ff2222" + END + END + END + LAYER + NAME fixpoints + EXTENT 2525789.640000 5481448.110000 2614324.201153 5582705.474713 + DEBUG 0 + + METADATA + "wms_title" "fixpoints (Geodaesie/Festpunkte)" + END + + TYPE POINT + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM fixpoints USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "fixpoints" + STYLE + COLOR "#ffff00" + SYMBOL 'square' + SIZE 6 + END + END + END + LAYER + NAME riveraxis + EXTENT 2525866.883066 5480800.000000 2614283.382914 5582578.641600 + DEBUG 0 + + METADATA + "wms_title" "River Axes" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "riveraxes" + STYLE + COLOR "#0000ff" + END + END + END + + LAYER + NAME qps + EXTENT 2525526.058176 5481412.836939 2614739.057487 5582746.998120 + DEBUG 0 + + METADATA + "wms_title" "QPS (CrossSectionTracks)" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM cross_section_tracks USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + MAXSCALEDENOM 100000 + + CLASS + NAME "qps" + STYLE + COLOR "#0000ff" + END + END + END + + LAYER + NAME hws + EXTENT 2531846.270698 5501745.060309 2580199.261246 5535383.855597 + DEBUG 0 + + METADATA + "wms_title" "HWS" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM hws USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "hws" + STYLE + COLOR "#ff2222" + END + END + END + LAYER + NAME floodplain + EXTENT 2583046.060000 5556912.223213 2614325.275661 5582743.883699 + DEBUG 0 + + METADATA + "wms_title" "floodplain (Hydr. Grenzen/Talaue)" + END + + TYPE POLYGON + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM floodplain USING SRID 31466" + FILTER 'river_id=2' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "floodplain" + STYLE + COLOR "#800080" + OUTLINECOLOR "#000080" + END + END + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/oracle_dbconnection.include Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,2 @@ +CONNECTIONTYPE oraclespatial +CONNECTION "flys3/flys3@localhost"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/saar-mapfile.map Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,301 @@ +MAP + NAME "Saar" + STATUS ON + SIZE 600 400 + MAXSIZE 4000 + EXTENT 2539488.036000 5450928.892000 2575486.407000 5507352.839000 + UNITS DD + FONTSET "fontset.txt" + SYMBOLSET './symbols/symbols.sym' + IMAGECOLOR 255 255 255 + PROJECTION + "init=epsg:31466" + END + + + OUTPUTFORMAT + NAME agg + DRIVER AGG/PNG + IMAGEMODE RGB + END + + CONFIG "MS_ERRORFILE" "logs/flys-saar-wms.log" + DEBUG 0 + + WEB + METADATA + "wms_title" "FLYS-3.0 WMS (SAAR)" + "wms_onlineresource" "http://czech-republic.atlas.intevation.de/cgi-bin/saar-wms" + "wms_accessconstraints" "none" + "wms_fees" "none" + "wms_addresstype" "postal" + "wms_address" "Any Street" + "wms_city" "Any City" + "wms_stateorprovince" "Any state" + "wms_postcode" "My Postalcode" + "wms_country" "Any Country" + "wms_contactperson" "Any Person" + "wms_contactorganization" "Any Orga" + "wms_contactelectronicmailaddress" "any-email@example.com" + "wms_contactvoicetelephone" "Any's telephone number" + "wms_srs" "EPSG:31466 EPSG:4326" + "wms_feature_info_mime_type" "text/html" + "ows_enable_request" "*" + END + END + + LAYER + NAME catchment + EXTENT 2520667.897954 5376316.575645 2634771.191263 5508288.005707 + DEBUG 0 + + METADATA + "wms_title" "catchment" + END + + TYPE POLYGON + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM catchment USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "catchment" + STYLE + COLOR "#000080" + OUTLINECOLOR "#000000" + END + END + END + LAYER + NAME km + GROUP km + EXTENT 2539489.068000 5450953.000500 2575482.527500 5507278.634500 + DEBUG 0 + DUMP TRUE + + METADATA + "wms_title" "km" + END + + TYPE POINT + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes_km USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "km" + STYLE + COLOR "#ff0000" + SYMBOL 'square' + SIZE 5 + END + END + END + + LAYER + NAME km_annotation + GROUP km + EXTENT 2539489.068000 5450953.000500 2575482.527500 5507278.634500 + DEBUG 5 + DUMP TRUE + + METADATA + "wms_title" "km_annotation" + END + + TYPE ANNOTATION + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes_km USING SRID 31466" + FILTER 'river_id=1' + LABELITEM km + + MAXSCALE 25000 + + CLASS + LABEL + ANGLE auto + SIZE 10 + COLOR "#000000" + TYPE truetype + FONT LiberationSans-Italic + POSITION ur + OFFSET 2 2 + END + END + END + + LAYER + NAME buildings + EXTENT 2540544.253718 5456266.217464 2567747.834199 5502557.982120 + DEBUG 0 + + METADATA + "wms_title" "buildings (Bauwerke/Wehre)" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM buildings USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "buildings" + STYLE + COLOR "#ff2222" + END + END + END + LAYER + NAME fixpoints + EXTENT 2539388.036000 5450896.688000 2575586.296000 5507370.606000 + DEBUG 0 + + METADATA + "wms_title" "fixpoints (Geodaesie/Festpunkte)" + END + + TYPE POINT + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM fixpoints USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "fixpoints" + STYLE + COLOR "#ffff00" + SYMBOL 'square' + SIZE 6 + END + END + END + LAYER + NAME riveraxes + EXTENT 2539488.036000 5450928.892000 2575486.407000 5507352.839000 + DEBUG 0 + + METADATA + "wms_title" "River Axes" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM river_axes USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "riveraxes" + STYLE + COLOR "#0000ff" + END + END + END + + LAYER + NAME qps + EXTENT 2539289.724000 5450852.896743 2576589.878311 5507289.656000 + DEBUG 0 + + METADATA + "wms_title" "QPS (CrossSectionTracks)" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM cross_section_tracks USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + MAXSCALEDENOM 100000 + + CLASS + NAME "qps" + STYLE + COLOR "#0000ff" + END + END + END + + LAYER + NAME hws + EXTENT 2539778.101933 5456638.161347 2567463.841704 5500605.745332 + DEBUG 0 + + METADATA + "wms_title" "HWS" + END + + TYPE LINE + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM hws USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "hws" + STYLE + COLOR "#ff2222" + END + END + END + LAYER + NAME floodplain + EXTENT 2539343.776823 5451397.340027 2576021.009478 5507230.640000 + DEBUG 0 + + METADATA + "wms_title" "floodplain (Hydr. Grenzen/Talaue)" + END + + TYPE POLYGON + STATUS ON + INCLUDE "oracle_dbconnection.include" + DATA "GEOM FROM floodplain USING SRID 31466" + FILTER 'river_id=1' + + PROJECTION + "init=epsg:31466" + END + + CLASS + NAME "floodplain" + STYLE + COLOR "#800080" + OUTLINECOLOR "#000080" + END + END + END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/doc/mapserver/symbols/symbols.sym Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,22 @@ +SYMBOLSET +SYMBOL + NAME 'point' + TYPE ELLIPSE + POINTS + 1 1 + END + FILLED TRUE +END +SYMBOL + NAME "square" + TYPE VECTOR + POINTS + 0 0 + 0 1 + 1 1 + 1 0 + 0 0 + END + FILLED TRUE +END +END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/pom.xml Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,141 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>de.intevation.flys.artifacts</groupId> + <artifactId>flys-artifacts</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>flys-artifacts</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.0.2</version> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>net.sf.ehcache</groupId> + <artifactId>ehcache-core</artifactId> + <version>2.4.2</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.0.13</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>trove</groupId> + <artifactId>trove</artifactId> + <version>1.1-beta-5</version> + </dependency> + <dependency> + <groupId>net.sf.opencsv</groupId> + <artifactId>opencsv</artifactId> + <version>2.0</version> + </dependency> + <dependency> + <groupId>de.intevation.bsh.artifact-database</groupId> + <artifactId>artifact-database</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>de.intevation.artifacts.common</groupId> + <artifactId>artifacts-common</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>de.intevation.flys</groupId> + <artifactId>flys-backend</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.0.13</version> + </dependency> + <dependency> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>batik-dom</artifactId> + <version>1.7</version> + </dependency> + <dependency> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>batik-svggen</artifactId> + <version>1.7</version> + </dependency> + <dependency> + <groupId>com.lowagie</groupId> + <artifactId>itext</artifactId> + <version>2.1.7</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-math</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>1.3.158</version> + </dependency> + <dependency> + <groupId>commons-dbcp</groupId> + <artifactId>commons-dbcp</artifactId> + <version>1.2.2</version> + </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-shapefile</artifactId> + <version>2.7.2</version> + </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-epsg-wkt</artifactId> + <version>2.7.2</version> + </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-geojson</artifactId> + <version>2.7.2</version> + </dependency> + <dependency> + <groupId>org.apache.velocity</groupId> + <artifactId>velocity</artifactId> + <version>1.7</version> + </dependency> + </dependencies> + <repositories> + <repository> + <id>jboss-repo2</id> + <name>JBoss repo2</name> + <url>http://repository.jboss.org/nexus/content/groups/public/</url> + </repository> + <repository> + <id>gt2.repo</id> + <name>GeoTools2 Repository including JTS</name> + <url>http://download.osgeo.org/webdav/geotools</url> + </repository> + </repositories> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/AnnotationArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,273 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import net.sf.ehcache.Cache; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.states.DefaultState; +import de.intevation.flys.artifacts.cache.CacheFactory; +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.AnnotationsFactory; + +import de.intevation.flys.model.Annotation; + +import de.intevation.flys.utils.FLYSUtils; + +/** + * Artifact to access names of Points Of Interest along a segment of a river. + */ +public class AnnotationArtifact extends StaticFLYSArtifact { + + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(AnnotationArtifact.class); + + /** The name of the artifact. */ + public static final String ARTIFACT_NAME = "annotation"; + + /* Name of cache. */ + public static final String CACHE_NAME = "annotations"; + + @Override + protected void initialize(Artifact artifact, Object context, + CallMeta meta) { + logger.debug("AnnotationArtifact.initialize, id: " + + artifact.identifier()); + + FLYSArtifact flys = (FLYSArtifact) artifact; + addData("river", flys.getData("river")); + + List<Facet> fs = new ArrayList<Facet>(); + + // TODO Add CallMeta (duplicate TODO in RiverAxisArtifact.java). + DefaultState state = (DefaultState) getCurrentState(context); + state.computeInit(this, hash(), context, meta, fs); + + if (!fs.isEmpty()) { + logger.debug("Facets to add in AnnotationsArtifact.initialize ."); + facets.put(getCurrentStateId(), fs); + } + else { + logger.debug("No facets to add in AnnotationsArtifact.initialize ."); + } + } + + + public double[] getDistance() { + /** TODO In initialize(), access maximal range of river (via + * AnnotationFactory) instead of overriding getDistance, + * important for diagram generation. */ + return new double[] {0f, 1000f}; + } + + + /** + * Create the description of this AnnotationArtifact-instance. + * + * @param data Some data. + * @param context The CallContext. + * + * @return the description of this artifact. + */ + @Override + public Document describe(Document data, CallContext context) { + logger.debug("Describe: the current state is: " + getCurrentStateId()); + + if (logger.isDebugEnabled()) { + dumpArtifact(); + } + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine stateEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + Document description = XMLUtils.newDocument(); + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + description, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ProtocolUtils.createRootNode(creator); + description.appendChild(root); + + State current = getCurrentState(context); + + ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); + ProtocolUtils.appendState(creator, root, current); + + Element name = ProtocolUtils.createArtNode( + creator, "name", + new String[] { "value" }, + new String[] { getName() }); + + Element outs = ProtocolUtils.createArtNode( + creator, "outputmodes", null, null); + appendOutputModes(description, outs, context); + + root.appendChild(name); + root.appendChild(outs); + + return description; + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return ARTIFACT_NAME; + } + + + /** + * Append outputmode elements to given document. + * + * @param doc Document to add outputmodes to. + * @param outs Element to add outputmode elements to. + * @param context The given CallContext (mostly for internationalization). + */ + //@Override + protected void appendOutputModes( + Document doc, + Element outs, + CallContext context) + { + List<String> stateIds = getPreviousStateIds(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append output modes for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + + List<Output> list = state.getOutputs(); + if (list == null || list.size() == 0) { + logger.debug("-> No output modes for this state."); + continue; + } + + List<Facet> fs = facets.get(stateId); + if (fs == null || fs.size() == 0) { + logger.debug("No facets found."); + continue; + } + + logger.debug("Found " + fs.size() + " facets in previous states."); + + List<Output> generated = generateOutputs(list, fs); + + ProtocolUtils.appendOutputModes(doc, outs, generated); + } + + try { + DefaultState cur = (DefaultState) getCurrentState(context); + if (cur.validate(this)) { + List<Output> list = cur.getOutputs(); + if (list != null && list.size() > 0) { + logger.debug( + "Append output modes for state: " + cur.getID()); + + List<Facet> fs = facets.get(cur.getID()); + if (fs != null && fs.size() > 0) { + List<Output> generated = generateOutputs(list, fs); + + logger.debug("Found " + fs.size() + " current facets."); + if (!generated.isEmpty()) { + ProtocolUtils.appendOutputModes( + doc, outs, generated); + } + else{ + logger.debug("Cannot append output to generated document."); + } + } + else { + logger.debug("No facets found for the current state."); + } + } + } + } + catch (IllegalArgumentException iae) { + // state is not valid, so we do not append its outputs. + } + } + + + /** + * Get Annotations for Points (opposed to segments) in river in range. + * + * @return list of Annotations. + */ + public List<Annotation> getAnnotations() { + String river = FLYSUtils.getRiver(this).getName(); + logger.debug("Search annotations for river: " + river); + + Cache cache = CacheFactory.getCache(CACHE_NAME); + String key = river; + Object old = null; + + if (cache != null) { + logger.debug("We are using a cache for annotations."); + + net.sf.ehcache.Element element = cache.get(key); + if (element != null) { + logger.info("Fetched annotations from cache."); + old = element.getValue(); + } + } + + if (old == null) { + old = getAnnotationsUncached(river); + } + + if (cache != null && old != null) { + cache.put(new net.sf.ehcache.Element(key, old)); + } + + return old != null + ? (List<Annotation>) old + : new ArrayList<Annotation>(); + } + + /** + * Gets Annotations from Session/Database. + * + * @return List of Annotations fetched fresh from session/database. + * @see DistanceInfoService to access cached documents. + */ + protected List<Annotation> getAnnotationsUncached(String river) { + logger.info("Fetch annotations from database."); + + List<Annotation> annotations = new ArrayList<Annotation>(); + annotations = AnnotationsFactory.getPointAnnotations(river); + + return annotations; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,98 @@ +package de.intevation.flys.artifacts; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.Hook; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.flys.artifacts.datacage.Recommendations; + + +public class CollectionMonitor implements Hook { + + public static final String XPATH_RESULT = "/art:result"; + + + private static final Logger logger = + Logger.getLogger(CollectionMonitor.class); + + + @Override + public void setup(Node cfg) { + } + + + @Override + public void execute(Artifact artifact, CallContext context, Document doc) { + FLYSArtifact flys = (FLYSArtifact) artifact; + + Element result = (Element) XMLUtils.xpath( + doc, + XPATH_RESULT, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + ElementCreator creator = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element recommended = creator.create("recommended-artifacts"); + result.appendChild(recommended); + + String[] outs = extractOutputNames(flys, context); + Map<String, Object> params = getNoneUserSpecificParameters(flys, context); + + Recommendations rec = Recommendations.getInstance(); + rec.recommend(flys, null, outs, params, recommended); + } + + + public static String[] extractOutputNames( + FLYSArtifact flys, + CallContext context) + { + State state = flys.getCurrentState(context); + List<Output> outs = state.getOutputs(); + + int num = outs == null ? 0 : outs.size(); + + String[] names = new String[num]; + + for (int i = 0; i < num; i++) { + names[i] = outs.get(i).getName(); + } + + return names; + } + + + protected Map<String, Object> getNoneUserSpecificParameters( + FLYSArtifact flys, + CallContext context) + { + Map<String, Object> params = new HashMap<String, Object>(1); + params.put("recommended", "true"); + + return params; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1063 @@ +package de.intevation.flys.artifacts; + +import de.intevation.artifactdatabase.ArtifactDatabaseImpl; +import de.intevation.artifactdatabase.DefaultArtifact; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; + +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.artifacts.context.FLYSContext; + +import de.intevation.flys.artifacts.states.DefaultState; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + +import de.intevation.flys.utils.FLYSUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import javax.xml.xpath.XPathConstants; + +import net.sf.ehcache.Cache; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * The defaul FLYS artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class FLYSArtifact extends DefaultArtifact { + + /** The logger that is used in this artifact. */ + private static Logger logger = Logger.getLogger(FLYSArtifact.class); + + public static final String COMPUTING_CACHE = "computed.values"; + + /** The XPath that points to the input data elements of the FEED document. */ + public static final String XPATH_FEED_INPUT = + "/art:action/art:data/art:input"; + + /** The XPath that points to the name of the target state of ADVANCE. */ + public static final String XPATH_ADVANCE_TARGET = + "/art:action/art:target/@art:name"; + + public static final String XPATH_MODEL_ARTIFACT = + "/art:action/art:template/@uuid"; + + public static final String XPATH_FILTER = + "/art:action/art:filter/art:out"; + + /** The constant string that shows that an operation was successful. */ + public static final String OPERATION_SUCCESSFUL = "SUCCESS"; + + /** The constant string that shows that an operation failed. */ + public static final String OPERATION_FAILED = "FAILURE"; + + /** The identifier of the current state. */ + protected String currentStateId; + + /** The identifiers of previous states on a stack. */ + protected List<String> previousStateIds; + + /** The name of the artifact. */ + protected String name; + + /** The data that have been inserted into this artifact. */ + protected Map<String, StateData> data; + + /** Mapping of state names to created facets. */ + protected Map<String, List<Facet>> facets; + + /** + * Used to generates "view" on the facets (hides facets not matching the + * filter in output of collection); out -> facets. + */ + protected Map<String, List<Facet>> filterFacets; + + + /** + * The default constructor that creates an empty FLYSArtifact. + */ + public FLYSArtifact() { + data = new TreeMap<String, StateData>(); + previousStateIds = new ArrayList<String>(); + facets = new HashMap<String, List<Facet>>(); + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return name; + } + + + /** + * Initialize the artifact and insert new data if <code>data</code> contains + * information necessary for this artifact. + * + * @param identifier The UUID. + * @param factory The factory that is used to create this artifact. + * @param context The CallContext. + * @param data Some optional data. + */ + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("Setup this artifact with the uuid: " + identifier); + + super.setup(identifier, factory, context, callMeta, data); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + + List<State> states = getStates(context); + + String name = getName(); + logger.debug("Set initial state for artifact '" + name + "'"); + + setCurrentState(states.get(0)); + + String model = XMLUtils.xpathString( + data, + XPATH_MODEL_ARTIFACT, + ArtifactNamespaceContext.INSTANCE); + + if (model != null && model.length() > 0) { + ArtifactDatabase db = (ArtifactDatabase) flysContext.get( + ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY); + + try { + initialize(db.getRawArtifact(model), context, callMeta); + } + catch (ArtifactDatabaseException adbe) { + logger.error(adbe, adbe); + } + } + + filterFacets = buildFilterFacets(data); + } + + + protected List<String> clonePreviousStateIds() { + return new ArrayList<String>(previousStateIds); + } + + + protected Map<String, StateData> cloneData() { + Map<String, StateData> copy = new TreeMap<String, StateData>(); + + for (Map.Entry<String, StateData> entry: data.entrySet()) { + copy.put(entry.getKey(), entry.getValue().deepCopy()); + } + + return copy; + } + + /** + * Return a copy of the facet mapping. + * @return Mapping of state-ids to facets. + */ + protected Map<String, List<Facet>> cloneFacets() { + Map copy = new HashMap<String, List<Facet>>(); + + for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) { + List<Facet> facets = entry.getValue(); + List<Facet> facetCopies = new ArrayList<Facet>(facets.size()); + for (Facet facet: facets) { + facetCopies.add(facet.deepCopy()); + } + copy.put(entry.getKey(), facetCopies); + } + + return copy; + } + + + /** + * (called from setup). + * @param artifact master-artifact (if any, otherwise initialize is not called). + */ + protected void initialize( + Artifact artifact, + Object context, + CallMeta callMeta) + { + if (!(artifact instanceof FLYSArtifact)) { + return; + } + + FLYSArtifact flys = (FLYSArtifact)artifact; + + currentStateId = flys.currentStateId; + previousStateIds = flys.clonePreviousStateIds(); + name = flys.name; + data = flys.cloneData(); + facets = flys.cloneFacets(); + // Do not clone filter facets! + } + + + /** + * Builds filter facets from document. + * @see filterFacets + */ + protected Map<String, List<Facet>> buildFilterFacets(Document document) { + + NodeList nodes = (NodeList)XMLUtils.xpath( + document, + XPATH_FILTER, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (nodes == null || nodes.getLength() == 0) { + return null; + } + + Map<String, List<Facet>> result = new HashMap<String, List<Facet>>(); + + for (int i = 0, N = nodes.getLength(); i < N; ++i) { + Element element = (Element)nodes.item(i); + String oName = element.getAttribute("name"); + if (oName.length() == 0) { + continue; + } + + List<Facet> facets = new ArrayList<Facet>(); + + NodeList facetNodes = element.getElementsByTagNameNS( + ArtifactNamespaceContext.NAMESPACE_URI, + "facet"); + + for (int j = 0, M = facetNodes.getLength(); j < M; ++j) { + Element facetElement = (Element)facetNodes.item(j); + + String fName = facetElement.getAttribute("name"); + + int index; + try { + index = Integer.parseInt(facetElement.getAttribute("index")); + } + catch (NumberFormatException nfe) { + logger.warn(nfe); + index = 0; + } + facets.add(new DefaultFacet(index, fName, "")); + } + + if (!facets.isEmpty()) { + result.put(oName, facets); + } + } + + return result; + } + + + /** + * Insert new data included in <code>input</code> into the current state. + * + * @param target XML document that contains new data. + * @param context The CallContext. + * + * @return a document that contains a SUCCESS or FAILURE message. + */ + @Override + public Document feed(Document target, CallContext context) { + logger.info("FLYSArtifact.feed()"); + + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element result = creator.create("result"); + doc.appendChild(result); + + try { + saveData(target, XPATH_FEED_INPUT, context); + + compute(context, ComputeType.FEED, true); + + return describe(target, context); + } + catch (IllegalArgumentException iae) { + // do not store state if validation fails. + context.afterCall(CallContext.NOTHING); + creator.addAttr(result, "type", OPERATION_FAILED, true); + + result.setTextContent(iae.getMessage()); + } + + return doc; + } + + + /** + * This method handles request for changing the current state of an + * artifact. It is possible to step forward or backward. + * + * @param target The incoming ADVANCE document. + * @param context The CallContext. + * + * @return a document that contains a SUCCESS or FAILURE message. + */ + public Document advance(Document target, CallContext context) { + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element result = ec.create("result"); + + String targetState = XMLUtils.xpathString( + target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE); + + logger.info("FLYSArtifact.advance() to '" + targetState + "'"); + + if (isStateReachable(targetState, context)) { + logger.info("Advance: Step forward"); + + List<String> prev = getPreviousStateIds(); + prev.add(getCurrentStateId()); + + setCurrentStateId(targetState); + + logger.debug("Compute data for state: " + targetState); + compute(context, ComputeType.ADVANCE, true); + + return describe(target, context); + } + else if (isPreviousState(targetState, context)) { + logger.info("Advance: Step back to"); + + List<String> prevs = getPreviousStateIds(); + int targetIdx = prevs.indexOf(targetState); + int start = prevs.size() - 1; + + destroyStates(prevs, context); + + for (int i = start; i >= targetIdx; i--) { + String prev = prevs.get(i); + logger.debug("Remove state id '" + prev + "'"); + + prevs.remove(prev); + facets.remove(prev); + } + + destroyState(getCurrentStateId(), context); + setCurrentStateId(targetState); + + return describe(target, context); + } + + logger.warn("Advance: Cannot advance to '" + targetState + "'"); + ec.addAttr(result, "type", OPERATION_FAILED, true); + + doc.appendChild(result); + + return doc; + } + + + /** + * Returns the identifier of the current state. + * + * @return the identifier of the current state. + */ + public String getCurrentStateId() { + return currentStateId; + } + + + /** + * Sets the identifier of the current state. + * + * @param id the identifier of a state. + */ + protected void setCurrentStateId(String id) { + currentStateId = id; + } + + + /** + * Set the current state of this artifact. <b>NOTE</b>We don't store the + * State object itself - which is not necessary - but its identifier. So + * this method will just call the setCurrentStateId() method with the + * identifier of <i>state</i>. + * + * @param state The new current state. + */ + protected void setCurrentState(State state) { + setCurrentStateId(state.getID()); + } + + + /** + * Returns the current state of the artifact. + * + * @return the current State of the artifact. + */ + public State getCurrentState(Object context) { + return getState(context, getCurrentStateId()); + } + + + /** + * Get list of existant states for this Artifact. + * @param context Contex to get StateEngine from. + * @return list of states. + */ + protected List<State> getStates(Object context) { + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + return engine.getStates(getName()); + } + + + /** + * Get state with given ID. + * @param context Context to get StateEngine from. + * @param stateID ID of state to get. + * @return state with given ID. + */ + protected State getState(Object context, String stateID) { + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + return engine.getState(stateID); + } + + + /** + * Returns the vector of previous state identifiers. + * + * @return the vector of previous state identifiers. + */ + protected List<String> getPreviousStateIds() { + return previousStateIds; + } + + + /** + * Get all previous and the current state id. + * @return #getPreviousStateIds() + #getCurrentStateId() + */ + public List<String> getStateHistoryIds() { + List<String> allIds = getPreviousStateIds(); + allIds.add(getCurrentStateId()); + return allIds; + } + + + /** + * Adds a new StateData item to the data pool of this artifact. + * + * @param name the name of the data object. + * @param data the data object itself. + */ + protected void addData(String name, StateData data) { + this.data.put(name, data); + } + + + protected StateData removeData(String name) { + return this.data.remove(name); + } + + + /** + * This method returns a specific StateData object that is stored in the + * data pool of this artifact. + * + * @param name The name of the data object. + * + * @return the StateData object if existing, otherwise null. + */ + public StateData getData(String name) { + return data.get(name); + } + + + public String getDataAsString(String name) { + StateData data = getData(name); + return data != null ? (String) data.getValue() : null; + } + + + /** + * Add StateData containing a given string. + * @param name Name of the data object. + * @param value String to store. + */ + public void addStringData(String name, String value) { + addData(name, new DefaultStateData(name, null, null, value)); + } + + + public Collection<StateData> getAllData() { + return data.values(); + } + + + /** + * Get facet as stored internally, with equalling name and index than given + * facet. + * @param facet that defines index and name of facet searched. + * @return facet instance or null if not found. + */ + public Facet getNativeFacet(Facet facet) { + String name = facet.getName(); + int index = facet.getIndex(); + + for (Map.Entry<String, List<Facet>> facetList: facets.entrySet()) { + for (Facet f: facetList.getValue()) { + if (f.getIndex() == index && f.getName().equals(name)) { + return f; + } + } + } + + logger.warn("Could not find facet: " + name + " at " + index); + return null; + } + + + /** + * This method stores the data that is contained in the FEED document. + * + * @param feed The FEED document. + * @param xpath The XPath that points to the data nodes. + */ + public void saveData(Document feed, String xpath, CallContext context) + throws IllegalArgumentException + { + if (feed == null || xpath == null || xpath.length() == 0) { + throw new IllegalArgumentException("error_feed_no_data"); + } + + NodeList nodes = (NodeList) XMLUtils.xpath( + feed, + xpath, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (nodes == null || nodes.getLength() == 0) { + throw new IllegalArgumentException("error_feed_no_data"); + } + + int count = nodes.getLength(); + logger.debug("Try to save " + count + " data items."); + + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + DefaultState current = (DefaultState) getCurrentState(context); + + for (int i = 0; i < count; i++) { + Element node = (Element)nodes.item(i); + + String name = node.getAttributeNS(uri, "name"); + String value = node.getAttributeNS(uri, "value"); + + if (name.length() > 0 && value.length() > 0) { + logger.debug("Save data item for '" + name + "' : " + value); + + addData(name, current.transform(this, context, name, value)); + } + else if (name.length() > 0 && value.length() == 0) { + if (removeData(name) != null) { + logger.debug("Removed data '" + name + "' successfully."); + } + } + } + + current.validate(this); + } + + + /** + * Determines if the state with the identifier <i>stateId</i> is reachable + * from the current state. The determination itself takes place in the + * TransitionEngine. + * + * @param stateId The identifier of a state. + * @param context The context object. + * + * @return true, if the state specified by <i>stateId</i> is reacahble, + * otherwise false. + */ + protected boolean isStateReachable(String stateId, Object context) { + logger.debug("Determine if the state '" + stateId + "' is reachable."); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + + State currentState = getCurrentState(context); + StateEngine sEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + TransitionEngine tEngine = (TransitionEngine) flysContext.get( + FLYSContext.TRANSITION_ENGINE_KEY); + + return tEngine.isStateReachable(this, stateId, currentState, sEngine); + } + + + /** + * Determines if the state with the identifier <i>stateId</i> is a previous + * state of the current state. + * + * @param stateId The target state identifier. + * @param context The context object. + */ + protected boolean isPreviousState(String stateId, Object context) { + logger.debug("Determine if the state '" + stateId + "' is old."); + + List<String> prevs = getPreviousStateIds(); + if (prevs.contains(stateId)) { + return true; + } + + return false; + } + + + /** + * Computes the hash code of the entered values. + * + * @return a hash code. + */ + @Override + public String hash() { + Set<Map.Entry<String, StateData>> entries = data.entrySet(); + + long hash = 0L; + int shift = 3; + + for (Map.Entry<String, StateData> entry: entries) { + String key = entry.getKey(); + Object value = entry.getValue().getValue(); + + hash ^= ((long)key.hashCode() << shift) + | ((long)value.hashCode() << (shift + 3)); + shift += 2; + } + + return getCurrentStateId() + hash; + } + + + /** + * Return List of outputs, where combinations of outputname and filtername + * that match content in filterFacets is left out. + * @return filtered Outputlist. + */ + protected List<Output> filterOutputs(List<Output> outs) { + + if (filterFacets == null || filterFacets.isEmpty()) { + logger.debug("No filter for Outputs."); + return outs; + } + + List<Output> filtered = new ArrayList<Output>(); + + for (Output out: outs) { + + List<Facet> fFacets = filterFacets.get(out.getName()); + if (fFacets != null) { + + List<Facet> resultFacets = new ArrayList<Facet>(); + + for (Facet facet: out.getFacets()) { + for (Facet fFacet: fFacets) { + if (facet.getIndex() == fFacet.getIndex() + && facet.getName().equals(fFacet.getName())) { + resultFacets.add(facet); + break; + } + } + } + + if (!resultFacets.isEmpty()) { + DefaultOutput nout = new DefaultOutput( + out.getName(), + out.getDescription(), + out.getMimeType(), + resultFacets); + filtered.add(nout); + } + } + } + + return filtered; + } + + + /** + * Get all outputs that the Artifact can do in this state (which includes + * all previous states). + * + * @return list of outputs + */ + public List<Output> getOutputs(Object context) { + List<String> stateIds = getPreviousStateIds(); + List<Output> generated = new ArrayList<Output>(); + + for (String stateId: stateIds) { + DefaultState state = (DefaultState) getState(context, stateId); + generated.addAll(getOutputForState(state)); + } + + generated.addAll(getCurrentOutputs(context)); + + return filterOutputs(generated); + } + + + /** + * Get output(s) for current state. + * @return list of outputs for current state. + */ + public List<Output> getCurrentOutputs(Object context) { + DefaultState cur = (DefaultState) getCurrentState(context); + + try { + if (cur.validate(this)) { + return getOutputForState(cur); + } + } + catch (IllegalArgumentException iae) { } + + return new ArrayList<Output>(); + } + + + /** + * Get output(s) for a specific state. + * @param state State of interest + * @return list of output(s) for given state. + */ + protected List<Output> getOutputForState(DefaultState state) { + List<Output> list = state.getOutputs(); + if (list == null || list.size() == 0) { + logger.debug("-> No output modes for this state."); + return new ArrayList<Output>(); + } + + List<Facet> fs = facets.get(state.getID()); + + if (fs == null || fs.size() == 0) { + logger.debug("No facets found."); + return new ArrayList<Output>(); + } + + return generateOutputs(list, fs); + } + + + /** + * Generate a list of outputs with facets from fs if type is found in list + * of output. + * + * @param list List of outputs + * @param fs List of facets + */ + protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) { + List<Output> generated = new ArrayList<Output>(); + + boolean debug = logger.isDebugEnabled(); + + for (Output out: list) { + Output o = new DefaultOutput( + out.getName(), + out.getDescription(), + out.getMimeType(), + out.getType()); + + Set<String> outTypes = new HashSet<String>(); + + for (Facet f: out.getFacets()) { + if (outTypes.add(f.getName()) && debug) { + logger.debug("configured facet " + f); + } + } + + boolean facetAdded = false; + for (Facet f: fs) { + String type = f.getName(); + + if (outTypes.contains(type)) { + if (debug) { + logger.debug("Add facet " + f); + } + facetAdded = true; + o.addFacet(f); + } + } + + if (facetAdded) { + generated.add(o); + } + } + + return generated; + } + + + /** + * Dispatches the computation request to compute(CallContext context, String + * hash) with the current hash value of the artifact which is provided by + * hash(). + * + * @param context The CallContext. + */ + public Object compute( + CallContext context, + ComputeType type, + boolean generateFacets + ) { + return compute(context, hash(), type, generateFacets); + } + + + /** + * Dispatches computation requests to the current state which needs to + * implement a createComputeCallback(String hash, FLYSArtifact artifact) + * method. + * + * @param context The CallContext. + * @param hash The hash value which is used to fetch computed data from + * cache. + * + * @return the computed data. + */ + public Object compute( + CallContext context, + String hash, + ComputeType type, + boolean generateFacets + ) { + DefaultState current = (DefaultState) getCurrentState(context); + return compute(context, hash, current, type, generateFacets); + } + + + /** + * Like compute, but identify State by it id (string). + */ + public Object compute( + CallContext context, + String hash, + String stateID, + ComputeType type, + boolean generateFacets + ) { + DefaultState current = stateID == null + ? (DefaultState)getCurrentState(context) + : (DefaultState)getState(context, stateID); + + if (hash == null) { + hash = hash(); + } + + return compute(context, hash, current, type, generateFacets); + } + + + /** + * Let current state compute and register facets. + * + * @param key key of state + * @param state state + * @param type Type of compute + * @param generateFacets Whether new facets shall be generated. + */ + public Object compute( + CallContext context, + String key, + DefaultState state, + ComputeType type, + boolean generateFacets + ) { + String stateID = state.getID(); + + List<Facet> fs = (generateFacets) ? new ArrayList<Facet>() : null; + + try { + Cache cache = CacheFactory.getCache(COMPUTING_CACHE); + + Object old = null; + + if (cache != null) { + net.sf.ehcache.Element element = cache.get(key); + if (element != null) { + logger.debug("Got computation result from cache."); + old = element.getValue(); + } + } + + Object res; + switch (type) { + case FEED: + res = state.computeFeed(this, key, context, fs, old); + break; + case ADVANCE: + res = state.computeAdvance(this, key, context, fs, old); + break; + case INIT: + res = state.computeInit(this, key, context, context.getMeta(), fs); + default: + res = null; + } + + if (cache != null && old != res && res != null) { + logger.debug("Store computation result to cache."); + net.sf.ehcache.Element element = + new net.sf.ehcache.Element(key, res); + cache.put(element); + } + + return res; + } + finally { + if (generateFacets) { + if (fs.isEmpty()) { + facets.remove(stateID); + } + else { + facets.put(stateID, fs); + } + } + } + } + + + /** + * Method to dump the artifacts state/data. + */ + protected void dumpArtifact() { + if (logger.isDebugEnabled()) { + logger.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++"); + + logger.debug("------ DUMP DATA ------"); + Collection<StateData> allData = data.values(); + + for (StateData d: allData) { + String name = d.getName(); + String value = (String) d.getValue(); + + logger.debug("- " + name + ": " + value); + } + + logger.debug("------ DUMP PREVIOUS STATES ------"); + List<String> stateIds = getPreviousStateIds(); + + for (String id: stateIds) { + logger.debug("- State: " + id); + } + + logger.debug("CURRENT STATE: " + getCurrentStateId()); + + logger.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++"); + } + } + + + protected void destroyState(String id, Object context) { + State s = getState(context, id); + s.endOfLife(this, context); + } + + + /** + * Calls endOfLife() for each state in the list <i>ids</i>. + * + * @param ids The State IDs that should be destroyed. + * @param context The FLYSContext. + */ + protected void destroyStates(List<String> ids, Object context) { + for (int i = 0, num = ids.size(); i < num; i++) { + destroyState(ids.get(i), context); + } + } + + + /** + * Destroy the states. + */ + @Override + public void endOfLife(Object context) { + logger.info("FLYSArtifact.endOfLife: " + identifier()); + + List<String> ids = getPreviousStateIds(); + ids.add(getCurrentStateId()); + + destroyStates(ids, context); + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param outputName name of the output. + * @param index index of the facet. + * + * @return 1 if wished to be initally active, 0 if not. FLYSArtifact + * defaults to "1". + */ + public int getInitialFacetActivity( + String outputName, + String facetName, + int index) + { + return 1; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/MainValuesArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,315 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallMeta; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.Calculation; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.MainValuesQFacet; +import de.intevation.flys.artifacts.model.MainValuesWFacet; +import de.intevation.flys.artifacts.model.NamedDouble; +import de.intevation.flys.artifacts.model.WstValueTable; +import de.intevation.flys.artifacts.model.WstValueTableFactory; + +import de.intevation.flys.artifacts.states.StaticState; +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.utils.FLYSUtils; + + +/** + * Artifact to access names of Points Of Interest along a segment of a river. + * This artifact neglects (Static)FLYSArtifacts capabilities of interaction + * with the StateEngine by overriding the getState*-methods. + */ +public class MainValuesArtifact +extends StaticFLYSArtifact +implements FacetTypes +{ + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(MainValuesArtifact.class); + + /** The name of the artifact. */ + public static final String ARTIFACT_NAME = "mainvalue"; + + /** The name of the static state for this artifact. */ + public static final String STATIC_STATE_NAME = "state.mainvalue.static"; + + /** One and only state to be in. */ + protected transient State state = null; + + + /** + * Trivial Constructor. + */ + public MainValuesArtifact() { + logger.debug("MainValuesArtifact.MainValuesartifact()"); + } + + + /** + * Gets called from factory, to set things up. + */ + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("MainValuesArtifact.setup"); + state = new StaticState(STATIC_STATE_NAME); + + Facet qfacet1 = new MainValuesQFacet( + COMPUTED_DISCHARGE_MAINVALUES_Q, + Resources.getMsg( + callMeta, + "facet.discharge_curves.mainvalues.q", + "facet.discharge_curves.mainvalues.q")); + Facet qfacet2 = new MainValuesQFacet( + MAINVALUES_Q, + Resources.getMsg( + callMeta, + "facet.discharge_curves.mainvalues.q", + "facet.discharge_curves.mainvalues.q")); + Facet wfacet1 = new MainValuesWFacet( + COMPUTED_DISCHARGE_MAINVALUES_W, + Resources.getMsg( + callMeta, + "facet.discharge_curves.mainvalues.w", + "facet.discharge_curves.mainvalues.w")); + Facet wfacet2 = new MainValuesWFacet( + MAINVALUES_W, + Resources.getMsg( + callMeta, + "facet.discharge_curves.mainvalues.w", + "facet.discharge_curves.mainvalues.w")); + + List<Facet> fs = new ArrayList<Facet>(); + fs.add(qfacet1); + fs.add(qfacet2); + fs.add(wfacet1); + fs.add(wfacet2); + + facets.put(state.getID(), fs); + spawnState(); + super.setup(identifier, factory, context, callMeta, data); + } + + + /** + * Create "the" state. + */ + protected State spawnState() { + state = new StaticState(STATIC_STATE_NAME); + List<Facet> fs = (List<Facet>) facets.get(STATIC_STATE_NAME); + + DefaultOutput mainValuesOutput2 = new DefaultOutput( + "computed_discharge_curve", + "output.computed_discharge_curve", "image/png", + fs, + "chart"); + + state.getOutputs().add(mainValuesOutput2); + return state; + } + + + @Override + protected void initialize(Artifact artifact, Object context, CallMeta meta) { + logger.debug("MainValuesArtifact.initialize"); + WINFOArtifact winfo = (WINFOArtifact) artifact; + double location = FLYSUtils.getLocations(winfo)[0]; + addData("location", new DefaultStateData("location", null, null, + String.valueOf(location))); + addData("river", winfo.getData("river")); + } + + + /** + * Get a list containing the one and only State. + * @param context ignored. + * @return list with one and only state. + */ + @Override + protected List<State> getStates(Object context) { + ArrayList<State> states = new ArrayList<State>(); + states.add(getState()); + return states; + } + + + /** + * Get the "current" state. + * @param cc ignored. + * @return the "current" state. + */ + @Override + public State getCurrentState(Object cc) { + return getState(); + } + + + /** + * Get the only possible state. + * @return the state. + */ + protected State getState() { + return getState(null, null); + } + + + /** + * Get the state. + * @param context ignored. + * @param stateID ignored. + * @return the state. + */ + @Override + protected State getState(Object context, String stateID) { + if (state != null) + return state; + else + return spawnState(); + } + + + /** + * Access the Gauge that the mainvalues are taken from. + * @return Gauge that main values are taken from or null in case of + * invalid parameterization. + */ + protected Gauge getGauge() { + River river = FLYSUtils.getRiver(this); + + if (river == null) { + return null; + } + + double location = Double.parseDouble( + getDataAsString("location")); + + return river.determineGaugeByPosition(location); + } + + + /** + * Get current location. + * @return the location. + */ + public double getLocation() { + double location = Double.parseDouble( + (String)getData("location").getValue()); + return location; + } + + + /** + * Get a list of "Q" main values. + * @return list of Q main values. + */ + public List<NamedDouble> getMainValuesQ() { + List<NamedDouble> filteredList = new ArrayList<NamedDouble>(); + Gauge gauge = getGauge(); + WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this)); + Calculation c = new Calculation(); + double w_out[] = {0.0f}; + double q_out[] = {0.0f}; + double kms[] = {getLocation()}; + double gaugeStation = gauge.getStation().doubleValue(); + if (gauge != null) { + List<MainValue> orig = gauge.getMainValues(); + for (MainValue mv : orig) { + if (mv.getMainValue().getType().getName().equals("Q")) { + interpolator.interpolate(mv.getValue().doubleValue(), + gaugeStation, kms, w_out, q_out, c); + filteredList.add(new NamedDouble( + mv.getMainValue().getName(), + q_out[0] + )); + } + } + } + return filteredList; + } + + + /** + * Get a list of "W" main values. + * @return list of W main values. + */ + public List<NamedDouble> getMainValuesW() { + List<NamedDouble> filteredList = new ArrayList<NamedDouble>(); + Gauge gauge = getGauge(); + WstValueTable interpolator = WstValueTableFactory.getTable(FLYSUtils.getRiver(this)); + Calculation c = new Calculation(); + double gaugeStation = gauge.getStation().doubleValue(); + double w_out[] = {0.0f}; + double q_out[] = {0.0f}; + double kms[] = {getLocation()}; + if (gauge != null) { + List<MainValue> orig = gauge.getMainValues(); + for (MainValue mv : orig) { + // We cannot interpolate the W values, so derive them + // from given Q values. + if (mv.getMainValue().getType().getName().equals("Q")) { + interpolator.interpolate(mv.getValue().doubleValue(), + gaugeStation, kms, w_out, q_out, c); + filteredList.add(new NamedDouble( + "W(" + mv.getMainValue().getName() +")", + w_out[0] + )); + } + } + } + return filteredList; + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param index index of the facet. + * @return 0 if not active + */ + @Override + public int getInitialFacetActivity( + String outputName, + String facetName, + int index) + { + logger.debug("MainValuesArtifact.active?: " + + outputName + + "/" + + facetName); + + if (outputName.equals("computed_discharge_curve")) { + return 0; + } + else { + return 1; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/RiverAxisArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,100 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.states.DefaultState; + + +public class RiverAxisArtifact extends StaticFLYSArtifact { + + public static final String NAME = "riveraxis"; + + public static final String XPATH_IDS = "/art:action/art:ids/@value"; + + + private static final Logger logger = + Logger.getLogger(RiverAxisArtifact.class); + + + @Override + public String getName() { + return NAME; + } + + + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("RiverAxisArtifact.setup"); + + super.setup(identifier, factory, context, callMeta, data); + + String ids = XMLUtils.xpathString( + data, XPATH_IDS, ArtifactNamespaceContext.INSTANCE); + + String[] splitted = ids != null ? ids.split(" ") : null; + + if (splitted != null && splitted.length > 0) { + try { + int river_id = Integer.parseInt(splitted[0]); + River river = RiverFactory.getRiver(river_id); + + if (river == null) { + throw new IllegalArgumentException( + "No river found for id: " + river_id); + } + + String name = river.getName(); + + addData("river", new DefaultStateData("river",null,null,name)); + + List<Facet> fs = new ArrayList<Facet>(); + + DefaultState state = (DefaultState) getCurrentState(context); + state.computeInit(this, hash(), context, callMeta, fs); + + if (!fs.isEmpty()) { + facets.put(getCurrentStateId(), fs); + } + } + catch (NumberFormatException nfe) { + logger.error("Could not create Artifact: " + nfe.getMessage()); + throw new IllegalArgumentException("No river id given."); + } + } + } + + + @Override + protected void initialize( + Artifact artifact, + Object context, + CallMeta callMeta) + { + // do nothing + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticFLYSArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,79 @@ +package de.intevation.flys.artifacts; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +public abstract class StaticFLYSArtifact extends FLYSArtifact { + + private static final Logger logger = + Logger.getLogger(StaticFLYSArtifact.class); + + + @Override + public Document describe(Document data, CallContext cc) { + logger.debug("Describe artifact: " + identifier()); + + Document desc = XMLUtils.newDocument(); + + ElementCreator creator = new ElementCreator( + desc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ProtocolUtils.createRootNode(creator); + desc.appendChild(root); + + ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); + root.appendChild(createOutputModes(cc, desc, creator)); + + return desc; + } + + + protected Element createOutputModes( + CallContext cc, + Document doc, + ElementCreator creator) + { + Element outs = ProtocolUtils.createArtNode( + creator, "outputmodes", null, null); + + State state = getCurrentState(cc); + List<Output> list = state.getOutputs(); + + if (list != null && list.size() > 0) { + List<Facet> fs = facets.get(state.getID()); + if (fs != null && fs.size() > 0) { + List<Output> generated = generateOutputs(list, fs); + + logger.debug("Found " + fs.size() + " current facets."); + if (!generated.isEmpty()) { + ProtocolUtils.appendOutputModes( + doc, outs, generated); + } + } + else { + logger.debug("No facets found for the current state."); + } + } + + return outs; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,237 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WKmsFacet; +import de.intevation.flys.artifacts.model.WKmsFactory; + +import de.intevation.flys.artifacts.states.StaticState; +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.artifacts.common.utils.XMLUtils; + +/** + * Artifact to access additional "waterlevel"-type of data, like the height + * of protective measures (dikes). + * + * This artifact neglects (Static)FLYSArtifacts capabilities of interaction + * with the StateEngine by overriding the getState*-methods. + */ +public class StaticWKmsArtifact +extends StaticFLYSArtifact +implements FacetTypes +{ + /** The logger for this class. */ + private static Logger logger = + Logger.getLogger(StaticWKmsArtifact.class); + + /** XPath to access initial parameter. */ + public static final String XPATH_DATA = + "/art:action/art:ids/@value"; + + public static final String STATIC_STATE_NAME = + "state.additional_wkms.static"; + + /** One and only state to be in. */ + protected transient State state = null; + + + /** + * Trivial Constructor. + */ + public StaticWKmsArtifact() { + logger.debug("StaticWKmsArtifact.StaticWKmsArtifact"); + } + + + /** + * Gets called from factory, to set things up. + */ + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("StaticWKmsArtifact.setup"); + + state = new StaticState(STATIC_STATE_NAME); + + List<Facet> fs = new ArrayList<Facet>(); + logger.debug(XMLUtils.toString(data)); + String code = XMLUtils.xpathString( + data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE); + + // TODO Go for JSON, one day. + //ex.: flood_protection-wstv-114-12 + if (code != null) { + String [] parts = code.split("-"); + + if (parts.length >= 4) { + int col = Integer.valueOf(parts[2]); + int wst = Integer.valueOf(parts[3]); + + addStringData("col_pos", parts[2]); + addStringData("wst_id", parts[3]); + + String wkmsName = WKmsFactory.getWKmsName(col, wst); + + String name; + if (parts[0].equals(HEIGHTMARKS_POINTS)) { + name = HEIGHTMARKS_POINTS; + } + else { + name = STATIC_WKMS; + } + + Facet facet = new WKmsFacet( + name, + Resources.getMsg( + callMeta, + wkmsName, + wkmsName)); + fs.add(facet); + facets.put(state.getID(), fs); + } + } + + spawnState(); + super.setup(identifier, factory, context, callMeta, data); + } + + + /** + * Initialize the static state with output. + * @return static state + */ + protected State spawnState() { + state = new StaticState(STATIC_STATE_NAME); + List<Facet> fs = facets.get(STATIC_STATE_NAME); + DefaultOutput output = new DefaultOutput( + "general", + "general", "image/png", + fs, + "chart"); + + state.getOutputs().add(output); + return state; + } + + + /** + * Called via setup. + * + * @param artifact The master-artifact. + */ + @Override + protected void initialize( + Artifact artifact, + Object context, + CallMeta meta) + { + logger.debug("StaticWKmsArtifact.initialize"); + WINFOArtifact winfo = (WINFOArtifact) artifact; + // TODO: The river is of no interest, so far. + addData("river", winfo.getData("river")); + } + + + /** + * Get a list containing the one and only State. + * @param context ignored. + * @return list with one and only state. + */ + @Override + protected List<State> getStates(Object context) { + ArrayList<State> states = new ArrayList<State>(); + states.add(getState()); + return states; + } + + + /** + * Get the "current" state (there is but one). + * @param cc ignored. + * @return the "current" (only possible) state. + */ + @Override + public State getCurrentState(Object cc) { + return getState(); + } + + + /** + * Get the only possible state. + * @return the state. + */ + protected State getState() { + return getState(null, null); + } + + + /** + * Get the state. + * @param context ignored. + * @param stateID ignored. + * @return the state. + */ + @Override + protected State getState(Object context, String stateID) { + return (state != null) + ? state + : spawnState(); + } + + + /** + * Get WKms from factory. + * @param TODO idx param is not needed + * @return WKms according to parameterization (can be null); + */ + public WKms getWKms(int idx) { + logger.debug("StaticWKmsArtifact.getWKms"); + + return WKmsFactory.getWKms( + Integer.valueOf(getDataAsString("col_pos")), + Integer.valueOf(getDataAsString("wst_id"))); + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param index index of the facet. + * + * @return Always 0. Static Data will enter plots inactive. + */ + @Override + public int getInitialFacetActivity( + String outputName, + String facetName, + int index) + { + return 0; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,247 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WQKmsFacet; +import de.intevation.flys.artifacts.model.WKmsFactory; +import de.intevation.flys.artifacts.model.WQKmsFactory; + +import de.intevation.flys.artifacts.states.StaticState; +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.artifacts.common.utils.XMLUtils; + +/** + * Artifact to access additional "waterlevel/discharge"-type of data, like + * fixation measurements. + * + * This artifact neglects (Static)FLYSArtifacts capabilities of interaction + * with the StateEngine by overriding the getState*-methods. + */ +public class StaticWQKmsArtifact +extends StaticFLYSArtifact +implements FacetTypes +{ + /** The logger for this class. */ + private static Logger logger = + Logger.getLogger(StaticWQKmsArtifact.class); + + /** XPath to access initial parameter. */ + public static final String XPATH_DATA = + "/art:action/art:ids/@value"; + + public static final String STATIC_STATE_NAME = + "state.additional_wqkms.static"; + + /** One and only state to be in. */ + protected transient State state = null; + + + /** + * Trivial Constructor. + */ + public StaticWQKmsArtifact() { + logger.debug("StaticWQKmsArtifact.StaticWQKmsArtifact"); + } + + + /** + * Gets called from factory, to set things up. + */ + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("StaticWQKmsArtifact.setup"); + + state = new StaticState(STATIC_STATE_NAME); + + List<Facet> fs = new ArrayList<Facet>(); + logger.debug(XMLUtils.toString(data)); + String code = XMLUtils.xpathString( + data, XPATH_DATA, ArtifactNamespaceContext.INSTANCE); + + // TODO Go for JSON, one day. + //ex.: flood_protection-wstv-114-12 + if (code != null) { + String [] parts = code.split("-"); + + if (parts.length >= 4) { + try { + int col = Integer.valueOf(parts[2]); + int wst = Integer.valueOf(parts[3]); + + addStringData("col_pos", parts[2]); + addStringData("wst_id", parts[3]); + + String wkmsName = WKmsFactory.getWKmsName(col, wst); + + String name; + if (parts[0].equals(HEIGHTMARKS_POINTS)) { + name = HEIGHTMARKS_POINTS; + } + else { + name = STATIC_WQKMS; + } + + Facet facet = new WQKmsFacet( + name, + Resources.getMsg( + callMeta, + wkmsName, + wkmsName)); + fs.add(facet); + facets.put(state.getID(), fs); + } + catch (Exception e) {} + } + } + + spawnState(); + super.setup(identifier, factory, context, callMeta, data); + } + + + /** + * Initialize the static state with output. + * @return static state + */ + protected State spawnState() { + state = new StaticState(STATIC_STATE_NAME); + List<Facet> fs = facets.get(STATIC_STATE_NAME); + DefaultOutput output1 = new DefaultOutput( + "w_differences", + "w_differences.longitudinal_section", "image/png", + fs, + "chart"); + DefaultOutput output2 = new DefaultOutput( + "longitudinal_section", + "longitudinal_section.longitudinal_section", "image/png", + fs, + "chart"); + + state.getOutputs().add(output1); + state.getOutputs().add(output2); + return state; + } + + + /** + * Called via setup. + * + * @param artifact The master-artifact. + */ + @Override + protected void initialize( + Artifact artifact, + Object context, + CallMeta meta) + { + logger.debug("StaticWQKmsArtifact.initialize"); + WINFOArtifact winfo = (WINFOArtifact) artifact; + // TODO: The river is of no interest, so far. + addData("river", winfo.getData("river")); + } + + + /** + * Get a list containing the one and only State. + * @param context ignored. + * @return list with one and only state. + */ + @Override + protected List<State> getStates(Object context) { + ArrayList<State> states = new ArrayList<State>(); + states.add(getState()); + return states; + } + + + /** + * Get the "current" state (there is but one). + * @param cc ignored. + * @return the "current" (only possible) state. + */ + @Override + public State getCurrentState(Object cc) { + return getState(); + } + + + /** + * Get the only possible state. + * @return the state. + */ + protected State getState() { + return getState(null, null); + } + + + /** + * Get the state. + * @param context ignored. + * @param stateID ignored. + * @return the state. + */ + @Override + protected State getState(Object context, String stateID) { + return (state != null) + ? state + : spawnState(); + } + + + /** + * Get WQKms from factory. + * @param TODO idx param is not needed + * @return WQKms according to parameterization (can be null); + */ + public WQKms getWQKms(int idx) { + logger.debug("StaticWQKmsArtifact.getWQKms"); + + return WQKmsFactory.getWQKms( + Integer.valueOf(getDataAsString("col_pos")), + Integer.valueOf(getDataAsString("wst_id"))); + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param index index of the facet. + * + * @return Always 0. Static Data will enter plots inactive. + */ + @Override + public int getInitialFacetActivity( + String outputName, + String facetName, + int index) + { + return 0; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1333 @@ +package de.intevation.flys.artifacts; + +import java.awt.geom.Point2D; + +import de.intevation.artifactdatabase.ProtocolUtils; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; + +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.Message; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.context.FLYSContext; + +import de.intevation.flys.artifacts.model.Calculation1; +import de.intevation.flys.artifacts.model.Calculation2; +import de.intevation.flys.artifacts.model.Calculation3; +import de.intevation.flys.artifacts.model.Calculation4; +import de.intevation.flys.artifacts.model.Calculation; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.CrossSectionFactory; +import de.intevation.flys.artifacts.model.DischargeTables; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.MainValuesFactory; +import de.intevation.flys.artifacts.model.Segment; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WstValueTable; +import de.intevation.flys.artifacts.model.WstValueTableFactory; + +import de.intevation.flys.artifacts.states.DefaultState; +import de.intevation.flys.artifacts.states.LocationDistanceSelect; + +import de.intevation.flys.geom.Lines; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; +import de.intevation.flys.model.CrossSection; +import de.intevation.flys.model.CrossSectionLine; + +import de.intevation.flys.utils.DoubleUtil; +import de.intevation.flys.utils.FLYSUtils; + +import gnu.trove.TDoubleArrayList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.flys.artifacts.model.CalculationMessage; + +/** + * The default WINFO artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WINFOArtifact +extends FLYSArtifact +implements FacetTypes +{ + + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(WINFOArtifact.class); + + /** The name of the artifact. */ + public static final String ARTIFACT_NAME = "winfo"; + + /** XPath */ + public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static"; + + /** The default number of steps between the start end end of a selected Q + * range. */ + public static final int DEFAULT_Q_STEPS = 30; + + /** The default step width between the start end end kilometer. */ + public static final double DEFAULT_KM_STEPS = 0.1; + + + /** + * The default constructor. + */ + public WINFOArtifact() { + } + + + /** + * This method returns a description of this artifact. + * + * @param data Some data. + * @param context The CallContext. + * + * @return the description of this artifact. + */ + public Document describe(Document data, CallContext context) { + logger.debug("Describe: the current state is: " + getCurrentStateId()); + + if (logger.isDebugEnabled()) { + dumpArtifact(); + } + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + + StateEngine stateEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + TransitionEngine transitionEngine = (TransitionEngine) flysContext.get( + FLYSContext.TRANSITION_ENGINE_KEY); + + List<State> reachable = transitionEngine.getReachableStates( + this, getCurrentState(context), stateEngine); + + Document description = XMLUtils.newDocument(); + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + description, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ProtocolUtils.createRootNode(creator); + description.appendChild(root); + + State current = getCurrentState(context); + + ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); + ProtocolUtils.appendState(creator, root, current); + ProtocolUtils.appendReachableStates(creator, root, reachable); + + appendBackgroundActivity(creator, root, context); + + Element name = ProtocolUtils.createArtNode( + creator, "name", + new String[] { "value" }, + new String[] { getName() }); + + Element ui = ProtocolUtils.createArtNode( + creator, "ui", null, null); + + Element staticUI = ProtocolUtils.createArtNode( + creator, "static", null, null); + + Element outs = ProtocolUtils.createArtNode( + creator, "outputmodes", null, null); + appendOutputModes(description, outs, context, identifier()); + + appendStaticUI(description, staticUI, context, identifier()); + + Element dynamic = current.describe( + this, + description, + root, + context, + identifier()); + + if (dynamic != null) { + ui.appendChild(dynamic); + } + + ui.appendChild(staticUI); + + root.appendChild(name); + root.appendChild(ui); + root.appendChild(outs); + + return description; + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return ARTIFACT_NAME; + } + + + protected static void appendBackgroundActivity( + ElementCreator cr, + Element root, + CallContext context + ) { + Element inBackground = cr.create("background-processing"); + root.appendChild(inBackground); + + cr.addAttr( + inBackground, + "value", + String.valueOf(context.isInBackground()), + true); + + LinkedList<Message> messages = context.getBackgroundMessages(); + + if (messages == null) { + return; + } + + CalculationMessage message = (CalculationMessage) messages.getLast(); + cr.addAttr( + inBackground, + "steps", + String.valueOf(message.getSteps()), + true); + + cr.addAttr( + inBackground, + "currentStep", + String.valueOf(message.getCurrentStep()), + true); + + inBackground.setTextContent(message.getMessage()); + } + + + /** + * Append output mode nodes to a document. + */ + protected void appendOutputModes( + Document doc, + Element outs, + CallContext context, + String uuid) + { + List<String> stateIds = getPreviousStateIds(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append output modes for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + + List<Output> list = state.getOutputs(); + if (list == null || list.size() == 0) { + logger.debug("-> No output modes for this state."); + continue; + } + + List<Facet> fs = facets.get(stateId); + + if (fs == null || fs.size() == 0) { + logger.debug("No facets for previous state found."); + continue; + } + + logger.debug("Found " + fs.size() + " facets in previous states."); + + List<Output> generated = generateOutputs(list, fs); + + ProtocolUtils.appendOutputModes(doc, outs, generated); + } + + try { + DefaultState cur = (DefaultState) getCurrentState(context); + if (cur.validate(this)) { + List<Output> list = cur.getOutputs(); + if (list != null && list.size() > 0) { + logger.debug( + "Append output modes for current state: " + cur.getID()); + + List<Facet> fs = facets.get(cur.getID()); + + if (fs != null && fs.size() > 0) { + List<Output> generated = generateOutputs(list, fs); + + logger.debug("Found " + fs.size() + " current facets."); + if (!generated.isEmpty()) { + ProtocolUtils.appendOutputModes( + doc, outs, generated); + } + } + else { + logger.debug("No facets found for the current state."); + } + } + } + } + catch (IllegalArgumentException iae) { + // state is not valid, so we do not append its outputs. + } + } + + + /** + * This method appends the static data - that has already been inserted by + * the user - to the static node of the DESCRIBE document. + * + * @param doc The document. + * @param ui The root node. + * @param context The CallContext. + * @param uuid The identifier of the artifact. + */ + protected void appendStaticUI( + Document doc, + Node ui, + CallContext context, + String uuid) + { + List<String> stateIds = getPreviousStateIds(); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append static data for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + + ui.appendChild(state.describeStatic(this, doc, ui, context, uuid)); + } + } + + + // + // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES + // + + /** + * Returns the data that is computed by a waterlevel computation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public CalculationResult getWaterlevelData() + { + logger.debug("WINFOArtifact.getWaterlevelData"); + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return error(new WQKms[0], "No river selected."); + } + + double[] kms = getKms(); + if (kms == null) { + return error(new WQKms[0], "No Kms selected."); + } + + double[] qs = getQs(); + double[] ws = null; + boolean qSel = true; + + if (qs == null) { + logger.debug("Determine Q values based on a set of W values."); + qSel = false; + ws = getWs(); + qs = getQsForWs(ws); + if (qs == null) { + return error(new WQKms[0], "conversion ws to qs failed."); + } + } + + WstValueTable wst = WstValueTableFactory.getTable(river); + if (wst == null) { + return error(new WQKms[0], "No Wst found for selected river."); + } + + + double [] range = FLYSUtils.getKmRange(this); + if (range == null) { + return error(new WQKms[0], "No range found"); + } + + double refKm; + + if (isFreeQ()) { + refKm = range[0]; + logger.debug("'free' calculation (km " + refKm + ")"); + } + else { + Gauge gauge = river.determineGaugeByPosition(range[0]); + if (gauge == null) { + return error( + new WQKms[0], "No gauge found for km " + range[0]); + } + + refKm = gauge.getStation().doubleValue(); + + logger.debug( + "reference gauge: " + gauge.getName() + " (km " + refKm + ")"); + } + + return computeWaterlevelData(kms, qs, ws, wst, refKm); + } + + + /** + * Computes the data of a waterlevel computation based on the interpolation + * in WstValueTable. + * + * @param kms The kilometer values. + * @param qa The discharge values. + * @param wst The WstValueTable used for the interpolation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public static CalculationResult computeWaterlevelData( + double [] kms, + double [] qs, + double [] ws, + WstValueTable wst, + double refKm + ) { + logger.info("WINFOArtifact.computeWaterlevelData"); + + Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm); + + return calc1.calculate(wst); + } + + + /** + * Returns the data that is computed by a duration curve computation. + * + * @return the data computed by a duration curve computation. + */ + public CalculationResult getDurationCurveData() { + logger.debug("WINFOArtifact.getDurationCurveData"); + + River r = FLYSUtils.getRiver(this); + + if (r == null) { + return error(null, "Cannot determine river."); + } + + Gauge g = getGauge(); + + if (g == null) { + return error(null, "Cannot determine gauge."); + } + + double[] locations = FLYSUtils.getLocations(this); + + if (locations == null) { + return error(null, "Cannot determine location."); + } + + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + return error(null, "No Wst found for selected river."); + } + + return computeDurationCurveData(g, wst, locations[0]); + } + + + /** + * Computes the data used to create duration curves. + * + * @param gauge The selected gauge. + * @param location The selected location. + * + * @return the computed data. + */ + public static CalculationResult computeDurationCurveData( + Gauge gauge, + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDurationCurveData"); + + Object[] obj = MainValuesFactory.getDurationCurveData(gauge); + + int[] days = (int[]) obj[0]; + double[] qs = (double[]) obj[1]; + + Calculation3 calculation = new Calculation3(location, days, qs); + + return calculation.calculate(wst); + } + + + /** + * Returns the data that is used to create discharge curves. + * + */ + public CalculationResult getDischargeCurveData() { + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return error(new WQKms[0], "no river found"); + } + + double [] distance = FLYSUtils.getKmRange(this); + + if (distance == null) { + return error(new WQKms[0], "no range found"); + } + + List<Gauge> gauges = river.determineGauges(distance[0], distance[1]); + + if (gauges.isEmpty()) { + return error(new WQKms[0], "no gauges found"); + } + + String [] names = new String[gauges.size()]; + + for (int i = 0; i < names.length; ++i) { + names[i] = gauges.get(i).getName(); + } + + DischargeTables dt = new DischargeTables(river.getName(), names); + + Map<String, double [][]> map = dt.getValues(100d); + + ArrayList<WQKms> res = new ArrayList<WQKms>(); + + for (Gauge gauge: gauges) { + String name = gauge.getName(); + double [][] values = map.get(name); + if (values == null) { + continue; + } + double [] kms = new double[values[0].length]; + Arrays.fill(kms, gauge.getStation().doubleValue()); + res.add(new WQKms(kms, values[0], values[1], name)); + } + + return new CalculationResult( + res.toArray(new WQKms[res.size()]), + new Calculation()); + } + + + /** + * Returns the data that is computed by a discharge curve computation. + * + * @return the data computed by a discharge curve computation. + */ + public CalculationResult getComputedDischargeCurveData() + throws NullPointerException + { + logger.debug("WINFOArtifact.getComputedDischargeCurveData"); + + River r = FLYSUtils.getRiver(this); + + if (r == null) { + return error(new WQKms[0], "Cannot determine river."); + } + + double[] locations = FLYSUtils.getLocations(this); + + if (locations == null) { + return error(new WQKms[0], "Cannot determine location."); + } + + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + return error(new WQKms[0], "No Wst found for selected river."); + } + + return computeDischargeCurveData(wst, locations[0]); + } + + + /** + * Computes the data used to create computed discharge curves. + * + * @param wst The WstValueTable that is used for the interpolation. + * @param location The location where the computation should be based on. + * + * @return an object that contains tuples of W/Q values at the specified + * location. + */ + public static CalculationResult computeDischargeCurveData( + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDischargeCurveData"); + + Calculation2 calculation = new Calculation2(location); + + return calculation.calculate(wst); + } + + protected static final CalculationResult error(Object data, String msg) { + return new CalculationResult(data, new Calculation(msg)); + } + + + /** + * Returns the data computed by the discharge longitudinal section + * computation. + * + * @return an array of WQKms object - one object for each given Q value. + */ + public CalculationResult getDischargeLongitudinalSectionData() { + + logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData"); + + River river = FLYSUtils.getRiver(this); + if (river == null) { + logger.debug("No river selected."); + return error(new WQKms[0], "No river selected."); + } + + WstValueTable table = WstValueTableFactory.getTable(river); + if (table == null) { + logger.debug("No wst found for selected river."); + return error(new WQKms[0], "No wst found for selected river."); + } + + List<Segment> segments = getSegments(); + + if (segments == null) { + logger.debug("Cannot create segments."); + return error(new WQKms[0], "Cannot create segments."); + } + + double [] range = getFromToStep(); + + if (range == null) { + logger.debug("Cannot figure out range."); + return error(new WQKms[0], "Cannot figure out range."); + } + + Calculation4 calc4 = new Calculation4(segments, river, isQ()); + + return calc4.calculate(table, range[0], range[1], range[2]); + } + + + public List<Segment> getSegments() { + StateData wqValues = getData("wq_values"); + if (wqValues == null) { + logger.warn("no wq_values given"); + return Collections.emptyList(); + } + String input = (String)wqValues.getValue(); + if (input == null || (input = input.trim()).length() == 0) { + logger.warn("wq_values are empty"); + return Collections.emptyList(); + } + return Segment.parseSegments(input); + } + + + /** + * Get List of all cross-sections for current river. + * + * @return List of CrossSections for current river, null in case of error. + */ + protected List<CrossSection> getCrossSections() { + River river = FLYSUtils.getRiver(this); + if (river == null) { + logger.warn("No river in WINFO found"); + return null; + } + return CrossSectionFactory.getCrossSections(river); + } + + + /** + * Get CrossSectionLine spatially closest to what is specified in the data + * "cross_section.km" (and from "cross_section.index"). + * + * @return CrossSectionLine closest to "cross_section.km". + */ + protected CrossSectionLine searchCrossSectionKmLine() { + double wishKM = 0.0f; + try { + wishKM = Double.parseDouble(getDataAsString("cross_section.km")); + } + catch (Exception e) { + logger.warn("Exception when parsing cross_section.km", e); + } + + int crossSectionIdx = 0; + + List<CrossSection> sections = getCrossSections(); + if (sections.size() < crossSectionIdx) { + logger.error("Cross-section not found: " + crossSectionIdx); + return null; + } + if (sections.size() == 0 + || sections.get(crossSectionIdx).getLines().size() == 0) + { + return null; + } + + // Get the cross section closest to requested km. + // Naive, linear approach. + List<CrossSectionLine> crossSectionLines = + sections.get(crossSectionIdx).getLines(); + CrossSectionLine oldLine = crossSectionLines.get(0); + double oldDiff = Math.abs(wishKM - oldLine.getKm().doubleValue()); + for (CrossSectionLine line: crossSectionLines) { + double diff = Math.abs(wishKM - line.getKm().doubleValue()); + if (diff > oldDiff) { + break; + } + oldDiff = diff; + oldLine = line; + } + return oldLine; + } + + + /** + * Get km for which a CrossSection is actually available (this may vary + * from the user picked "cross_section.km" data). + * + * @return km for which cross section is calculated. + */ + public double getCrossSectionSnapKm() { + // Note that this is this triggers a linear search. + CrossSectionLine line = searchCrossSectionKmLine(); + if (line == null) { + logger.warn("No Cross Section for given km found."); + return 0.0f; + } + else { + return line.getKm().doubleValue(); + } + } + + + /** + * Get points of Profile of cross section. + * + * @return an array holding coordinates of points of profile ( + * in the form {{x1, x2} {y1, y2}} ). + */ + public double [][] getCrossSectionData() { + logger.info("getCrossSectionData() for cross_section.km " + + getDataAsString("cross_section.km")); + CrossSectionLine line = searchCrossSectionKmLine(); + + return line != null + ? line.fetchCrossSectionProfile() + : null; + } + + + /** + * Get points of line describing the surface of water at cross section. + * + * @return an array holding coordinates of points of surface of water ( + * in the form {{x1, x2} {y1, y2}} ). + */ + public double [][] getWaterLines(int idx) { + logger.debug("getWaterLines()"); + CrossSectionLine csl = searchCrossSectionKmLine(); + List<Point2D> points = csl.fetchCrossSectionLinesPoints(); + + // Need W at km + WQKms [] wqkms = (WQKms[]) getWaterlevelData().getData(); + if (wqkms.length == 0) { + logger.error("No WQKms found."); + return Lines.createWaterLines(points, 0.0f); + } + + if (wqkms.length < idx) { + logger.error("getWaterLines() requested index (" + + idx + " not found."); + } + + // Find W at km, linear naive approach. + WQKms triple = wqkms[idx]; + + // Find index of km. + double wishKM = 0.0f; + int old_idx = 0; + try { + wishKM = Double.parseDouble(getDataAsString("cross_section.km")); + } + catch (Exception e) { + logger.warn("Exception when trying to get cross_section.km", e); + } + + if (triple.size() == 0) { + logger.warn("Calculation of waterline is empty."); + return Lines.createWaterLines(points, 0.0f); + } + + // Linear seach in WQKms for closest km. + double old_dist_wish = Math.abs(wishKM - triple.getKm(0)); + double last_w = triple.getW(0); + + for (int i = 0, T = triple.size(); i < T; i++) { + double diff = Math.abs(wishKM - triple.getKm(i)); + if (diff > old_dist_wish) { + break; + } + last_w = triple.getW(i); + old_dist_wish = diff; + } + return Lines.createWaterLines(points, last_w); + } + + + /** + * Get name of cross sections. + * @return list of names of cross-sections. + */ + public List<String> getCrossSectionNames() { + List<String> names = new ArrayList<String>(); + + for (CrossSection section: getCrossSections()) { + names.add(section.getDescription()); + } + + return names; + } + + + /** + * Returns the Qs for a number of Ws. This method makes use of + * DischargeTables.getQForW(). + * + * @param ws An array of W values. + * + * @return an array of Q values. + */ + public double[] getQsForWs(double[] ws) { + + boolean debug = logger.isDebugEnabled(); + + if (debug) { + logger.debug("FLYSArtifact.getQsForWs"); + } + + River r = FLYSUtils.getRiver(this); + if (r == null) { + logger.warn("no river found"); + return null; + } + + double [] range = FLYSUtils.getKmRange(this); + if (range == null) { + logger.warn("no ranges found"); + return null; + } + + if (debug) { + logger.debug("range: " + Arrays.toString(range)); + } + + Gauge g = r.determineGaugeByPosition(range[0]); + if (g == null) { + logger.warn("no gauge found for km: " + range[0]); + return null; + } + + if (debug) { + logger.debug("convert w->q with gauge '" + g.getName() + "'"); + } + + DischargeTables dt = new DischargeTables(r.getName(), g.getName()); + Map<String, double [][]> tmp = dt.getValues(); + + double[][] values = tmp.get(g.getName()); + double[] qs = new double[ws.length]; + + for (int i = 0; i < ws.length; i++) { + qs[i] = dt.getQForW(values, ws[i]); + if (debug) { + logger.debug("w: " + ws[i] + " -> q: " + qs[i]); + } + } + + return qs; + } + + + /** + * Determines the selected mode of distance/range input. + * + * @return true, if the range mode is selected otherwise false. + */ + public boolean isRange() { + StateData mode = getData("ld_mode"); + + if (mode == null) { + logger.warn("No mode location/range chosen. Defaults to range."); + return true; + } + + String value = (String) mode.getValue(); + + return value.equals("distance"); + } + + + /** + * Returns the selected distance based on a given range (from, to). + * + * @param dFrom The StateData that contains the lower value. + * @param dTo The StateData that contains the upper value. + * + * @return the selected distance. + */ + protected double[] getDistanceByRange(StateData dFrom, StateData dTo) { + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + return new double[] { from, to }; + } + + + /** + * Returns the selected Kms. + * + * @param distance An 2dim array with [lower, upper] values. + * + * @return the selected Kms. + */ + public double[] getKms(double[] distance) { + StateData dStep = getData("ld_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Kms."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // transform step from 'm' into 'km' + step = step / 1000; + + if (step == 0d) { + step = DEFAULT_KM_STEPS; + } + + return DoubleUtil.explode(distance[0], distance[1], step); + } + + + /** + * Returns the selected Kms. + * + * @return the selected kms. + */ + public double[] getKms() { + if (isRange()) { + double[] distance = FLYSUtils.getKmRange(this); + return getKms(distance); + + } + else { + return LocationDistanceSelect.getLocations(this); + } + } + + + public double [] getFromToStep() { + if (!isRange()) { + return null; + } + double [] fromTo = FLYSUtils.getKmRange(this); + + if (fromTo == null) { + return null; + } + + StateData dStep = getData("ld_step"); + if (dStep == null) { + return null; + } + + double [] result = new double[3]; + result[0] = fromTo[0]; + result[1] = fromTo[1]; + + try { + String step = (String)dStep.getValue(); + result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d); + } + catch (NumberFormatException nfe) { + return null; + } + + return result; + } + + + /** + * Returns the gauge based on the current distance and river. + * + * @return the gauge. + */ + public Gauge getGauge() { + River river = FLYSUtils.getRiver(this); + + if (river == null) { + logger.debug("no river found"); + return null; + } + + double[] dist = FLYSUtils.getKmRange(this); + + if (dist == null) { + logger.debug("no range found"); + return null; + } + + if (logger.isDebugEnabled()) { + logger.debug("Determine gauge for:"); + logger.debug("... river: " + river.getName()); + logger.debug("... distance: " + dist[0] + " - " + dist[1]); + } + + Gauge gauge = river.determineGauge(dist[0], dist[1]); + + String name = gauge != null ? gauge.getName() : "'n/a"; + logger.debug("Found gauge: " + name); + + return gauge; + } + + + /** + * Returns the gauges that match the selected kilometer range. + * + * @return the gauges based on the selected kilometer range. + */ + public List<Gauge> getGauges() { + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return null; + } + + double [] dist = FLYSUtils.getKmRange(this); + if (dist == null) { + return null; + } + + return river.determineGauges(dist[0], dist[1]); + } + + + /** + * This method returns the Q values. + * + * @return the selected Q values or null, if no Q values are selected. + */ + public double[] getQs() { + StateData dMode = getData("wq_mode"); + StateData dSelection = getData("wq_selection"); + + String mode = dMode != null ? (String) dMode.getValue() : ""; + String sel = dSelection != null ? (String)dSelection.getValue() : null; + + if (mode.equals("Q")) { + if (sel != null && sel.equals("single")) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Qs, but W has been inserted."); + return null; + } + } + + + public boolean isQ() { + StateData mode = getData("wq_mode"); + return mode != null && mode.getValue().equals("Q"); + } + + + /** + * Returns true, if the parameter is set to compute data on a free range. + * Otherwise it returns false, which tells the calculation that it is bound + * to a gauge. + * + * @return true, if the calculation should compute on a free range otherwise + * false and the calculation is bound to a gauge. + */ + public boolean isFreeQ() { + StateData mode = getData("wq_free"); + String value = (mode != null) ? (String) mode.getValue() : null; + + logger.debug("isFreeQ: " + value); + + if (value == null) { + return false; + } + + return Boolean.valueOf(value); + } + + + /** + * Returns the Q values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of Q values. + */ + public double[] getQs(double[] range) { + StateData dMode = getData("wq_mode"); + StateData dValues = getData("wq_values"); + + String mode = (dMode != null) ? (String) dMode.getValue() : ""; + + if (mode.equals("Q")) { + return getWQForDist(range); + } + + logger.warn("You try to get Qs, but Ws has been inserted."); + return null; + } + + + /** + * Returns the W values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of W values. + */ + public double[] getWs(double[] range) { + StateData dMode = getData("wq_mode"); + StateData dValues = getData("wq_values"); + + String mode = (dMode != null) ? (String) dMode.getValue() : ""; + + if (mode.equals("W")) { + return getWQForDist(range); + } + + logger.warn("You try to get Ws, but Qs has been inserted."); + return null; + } + + + /** + * This method returns the W values. + * + * @return the selected W values or null, if no W values are selected. + */ + public double[] getWs() { + StateData dMode = getData("wq_mode"); + StateData dSingle = getData("wq_single"); + + String mode = (dMode != null) ? (String) dMode.getValue() : ""; + + if (mode.equals("W")) { + if (dSingle != null) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Qs, but W has been inserted."); + return null; + } + } + + /** + * This method returns the given W or Q values for a specific range + * (inserted in the WQ input panel for discharge longitudinal sections). + * + * @param dist A 2dim array with lower und upper kilometer values. + * + * @return an array of W or Q values. + */ + protected double[] getWQForDist(double[] dist) { + logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]); + StateData data = getData("wq_values"); + + if (data == null) { + logger.warn("Missing wq values!"); + return null; + } + + String dataString = (String) data.getValue(); + String[] ranges = dataString.split(":"); + + for (String range: ranges) { + String[] parts = range.split(";"); + + double lower = Double.parseDouble(parts[0]); + double upper = Double.parseDouble(parts[1]); + + if (lower <= dist[0] && upper >= dist[1]) { + String[] values = parts[2].split(","); + + int num = values.length; + double[] res = new double[num]; + + for (int i = 0; i < num; i++) { + try { + res[i] = Double.parseDouble(values[i]); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return res; + } + } + + logger.warn("Specified range for WQ not found!"); + + return null; + } + + + /** + * This method returns an array of inserted WQ triples that consist of from, + * to and the step width. + * + * @return an array of from, to and step width. + */ + protected double[] getWQTriple() { + StateData dFrom = getData("wq_from"); + StateData dTo = getData("wq_to"); + + if (dFrom == null || dTo == null) { + logger.warn("Missing start or end value for range."); + return null; + } + + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + StateData dStep = getData("wq_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Qs."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // if no width is given, the DEFAULT_Q_STEPS is used to compute the step + // width. Maybe, we should round the value to a number of digits. + if (step == 0d) { + double diff = to - from; + step = diff / DEFAULT_Q_STEPS; + } + + return DoubleUtil.explode(from, to, step); + } + + + /** + * Returns an array of inserted WQ double values stored as whitespace + * separated list. + * + * @return an array of W or Q values. + */ + protected double[] getSingleWQValues() { + StateData dSingle = getData("wq_single"); + + if (dSingle == null) { + logger.warn("Cannot determine single WQ values. No data given."); + return null; + } + + String tmp = (String) dSingle.getValue(); + String[] strValues = tmp.split(" "); + + TDoubleArrayList values = new TDoubleArrayList(); + + for (String strValue: strValues) { + try { + values.add(Double.parseDouble(strValue)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + values.sort(); + + return values.toNativeArray(); + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param index index of the facet. + * @return 0 if not active + */ + @Override + public int getInitialFacetActivity(String outputName, String facetName, int index) { + String [] inactives = new String[] { + LONGITUDINAL_Q + }; + + logger.debug("WINFOArtifact.active?: " + + outputName + + "/" + + facetName); + + if ((facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) || + facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_W) + && outputName.equals("computed_discharge_curve"))) + { + return 0; + } + return Arrays.asList(inactives).contains(facetName) + ? 0 + : 1; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSBackgroundArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,48 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.states.DefaultState; + + +public class WMSBackgroundArtifact extends StaticFLYSArtifact { + + public static final String NAME = "wmsbackground"; + + private static final Logger logger = + Logger.getLogger(WMSBackgroundArtifact.class); + + + @Override + public String getName() { + return NAME; + } + + + @Override + protected void initialize(Artifact artifact, Object context, CallMeta meta) { + logger.debug("Initialize internal state with: "+ artifact.identifier()); + + FLYSArtifact flys = (FLYSArtifact) artifact; + addData("river", flys.getData("river")); + + List<Facet> fs = new ArrayList<Facet>(); + + // TODO Add CallMeta + DefaultState state = (DefaultState) getCurrentState(context); + state.computeInit(this, hash(), context, meta, fs); + + if (!fs.isEmpty()) { + facets.put(getCurrentStateId(), fs); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSDBArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,152 @@ +package de.intevation.flys.artifacts; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import com.vividsolutions.jts.geom.Envelope; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.states.DefaultState; +import de.intevation.flys.artifacts.model.WMSDBLayerFacet; + + +public abstract class WMSDBArtifact extends StaticFLYSArtifact { + + private static final Logger logger = Logger.getLogger(WMSDBArtifact.class); + + public static final String XPATH_IDS = "/art:action/art:ids/@value"; + + + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("WMSDBArtifact.setup"); + + super.setup(identifier, factory, context, callMeta, data); + + String ids = XMLUtils.xpathString( + data, XPATH_IDS, ArtifactNamespaceContext.INSTANCE); + + if (ids != null && ids.length() > 0) { + addData("ids", new DefaultStateData("ids", null, null, ids)); + } + else { + throw new IllegalArgumentException("No attribute 'ids' found!"); + } + + List<Facet> fs = new ArrayList<Facet>(); + + WMSDBState state = (WMSDBState) getCurrentState(context); + state.computeInit(this, hash(), context, callMeta, fs); + + if (!fs.isEmpty()) { + facets.put(getCurrentStateId(), fs); + } + } + + + @Override + protected void initialize( + Artifact artifact, + Object context, + CallMeta callMeta) + { + // do nothing + } + + + /** + * Get a list containing the one and only State. + * @param context ignored. + * @return list with one and only state. + */ + @Override + protected List<State> getStates(Object context) { + ArrayList<State> states = new ArrayList<State>(); + states.add(getCurrentState(context)); + + return states; + } + + + + public static abstract class WMSDBState extends DefaultState { + private static final Logger logger = Logger.getLogger(WMSDBState.class); + + protected WMSDBArtifact artifact; + + public WMSDBState(WMSDBArtifact artifact) { + this.artifact = artifact; + } + + @Override + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets + ) { + logger.debug("WMSDBState.computeInit"); + + String type = getFacetType(); + + WMSDBLayerFacet facet = new WMSDBLayerFacet( + 0, + type, + getTitle(meta), + ComputeType.INIT, + getID(), hash, + getUrl()); + + String name = artifact.identifier() + "-" + type; + + facet.addLayer(name); + facet.setExtent(getExtent()); + facet.setSrid(getSrid()); + facet.setData(getDataString()); + facet.setFilter(getFilter()); + facet.setGeometryType(getGeometryType()); + + facets.add(facet); + + return null; + } + + protected abstract String getFacetType(); + + protected abstract String getTitle(CallMeta meta); + + protected abstract String getUrl(); + + protected abstract String getSrid(); + + protected abstract Envelope getExtent(); + + protected abstract String getFilter(); + + protected abstract String getDataString(); + + protected abstract String getGeometryType(); + } // end of WMSDBState +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WMSKmArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,161 @@ +package de.intevation.flys.artifacts; + +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; + +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.flys.model.River; +import de.intevation.flys.model.RiverAxisKm; + +import de.intevation.flys.artifacts.WMSDBArtifact.WMSDBState; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.utils.FLYSUtils; + + +public class WMSKmArtifact extends WMSDBArtifact { + + public static final String NAME = "wmskm"; + + + private static final Logger logger = Logger.getLogger(WMSKmArtifact.class); + + + @Override + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + logger.debug("WMSKmArtifact.setup"); + + super.setup(identifier, factory, context, callMeta, data); + } + + + @Override + public String getName() { + return NAME; + } + + + @Override + public State getCurrentState(Object cc) { + State s = new WMSKmState(this); + + List<Facet> fs = facets.get(getCurrentStateId()); + + DefaultOutput o = new DefaultOutput( + "floodmap", + "floodmap", + "image/png", + fs, + "map"); + + s.getOutputs().add(o); + + return s; + } + + + + public static class WMSKmState extends WMSDBState implements FacetTypes { + + private static final Logger logger = Logger.getLogger(WMSKmState.class); + + protected Geometry geom; + protected int riverId; + + public WMSKmState(WMSDBArtifact artifact) { + super(artifact); + riverId = 0; + } + + public int getRiverId() { + if (riverId == 0) { + String ids = artifact.getDataAsString("ids"); + + try { + riverId = Integer.valueOf(ids); + } + catch (NumberFormatException nfe) { + logger.error("Cannot parse river id from '" + ids + "'"); + } + } + + return riverId; + } + + @Override + protected String getFacetType() { + return FLOODMAP_KMS; + } + + @Override + protected String getTitle(CallMeta meta) { + return Resources.getMsg(meta, FLOODMAP_KMS, FLOODMAP_KMS); + } + + @Override + protected String getUrl() { + return FLYSUtils.getUserWMSUrl(artifact.identifier()); + } + + @Override + protected String getSrid() { + River river = RiverFactory.getRiver(getRiverId()); + return FLYSUtils.getRiverSrid(river.getName()); + } + + @Override + protected Envelope getExtent() { + List<RiverAxisKm> kms = RiverAxisKm.getRiverAxisKms(getRiverId()); + + Envelope max = null; + + for (RiverAxisKm km: kms) { + Envelope env = km.getGeom().getEnvelopeInternal(); + + if (max == null) { + max = env; + continue; + } + + max.expandToInclude(env); + } + + return max; + } + + @Override + protected String getFilter() { + return "river_id=" + String.valueOf(getRiverId()); + } + + @Override + protected String getDataString() { + return "geom FROM river_axes_km USING UNIQUE id USING SRID 31466"; + } + + @Override + protected String getGeometryType() { + return "POINT"; + } + } // end of WMSKmState +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterlevelArtifact.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,117 @@ +package de.intevation.flys.artifacts; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallMeta; + +import de.intevation.flys.artifacts.states.DefaultState; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + + +/** + * Clone of an WINFOArtifact to expose exactly one waterlevel only. + * All Facets of the "longitudinal_section" output will be added to the + * "w_differences" output and filterFacets adjusted accordingly. + * + * @TODO Straighten inheritance-line (waterlevel-WINFO or vice versa). + */ +public class WaterlevelArtifact extends WINFOArtifact { + + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(WaterlevelArtifact.class); + + /** The name of the artifact. */ + public static final String ARTIFACT_NAME = "waterlevel"; + + + /** + * The default constructor. + */ + public WaterlevelArtifact() { + } + + + /** + * Setup and restate longitudinal_section filterfacets to apply to the + * w_differences output, too. + */ + public void setup( + String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + super.setup(identifier, factory, context, callMeta, data); + if(filterFacets != null) { + filterFacets.put( + "w_differences", + filterFacets.get("longitudinal_section")); + } + } + + /** + * Clone important stuff of an WINFOArtifact. + * @param artifact the WINFOArtifact to clone stuff from. + */ + protected void initialize( + Artifact artifact, + Object context, + CallMeta meta) + { + WINFOArtifact winfo = (WINFOArtifact) artifact; + this.data = winfo.cloneData(); + logger.debug("Cloned data of winfo artifact."); + // Statically add Facets. + List<Facet> fs = new ArrayList<Facet>(); + DefaultState state = (DefaultState) getCurrentState(context); + state.computeInit(this, hash(), context, meta, fs); + if (!fs.isEmpty()) { + logger.debug("Facets to add in WaterlevelArtifact.initialize ."); + facets.put(getCurrentStateId(), fs); + } + else { + logger.debug("No facets to add in WaterlevelArtifact.initialize (" + + state.getID() + ")."); + } + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return ARTIFACT_NAME; + } + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). + * WaterlevelArtifact Facets should come to live "inactive" (always + * return 0). + * + * @param facetName name of the facet. + * @param index index of the facet. + * + * @return Always 0 to have Facets initial predisposition to "inactive". + */ + @Override + public int getInitialFacetActivity( + String outputName, + String facetName, + int index) + { + return 0; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,79 @@ +package de.intevation.flys.artifacts.cache; + +import de.intevation.artifacts.common.utils.Config; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheException; +import net.sf.ehcache.CacheManager; + +import org.apache.log4j.Logger; + +public final class CacheFactory +{ + private static Logger log = Logger.getLogger(CacheFactory.class); + + public static final String CACHE_CONFIG_FILE_PROPERTY = + "flys.artifacts.cache.config.file"; + + public static final String XPATH_CACHE_CONFIG_FILE = + "/artifact-database/cache/config-file/text()"; + + private CacheFactory() { + } + + private static boolean initialized; + + private static CacheManager cacheManager; + + public static final Cache getCache() { + return getCache(Cache.DEFAULT_CACHE_NAME); + } + + public static final String getConfigFile() { + String configFile = System.getProperty(CACHE_CONFIG_FILE_PROPERTY); + + if (configFile != null) { + return configFile; + } + + configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE); + + if (configFile != null) { + configFile = Config.replaceConfigDir(configFile); + } + + return configFile; + } + + public static final synchronized Cache getCache(String cacheName) { + if (!initialized) { + initialized = true; // try only once + String configFile = getConfigFile(); + if (configFile != null) { + try { + cacheManager = CacheManager.create(configFile); + //System.setProperty( + // "net.sf.ehcache.enableShutdownHook", "true"); + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + log.info("shutting down caches"); + for (String name: cacheManager.getCacheNames()) { + log.info("\tflushing '" + name + "'"); + cacheManager.getCache(name).flush(); + } + cacheManager.shutdown(); + } + }); + } + catch (CacheException ce) { + log.error("cannot configure cache", ce); + } + } + } + + return cacheManager != null + ? cacheManager.getCache(cacheName) + : null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,536 @@ +package de.intevation.flys.artifacts.charts; + +import de.intevation.flys.backend.SessionFactoryProvider; + +import de.intevation.flys.geom.Lines; + +import de.intevation.flys.model.CrossSection; +import de.intevation.flys.model.CrossSectionLine; +import de.intevation.flys.model.CrossSectionPoint; + +import de.intevation.flys.utils.Pair; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import java.awt.geom.Point2D; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import java.math.MathContext; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; + +import javax.swing.table.AbstractTableModel; + +import org.hibernate.Query; +import org.hibernate.Session; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartUtilities; +import org.jfree.chart.JFreeChart; + +import org.jfree.chart.axis.NumberAxis; + +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; + +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import org.jfree.ui.ApplicationFrame; +import org.jfree.ui.RefineryUtilities; + +public class CrossSectionApp +extends ApplicationFrame +{ + public static final String RIVER = System.getProperty("river", "Saar"); + + public static final double EPSILON = 1e-4; + + protected Session session; + + protected JComboBox crossSectionLinesCB; + protected JTextField waterlevelTF; + + protected ChartPanel chartPanel; + + protected Double lastWaterLevel; + + protected List<CrossSection> crossSections; + protected boolean [] drawCrossSection; + protected boolean [] drawWaterLevel; + protected boolean [] drawGround; + + protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines; + + public class CrossSectionTableModel extends AbstractTableModel { + + @Override + public String getColumnName(int col) { + switch (col) { + case 0: return "Peilungsname"; + case 1: return "Peilung"; + case 2: return "Wasserstand"; + case 3: return "Boden"; + } + return ""; + } + + @Override + public int getColumnCount() { + return 4; + } + + @Override + public int getRowCount() { + return crossSections != null ? crossSections.size() : 0; + } + + @Override + public Object getValueAt(int row, int col) { + if (crossSections == null) return null; + switch (col) { + case 0: return crossSections.get(row).getDescription(); + case 1: return drawCrossSection[row]; + case 2: return drawWaterLevel[row]; + case 3: return drawGround[row]; + } + return null; + } + + @Override + public void setValueAt(Object value, int row, int col) { + switch (col) { + case 1: + if (change(drawCrossSection, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + case 2: + if (change(drawWaterLevel, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + case 3: + if (change(drawGround, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + } + } + + @Override + public Class<?> getColumnClass(int columnIndex) { + switch (columnIndex) { + case 0: return String.class; + case 1: return Boolean.class; + case 2: return Boolean.class; + case 3: return Boolean.class; + } + return null; + } + + @Override + public boolean isCellEditable( + int rowIndex, + int columnIndex + ) { + return columnIndex >= 1 && columnIndex <= 3; + } + } // class CrossSectionTableModel + + private static boolean change( + boolean [] values, + int index, + boolean value + ) { + if (values[index] != value) { + values[index] = value; + return true; + } + return false; + } + + public static class CrossSectionLineItem { + + Double km; + List<Pair<CrossSection, CrossSectionLine>> lines; + + public CrossSectionLineItem( + Double km, + List<Pair<CrossSection, CrossSectionLine>> lines + ) { + this.km = km; + this.lines = lines; + } + + public String toString() { + return String.valueOf(km); + } + } // CrossSectionLineItem + + public CrossSectionApp(String title) { + super(title); + + session = SessionFactoryProvider + .createSessionFactory() + .openSession(); + + JPanel content = createContent(); + content.setPreferredSize(new Dimension(800, 480)); + setContentPane(content); + + } + + public List<CrossSection> crossSections(String river) { + Query query = session.createQuery( + "from CrossSection where river.name = :river"); + query.setParameter("river", river); + return query.list(); + } + + protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> loadAllLines( + List<CrossSection> crossSections + ) { + Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines = + new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>(); + for (CrossSection cs: crossSections) { + List<CrossSectionLine> lines = cs.getLines(); + for (CrossSectionLine csl: lines) { + Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d; + List<Pair<CrossSection, CrossSectionLine>> ls = km2lines.get(km); + if (ls == null) { + ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2); + km2lines.put(km, ls); + } + ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl)); + } + } + return km2lines; + } + + public JPanel createContent() { + JPanel panel = new JPanel(new BorderLayout()); + + + JPanel nav = new JPanel(new FlowLayout()); + + crossSections = crossSections(RIVER); + km2lines = loadAllLines(crossSections); + drawCrossSection = new boolean[crossSections.size()]; + Arrays.fill(drawCrossSection, true); + + drawWaterLevel = new boolean[crossSections.size()]; + drawGround = new boolean[crossSections.size()]; + + Object [] clis = createCrossSectionLineItems(km2lines); + + DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis); + + crossSectionLinesCB = new JComboBox(dcbm); + + nav.add(crossSectionLinesCB); + + crossSectionLinesCB.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent ie) { + if (ie.getStateChange() == ItemEvent.SELECTED) { + updateChart(); + } + } + }); + + waterlevelTF = new JTextField(5); + + waterlevelTF.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + waterLevelChanged(); + } + }); + + nav.add(waterlevelTF); + + JButton dump = new JButton("dump"); + + dump.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + dumpData(); + } + }); + + nav.add(dump); + + + chartPanel = createChartPanel(); + + panel.add(chartPanel, BorderLayout.CENTER); + + + CrossSectionTableModel cstm = new CrossSectionTableModel(); + + cstm.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + updateChart(); + } + }); + + JTable crossTable = new JTable(cstm); + + JPanel west = new JPanel(new BorderLayout()); + JScrollPane scrollPane = new JScrollPane(crossTable); + west.add(scrollPane); + + west.add(nav, BorderLayout.SOUTH); + + panel.add(west, BorderLayout.WEST); + + return panel; + } + + protected void waterLevelChanged() { + String value = waterlevelTF.getText(); + try { + lastWaterLevel = Double.parseDouble(value); + } + catch (NumberFormatException nfe) { + waterlevelTF.setText( + lastWaterLevel != null ? lastWaterLevel.toString() : ""); + return; + } + updateChart(); + } + + protected void updateChart() { + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + JFreeChart chart = createChart(csli == null + ? new XYSeriesCollection() + : generateDataset()); + + chartPanel.setChart(chart); + } + + protected ChartPanel createChartPanel() { + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + JFreeChart chart = createChart(csli == null + ? new XYSeriesCollection() + : generateDataset()); + + return new ChartPanel(chart); + } + + protected void dumpData() { + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + if (csli == null) { + return; + } + + + double km = Math.round(csli.km.doubleValue() * 1000d)/1000d; + + String kmS = String.valueOf(km).replace(".", "-"); + + int i = 1; + File file = new File("cross-section-" + kmS + ".txt"); + while (file.exists()) { + file = new File("cross-section-" + kmS + "[" + (i++) + "].txt"); + } + + System.err.println("dump points to file '" + file + "'"); + + PrintWriter out = null; + + MathContext mc = new MathContext(3); + + try { + out = + new PrintWriter( + new FileWriter(file)); + + for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) { + out.println("# " + pair.getA().getDescription()); + for (CrossSectionPoint point: pair.getB().getPoints()) { + out.println( + point.getX().round(mc) + " " + + point.getY().round(mc)); + } + } + + out.flush(); + } + catch (IOException ioe) { + ioe.printStackTrace(); + } + finally { + if (out != null) { + out.close(); + } + } + } + + public void generateWaterLevels( + List<Point2D> points, + XYSeriesCollection collection + ) { + if (points == null || points.isEmpty()) { + return; + } + + if (lastWaterLevel != null) { + double [][] data = Lines.createWaterLines(points, lastWaterLevel); + XYSeries series = + new XYSeries(String.valueOf(lastWaterLevel), false); + + double [] x = data[0]; + double [] y = data[1]; + for (int i = 0; i < x.length; ++i) { + series.add(x[i], y[i], false); + } + + collection.addSeries(series); + } + } + + public void generateProfile( + List<Point2D> points, + String legend, + XYSeriesCollection collection + ) { + if (points == null || points.isEmpty()) { + return; + } + + double [][] values = CrossSectionLine.fetchCrossSectionProfile(points); + + XYSeries series = new XYSeries(legend, false); + + double [] x = values[0]; + double [] y = values[1]; + for (int i = 0; i < x.length; ++i) { + series.add(x[i], y[i], false); + } + + collection.addSeries(series); + } + + public XYSeriesCollection generateDataset() { + XYSeriesCollection collection = new XYSeriesCollection(); + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + for (int i = 0; i < drawCrossSection.length; ++i) { + List<Point2D> points = null; + + if (drawCrossSection[i]) { + CrossSection cs = crossSections.get(i); + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + points = csl.getB().fetchCrossSectionLinesPoints(); + generateProfile( + points, + cs.getDescription(), + collection); + break; + } + } + } + + if (drawWaterLevel[i]) { + CrossSection cs = crossSections.get(i); + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + if (points != null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + generateWaterLevels( + points, + collection); + break; + } + } + } + + } + + return collection; + } + + protected Object [] createCrossSectionLineItems( + Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines + ) { + Object [] result = new Object[km2lines.size()]; + int i = 0; + for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry: + km2lines.entrySet()) { + result[i++] = new CrossSectionLineItem( + entry.getKey(), + entry.getValue()); + } + return result; + } + + + public static JFreeChart createChart(XYSeriesCollection collection) { + JFreeChart chart = ChartFactory.createXYLineChart( + null, + "Abstand [m]", + "H\u00f6he [m]", + collection, + PlotOrientation.VERTICAL, + true, + true, + false); + + XYPlot plot = chart.getXYPlot(); + NumberAxis yAxis = (NumberAxis)plot.getRangeAxis(); + yAxis.setAutoRangeIncludesZero(false); + + ChartUtilities.applyCurrentTheme(chart); + return chart; + } + + public static void main(String [] args) { + CrossSectionApp csa = new CrossSectionApp("Querprofile"); + csa.pack(); + RefineryUtilities.centerFrameOnScreen(csa); + csa.setVisible(true); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,65 @@ +package de.intevation.flys.artifacts.context; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifactdatabase.DefaultArtifactContext; + + +/** + * This class is used to store application wide information in a global context. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSContext extends DefaultArtifactContext { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(FLYSContext.class); + + /** The key that is used to store the StateEngine in the context. */ + public static final String ARTIFACT_KEY = + "artifact"; + + /** The key that is used to store the TransitionEngine in the context. */ + public static final String TRANSITION_ENGINE_KEY = + "artifact.transition.engine"; + + /** The key that is used to store the StateEngine in the context. */ + public static final String STATE_ENGINE_KEY = + "artifact.state.engine"; + + /** The key that is used to store the Map of OutGenerator classes in the + * context. */ + public static final String OUTGENERATORS_KEY = + "flys.export.outgenerators"; + + /** The key that is used to store the map of themes in the context. */ + public static final String THEMES = + "flys.themes.map"; + + /** The key that is used to store a map of theme mappings in the context. */ + public static final String THEME_MAPPING = + "flys.themes.mapping.map"; + + /** The key that is used to store a map of WMS urls for each river. */ + public static final String RIVER_WMS = + "flys.floodmap.river.wms"; + + + /** + * The default constructor. + */ + public FLYSContext() { + super(); + } + + + /** + * A constructor with a config document. + */ + public FLYSContext(Document config) { + super(config); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,412 @@ +package de.intevation.flys.artifacts.context; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactContextFactory; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; +import de.intevation.artifactdatabase.transition.Transition; +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.flys.artifacts.states.StateFactory; +import de.intevation.flys.artifacts.transitions.TransitionFactory; +import de.intevation.flys.themes.Theme; +import de.intevation.flys.themes.ThemeFactory; +import de.intevation.flys.themes.ThemeMapping; + + +/** + * The ArtifactContextFactory is used to initialize basic components and put + * them into the global context of the application. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSContextFactory implements ArtifactContextFactory { + + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(FLYSContextFactory.class); + + /** The XPath to the artifacts configured in the configuration. */ + public static final String XPATH_ARTIFACTS = + "/artifact-database/artifacts/artifact"; + + /** The XPath to the name of the artifact. */ + public static final String XPATH_ARTIFACT_NAME = "/artifact/@name"; + + /** The XPath to the xlink ref in an artifact configuration. */ + public static final String XPATH_XLINK = "xlink:href"; + + /** The XPath to the transitions configured in the artifact config. */ + public static final String XPATH_TRANSITIONS = + "/artifact/states/transition"; + + /** The XPath to the states configured in the artifact config. */ + public static final String XPATH_STATES = + "/artifact/states/state"; + + public static final String XPATH_OUTPUT_GENERATORS = + "/artifact-database/output-generators/output-generator"; + + public static final String XPATH_THEME_CONFIG = + "/artifact-database/flys/themes/configuration/text()"; + + public static final String XPATH_THEMES = + "/themes/theme"; + + public static final String XPATH_THEME_MAPPINGS = + "/themes/mappings/mapping"; + + public static final String XPATH_RIVER_WMS = + "/artifact-database/floodmap/river"; + + /** + * Creates a new FLYSArtifactContext object and initialize all + * components required by the application. + * + * @param config The artifact server configuration. + * @return a FLYSArtifactContext. + */ + public GlobalContext createArtifactContext(Document config) { + FLYSContext context = new FLYSContext(config); + + configureTransitions(config, context); + configureStates(config, context); + configureOutGenerators(config, context); + configureThemes(config, context); + configureThemesMappings(config, context); + configureRiverWMS(config, context); + + return context; + } + + + /** + * This method initializes the transition configuration. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureTransitions(Document config, FLYSContext context) { + TransitionEngine engine = new TransitionEngine(); + + Document[] artifacts = getArtifactConfigurations(config); + logger.info("Found " + artifacts.length + " artifacts in the config."); + + for (Document doc: artifacts) { + List<Transition> transitions = new ArrayList<Transition>(); + + String artName = (String) XMLUtils.xpath( + doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING); + + NodeList list = (NodeList) XMLUtils.xpath( + doc, XPATH_TRANSITIONS, XPathConstants.NODESET); + + if (list == null) { + logger.warn("The artifact has no transitions configured."); + continue; + } + + int trans = list.getLength(); + + logger.info( + "Artifact '" + artName + "' has " + trans + " transitions."); + + for (int i = 0; i < trans; i++) { + Transition t = TransitionFactory.createTransition(list.item(i)); + String s = t.getFrom(); + engine.addTransition(s, t); + } + } + + context.put(FLYSContext.TRANSITION_ENGINE_KEY, engine); + } + + + /** + * This method returns all artifact documents defined in + * <code>config</code>. <br>NOTE: The artifact configurations need to be + * stored in own files referenced by an xlink. + * + * @param config The global configuration. + * + * @return an array of Artifact configurations. + */ + protected Document[] getArtifactConfigurations(Document config) { + NodeList artifacts = (NodeList) XMLUtils.xpath( + config, XPATH_ARTIFACTS, XPathConstants.NODESET); + + int count = artifacts.getLength(); + + Document[] artifactDocs = new Document[count]; + + for (int i = 0; i < count; i++) { + Element tmp = (Element) artifacts.item(i); + + String xlink = tmp.getAttribute(XPATH_XLINK); + xlink = Config.replaceConfigDir(xlink); + + File artifactFile = new File(xlink); + artifactDocs[i] = XMLUtils.parseDocument(artifactFile); + } + + return artifactDocs; + } + + + /** + * This method initializes the transition configuration. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureStates(Document config, FLYSContext context) { + StateEngine engine = new StateEngine(); + + Document[] artifacts = getArtifactConfigurations(config); + logger.info("Found " + artifacts.length + " artifacts in the config."); + + for (Document doc: artifacts) { + List<State> states = new ArrayList<State>(); + + String artName = (String) XMLUtils.xpath( + doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING); + + NodeList stateList = (NodeList) XMLUtils.xpath( + doc, XPATH_STATES, XPathConstants.NODESET); + + if (stateList == null) { + logger.warn("The artifact has no states configured."); + continue; + } + + int count = stateList.getLength(); + + logger.info( + "Artifact '" + artName + "' has " + count + " states."); + + for (int i = 0; i < count; i++) { + states.add(StateFactory.createState( + stateList.item(i))); + } + + engine.addStates(artName, states); + } + + context.put(FLYSContext.STATE_ENGINE_KEY, engine); + } + + + /** + * This method intializes the provided output generators. + * + * @param config the config document. + * @param context the FLYSContext. + */ + protected void configureOutGenerators(Document config, FLYSContext context){ + Map<String, Class> generators = new HashMap<String, Class>(); + + NodeList outGenerators = (NodeList) XMLUtils.xpath( + config, + XPATH_OUTPUT_GENERATORS, + XPathConstants.NODESET); + + int num = outGenerators == null ? 0 : outGenerators.getLength(); + + if (num == 0) { + logger.warn("No output generators configured in this application."); + return; + } + + logger.info("Found " + num + " configured output generators."); + + int idx = 0; + + for (int i = 0; i < num; i++) { + Node item = outGenerators.item(i); + + String name = (String) XMLUtils.xpath( + item, "@name", XPathConstants.STRING); + + String clazz = (String) XMLUtils.xpath( + item, "text()", XPathConstants.STRING); + + if (name == null || clazz == null) { + continue; + } + + try { + generators.put(name, Class.forName(clazz)); + + idx++; + } + catch (ClassNotFoundException cnfe) { + logger.warn(cnfe, cnfe); + } + } + + logger.info("Successfully loaded " + idx + " output generators."); + context.put(FLYSContext.OUTGENERATORS_KEY, generators); + } + + + /** + * This methods reads the configured themes and puts them into the + * FLYSContext. + * + * @param config The global configuration. + * @param context The FLYSContext. + */ + protected void configureThemes(Document config, FLYSContext context) { + logger.debug("FLYSContextFactory.configureThemes"); + + Document cfg = getThemeConfig(config); + + NodeList themes = (NodeList) XMLUtils.xpath( + cfg, XPATH_THEMES, XPathConstants.NODESET); + + int num = themes != null ? themes.getLength() : 0; + + if (num == 0) { + logger.warn("There are no themes configured!"); + return; + } + + logger.debug("Found " + num + " configured themes."); + + Map<String, Theme> theThemes = new HashMap<String, Theme>(); + + for (int i = 0; i < num; i++) { + Node theme = themes.item(i); + + Theme theTheme = ThemeFactory.createTheme(cfg, theme); + + if (theme != null) { + theThemes.put(theTheme.getName(), theTheme); + } + } + + logger.debug("Initialized " + theThemes.size() + "/" + num + " themes"); + + context.put(FLYSContext.THEMES, theThemes); + } + + + /** + * This method is used to retrieve the theme configuration document. + * + * @param config The global configuration. + * + * @return the theme configuration. + */ + protected Document getThemeConfig(Document config) { + String themeConfig = (String) XMLUtils.xpath( + config, + XPATH_THEME_CONFIG, + XPathConstants.STRING); + + themeConfig = Config.replaceConfigDir(themeConfig); + + logger.debug("Parse theme cfg: " + themeConfig); + + return XMLUtils.parseDocument(new File(themeConfig)); + } + + + protected void configureThemesMappings(Document cfg, FLYSContext context) { + logger.debug("FLYSContextFactory.configureThemesMappings"); + + Document config = getThemeConfig(cfg); + + NodeList mappings = (NodeList) XMLUtils.xpath( + config, XPATH_THEME_MAPPINGS, XPathConstants.NODESET); + + int num = mappings != null ? mappings.getLength() : 0; + + if (num == 0) { + logger.warn("No theme <--> facet mappins found!"); + return; + } + + Map<String, List<ThemeMapping>> mapping = + new HashMap<String, List<ThemeMapping>>(); + + for (int i = 0; i < num; i++) { + Node node = mappings.item(i); + + String from = (String) XMLUtils.xpath( + node, "@from", XPathConstants.STRING); + + String to = (String) XMLUtils.xpath( + node, "@to", XPathConstants.STRING); + + String pattern = (String) XMLUtils.xpath( + node, "@pattern", XPathConstants.STRING); + + String masterAttrPattern = (String) XMLUtils.xpath( + node, "@masterAttr", XPathConstants.STRING); + + String outputPattern = (String) XMLUtils.xpath( + node, "@output", XPathConstants.STRING); + + if (from != null && to != null) { + List<ThemeMapping> tm = mapping.get(from); + + if (tm == null) { + tm = new ArrayList<ThemeMapping>(); + mapping.put(from, tm); + } + + tm.add(new ThemeMapping( + from, to, pattern, masterAttrPattern, outputPattern)); + } + } + + logger.debug("Found " + mapping.size() + " theme mappings."); + + context.put(FLYSContext.THEME_MAPPING, mapping); + } + + + protected void configureRiverWMS(Document cfg, FLYSContext context) { + Map<String, String> riverWMS = new HashMap<String, String>(); + + NodeList rivers = (NodeList) XMLUtils.xpath( + cfg, XPATH_RIVER_WMS, XPathConstants.NODESET); + + int num = rivers != null ? rivers.getLength() : 0; + + for (int i = 0; i < num; i++) { + Element e = (Element) rivers.item(i); + + String river = e.getAttribute("name"); + String url = XMLUtils.xpathString(e, "river-wms/@url", null); + + if (river != null && url != null) { + riverWMS.put(river, url); + } + } + + logger.debug("Found " + riverWMS.size() + " river WMS."); + + context.put(FLYSContext.RIVER_WMS, riverWMS); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,68 @@ +package de.intevation.flys.artifacts.context; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import org.hibernate.Session; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallContext.Listener; + + +/** + * This CallContextListener is used to initialize a ThreadLocal variable in + * each CallContext (for each request) that holds Sessions. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class SessionCallContextListener implements Listener { + + public static final String SESSION_KEY = "context.session"; + + /** The logger that is used in this class.*/ + private static Logger logger = + Logger.getLogger(SessionCallContextListener.class); + + + public SessionCallContextListener() { + } + + + public void setup(Document config, Node listenerConfig) { + // nothing to do here + } + + + /** + * Initializes a ThreadLocal variable that is used to hold sessions. + * + * @param context The CallContext. + */ + public void init(CallContext context) { + logger.debug("SessionCallContextListener.init"); + + Session session = SessionHolder.acquire(); + + context.putContextValue(SESSION_KEY, session); + } + + + /** + * Closes open sessions of the ThreadLocal variable opened in init(). + * + * @param context The CallContext. + */ + public void close(CallContext context) { + logger.debug("SessionCallContextListener.close"); + + Session session = (Session)context.getContextValue(SESSION_KEY); + session.close(); + + SessionHolder.release(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,108 @@ +package de.intevation.flys.artifacts.datacage; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifactdatabase.db.SQL; +import de.intevation.artifactdatabase.db.DBConnection; + +import org.apache.log4j.Logger; + +public class DBConfig +{ + private static Logger logger = Logger.getLogger(DBConfig.class); + + /** + * XPath to access the database driver within the global configuration. + */ + public static final String DB_DRIVER = + "/artifact-database/datacage/driver/text()"; + /** + * XPath to access the database URL within the global configuration. + */ + public static final String DB_URL = + "/artifact-database/datacage/url/text()"; + /** + * XPath to access the database use within the global configuration. + */ + public static final String DB_USER = + "/artifact-database/datacage/user/text()"; + /** + * XPath to access the database password within the global configuration. + */ + public static final String DB_PASSWORD = + "/artifact-database/datacage/password/text()"; + + /** + * The default database driver: H2 + */ + public static final String DEFAULT_DRIVER = + "org.h2.Driver"; + + /** + * The default database user: "" + */ + public static final String DEFAULT_USER = ""; + + /** + * The default database password: "" + */ + public static final String DEFAULT_PASSWORD = ""; + + + public static final String DEFAULT_URL = + "jdbc:h2:mem:datacage;INIT=RUNSCRIPT FROM '${artifacts.config.dir}/datacage.sql'"; + + public static final String RESOURCE_PATH = "/datacage-sql"; + + private static DBConfig instance; + + protected DBConnection dbConnection; + protected SQL sql; + + public DBConfig() { + } + + public DBConfig(DBConnection dbConnection, SQL sql) { + this.dbConnection = dbConnection; + this.sql = sql; + } + + public static synchronized DBConfig getInstance() { + if (instance == null) { + instance = createInstance(); + } + return instance; + } + + protected static DBConfig createInstance() { + String driver = Config.getStringXPath( + DB_DRIVER, DEFAULT_DRIVER); + + String url = Config.getStringXPath( + DB_URL, DEFAULT_URL); + + url = Config.replaceConfigDir(url); + + String user = Config.getStringXPath( + DB_USER, DEFAULT_USER); + + String password = Config.getStringXPath( + DB_PASSWORD, DEFAULT_PASSWORD); + + DBConnection dbConnection = new DBConnection( + driver, url, user, password); + + SQL sql = new SQL(DBConfig.class, RESOURCE_PATH, driver); + + return new DBConfig(dbConnection, sql); + } + + public DBConnection getDBConnection() { + return dbConnection; + } + + public SQL getSQL() { + return sql; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Datacage.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1074 @@ +package de.intevation.flys.artifacts.datacage; + +import java.util.Collection; +import java.util.List; +import java.util.Date; + +import java.sql.SQLException; +import java.sql.PreparedStatement; +import java.sql.Types; +import java.sql.Timestamp; + +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.User; + +import de.intevation.artifactdatabase.db.SQL; +import de.intevation.artifactdatabase.db.SQLExecutor; + +import de.intevation.artifactdatabase.LifetimeListener; +import de.intevation.artifactdatabase.Backend; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.artifacts.common.utils.LRUCache; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +public class Datacage +implements LifetimeListener +{ + private static Logger log = Logger.getLogger(Datacage.class); + + public static final String DATACAGE_KEY = + "global.datacage.instance"; + + public static final String ARTEFACT_DATABASE_KEY = + "global.artifact.database"; + + private String SQL_DELETE_ALL_USERS = "delete.all.users"; + private String SQL_DELETE_ALL_ARTIFACTS = "delete.all.artifacts"; + private String SQL_USER_ID_NEXTVAL = "user.id.nextval"; + private String SQL_USER_BY_GID = "user.by.gid"; + private String SQL_INSERT_USER = "insert.user"; + private String SQL_COLLECTION_BY_GID = "collection.by.gid"; + private String SQL_COLLECTION_ID_NEXTVAL = "collection.id.nextval"; + private String SQL_INSERT_COLLECTION = "insert.collection"; + private String SQL_ARTIFACT_BY_GID = "artifact.by.gid"; + private String SQL_COLLECTION_ITEM_ID_NEXTVAL = + "collection.item.id.nextval"; + private String SQL_INSERT_COLLECTION_ITEM = "insert.collection.item"; + private String SQL_ARTIFACT_ID_NEXTVAL = "artifact.id.nextval"; + private String SQL_INSERT_ARTIFACT = "insert.artifact"; + private String SQL_ARTIFACT_DATA_ID_NEXTVAL = "artifact.data.id.nextval"; + private String SQL_INSERT_ARTIFACT_DATA = "insert.artifact.data"; + private String SQL_OUT_ID_NEXTVALUE = "out.id.nextval"; + private String SQL_INSERT_OUT = "insert.out"; + private String SQL_FACET_ID_NEXTVAL = "facet.id.nextval"; + private String SQL_INSERT_FACET = "insert.facet"; + private String SQL_UPDATE_COLLECTION_NAME = "update.collection.name"; + private String SQL_DELETE_ARTIFACT_FROM_COLLECTION = + "delete.artifact.from.collection"; + private String SQL_DELETE_COLLECTION_BY_GID = + "delete.collection.by.gid"; + private String SQL_DELETE_USER_BY_GID = "delete.user.by.gid"; + private String SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID = + "delete.artifact.data.by.artifact.id"; + private String SQL_DELETE_OUTS_BY_ARTIFACT_ID = + "delete.outs.by.artifact.id"; + private String SQL_DELETE_FACETS_BY_ARTIFACT_ID = + "delete.facets.by.artifact.id"; + private String SQL_DELETE_ARTIFACT_BY_GID = + "delete.artifact.by.gid"; + + protected SQLExecutor sqlExecutor; + + public class InitialScan + implements ArtifactDatabase.ArtifactLoadedCallback + { + protected LRUCache<String, Integer> users; + protected LRUCache<String, Integer> collections; + protected LRUCache<String, Integer> artifacts; + + protected GlobalContext context; + + public InitialScan() { + users = new LRUCache<String, Integer>(); + collections = new LRUCache<String, Integer>(); + artifacts = new LRUCache<String, Integer>(); + } + + public InitialScan(GlobalContext context) { + this(); + this.context = context; + } + + @Override + public void artifactLoaded( + String userId, + String collectionId, + String collectionName, + Date collectionCreated, + String artifactId, + Date artifactCreated, + Artifact artifact + ) { + if (!(artifact instanceof FLYSArtifact)) { + log.warn("ignoring none FLYS artifacts"); + return; + } + + FLYSArtifact flysArtifact = (FLYSArtifact)artifact; + + Integer uId = getUserId(userId); + Integer cId = getCollectionId( + collectionId, uId, collectionName, collectionCreated); + + storeArtifact(artifactId, cId, flysArtifact, artifactCreated); + } + + protected Integer getId( + LRUCache<String, Integer> cache, + final String idString, + final String selectById + ) { + Integer id = cache.get(idString); + if (id != null) { + return id; + } + + final Integer [] res = new Integer[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(selectById); + stmnt.setString(1, idString); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + res[0] = result.getInt(1); + return true; + } + }; + + if (exec.runRead()) { + cache.put(idString, res[0]); + return res[0]; + } + + return null; + } + + protected void storeArtifact( + final String artifactId, + Integer collectionId, + final FLYSArtifact artifact, + final Date artifactCreated + ) { + Integer aId = getId(artifacts, artifactId, SQL_ARTIFACT_BY_GID); + + if (aId != null) { + // We've already stored it. Just create the collection item. + storeCollectionItem(collectionId, aId); + return; + } + // We need to write it to database + + final Integer [] res = new Integer[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_ARTIFACT_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + res[0] = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_ARTIFACT); + stmnt.setInt (1, res[0]); + stmnt.setString(2, artifactId); + stmnt.setString(3, artifact.getCurrentStateId()); + Timestamp timestamp = new Timestamp(artifactCreated != null + ? artifactCreated.getTime() + : System.currentTimeMillis()); + stmnt.setTimestamp(4, timestamp); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing of artifact failed."); + return; + } + + artifacts.put(artifactId, aId = res[0]); + + storeCollectionItem(collectionId, aId); + + storeData(aId, artifact); + + storeOuts(aId, artifact, context); + } + + + protected void storeCollectionItem( + final Integer collectionId, + final Integer artifactId + ) { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int ciId = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_COLLECTION_ITEM); + stmnt.setInt(1, ciId); + stmnt.setInt(2, collectionId); + stmnt.setInt(3, artifactId); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing of collection item failed."); + } + } + + protected Integer getCollectionId( + final String collectionId, + final Integer ownerId, + final String collectionName, + final Date collectionCreated + ) { + Integer c = getId(collections, collectionId, SQL_COLLECTION_BY_GID); + + if (c != null) { + return c; + } + + final Integer [] res = new Integer[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + res[0] = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_COLLECTION); + stmnt.setInt (1, res[0]); + stmnt.setString(2, collectionId); + stmnt.setInt (3, ownerId); + setString(stmnt, 4, collectionName); + Timestamp timestamp = new Timestamp(collectionCreated != null + ? collectionCreated.getTime() + : System.currentTimeMillis()); + stmnt.setTimestamp(5, timestamp); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (exec.runWrite()) { + collections.put(collectionId, res[0]); + return res[0]; + } + + return null; + } + + protected Integer getUserId(final String userId) { + + Integer u = getId(users, userId, SQL_USER_BY_GID); + + if (u != null) { + return u; + } + + final Integer [] res = new Integer[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_USER_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + res[0] = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_USER); + stmnt.setInt (1, res[0]); + stmnt.setString(2, userId); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (exec.runWrite()) { + users.put(userId, res[0]); + return res[0]; + } + + return null; + } + + public boolean scan(ArtifactDatabase adb) { + log.debug("scan"); + try { + adb.loadAllArtifacts(this); + } + catch (ArtifactDatabaseException ade) { + log.error(ade); + return false; + } + return true; + } + } // class InitialScan + + + public Datacage() { + } + + @Override + public void setup(Document document) { + log.debug("setup"); + DBConfig config = DBConfig.getInstance(); + setupSQL(config.getSQL()); + sqlExecutor = new SQLExecutor(config.getDBConnection()); + } + + protected void setupSQL(SQL sql) { + SQL_DELETE_ALL_USERS = sql.get(SQL_DELETE_ALL_USERS); + SQL_DELETE_ALL_ARTIFACTS = sql.get(SQL_DELETE_ALL_ARTIFACTS); + SQL_USER_ID_NEXTVAL = sql.get(SQL_USER_ID_NEXTVAL); + SQL_USER_BY_GID = sql.get(SQL_USER_BY_GID); + SQL_INSERT_USER = sql.get(SQL_INSERT_USER); + SQL_COLLECTION_BY_GID = sql.get(SQL_COLLECTION_BY_GID); + SQL_COLLECTION_ID_NEXTVAL = sql.get(SQL_COLLECTION_ID_NEXTVAL); + SQL_INSERT_COLLECTION = sql.get(SQL_INSERT_COLLECTION); + SQL_ARTIFACT_BY_GID = sql.get(SQL_ARTIFACT_BY_GID); + SQL_COLLECTION_ITEM_ID_NEXTVAL = + sql.get(SQL_COLLECTION_ITEM_ID_NEXTVAL); + SQL_INSERT_COLLECTION_ITEM = + sql.get(SQL_INSERT_COLLECTION_ITEM); + SQL_ARTIFACT_ID_NEXTVAL = sql.get(SQL_ARTIFACT_ID_NEXTVAL); + SQL_INSERT_ARTIFACT = sql.get(SQL_INSERT_ARTIFACT); + SQL_ARTIFACT_DATA_ID_NEXTVAL = sql.get(SQL_ARTIFACT_DATA_ID_NEXTVAL); + SQL_INSERT_ARTIFACT_DATA = sql.get(SQL_INSERT_ARTIFACT_DATA); + SQL_OUT_ID_NEXTVALUE = sql.get(SQL_OUT_ID_NEXTVALUE); + SQL_INSERT_OUT = sql.get(SQL_INSERT_OUT); + SQL_FACET_ID_NEXTVAL = sql.get(SQL_FACET_ID_NEXTVAL); + SQL_INSERT_FACET = sql.get(SQL_INSERT_FACET); + SQL_UPDATE_COLLECTION_NAME = sql.get(SQL_UPDATE_COLLECTION_NAME); + SQL_DELETE_ARTIFACT_FROM_COLLECTION = + sql.get(SQL_DELETE_ARTIFACT_FROM_COLLECTION); + SQL_DELETE_COLLECTION_BY_GID = sql.get(SQL_DELETE_COLLECTION_BY_GID); + SQL_DELETE_USER_BY_GID = sql.get(SQL_DELETE_USER_BY_GID); + SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID = + sql.get(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID); + SQL_DELETE_OUTS_BY_ARTIFACT_ID = + sql.get(SQL_DELETE_OUTS_BY_ARTIFACT_ID); + SQL_DELETE_FACETS_BY_ARTIFACT_ID = + sql.get(SQL_DELETE_FACETS_BY_ARTIFACT_ID); + SQL_DELETE_ARTIFACT_BY_GID = + sql.get(SQL_DELETE_ARTIFACT_BY_GID); + } + + protected static final int numFacets(List<Output> outs) { + int sum = 0; + for (Output out: outs) { + sum += out.getFacets().size(); + } + return sum; + } + + protected static final void setString( + PreparedStatement stmnt, + int index, + Object value + ) + throws SQLException + { + if (value == null) { + stmnt.setNull(index, Types.VARCHAR); + } + else { + stmnt.setString(index, value.toString()); + } + } + + @Override + public void systemUp(GlobalContext context) { + log.debug("systemUp entered"); + initialScan(context); + context.put(DATACAGE_KEY, this); + log.debug("systemUp leaved"); + } + + protected void initialScan(GlobalContext context) { + log.debug("initialScan"); + + Object adbObject = context.get(ARTEFACT_DATABASE_KEY); + + if (!(adbObject instanceof ArtifactDatabase)) { + log.error("missing artefact database. Cannot scan"); + return; + } + + ArtifactDatabase adb = (ArtifactDatabase)adbObject; + + if (!cleanDatabase()) { + log.error("cleaning database failed"); + return; + } + + InitialScan is = new InitialScan(context); + + if (!is.scan(adb)) { + log.error("initial scan failed"); + return; + } + + } + + protected boolean cleanDatabase() { + + log.debug("cleanDatabase"); + + boolean success = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_ALL_USERS); + stmnt.execute(); + prepareStatement(SQL_DELETE_ALL_ARTIFACTS); + stmnt.execute(); + conn.commit(); + return true; + } + }.runWrite(); + + log.debug("after runWrite(): " + success); + + return success; + } + + + @Override + public void systemDown(GlobalContext context) { + log.debug("systemDown"); + } + + public void setup(GlobalContext globalContext) { + log.debug("setup"); + } + + public void createdArtifact( + Artifact artifact, + Backend backend, + GlobalContext context + ) { + log.debug("createdArtifact"); + + if (artifact == null) { + log.warn("artifact to create is null"); + return; + } + + if (!(artifact instanceof FLYSArtifact)) { + log.warn("need FLYSArtifact here (have " + artifact.getClass() + ")"); + return; + } + + final FLYSArtifact flys = (FLYSArtifact)artifact; + + final int [] res = new int[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_ARTIFACT_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("id generation for artifact failed"); + return false; + } + res[0] = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_ARTIFACT); + stmnt.setInt (1, res[0]); + stmnt.setString (2, flys.identifier()); + stmnt.setString (3, flys.getCurrentStateId()); + stmnt.setTimestamp(4, + new Timestamp(System.currentTimeMillis())); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing of artifact failed."); + return; + } + + storeData(res[0], flys); + storeOuts(res[0], flys, context); + } + + public void storedArtifact( + Artifact artifact, + Backend backend, + GlobalContext context + ) { + log.debug("storedArtifact"); + if (!(artifact instanceof FLYSArtifact)) { + log.warn("need FLYSArtifact here but have a " + artifact.getClass()); + return; + } + + final FLYSArtifact flys = (FLYSArtifact)artifact; + + final Integer [] res = new Integer[1]; + + // check first if artifact already exists + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_ARTIFACT_BY_GID); + stmnt.setString(1, flys.identifier()); + result = stmnt.executeQuery(); + if (!result.next()) { + // new artifact + return true; + } + res[0] = result.getInt(1); + return true; + } + }; + + if (!exec.runRead()) { + log.error("querying artifact failed"); + return; + } + + if (res[0] == null) { // new artifact + createdArtifact(artifact, backend, context); + return; + } + + // artifact already exists -> delete old data + exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_ARTIFACT_DATA_BY_ARTIFACT_ID); + stmnt.setInt(1, res[0]); + stmnt.execute(); + prepareStatement(SQL_DELETE_FACETS_BY_ARTIFACT_ID); + stmnt.setInt(1, res[0]); + stmnt.execute(); + prepareStatement(SQL_DELETE_OUTS_BY_ARTIFACT_ID); + stmnt.setInt(1, res[0]); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("deleting old artifact data failed"); + return; + } + + // write new data + storeData(res[0], flys); + storeOuts(res[0], flys, context); + } + + public void createdUser( + final User user, + Backend backend, + GlobalContext context + ) { + log.debug("createdUser"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_USER_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("id generation for user failed"); + return false; + } + int uId = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_USER); + stmnt.setInt(1, uId); + stmnt.setString(2, user.identifier()); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("create user failed"); + } + } + + public void deletedUser( + final String identifier, + Backend backend, + GlobalContext context + ) { + log.debug("deletedUser"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_USER_BY_GID); + stmnt.setString(1, identifier); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("delete user failed"); + } + } + + public void createdCollection( + final ArtifactCollection collection, + Backend backend, + GlobalContext context + ) { + log.debug("createdCollection"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + String userId = collection.getUser().identifier(); + prepareStatement(SQL_USER_BY_GID); + stmnt.setString(1, userId); + result = stmnt.executeQuery(); + int uId; + if (result.next()) { + uId = result.getInt(1); + reset(); + } + else { + // need to create user first + reset(); + prepareStatement(SQL_USER_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("id generation for user failed"); + return false; + } + uId = result.getInt(1); + reset(); + prepareStatement(SQL_INSERT_USER); + stmnt.setInt(1, uId); + stmnt.setString(2, userId); + stmnt.execute(); + conn.commit(); + reset(); + } + + prepareStatement(SQL_COLLECTION_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("id generation for collection failed"); + return false; + } + int cId = result.getInt(1); + reset(); + + String identifier = collection.identifier(); + String name = collection.getName(); + + prepareStatement(SQL_INSERT_COLLECTION); + stmnt.setInt(1, cId); + stmnt.setString(2, identifier); + stmnt.setInt(3, uId); + setString(stmnt, 4, name); + stmnt.setTimestamp(5, + new Timestamp(System.currentTimeMillis())); + stmnt.execute(); + + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("create collection failed"); + } + } + + public void deletedCollection( + final String identifier, + Backend backend, + GlobalContext context + ) { + log.debug("deletedCollection"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_COLLECTION_BY_GID); + stmnt.setString(1, identifier); + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("delete collection failed"); + } + } + + public void changedCollectionAttribute( + String identifier, + Document document, + Backend backend, + GlobalContext context + ) { + log.debug("changedCollectionAttribute"); + } + + public void changedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document, + Backend backend, + GlobalContext context + ) { + log.debug("changedCollectionItemAttribute"); + } + + public void addedArtifactToCollection( + final String artifactId, + final String collectionId, + Backend backend, + GlobalContext context + ) { + log.debug("addedArtifactToCollection"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_ARTIFACT_BY_GID); + stmnt.setString(1, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int aId = result.getInt(1); + reset(); + + prepareStatement(SQL_COLLECTION_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int cId = result.getInt(1); + reset(); + + prepareStatement(SQL_COLLECTION_ITEM_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int ciId = result.getInt(1); + reset(); + + prepareStatement(SQL_INSERT_COLLECTION_ITEM); + stmnt.setInt(1, ciId); + stmnt.setInt(2, cId); + stmnt.setInt(3, aId); + stmnt.execute(); + + conn.commit(); + return true; + } + }; + if (!exec.runWrite()) { + log.error("added artifact to collection failed"); + } + } + + public void removedArtifactFromCollection( + final String artifactId, + final String collectionId, + Backend backend, + GlobalContext context + ) { + log.debug("removedArtifactFromCollection"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_ARTIFACT_BY_GID); + stmnt.setString(1, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int aId = result.getInt(1); + reset(); + prepareStatement(SQL_COLLECTION_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + return false; + } + int cId = result.getInt(1); + reset(); + prepareStatement(SQL_DELETE_ARTIFACT_FROM_COLLECTION); + stmnt.setInt(1, cId); + stmnt.setInt(2, aId); + stmnt.execute(); + conn.commit(); + return true; + } + }; + if (!exec.runWrite()) { + log.error("removing artifact from collection failed"); + } + } + + public void setCollectionName( + final String collectionId, + final String name, + GlobalContext context + ) { + log.debug("setCollectionName"); + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE_COLLECTION_NAME); + stmnt.setString(1, name); + stmnt.setString(2, collectionId); + stmnt.execute(); + conn.commit(); + return true; + } + }; + if (!exec.runWrite()) { + log.error("changing name failed"); + } + } + + protected void storeData( + final int artifactId, + FLYSArtifact artifact + ) { + final Collection<StateData> data = artifact.getAllData(); + + if (data.isEmpty()) { + return; + } + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + int [] ids = new int[data.size()]; + prepareStatement(SQL_ARTIFACT_DATA_ID_NEXTVAL); + + for (int i = 0; i < ids.length; ++i) { + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("generating id for artifact data failed"); + return false; + } + ids[i] = result.getInt(1); + result.close(); result = null; + } + reset(); + prepareStatement(SQL_INSERT_ARTIFACT_DATA); + + int i = 0; + for (StateData sd: data) { + int id = ids[i++]; + stmnt.setInt(1, id); + stmnt.setInt(2, artifactId); + // XXX: Where come the nulls from? + String type = sd.getType(); + if (type == null) type = "String"; + stmnt.setString(3, type); + stmnt.setString(4, sd.getName()); + setString(stmnt, 5, sd.getValue()); + stmnt.execute(); + } + + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing artifact data failed"); + } + } + + protected void storeOuts( + final int artifactId, + final FLYSArtifact artifact, + GlobalContext context + ) { + final List<Output> outs = artifact.getOutputs(context); + + if (outs.isEmpty()) { + return; + } + + final int [] outIds = new int[outs.size()]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_OUT_ID_NEXTVALUE); + for (int i = 0; i < outIds.length; ++i) { + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("generation of out ids failed"); + return false; + } + outIds[i] = result.getInt(1); + result.close(); result = null; + } + reset(); + prepareStatement(SQL_INSERT_OUT); + for (int i = 0; i < outIds.length; ++i) { + Output out = outs.get(i); + stmnt.setInt(1, outIds[i]); + stmnt.setInt(2, artifactId); + stmnt.setString(3, out.getName()); + setString(stmnt, 4, out.getDescription()); + setString(stmnt, 5, out.getType()); + stmnt.execute(); + } + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing artifact outs failed"); + return; + } + + final int FACETS = numFacets(outs); + + if (FACETS == 0) { + return; + } + + exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + int [] facetIds = new int[FACETS]; + prepareStatement(SQL_FACET_ID_NEXTVAL); + for (int i = 0; i < facetIds.length; ++i) { + result = stmnt.executeQuery(); + if (!result.next()) { + log.error("generation of facet ids failed"); + return false; + } + facetIds[i] = result.getInt(1); + result.close(); result = null; + } + reset(); + prepareStatement(SQL_INSERT_FACET); + int index = 0; + for (int i = 0, N = outs.size(); i < N; ++i) { + Output out = outs.get(i); + int outId = outIds[i]; + for (Facet facet: out.getFacets()) { + stmnt.setInt(1, facetIds[index]); + stmnt.setInt(2, outId); + stmnt.setString(3, facet.getName()); + stmnt.setInt(4, facet.getIndex()); + stmnt.setString(5, "XXX"); // TODO: handle states + setString(stmnt, 6, facet.getDescription()); + stmnt.execute(); + ++index; + } + } + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("storing facets failed"); + } + } + + public void killedCollections( + final List<String> identifiers, + GlobalContext context + ) { + log.debug("killedCollections"); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_COLLECTION_BY_GID); + for (String identifier: identifiers) { + stmnt.setString(1, identifier); + stmnt.execute(); + } + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("killing collections failed"); + } + } + + public void killedArtifacts( + final List<String> identifiers, + GlobalContext context + ) { + log.debug("killedArtifacts"); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + prepareStatement(SQL_DELETE_ARTIFACT_BY_GID); + for (String identifier: identifiers) { + stmnt.setString(1, identifier); + stmnt.execute(); + } + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + log.error("killing artifacts failed"); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DatacageBackendListener.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,190 @@ +package de.intevation.flys.artifacts.datacage; + +import java.util.List; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.User; + +import de.intevation.artifactdatabase.BackendListener; +import de.intevation.artifactdatabase.Backend; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +public class DatacageBackendListener +implements BackendListener +{ + private static Logger log = + Logger.getLogger(DatacageBackendListener.class); + + protected GlobalContext context; + + public DatacageBackendListener() { + log.debug("new DatacageBackendListener"); + } + + protected Datacage getDatacage() { + Object listener = context.get(Datacage.DATACAGE_KEY); + return listener instanceof Datacage + ? (Datacage)listener + : null; + } + + @Override + public void setup(GlobalContext context) { + log.debug("setup"); + this.context = context; + Datacage l = getDatacage(); + if (l != null) { + l.setup(context); + } + } + + @Override + public void createdArtifact(Artifact artifact, Backend backend) { + log.debug("createdArtifact"); + Datacage l = getDatacage(); + if (l != null) { + l.createdArtifact(artifact, backend, context); + } + } + + @Override + public void storedArtifact(Artifact artifact, Backend backend) { + log.debug("storedArtifact"); + Datacage l = getDatacage(); + if (l != null) { + l.storedArtifact(artifact, backend, context); + } + } + + @Override + public void createdUser(User user, Backend backend) { + log.debug("createdUser"); + Datacage l = getDatacage(); + if (l != null) { + l.createdUser(user, backend, context); + } + } + + @Override + public void deletedUser(String identifier, Backend backend) { + log.debug("deletedUser"); + Datacage l = getDatacage(); + if (l != null) { + l.deletedUser(identifier, backend, context); + } + } + + @Override + public void createdCollection( + ArtifactCollection collection, + Backend backend + ) { + log.debug("createdCollection"); + Datacage l = getDatacage(); + if (l != null) { + l.createdCollection(collection, backend, context); + } + } + + @Override + public void deletedCollection(String identifier, Backend backend) { + log.debug("deletedCollection"); + Datacage l = getDatacage(); + if (l != null) { + l.deletedCollection(identifier, backend, context); + } + } + + @Override + public void changedCollectionAttribute( + String identifier, + Document document, + Backend backend + ) { + log.debug("changedCollectionAttribute"); + Datacage l = getDatacage(); + if (l != null) { + l.changedCollectionAttribute( + identifier, document, backend, context); + } + } + + @Override + public void changedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document, + Backend backend + ) { + log.debug("changedCollectionItemAttribute"); + Datacage l = getDatacage(); + if (l != null) { + l.changedCollectionItemAttribute( + collectionId, artifactId, document, backend, context); + } + } + + @Override + public void addedArtifactToCollection( + String artifactId, + String collectionId, + Backend backend + ) { + log.debug("addedArtifactToCollection"); + Datacage l = getDatacage(); + if (l != null) { + l.addedArtifactToCollection( + artifactId, collectionId, backend, context); + } + } + + @Override + public void removedArtifactFromCollection( + String artifactId, + String collectionId, + Backend backend + ) { + log.debug("removedArtifactFromCollection"); + Datacage l = getDatacage(); + if (l != null) { + l.removedArtifactFromCollection( + artifactId, collectionId, backend, context); + } + } + + @Override + public void setCollectionName( + String collectionId, + String name + ) { + log.debug("setCollectionName"); + Datacage l = getDatacage(); + if (l != null) { + l.setCollectionName(collectionId, name, context); + } + } + + @Override + public void killedCollections(List<String> identifiers, Backend backend) { + log.debug("killedCollections"); + Datacage l = getDatacage(); + if (l != null) { + l.killedCollections(identifiers, context); + } + } + + @Override + public void killedArtifacts(List<String> identifiers, Backend backend) { + log.debug("killedArtifacts"); + Datacage l = getDatacage(); + if (l != null) { + l.killedArtifacts(identifiers, context); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,291 @@ +package de.intevation.flys.artifacts.datacage; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +import java.io.InputStream; +import java.io.IOException; +import java.io.File; + +import java.io.FileInputStream; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import org.hibernate.Session; + +import org.hibernate.jdbc.Work; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.datacage.templating.Builder; + +public class Recommendations +{ + private static Logger log = Logger.getLogger(Recommendations.class); + + private static final boolean DEVELOPMENT_MODE = + Boolean.getBoolean("flys.datacage.recommendations.development"); + + public static final String XPATH_TEMPLATE = + "/artifact-database/metadata/template/text()"; + + public static final String DEFAULT_TEMPLATE_PATH = + "${artifacts.config.dir}/meta-data.xml"; + + private static Recommendations INSTANCE; + + public static class BuilderProvider + { + protected Builder builder; + + public BuilderProvider() { + } + + public BuilderProvider(Builder builder) { + this.builder = builder; + } + + public Builder getBuilder() { + return builder; + } + } // class BuilderProvider + + public static class FileBuilderProvider + extends BuilderProvider + { + protected File file; + protected long lastModified; + + public FileBuilderProvider() { + } + + public FileBuilderProvider(File file) { + this.file = file; + lastModified = Long.MIN_VALUE; + } + + @Override + public synchronized Builder getBuilder() { + long modified = file.lastModified(); + if (modified > lastModified) { + lastModified = modified; + try { + Document template = loadTemplate(file); + builder = new Builder(template); + } + catch (IOException ioe) { + log.error(ioe); + } + } + return builder; + } + + public BuilderProvider toStaticProvider() { + return new BuilderProvider(builder); + } + } // class BuilderProvider + + protected BuilderProvider builderProvider; + + public Recommendations() { + } + + public Recommendations(BuilderProvider builderProvider) { + this.builderProvider = builderProvider; + } + + public Builder getBuilder() { + return builderProvider.getBuilder(); + } + + protected static void artifactToParameters( + FLYSArtifact artifact, + Map<String, Object> parameters + ) { + parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId()); + parameters.put("ARTIFACT-ID", artifact.identifier()); + + for (StateData sd: artifact.getAllData()) { + Object value = sd.getValue(); + if (value == null) { + continue; + } + String key = sd.getName().replace('.', '-').toUpperCase(); + parameters.put(key, value); + } + } + + public static void convertKeysToUpperCase( + Map<String, Object> src, + Map<String, Object> dst + ) { + for (Map.Entry<String, Object> entry: src.entrySet()) { + dst.put(entry.getKey().toUpperCase(), entry.getValue()); + } + } + + public void recommend( + FLYSArtifact artifact, + String userId, + String [] outs, + Map<String, Object> extraParameters, + Node result + ) { + Map<String, Object> parameters = new HashMap<String, Object>(); + + if (extraParameters != null) { + convertKeysToUpperCase(extraParameters, parameters); + } + + if (userId != null) { + parameters.put("USER-ID", userId); + } + + if (artifact != null) { + artifactToParameters(artifact, parameters); + } + + parameters.put("ARTIFACT-OUTS", StringUtils.toUpperCase(outs)); + + parameters.put("PARAMETERS", parameters); + + recommend(parameters, userId, result); + } + + public void recommend( + Map<String, Object> parameters, + String userId, + Node result + ) { + recommend(parameters, userId, result, SessionHolder.HOLDER.get()); + } + + public void recommend( + final Map<String, Object> parameters, + final String userId, + final Node result, + Session session + ) { + session.doWork(new Work() { + @Override + public void execute(Connection systemConnection) + throws SQLException + { + List<Builder.NamedConnection> connections = + new ArrayList<Builder.NamedConnection>(2); + + Connection userConnection = userId != null + ? DBConfig + .getInstance() + .getDBConnection() + .getDataSource() + .getConnection() + : null; + + try { + if (userConnection != null) { + connections.add(new Builder.NamedConnection( + Builder.CONNECTION_USER, userConnection, false)); + } + + connections.add(new Builder.NamedConnection( + Builder.CONNECTION_SYSTEM, systemConnection, true)); + + getBuilder().build(connections, result, parameters); + } + finally { + if (userConnection != null) { + userConnection.close(); + } + } + } + }); + } + + public static synchronized Recommendations getInstance() { + if (INSTANCE == null) { + INSTANCE = createRecommendations(); + } + return INSTANCE; + } + + protected static Document loadTemplate(File file) throws IOException { + InputStream in = null; + + try { + in = new FileInputStream(file); + + Document template = XMLUtils.parseDocument(in); + + if (template == null) { + throw new IOException("cannot load template"); + } + return template; + } + finally { + if (in != null) { + try { + in.close(); + } + catch (IOException ioe) { + log.error(ioe); + } + } + } + } + + public static Recommendations createRecommendations(File file) { + log.debug("Recommendations.createBuilder"); + + if (!file.isFile() || !file.canRead()) { + log.error("Cannot open template file '" + file + "'"); + return null; + } + + FileBuilderProvider fbp = new FileBuilderProvider(file); + + if (fbp.getBuilder() == null) { + log.error("failed loading builder"); + return null; + } + + BuilderProvider bp = DEVELOPMENT_MODE + ? fbp + : fbp.toStaticProvider(); + + return new Recommendations(bp); + } + + protected static Recommendations createRecommendations() { + log.debug("Recommendations.createRecommendations"); + + String path = Config.getStringXPath(XPATH_TEMPLATE); + + if (path == null) { + path = DEFAULT_TEMPLATE_PATH; + } + + path = Config.replaceConfigDir(path); + + log.info("Meta data template: " + path); + + return createRecommendations(new File(path)); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,109 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.util.Map; +import java.util.HashMap; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.File; +import java.io.FileOutputStream; + +import de.intevation.flys.backend.SessionFactoryProvider; + +import org.hibernate.Session; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.datacage.Recommendations; + +public class App +{ + private static Logger log = Logger.getLogger(App.class); + + public static final String template = + System.getProperty("meta.data.template", "meta-data.xml"); + + public static final String userId = + System.getProperty("user.id"); + + public static final String PARAMETERS = + System.getProperty("meta.data.parameters", ""); + + public static final String OUTPUT = + System.getProperty("meta.data.output"); + + public static Map<String, Object> getParameters() { + HashMap<String, Object> map = new HashMap<String, Object>(); + String [] parts = PARAMETERS.split("\\s*;\\s*"); + for (String part: parts) { + String [] kv = part.split("\\s*:\\s*"); + if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) { + continue; + } + String [] values = kv[1].split("\\s*,\\s*"); + map.put(kv[0], values.length == 1 ? values[0] : values); + } + return map; + } + + public static void main(String [] args) { + + Recommendations rec = Recommendations.createRecommendations( + new File(template)); + + if (rec == null) { + System.err.println("No recommendations created"); + return; + } + + final Document result = XMLUtils.newDocument(); + + final Map<String, Object> parameters = getParameters(); + + Session session = SessionFactoryProvider + .createSessionFactory() + .openSession(); + + try { + rec.recommend(parameters, userId, result, session); + } + finally { + session.close(); + } + + OutputStream out; + + if (OUTPUT == null) { + out = System.out; + } + else { + try { + out = new FileOutputStream(OUTPUT); + } + catch (IOException ioe) { + log.error(ioe); + return; + } + } + + try { + XMLUtils.toStream(result, out); + } + finally { + if (OUTPUT != null) { + try { + out.close(); + } + catch (IOException ioe) { + log.error(ioe); + } + } + } + System.exit(0); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,605 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.Deque; +import java.util.ArrayDeque; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathConstants; + +import java.sql.SQLException; +import java.sql.Connection; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.utils.Pair; + +import org.apache.log4j.Logger; + +public class Builder +{ + private static Logger log = Logger.getLogger(Builder.class); + + public static final String CONNECTION_USER = "user"; + public static final String CONNECTION_SYSTEM = "system"; + public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM; + + public static final Pattern STRIP_LINE_INDENT = + Pattern.compile("\\s*\\r?\\n\\s*"); + + public static final String DC_NAMESPACE_URI = + "http://www.intevation.org/2011/Datacage"; + + private static final Document EVAL_DOCUMENT = + XMLUtils.newDocument(); + + private static final XPathFactory XPATH_FACTORY = + XPathFactory.newInstance(); + + protected Document template; + + protected Map<String, CompiledStatement> compiledStatements; + + public static class NamedConnection { + + protected String name; + protected Connection connection; + protected boolean cached; + + public NamedConnection() { + } + + public NamedConnection( + String name, + Connection connection + ) { + this(name, connection, true); + } + + public NamedConnection( + String name, + Connection connection, + boolean cached + ) { + this.name = name; + this.connection = connection; + this.cached = cached; + } + } // class NamedConnection + + + public class BuildHelper + { + protected Node output; + protected Document owner; + protected StackFrames frames; + protected List<NamedConnection> connections; + protected Map<String, CompiledStatement.Instance> statements; + protected Deque<Pair<NamedConnection, ResultData>> connectionsStack; + + public BuildHelper( + Node output, + List<NamedConnection> connections, + Map<String, Object> parameters + ) { + if (connections.isEmpty()) { + throw new IllegalArgumentException("no connections given."); + } + + this.connections = connections; + connectionsStack = + new ArrayDeque<Pair<NamedConnection, ResultData>>(); + this.output = output; + frames = new StackFrames(parameters); + owner = getOwnerDocument(output); + statements = + new HashMap<String, CompiledStatement.Instance>(); + } + + public void build() throws SQLException { + try { + synchronized (template) { + for (Node current: rootsToList()) { + build(output, current); + } + } + } + finally { + closeStatements(); + } + } + + protected void closeStatements() { + for (CompiledStatement.Instance csi: statements.values()) { + csi.close(); + } + statements.clear(); + } + + protected void context(Node parent, Element current) + throws SQLException + { + log.debug("dc:context"); + + NodeList subs = current.getChildNodes(); + int S = subs.getLength(); + + // check only direct children + Node stmntNode = null; + for (int i = 0; i < S; ++i) { + Node node = subs.item(i); + String ns; + if (node.getNodeType() == Node.ELEMENT_NODE + && node.getLocalName().equals("statement") + && (ns = node.getNamespaceURI()) != null + && ns.equals(DC_NAMESPACE_URI)) { + stmntNode = node; + break; + } + } + + if (stmntNode == null) { + log.warn("dc:context: cannot find statement"); + return; + } + + String con = current.getAttribute("connection"); + + String stmntText = stmntNode.getTextContent(); + + String key = con + "-" + stmntText; + + CompiledStatement.Instance csi = statements.get(key); + + if (csi == null) { + CompiledStatement cs = compiledStatements.get(stmntText); + csi = cs.new Instance(); + statements.put(key, csi); + } + + NamedConnection connection = connectionsStack.isEmpty() + ? connections.get(0) + : connectionsStack.peek().getA(); + + if (con.length() > 0) { + for (NamedConnection nc: connections) { + if (con.equals(nc.name)) { + connection = nc; + break; + } + } + } + + ResultData rd = csi.execute( + connection.connection, + frames, + connection.cached); + + // only descent if there are results + if (!rd.isEmpty()) { + connectionsStack.push(new Pair(connection, rd)); + try { + for (int i = 0; i < S; ++i) { + build(parent, subs.item(i)); + } + } + finally { + connectionsStack.pop(); + } + } + } + + protected void elements(Node parent, Element current) + throws SQLException + { + log.debug("dc:elements"); + + if (connectionsStack.isEmpty()) { + log.warn("dc:elements without having results"); + return; + } + + NodeList subs = current.getChildNodes(); + int S = subs.getLength(); + + if (S == 0) { + log.debug("dc:elements has no children"); + return; + } + + ResultData rd = connectionsStack.peek().getB(); + + String [] columns = rd.getColumnLabels(); + + //if (log.isDebugEnabled()) { + // log.debug("pushing vars: " + // + java.util.Arrays.toString(columns)); + //} + + for (Object [] row: rd.getRows()) { + frames.enter(); + try { + frames.put(columns, row); + //if (log.isDebugEnabled()) { + // log.debug("current vars: " + frames.dump()); + //} + for (int i = 0; i < S; ++i) { + build(parent, subs.item(i)); + } + } + finally { + frames.leave(); + } + } + } + + protected void element(Node parent, Element current) + throws SQLException + { + String attr = expand(current.getAttribute("name")); + + if (log.isDebugEnabled()) { + log.debug("dc:element -> '" + attr + "'"); + } + + if (attr.length() == 0) { + log.warn("no name attribute found"); + return; + } + + Element element = owner.createElement(attr); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(element, children.item(i)); + } + + parent.appendChild(element); + } + + protected void text(Node parent, Element current) + throws SQLException + { + log.debug("dc:text"); + String value = expand(current.getTextContent()); + parent.appendChild(owner.createTextNode(value)); + } + + + protected void attribute(Node parent, Element current) { + + if (parent.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need element here"); + return; + } + + String name = expand(current.getAttribute("name")); + String value = expand(current.getAttribute("value")); + + Element element = (Element)parent; + + element.setAttribute(name, value); + } + + protected void callMacro(Node parent, Element current) + throws SQLException + { + String name = current.getAttribute("name"); + + if (name.length() == 0) { + log.warn("missing 'name' attribute in 'call-macro'"); + return; + } + + NodeList macros = template.getElementsByTagNameNS( + DC_NAMESPACE_URI, "macro"); + + for (int i = 0, N = macros.getLength(); i < N; ++i) { + Element macro = (Element)macros.item(i); + if (name.equals(macro.getAttribute("name"))) { + NodeList subs = macro.getChildNodes(); + for (int j = 0, M = subs.getLength(); j < M; ++j) { + build(parent, subs.item(j)); + } + return; + } + } + + log.warn("no macro '" + name + "' found."); + } + + protected void ifClause(Node parent, Element current) + throws SQLException + { + String test = current.getAttribute("test"); + + if (test.length() == 0) { + log.warn("missing 'test' attribute in 'if'"); + return; + } + + Boolean result = evaluateXPath(test); + + if (result != null && result.booleanValue()) { + NodeList subs = current.getChildNodes(); + for (int i = 0, N = subs.getLength(); i < N; ++i) { + build(parent, subs.item(i)); + } + } + } + + protected void choose(Node parent, Element current) + throws SQLException + { + Node branch = null; + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + Node child = children.item(i); + String ns = child.getNamespaceURI(); + if (ns == null + || !ns.equals(DC_NAMESPACE_URI) + || child.getNodeType() != Node.ELEMENT_NODE + ) { + continue; + } + String name = child.getLocalName(); + if ("when".equals(name)) { + Element when = (Element)child; + String test = when.getAttribute("test"); + if (test.length() == 0) { + log.warn("no 'test' attribute found for when"); + continue; + } + + Boolean result = evaluateXPath(test); + if (result != null && result.booleanValue()) { + branch = child; + break; + } + + continue; + } + else if ("otherwise".equals(name)) { + branch = child; + // No break here. + } + } + + if (branch != null) { + NodeList subs = branch.getChildNodes(); + for (int i = 0, N = subs.getLength(); i < N; ++i) { + build(parent, subs.item(i)); + } + } + } + + protected Boolean evaluateXPath(String expr) { + + if (log.isDebugEnabled()) { + log.debug("evaluate: '" + expr + "'"); + } + + try { + XPath xpath = XPATH_FACTORY.newXPath(); + xpath.setXPathVariableResolver(frames); + xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS); + Object result = xpath.evaluate( + expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN); + + return result instanceof Boolean + ? (Boolean)result + : null; + } + catch (XPathExpressionException xfce) { + log.error(xfce); + } + return null; + } + + protected void convert(Node parent, Element current) { + + String variable = expand(current.getAttribute("var")); + String type = expand(current.getAttribute("type")); + + Object [] result = new Object[1]; + + if (frames.getStore(variable, result)) { + Object object = TypeConverter.convert(result[0], type); + frames.put(variable.toUpperCase(), object); + } + } + + protected String expand(String s) { + Matcher m = CompiledStatement.VAR.matcher(s); + + Object [] result = new Object[1]; + + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String key = m.group(1); + result[0] = null; + if (frames.getStore(key, result)) { + m.appendReplacement( + sb, result[0] != null ? result[0].toString() : ""); + } + else { + m.appendReplacement(sb, "\\${" + key + "}"); + } + } + m.appendTail(sb); + return sb.toString(); + } + + protected void build(Node parent, Node current) + throws SQLException + { + String ns = current.getNamespaceURI(); + if (ns != null && ns.equals(DC_NAMESPACE_URI)) { + if (current.getNodeType() != Node.ELEMENT_NODE) { + log.warn("need elements here"); + } + else { + String localName = current.getLocalName(); + if ("attribute".equals(localName)) { + attribute(parent, (Element)current); + } + else if ("context".equals(localName)) { + context(parent, (Element)current); + } + else if ("if".equals(localName)) { + ifClause(parent, (Element)current); + } + else if ("choose".equals(localName)) { + choose(parent, (Element)current); + } + else if ("call-macro".equals(localName)) { + callMacro(parent, (Element)current); + } + else if ("macro".equals(localName)) { + // Simply ignore the definition. + } + else if ("element".equals(localName)) { + element(parent, (Element)current); + } + else if ("elements".equals(localName)) { + elements(parent, (Element)current); + } + else if ("text".equals(localName)) { + text(parent, (Element)current); + } + else if ("comment".equals(localName) + || "statement".equals(localName)) { + // ignore comments and statements in output + } + else if ("convert".equals(localName)) { + convert(parent, (Element)current); + } + else { + log.warn("unknown '" + localName + "' -> ignore"); + } + } + return; + } + + if (current.getNodeType() == Node.TEXT_NODE) { + String txt = current.getNodeValue(); + if (txt != null && txt.trim().length() == 0) { + return; + } + } + + Node copy = owner.importNode(current, false); + + NodeList children = current.getChildNodes(); + for (int i = 0, N = children.getLength(); i < N; ++i) { + build(copy, children.item(i)); + } + parent.appendChild(copy); + } + } // class BuildHelper + + + public Builder() { + compiledStatements = new HashMap<String, CompiledStatement>(); + } + + public Builder(Document template) { + this(); + this.template = template; + compileStatements(); + } + + protected void compileStatements() { + + NodeList nodes = template.getElementsByTagNameNS( + DC_NAMESPACE_URI, "statement"); + + for (int i = 0, N = nodes.getLength(); i < N; ++i) { + Element stmntElement = (Element)nodes.item(i); + String stmnt = trimStatement(stmntElement.getTextContent()); + if (stmnt == null || stmnt.length() == 0) { + throw new IllegalArgumentException("found empty statement"); + } + CompiledStatement cs = new CompiledStatement(stmnt); + // For faster lookup store a shortend string into the template. + stmnt = "s" + i; + stmntElement.setTextContent(stmnt); + compiledStatements.put(stmnt, cs); + } + } + + protected List<Node> rootsToList() { + + NodeList roots = template.getElementsByTagNameNS( + DC_NAMESPACE_URI, "template"); + + List<Node> elements = new ArrayList<Node>(); + + for (int i = 0, N = roots.getLength(); i < N; ++i) { + NodeList rootChildren = roots.item(i).getChildNodes(); + for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { + Node child = rootChildren.item(j); + if (child.getNodeType() == Node.ELEMENT_NODE) { + elements.add(child); + } + } + } + + return elements; + } + + protected static final String trimStatement(String stmnt) { + if (stmnt == null) return null; + //XXX: Maybe a bit to radical for multiline strings? + return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" "); + } + + protected static Document getOwnerDocument(Node node) { + Document document = node.getOwnerDocument(); + return document != null ? document : (Document)node; + } + + private static final List<NamedConnection> wrap(Connection connection) { + List<NamedConnection> list = new ArrayList<NamedConnection>(1); + list.add(new NamedConnection(DEFAULT_CONNECTION_NAME, connection)); + return list; + } + + public void build( + Connection connection, + Node output, + Map<String, Object> parameters + ) + throws SQLException + { + build(wrap(connection), output, parameters); + } + + public void build( + List<NamedConnection> connections, + Node output, + Map<String, Object> parameters + ) + throws SQLException + { + BuildHelper helper = new BuildHelper(output, connections, parameters); + + helper.build(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,213 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.ArrayList; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Connection; +import java.sql.ResultSet; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import org.apache.log4j.Logger; + +public class CompiledStatement +{ + private static Logger log = Logger.getLogger(CompiledStatement.class); + + public static final String DATACAGE_DB_CACHE = + "datacage.db"; + + public static final Pattern VAR = + Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}"); + + protected String original; + protected String statement; + + protected Map<String, List<Integer>> positions; + + protected int numVars; + + public class Instance { + + protected PreparedStatement preparedStatement; + + public Instance() { + } + + protected ResultData executeCached( + Cache cache, + Connection connection, + StackFrames frames + ) + throws SQLException + { + log.debug("executeCached"); + Object [] values = new Object[numVars]; + + StringBuilder sb = new StringBuilder(original); + + for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) { + String key = entry.getKey(); + Object value = frames.get(key); + sb.append(';').append(key).append(':').append(value); + for (Integer index: entry.getValue()) { + values[index] = value; + } + } + + // XXX: Maybe too many collisions? + // String key = original + Arrays.hashCode(values); + String key = sb.toString(); + + Element element = cache.get(key); + + if (element != null) { + return (ResultData)element.getValue(); + } + + if (preparedStatement == null) { + preparedStatement = connection.prepareStatement(statement); + } + + for (int i = 0; i < values.length; ++i) { + preparedStatement.setObject(i+1, values[i]); + } + + ResultData data; + + if (log.isDebugEnabled()) { + log.debug("executing: " + statement); + } + + ResultSet result = preparedStatement.executeQuery(); + try { + data = new ResultData(preparedStatement.getMetaData()) + .addAll(result); + } + finally { + result.close(); + } + + element = new Element(key, data); + cache.put(element); + + return data; + } + + protected ResultData executeUncached( + Connection connection, + StackFrames frames + ) + throws SQLException + { + log.debug("executeUncached"); + if (preparedStatement == null) { + if (log.isDebugEnabled()) { + log.debug("preparing statement: " + statement); + } + preparedStatement = connection.prepareStatement(statement); + } + + for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) { + Object value = frames.get(entry.getKey()); + for (Integer index: entry.getValue()) { + preparedStatement.setObject(index+1, value); + } + } + + if (log.isDebugEnabled()) { + log.debug("executing: " + statement); + } + + ResultSet result = preparedStatement.executeQuery(); + try { + return new ResultData(preparedStatement.getMetaData()) + .addAll(result); + } + finally { + result.close(); + } + } + + public ResultData execute( + Connection connection, + StackFrames frames, + boolean cached + ) + throws SQLException + { + if (!cached) { + return executeUncached(connection, frames); + } + + Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE); + + return cache != null + ? executeCached(cache, connection, frames) + : executeUncached(connection, frames); + } + + public void close() { + if (preparedStatement != null) { + try { + preparedStatement.close(); + } + catch (SQLException sqle) { + } + preparedStatement = null; + } + } + } // class Instance + + public CompiledStatement() { + } + + public CompiledStatement(String original) { + this.original = original; + // TreeMap to ensure order + positions = new TreeMap<String, List<Integer>>(); + compile(); + } + + protected void compile() { + + StringBuffer sb = new StringBuffer(); + + Matcher m = VAR.matcher(original); + + int index = 0; + + while (m.find()) { + String key = m.group(1).toUpperCase(); + List<Integer> indices = positions.get(key); + if (indices == null) { + indices = new ArrayList<Integer>(); + positions.put(key, indices); + } + indices.add(index); + m.appendReplacement(sb, "?"); + ++index; + } + + m.appendTail(sb); + + numVars = index; + + statement = sb.toString(); + } + + public String getStatement() { + return statement; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,108 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.util.List; +import java.util.Collection; +import java.util.Map; +import java.util.ArrayList; + +import javax.xml.xpath.XPathFunctionResolver; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathFunctionException; + +import javax.xml.namespace.QName; + +import org.apache.log4j.Logger; + +public class FunctionResolver +implements XPathFunctionResolver +{ + private static Logger log = Logger.getLogger(FunctionResolver.class); + + public static final String FUNCTION_NAMESPACE_URI = "dc"; + + public static final class Entry { + + String name; + XPathFunction function; + int arity; + + public Entry() { + } + + public Entry(String name, XPathFunction function, int arity) { + this.name = name; + this.function = function; + this.arity = arity; + } + } // class Entry + + public static final FunctionResolver FUNCTIONS = new FunctionResolver(); + + static { + FUNCTIONS.addFunction("contains", 2, new XPathFunction() { + @Override + public Object evaluate(List args) throws XPathFunctionException { + Object haystack = args.get(0); + Object needle = args.get(1); + + if (needle instanceof String) { + needle = ((String)needle).toUpperCase(); + } + + try { + if (haystack instanceof Collection) { + return Boolean.valueOf( + ((Collection)haystack).contains(needle)); + } + + if (haystack instanceof Map) { + return Boolean.valueOf( + ((Map)haystack).containsKey(needle)); + } + + if (haystack instanceof Object []) { + for (Object straw: (Object [])haystack) { + if (straw.equals(needle)) { + return Boolean.TRUE; + } + } + } + + return Boolean.FALSE; + } + catch (Exception e) { + log.error(e); + throw new XPathFunctionException(e); + } + } + }); + } + + protected List<Entry> functions; + + public FunctionResolver() { + functions = new ArrayList<Entry>(); + } + + public void addFunction(String name, int arity, XPathFunction function) { + functions.add(new Entry(name, function, arity)); + } + + @Override + public XPathFunction resolveFunction(QName functionName, int arity) { + + if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) { + return null; + } + + String name = functionName.getLocalPart(); + for (Entry entry: functions) { + if (entry.arity == arity && entry.name.equals(name)) { + return entry.function; + } + } + + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,77 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.io.Serializable; + +import java.sql.ResultSetMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.List; +import java.util.ArrayList; + +import org.apache.log4j.Logger; + +public class ResultData +implements Serializable +{ + private static Logger log = Logger.getLogger(ResultData.class); + + protected String [] columns; + + protected List<Object []> rows; + + public ResultData() { + rows = new ArrayList<Object []>(); + } + + public ResultData(ResultSetMetaData meta) + throws SQLException + { + this(); + + boolean debug = log.isDebugEnabled(); + + int N = meta.getColumnCount(); + + columns = new String[N]; + + if (debug) { + log.debug("ResultSet column names:"); + } + + for (int i = 1; i <= N; ++i) { + columns[i-1] = meta.getColumnLabel(i).toUpperCase(); + if (debug) { + log.debug(" " + i + ": " + columns[i-1]); + } + } + } + + public String [] getColumnLabels() { + return columns; + } + + public ResultData addAll(ResultSet result) throws SQLException { + while (result.next()) { + add(result); + } + return this; + } + + public void add(ResultSet result) throws SQLException { + Object [] row = new Object[columns.length]; + for (int i = 0; i < columns.length; ++i) { + row[i] = result.getObject(i+1); + } + rows.add(row); + } + + public List<Object []> getRows() { + return rows; + } + + public boolean isEmpty() { + return rows.isEmpty(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,127 @@ +package de.intevation.flys.artifacts.datacage.templating; + +import java.util.ArrayList; +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import javax.xml.xpath.XPathVariableResolver; + +import javax.xml.namespace.QName; + +import org.apache.log4j.Logger; + +public class StackFrames +implements XPathVariableResolver +{ + private static Logger log = Logger.getLogger(StackFrames.class); + + protected List<Map<String, Object>> frames; + + public StackFrames() { + frames = new ArrayList<Map<String, Object>>(); + } + + public StackFrames(Map<String, Object> initialFrame) { + this(); + if (initialFrame != null) { + frames.add(new HashMap<String, Object>(initialFrame)); + } + } + + public void enter() { + frames.add(new HashMap<String, Object>()); + } + + public void leave() { + frames.remove(frames.size()-1); + } + + public void put(String key, Object value) { + int N = frames.size(); + if (N > 0) { + frames.get(N-1).put(key, value); + } + } + + public void put(String [] keys, Object [] values) { + Map<String, Object> top = frames.get(frames.size()-1); + for (int i = 0; i < keys.length; ++i) { + top.put(keys[i], values[i]); + } + } + + public boolean containsKey(String key) { + key = key.toUpperCase(); + for (int i = frames.size()-1; i >= 0; --i) { + if (frames.get(i).containsKey(key)) { + return true; + } + } + return false; + } + + public Object get(String key) { + return get(key, null); + } + + public boolean getStore(String key, Object [] result) { + + key = key.toUpperCase(); + + for (int i = frames.size()-1; i >= 0; --i) { + Map<String, Object> frame = frames.get(i); + if (frame.containsKey(key)) { + result[0] = frame.get(key); + return true; + } + } + + return false; + } + + public Object get(String key, Object def) { + + key = key.toUpperCase(); + + for (int i = frames.size()-1; i >= 0; --i) { + Map<String, Object> frame = frames.get(i); + if (frame.containsKey(key)) { + return frame.get(key); + } + } + + return def; + } + + @Override + public Object resolveVariable(QName variableName) { + if (log.isDebugEnabled()) { + log.debug("resolve var: " + variableName); + } + return get(variableName.getLocalPart()); + } + + public String dump() { + StringBuilder sb = new StringBuilder("["); + Set<String> already = new HashSet<String>(); + + boolean first = true; + + for (int i = frames.size()-1; i >= 0; --i) { + Map<String, Object> frame = frames.get(i); + for (Map.Entry<String, Object> entry: frame.entrySet()) { + if (already.add(entry.getKey())) { + if (first) { first = false; } + else { sb.append(", "); } + sb.append('\'').append(entry.getKey()) + .append("'='").append(entry.getValue()).append('\''); + } + } + } + return sb.append(']').toString(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/TypeConverter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,31 @@ +package de.intevation.flys.artifacts.datacage.templating; + +public class TypeConverter +{ + private TypeConverter() { + } + + public static Object convert(Object object, String type) { + + if (type == null) { + return object; + } + + if ("Integer".equals(type)) { + return Integer.valueOf(object.toString()); + } + + if ("Double".equals(type)) { + return Double.valueOf(object.toString()); + } + + if ("String".equals(type)) { + return object.toString(); + } + + // TODO: Add more types + + return object; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,280 @@ +package de.intevation.flys.geom; + +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +import de.intevation.flys.artifacts.math.Linear; + +import org.apache.log4j.Logger; + +import gnu.trove.TDoubleArrayList; + +public class Lines +{ + private static Logger log = Logger.getLogger(Lines.class); + + public static final double EPSILON = 1e-4; + + public static enum Mode { UNDEF, WET, DRY }; + + protected Lines() { + } + + public static List<Line2D> fillWater(List<Point2D> points, double waterLevel) { + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("fillWater"); + log.debug("----------------------------"); + } + + List<Line2D> result = new ArrayList(); + + int N = points.size(); + + if (N == 0) { + return result; + } + + if (N == 1) { + Point2D p = points.get(0); + // Only generate point if over water + if (waterLevel > p.getY()) { + result.add(new Line2D.Double( + p.getX(), waterLevel, + p.getX(), waterLevel)); + } + return result; + } + + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = -Double.MAX_VALUE; + double maxY = -Double.MAX_VALUE; + + // To ensure for sequences of equals x's that + // the original index order is preserved. + for (Point2D p: points) { + double x = p.getX(), y = p.getY(); + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + + if (minY > waterLevel) { // profile completely over water level + log.debug("complete over water"); + return result; + } + + if (waterLevel > maxY) { // water floods profile + log.debug("complete under water"); + result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel)); + return result; + } + + Mode mode = Mode.UNDEF; + + double startX = minX; + + for (int i = 1; i < N; ++i) { + Point2D p1 = points.get(i-1); + Point2D p2 = points.get(i); + + if (p1.getY() < waterLevel && p2.getY() < waterLevel) { + // completely under water + if (debug) { + log.debug("under water: " + p1 + " " + p2); + } + if (mode != Mode.WET) { + startX = p1.getX(); + mode = Mode.WET; + } + continue; + } + + if (p1.getY() > waterLevel && p2.getY() > waterLevel) { + if (debug) { + log.debug("over water: " + p1 + " " + p2); + } + // completely over water + if (mode == Mode.WET) { + log.debug("over/wet"); + result.add(new Line2D.Double( + startX, waterLevel, + p1.getX(), waterLevel)); + } + mode = Mode.DRY; + continue; + } + + if (Math.abs(p1.getX() - p2.getX()) < EPSILON) { + // vertical line + switch (mode) { + case WET: + log.debug("vertical/wet"); + mode = Mode.DRY; + result.add(new Line2D.Double( + startX, waterLevel, + p1.getX(), waterLevel)); + break; + case DRY: + log.debug("vertical/dry"); + mode = Mode.WET; + startX = p2.getX(); + break; + default: // UNDEF + log.debug("vertical/undef"); + if (p2.getY() < waterLevel) { + mode = Mode.WET; + startX = p2.getX(); + } + else { + mode = Mode.DRY; + } + } + continue; + } + + // check if waterlevel directly hits the vertices; + + boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON; + boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON; + + if (p1W || p2W) { + if (debug) { + log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode); + } + if (p1W && p2W) { // parallel to water -> dry + log.debug("water hits both vertices"); + if (mode == Mode.WET) { + result.add(new Line2D.Double( + startX, waterLevel, + p1.getX(), waterLevel)); + } + mode = Mode.DRY; + } + else if (p1W) { // p1 == waterlevel + log.debug("water hits first vertex"); + if (p2.getY() > waterLevel) { // --> dry + if (mode == Mode.WET) { + result.add(new Line2D.Double( + startX, waterLevel, + p1.getX(), waterLevel)); + } + mode = Mode.DRY; + } + else { // --> wet + if (mode != Mode.WET) { + startX = p1.getX(); + mode = Mode.WET; + } + } + } + else { // p2 == waterlevel + log.debug("water hits second vertex"); + if (p1.getY() > waterLevel) { // --> wet + if (mode != Mode.WET) { + startX = p2.getX(); + mode = Mode.WET; + } + } + else { // --> dry + if (mode == Mode.WET) { + result.add(new Line2D.Double( + startX, waterLevel, + p2.getX(), waterLevel)); + } + mode = Mode.DRY; + } + } + if (debug) { + log.debug("mode is now: " + mode); + } + continue; + } + + // intersection case + double x = Linear.linear( + waterLevel, + p1.getY(), p2.getY(), + p1.getX(), p2.getX()); + + if (debug) { + log.debug("intersection p1:" + p1); + log.debug("intersection p2:" + p2); + log.debug("intersection at x: " + x); + } + + switch (mode) { + case WET: + log.debug("intersect/wet"); + mode = Mode.DRY; + result.add(new Line2D.Double( + startX, waterLevel, + x, waterLevel)); + break; + + case DRY: + log.debug("intersect/dry"); + mode = Mode.WET; + startX = x; + break; + + default: // UNDEF + log.debug("intersect/undef"); + if (p2.getY() > waterLevel) { + log.debug("intersect/undef/over"); + mode = Mode.DRY; + result.add(new Line2D.Double( + p1.getX(), waterLevel, + x, waterLevel)); + } + else { + mode = Mode.WET; + startX = x; + } + } // switch mode + } // for all points p[i] and p[i-1] + + if (mode == Mode.WET) { + result.add(new Line2D.Double( + startX, waterLevel, + maxX, waterLevel)); + } + + return result; + } + + public static double [][] createWaterLines( + List<Point2D> points, + double waterlevel + ) { + List<Line2D> lines = fillWater(points, waterlevel); + + TDoubleArrayList lxs = new TDoubleArrayList(); + TDoubleArrayList lys = new TDoubleArrayList(); + + for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) { + Line2D l = iter.next(); + Point2D p1 = l.getP1(); + Point2D p2 = l.getP2(); + lxs.add(p1.getX()); + lys.add(p1.getY()); + lxs.add(p2.getX()); + lys.add(p2.getY()); + if (iter.hasNext()) { + lxs.add(Double.NaN); + lys.add(Double.NaN); + } + } + + return new double [][] { lxs.toNativeArray(), lys.toNativeArray() }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,430 @@ +package de.intevation.flys.geom; + +import java.io.Serializable; + +import java.awt.Shape; + +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Collections; + +import de.intevation.flys.artifacts.math.Linear; + +import static de.intevation.flys.geom.VectorUtils.X; +import static de.intevation.flys.geom.VectorUtils.Y; +import static de.intevation.flys.geom.VectorUtils.EPSILON; + +public class Polygon2D +implements Serializable +{ + public static final Comparator<Point2D> POINT_X_CMP = + new Comparator<Point2D>() { + public int compare(Point2D a, Point2D b) { + double d = X(a) - X(b); + if (d < 0d) return -1; + return d > 0d ? + 1 : 0; + } + }; + + public static final Comparator<Point2D []> FIRST_POINT_X = + new Comparator<Point2D []>() { + public int compare(Point2D [] a, Point2D [] b) { + if (a.length == 0) return -1; // should not happen. + if (b.length == 0) return +1; // should not happen. + double d = X(a[0]) - Y(b[0]); + if (d < 0d) return -1; + return d > 0d ? + 1 : 0; + } + }; + + protected List<Point2D> points; + + public Polygon2D() { + points = new ArrayList<Point2D>(); + } + + public Polygon2D(List<Point2D> points) { + this.points = points; + } + + public void add(double x, double y) { + points.add(new Point2D.Double(x, y)); + } + + protected static boolean addCheck(Point2D p, List<Point2D> points) { + switch (points.size()) { + case 0: + points.add(p); + return true; + case 1: + if (VectorUtils.epsilonEquals(points.get(0), p)) { + return false; + } + points.add(p); + return true; + default: + int L = points.size()-1; + Point2D last = points.get(L); + if (VectorUtils.epsilonEquals(last, p)) { + return false; + } + Point2D before = points.get(L-1); + if (VectorUtils.collinear(before, last, p)) { + points.set(L, p); + } + else { + points.add(p); + } + return true; + } + } + + public boolean addCheck(Point2D p) { + return addCheck(p, points); + } + + public void addReversed(List<Point2D> other) { + for (int i = other.size()-1; i >= 0; --i) { + addCheck(other.get(i)); + } + } + + public double area() { + double area = 0d; + + for (int i = 0, N = points.size(); i < N; ++i) { + int j = (i + 1) % N; + Point2D pi = points.get(i); + Point2D pj = points.get(j); + area += X(pi)*Y(pj); + area -= X(pj)*Y(pi); + } + + return 0.5d*area; + } + + public Shape toShape() { + Path2D.Double path = new Path2D.Double(); + + int N = points.size(); + + if (N > 0) { + Point2D p = points.get(0); + path.moveTo(X(p), Y(p)); + for (int i = 1; i < N; ++i) { + p = points.get(i); + path.lineTo(X(p), Y(p)); + } + path.closePath(); + } + + return path; + } + + protected static List<Point2D []> splitByNaNs( + double [] xs, + double [] ys + ) { + List<Point2D []> parts = new ArrayList<Point2D []>(); + + List<Point2D> tmp = new ArrayList<Point2D>(xs.length); + + for (int i = 0; i < xs.length; ++i) { + double x = xs[i]; + double y = ys[i]; + + if (Double.isNaN(x) || Double.isNaN(y)) { + if (!tmp.isEmpty()) { + Point2D [] part = new Point2D[tmp.size()]; + parts.add(tmp.toArray(part)); + tmp.clear(); + } + } + else { + tmp.add(new Point2D.Double(x, y)); + } + } + + if (!tmp.isEmpty()) { + Point2D [] part = new Point2D[tmp.size()]; + parts.add(tmp.toArray(part)); + } + + return parts; + } + + protected static boolean removeNoneIntersecting( + List<Point2D []> As, + List<Point2D []> Bs + ) { + int B = Bs.size()-1; + OUTER: for (int i = 0; i < As.size();) { + Point2D [] a = As.get(i); + int lo = 0, hi = B; + while (lo <= hi) { + int mid = (lo + hi) >> 1; + Point2D [] b = Bs.get(mid); + if (X(a[0]) > X(b[b.length-1])) lo = mid+1; + else if (X(a[a.length-1]) < X(b[0])) hi = mid-1; + else { + // found: keep + ++i; + continue OUTER; + } + } + // not found: remove + As.remove(i); + } + + return As.isEmpty(); + } + + protected static void buildPolygons( + Point2D [] as, + Point2D [] bs, + Point2D [][] rest, + List<Polygon2D> positives, + List<Polygon2D> negatives + ) { + List<Point2D> apoints = new ArrayList<Point2D>(); + List<Point2D> bpoints = new ArrayList<Point2D>(); + + double ax = X(as[0]); + double bx = X(bs[0]); + + int ai = 1; + int bi = 1; + + boolean intersect = false; + + if (ax == bx) { + apoints.add(as[0]); + bpoints.add(bs[0]); + } + else if (ax > bx) { + apoints.add(as[0]); + double bx1; + while ((bx1 = X(bs[bi])) < ax) ++bi; + if (bx1 == ax) { + bpoints.add(bs[bi]); + } + else { // need to calculate start b point. + intersect = true; + double by1 = Linear.linear( + ax, + X(bs[bi-1]), bx1, + Y(bs[bi-1]), Y(bs[bi])); + + bpoints.add(new Point2D.Double(ax, by1)); + } + } + else { // bx > ax: Symmetric case + bpoints.add(bs[0]); + double ax1; + while ((ax1 = X(as[ai])) < bx) ++ai; + if (ax1 == bx) { + apoints.add(as[ai]); + } + else { // need to calculate start b point. + intersect = true; + double ay1 = Linear.linear( + bx, + X(as[ai-1]), ax1, + Y(as[ai-1]), Y(as[ai])); + + apoints.add(new Point2D.Double(bx, ay1)); + } + } + + // now we have a point in each list, decide if neg/pos. + boolean neg = Y(bpoints.get(0)) > Y(apoints.get(0)); + + // Continue with inner points + + Line line = new Line(); + + while (ai < as.length && bi < bs.length) { + double xan = X(as[ai]); + double xbn = X(bs[bi]); + if (xan == xbn) { + double yan = Y(as[ai]); + double ybn = Y(bs[ai]); + + if (neg) { + if (yan > ybn) { // intersection + Point2D ip = VectorUtils.intersection( + apoints.get(apoints.size()-1), as[ai], + bpoints.get(bpoints.size()-1), bs[bi]); + addCheck(ip, apoints); + addCheck(ip, bpoints); + Polygon2D p = new Polygon2D( + new ArrayList<Point2D>(apoints)); + p.addReversed(bpoints); + negatives.add(p); + apoints.clear(); + bpoints.clear(); + apoints.add(ip); + bpoints.add(ip); + neg = !neg; + } + else { // no intersection + addCheck(as[ai], apoints); + addCheck(bs[bi], bpoints); + } + } + else { // not neg + if (ybn > yan) { // intersection + Point2D ip = VectorUtils.intersection( + apoints.get(apoints.size()-1), as[ai], + bpoints.get(bpoints.size()-1), bs[bi]); + addCheck(ip, apoints); + addCheck(ip, bpoints); + Polygon2D p = new Polygon2D( + new ArrayList<Point2D>(apoints)); + p.addReversed(bpoints); + positives.add(p); + apoints.clear(); + bpoints.clear(); + apoints.add(ip); + bpoints.add(ip); + neg = !neg; + } + else { // no intersection + addCheck(as[ai], apoints); + addCheck(bs[bi], bpoints); + } + } + ++ai; + ++bi; + } + else if (xan > xbn) { + line.set(apoints.get(apoints.size()-1), as[ai]); + double dir = neg ? -1d: 1d; // XXX: correct sign? + while (bi < bs.length + && X(bs[bi]) < xan + && line.eval(bs[bi])*dir > EPSILON) + ++bi; + if (bi == bs.length) { + // b run out of points + // calculate Y(last_a, as[ai]) for X(bs[bi-1]) + double ay1 = Linear.linear( + X(bs[bi-1]), + X(apoints.get(apoints.size()-1)), X(as[ai]), + Y(apoints.get(apoints.size()-1)), Y(as[ai])); + addCheck(new Point2D.Double(X(bs[bi-1]), ay1), apoints); + addCheck(bs[bi-1], bpoints); + Polygon2D p = new Polygon2D( + new ArrayList<Point2D>(apoints)); + p.addReversed(bpoints); + apoints.clear(); + bpoints.clear(); + (neg ? negatives : positives).add(p); + break; + } + else { + // TODO: intersect line and/or X(bs[bi]) >= xan? + } + } + else { // xbn > xan + line.set(bpoints.get(bpoints.size()-1), bs[bi]); + // TODO: continue symmetric + } + } + + // TODO: Continue with closing segment + } + + public static final class Line { + + private double a; + private double b; + private double c; + + public Line() { + } + + public Line(Point2D p1, Point2D p2) { + set(p1, p2); + } + + public void set(Point2D p1, Point2D p2) { + Point2D p3 = + VectorUtils.normalize( + VectorUtils.sub(p1, p2)); + + Point2D n = VectorUtils.ortho(p3); + + a = X(n); + b = Y(n); + + // a*x + b*y + c = 0 + // c = -a*x -b*y + + c = -a*X(p1) - b*Y(p1); + } + + public double eval(Point2D p) { + return a*X(p) + b*Y(p) + c; + } + } + + public static void createPolygons( + double [] xAs, double [] yAs, + double [] xBs, double [] yBs, + List<Polygon2D> positives, + List<Polygon2D> negatives + ) { + if (xAs.length == 0 || xBs.length == 0) { + return; + } + + List<Point2D []> splAs = splitByNaNs(xAs, yAs); + List<Point2D []> splBs = splitByNaNs(xBs, yBs); + + // They feeded us with NaNs only. + if (splAs.isEmpty() || splBs.isEmpty()) { + return; + } + + // Sort each part by x to ensure that the first + // is the smallest. + for (Point2D [] splA: splAs) { + Arrays.sort(splA, POINT_X_CMP); + } + + for (Point2D [] splB: splBs) { + Arrays.sort(splB, POINT_X_CMP); + } + + // Now sort all parts by there first elements. + // Should be good enough to find overlapping regions. + Collections.sort(splAs, FIRST_POINT_X); + Collections.sort(splBs, FIRST_POINT_X); + + // Check if the two series intersect at all. + // If no then there will be no area between them. + + Point2D [] p1 = splAs.get(0); + Point2D [] p2 = splBs.get(splBs.size()-1); + + // Sort out the ranges that are not intersecting + // the ranges in the other series. + // We are going to merge them anyway + // so this is not strictly required. + // Keep it to recude cases. + if (removeNoneIntersecting(splAs, splBs) + || removeNoneIntersecting(splBs, splAs) + ) { + // They do not intersect at all. + return; + } + + // TODO: Intersect/split the two series parts. + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/VectorUtils.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,149 @@ +package de.intevation.flys.geom; + +import java.awt.geom.Point2D; + +public final class VectorUtils +{ + public static final double EPSILON = 1e-4; + + private VectorUtils() { + } + + public static final double X(Point2D p) { + return p.getX(); + } + + public static final double Y(Point2D p) { + return p.getY(); + } + + public static final Point2D sub(Point2D a, Point2D b) { + return new Point2D.Double(X(a)-X(b), Y(a)-Y(b)); + } + + public static final double dot(Point2D a, Point2D b) { + return X(a)*X(b) + Y(a)*Y(b); + } + + public static final Point2D add(Point2D a, Point2D b) { + return new Point2D.Double(X(a)+X(b), Y(a)+Y(b)); + } + + public static final Point2D negate(Point2D a) { + return new Point2D.Double(-X(a), -Y(a)); + } + + public static final Point2D ortho(Point2D a) { + return new Point2D.Double(-Y(a), X(a)); + } + + public static final Point2D scale(Point2D a, double s) { + return new Point2D.Double(s*X(a), s*Y(a)); + } + + public static final double lengthSq(Point2D a) { + double x = X(a); + double y = Y(a); + return x*x + y*y; + } + + public static final double length(Point2D a) { + return Math.sqrt(lengthSq(a)); + } + + public static final Point2D normalize(Point2D a) { + double length = length(a); + return length != 0d + ? scale(a, 1d/length) + : new Point2D.Double(X(a), Y(a)); + } + + public static final double L1(Point2D a, Point2D b) { + return Math.abs(X(a)-X(b)) + Math.abs(Y(a)-Y(b)); + } + + public static final boolean collinear(Point2D a, Point2D b, Point2D c) { + double x1 = X(a); + double y1 = Y(a); + double x2 = X(b); + double y2 = Y(b); + double x3 = X(c); + double y3 = Y(c); + + return Math.abs((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1)) < EPSILON; + } + + public static boolean epsilonEquals(Point2D a, Point2D b) { + return Math.abs(X(a)-X(b)) < EPSILON + && Math.abs(Y(a)-Y(b)) < EPSILON; + } + + public static final Point2D intersection( + Point2D p1, Point2D p2, + Point2D p3, Point2D p4 + ) { + double x1 = X(p1); + double y1 = Y(p1); + double x2 = X(p2); + double y2 = Y(p2); + double x3 = X(p3); + double y3 = Y(p3); + double x4 = X(p4); + double y4 = Y(p4); + + // Compute a1, b1, c1, where line joining points 1 and 2 + // is "a1 x + b1 y + c1 = 0". + double a1 = y2 - y1; + double b1 = x1 - x2; + double c1 = x2*y1 - x1*y2; + + // Compute r3 and r4. + double r3 = a1*x3 + b1*y3 + c1; + double r4 = a1*x4 + b1*y4 + c1; + + if (r3 != 0d && r4 != 0d && r3*r4 >= 0) { + return null; + } + + // Compute a2, b2, c2 + double a2 = y4 - y3; + double b2 = x3 - x4; + double c2 = (x4 * y3) - (x3 * y4); + + // Compute r1 and r2 + double r1 = a2*x1 + b2*y1 + c2; + double r2 = a2*x2 + b2*y2 + c2; + + if (r1 != 0d && r2 != 0d && r1*r2 >= 0) { + return null; + } + + // Line segments intersect: compute intersection point. + double denom = a1*b2 - a2*b1; + + if (denom == 0d) { // collinear + return null; + } + + double offset = Math.abs(denom)/2d; + + // The denom/2 is to get rounding instead of truncating. It + // is added or subtracted to the numerator, depending upon the + // sign of the numerator. + double num = b1*c2 - b2*c1; + + double x = num < 0d + ? (num - offset)/denom + : (num + offset)/denom; + + num = a2*c1 - a1*c2; + + double y = num < 0d + ? (num - offset)/denom + : (num + offset)/denom; + + return new Point2D.Double(x, y); + } + +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,219 @@ +package de.intevation.flys.artifacts.math; + +import java.util.ArrayList; +import java.util.List; + +import java.io.Serializable; + +import org.apache.commons.math.analysis.interpolation.SplineInterpolator; + +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; + +import org.apache.commons.math.ArgumentOutsideDomainException; + +import org.apache.commons.math.exception.MathIllegalArgumentException; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.Calculation; + +import de.intevation.flys.utils.DoubleUtil; + +public class BackJumpCorrector +implements Serializable +{ + private static Logger log = Logger.getLogger(BackJumpCorrector.class); + + protected ArrayList<Double> backjumps; + + protected double [] corrected; + + public BackJumpCorrector() { + backjumps = new ArrayList<Double>(); + } + + public boolean hasBackJumps() { + return !backjumps.isEmpty(); + } + + public List<Double> getBackJumps() { + return backjumps; + } + + public double [] getCorrected() { + return corrected; + } + + public boolean doCorrection( + double [] km, + double [] ws, + Calculation errors + ) { + boolean wsUp = DoubleUtil.isIncreasing(ws); + + if (wsUp) { + km = DoubleUtil.swapClone(km); + ws = DoubleUtil.swapClone(ws); + } + + boolean kmUp = DoubleUtil.isIncreasing(km); + + if (!kmUp) { + km = DoubleUtil.sumDiffs(km); + } + + if (log.isDebugEnabled()) { + log.debug("BackJumpCorrector.doCorrection ------- enter"); + log.debug(" km increasing: " + DoubleUtil.isIncreasing(km)); + log.debug(" ws increasing: " + DoubleUtil.isIncreasing(ws)); + log.debug("BackJumpCorrector.doCorrection ------- leave"); + } + + boolean hasBackJumps = doCorrectionClean(km, ws, errors); + + if (hasBackJumps && wsUp) { + // mirror back + DoubleUtil.swap(corrected); + } + + return hasBackJumps; + } + + protected boolean doCorrectionClean( + double [] km, + double [] ws, + Calculation errors + ) { + int N = km.length; + + if (N != ws.length) { + throw new IllegalArgumentException("km.length != ws.length"); + } + + if (N < 2) { + return false; + } + + SplineInterpolator interpolator = null; + + for (int i = 1; i < N; ++i) { + if (ws[i] <= ws[i-1]) { + // no back jump + continue; + } + backjumps.add(km[i]); + + if (corrected == null) { + // lazy cloning + ws = corrected = (double [])ws.clone(); + } + + double above = aboveWaterKM(km, ws, i); + + if (Double.isNaN(above)) { // run over start km + // fill all previous + for (int j = 0; j < i; ++j) { + ws[j] = ws[i]; + } + continue; + } + + double distance = Math.abs(km[i] - above); + + double quarterDistance = 0.25*distance; + + double start = above - quarterDistance; + + double startHeight = DoubleUtil.interpolateSorted(km, ws, start); + + if (Double.isNaN(startHeight)) { + // run over start km + startHeight = ws[0]; + } + + double between = above + quarterDistance; + + double aboveHeight = ws[i] + 0.25*(startHeight - ws[i]); + + double [] x = { start, above, between }; + double [] y = { startHeight, aboveHeight, ws[i] }; + + if (log.isDebugEnabled()) { + for (int j = 0; j < x.length; ++j) { + log.debug(" " + x[j] + " -> " + y[j]); + } + } + + if (interpolator == null) { + interpolator = new SplineInterpolator(); + } + + PolynomialSplineFunction spline; + + try { + spline = interpolator.interpolate(x, y); + } + catch (MathIllegalArgumentException miae) { + // TODO: I18N + errors.addProblem("creating spline interpolation failed."); + log.error(miae); + continue; + } + + try { + if (log.isDebugEnabled()) { + log.debug("spline points:"); + for (int j = 0; j < x.length; ++j) { + log.debug(x[j] + " " + y[j] + " " + spline.value(x[j])); + } + } + + int j = i-1; + + for (; j >= 0 && km[j] >= between; --j) { + ws[j] = ws[i]; + } + + for (; j >= 0 && ws[j] < startHeight; --j) { + ws[j] = spline.value(km[j]); + } + } + catch (ArgumentOutsideDomainException aode) { + // TODO: I18N + errors.addProblem("spline interpolation failed."); + log.error("spline interpolation failed", aode); + } + } // for all km + + return !backjumps.isEmpty(); + } + + + protected static double aboveWaterKM( + double [] km, + double [] ws, + int wIndex + ) { + double w = ws[wIndex]; + + while (--wIndex >= 0) { + // still under water + if (ws[wIndex] < w) continue; + + if (ws[wIndex] > w) { + // f(ws[wIndex]) = km[wIndex] + // f(ws[wIndex+1]) = km[wIndex+1] + return Linear.linear( + w, + ws[wIndex], ws[wIndex+1], + km[wIndex], km[wIndex+1]); + } + else { + return km[wIndex]; + } + } + + return Double.NaN; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/DifferenceCurveFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +/** + * Facet with the curve of a subtraction of two waterlevel-lines. + */ +public class DifferenceCurveFacet extends WaterlevelFacet { + + private static Logger logger = Logger.getLogger(DifferenceCurveFacet.class); + + + public DifferenceCurveFacet() {;} + + public DifferenceCurveFacet( + int index, + String name, + String description, + ComputeType type, + String stateID, + String hash + + ) { + super(index, name, description, type, stateID, hash); + } + + /** + * Get difference curve data. + * @return a WKms at given index. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res = (CalculationResult) + winfo.compute(context, hash, stateID, type, false); + + WKms [] wkms = (WKms [])res.getData(); + + WKms result = wkms[index]; + logger.debug("Got difference curve data (" + result.getName() + + ") at index: " + index); + + return result; + } + + + /** Copy deeply. */ + @Override + public Facet deepCopy() { + WaterlevelFacet copy = new DifferenceCurveFacet(); + copy.set(this); + copy.type = type; + copy.stateID = stateID; + copy.hash = hash; + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Function.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,6 @@ +package de.intevation.flys.artifacts.math; + +public interface Function { + double value(double x); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Identity.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,15 @@ +package de.intevation.flys.artifacts.math; + +public final class Identity +implements Function +{ + public static final Identity IDENTITY = new Identity(); + + public Identity() { + } + + public double value(double x) { + return x; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/Linear.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,68 @@ +package de.intevation.flys.artifacts.math; + +public final class Linear +implements Function +{ + private double m; + private double b; + + public Linear( + double x1, double x2, + double y1, double y2 + ) { + // y1 = m*x1 + b + // y2 = m*x2 + b + // y2 - y1 = m*(x2 - x1) + // m = (y2 - y1)/(x2 - x1) # x2 != x1 + // b = y1 - m*x1 + + if (x1 == x2) { + m = 0; + b = 0.5*(y1 + y2); + } + else { + m = (y2 - y1)/(x2 - x1); + b = y1 - m*x1; + } + } + + public static final double linear( + double x, + double x1, double x2, + double y1, double y2 + ) { + // y1 = m*x1 + b + // y2 = m*x2 + b + // y2 - y1 = m*(x2 - x1) + // m = (y2 - y1)/(x2 - x1) # x2 != x1 + // b = y1 - m*x1 + + if (x1 == x2) { + return 0.5*(y1 + y2); + } + double m = (y2 - y1)/(x2 - x1); + double b = y1 - m*x1; + return x*m + b; + } + + @Override + public double value(double x) { + return m*x + b; + } + + public static final double factor(double x, double p1, double p2) { + // 0 = m*p1 + b <=> b = -m*p1 + // 1 = m*p2 + b + // 1 = m*(p2 - p1) + // m = 1/(p2 - p1) # p1 != p2 + // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1) + + return p1 == p2 ? 0.0 : (x-p1)/(p2-p1); + } + + public static final double weight(double factor, double a, double b) { + //return (1.0-factor)*a + factor*b; + return a + factor*(b-a); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/WKmsOperation.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,148 @@ +package de.intevation.flys.artifacts.math; + +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WKmsImpl; + +import java.util.Arrays; + +public abstract class WKmsOperation +{ + public static final double EPSILON = 1e-6; + + public static final class KmW + implements Comparable<KmW> + { + protected double km; + protected double w; + + public KmW(double km, double w) { + this.km = km; + this.w = w; + } + + public int compareTo(KmW other) { + return km < other.km + ? -1 + : km > other.km ? +1 : 0; + } + + public boolean kmEquals(KmW other) { + return Math.abs(km - other.km) < EPSILON; + } + + public double subtract(KmW other) { + return w - other.w; + } + } // class KmW + + public static final WKmsOperation SUBTRACTION = new WKmsOperation() { + + @Override + public WKms operate(WKms a, WKms b) { + return subtract(a, b); + } + }; + + protected WKmsOperation() { + } + + public abstract WKms operate(WKms a, WKms b); + + /** + * Subtract two series from each other, interpolate values + * missing in one series in the other. + */ + public static WKms subtract(WKms minuend, WKms subtrahend) { + + int M = minuend .size(); + int S = subtrahend.size(); + + // Don't subtract empty sets + if (M < 1 || S < 1) { + return new WKmsImpl(); + } + + KmW [] ms = new KmW[M]; + KmW [] ss = new KmW[S]; + + for (int i = 0; i < M; ++i) { + ms[i] = new KmW(minuend.getKm(i), minuend.getW(i)); + } + + for (int i = 0; i < S; ++i) { + ss[i] = new KmW(subtrahend.getKm(i), subtrahend.getW(i)); + } + + Arrays.sort(ms); + Arrays.sort(ss); + + // no overlap -> empty result set + if (ms[0].km > ss[S-1].km || ss[0].km > ms[M-1].km) { + return new WKmsImpl(); + } + + WKmsImpl result = new WKmsImpl(); + + int mi = 0; + int si = 0; + + OUT: while (mi < M && si < S) { + KmW m = ms[mi]; + KmW s = ss[si]; + + if (m.km + EPSILON < s.km) { + // minuend is before subtrahend + + while (ms[mi].km + EPSILON < s.km) { + if (++mi >= M) { + break OUT; + } + } + + if (ms[mi].km + EPSILON > s.km) { + double mw = Linear.linear( + s.km, + ms[mi-1].km, ms[mi].km, + ms[mi-1].w, ms[mi].w); + result.add(s.km, mw - s.w); + ++si; + } + else { // s.km == ms[mi].km + result.add(s.km, ms[mi].subtract(s)); + ++mi; + ++si; + } + } + else if (m.km > s.km + EPSILON) { + // subtrahend is before minuend + + while (m.km > ss[si].km + EPSILON) { + if (++si >= S) { + break OUT; + } + } + + if (ss[si].km + EPSILON > m.km) { + double sw = Linear.linear( + m.km, + ss[si-1].km, ss[si].km, + ss[si-1].w, ss[si].w); + result.add(m.km, m.w - sw); + } + else { // ss[si].km == m.km + result.add(m.km, m.subtract(ss[si])); + ++mi; + ++si; + } + } + else { // m.km == s.km + result.add(s.km, m.subtract(s)); + ++mi; + ++si; + } + } + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,81 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.jfree.chart.annotations.XYTextAnnotation; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.AnnotationArtifact; +import de.intevation.flys.jfree.FLYSAnnotation; +import de.intevation.flys.jfree.StickyAxisAnnotation; + +import de.intevation.flys.model.Annotation; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +/** + * Facet to access Annotations (landmarks, POIs) of a river. + */ +public class AnnotationFacet +extends DefaultFacet +{ + /** Logger for this class. */ + private static final Logger logger = Logger.getLogger(AnnotationFacet.class); + + + /** + * Trivial Constructor. + */ + public AnnotationFacet() { + } + + + /** + * Trivial Constructor for a AnnotationFacet. + * + * @param index Database-Index to use. + * @param name Name (~type) of Facet. + * @param description Description of Facet. + */ + public AnnotationFacet(int index, String name, String description) { + super(index, name, description); + } + + + /** + * Get List of Annotations for river from Artifact. + * + * @param artifact (Annotation-)Artifact to query for list of Annotations. + * @param context Ignored. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + AnnotationArtifact annotationArtifact = (AnnotationArtifact) artifact; + + List<Annotation> as = annotationArtifact.getAnnotations(); + List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>(); + + for (Annotation a: as) { + xy.add(new StickyAxisAnnotation( + a.getPosition().getValue(), + (float) a.getRange().getA().doubleValue(), + StickyAxisAnnotation.SimpleAxis.X_AXIS)); + } + + return new FLYSAnnotation(description, xy); + } + + + @Override + public Facet deepCopy() { + AnnotationFacet copy = new AnnotationFacet(); + copy.set(this); + return copy; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,102 @@ +package de.intevation.flys.artifacts.model; + +import java.math.BigDecimal; + +import java.util.List; +import java.util.Iterator; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Annotation; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; + +import org.hibernate.Session; +import org.hibernate.Query; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class AnnotationsFactory { + + public static List<Annotation> getAnnotations(River river) { + return getAnnotations(river.getName()); + } + + + /** + * Get Annotations which do not have a "b" ("to")-value set. + * + * @param river name of the river of interest. + * + * @return List of Annotations for river which have only "a" ("from") + * value set. + */ + public static List<Annotation> getPointAnnotations(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query rangesQuery = session.createQuery( + "from Range where river.name=:name and b = null"); + rangesQuery.setParameter("name", river); + List<Range> ranges = rangesQuery.list(); + + Query query = session.createQuery( + "from Annotation where range in (:ranges) order by range.a"); + query.setParameterList("ranges", ranges); + return query.list(); + } + + + public static List<Annotation> getAnnotations(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query rangesQuery = session.createQuery( + "from Range where river.name=:name"); + rangesQuery.setParameter("name", river); + List<Range> ranges = rangesQuery.list(); + + Query query = session.createQuery( + "from Annotation where range in (:ranges) order by range.a"); + query.setParameterList("ranges", ranges); + return query.list(); + } + + + /** + * Get minimal "a" ("from") and maximal "b" ("to") value of annotations' + * ranges of a river. + * + * @param river name of the river of interest. + * + * @return Array containing minimal "a" and max "b" value of any + * annotation stored for the given river. + */ + public static double[] getAnnotationsBreadth(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query minAQuery = session.createQuery( + "select min(a), max(b) from Range where river.name=:name"); + minAQuery.setParameter("name", river); + + double[] minAmaxB = {0.0f, 0.0f}; + Object[] row = (Object[]) minAQuery.list().iterator().next(); + minAmaxB[0] = ((BigDecimal) row[0]).doubleValue(); + minAmaxB[1] = ((BigDecimal) row[1]).doubleValue(); + return minAmaxB; + } + + + public static Iterator<Annotation> getAnnotationsIterator(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query rangesQuery = session.createQuery( + "from Range where river.name=:name"); + rangesQuery.setParameter("name", river); + List<Range> ranges = rangesQuery.list(); + + Query query = session.createQuery( + "from Annotation where range in (:ranges) order by range.a"); + query.setParameterList("ranges", ranges); + return (Iterator<Annotation>)query.iterate(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,94 @@ +package de.intevation.flys.artifacts.model; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.List; +import java.util.ArrayList; + +import java.io.Serializable; + +import de.intevation.artifacts.CallMeta; + +public class Calculation +implements Serializable +{ + public static class Problem + implements Serializable + { + protected Double km; + protected String msg; + + public Problem() { + } + + public Problem(String msg) { + this.msg = msg; + } + + public Problem(double km, String msg) { + this.km = km; + this.msg = msg; + } + + public Element toXML(Document document, CallMeta meta) { + // TODO: i18n + Element problem = document.createElement("problem"); + if (km != null) { + problem.setAttribute("km", String.valueOf(km)); + } + problem.setTextContent(msg); + return problem; + } + } // class Problem + + protected List<Problem> problems; + + public Calculation() { + } + + public Calculation(String msg) { + addProblem(msg); + } + + protected List<Problem> checkProblems() { + if (problems == null) { + problems = new ArrayList<Problem>(); + } + return problems; + } + + public void addProblem(String msg) { + checkProblems().add(new Problem(msg)); + } + + public void addProblem(double km, String msg) { + checkProblems().add(new Problem(km, msg)); + } + + public boolean hasProblems() { + return problems != null && !problems.isEmpty(); + } + + public int numProblems() { + return problems != null ? problems.size() : 0; + } + + public List<Problem> getProblems() { + return problems; + } + + public void toXML(Document document, CallMeta meta) { + + Element root = document.createElement("problems"); + + if (hasProblems()) { + for (Problem problem: problems) { + root.appendChild(problem.toXML(document, meta)); + } + } + + document.appendChild(root); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation1.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,73 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; + +public class Calculation1 +extends Calculation +{ + private static Logger logger = Logger.getLogger(Calculation1.class); + + protected double [] kms; + protected double [] qs; + protected double [] ws; + protected double refKm; + + public Calculation1() { + } + + public Calculation1( + double [] kms, + double [] qs, + double [] ws, + double refKm + ) { + this.kms = kms; + this.qs = qs; + this.ws = ws; + this.refKm = refKm; + } + + public CalculationResult calculate(WstValueTable wst) { + + ArrayList<WQKms> results = new ArrayList<WQKms>(); + + String prefix; + double [] origData; + + if (ws != null) { prefix = "W="; origData = ws; } + else { prefix = "Q="; origData = qs; } + + int oldNumProblems = numProblems(); + + for (int i = 0; i < qs.length; i++) { + + double [] oqs = new double[kms.length]; + double [] ows = new double[kms.length]; + + boolean success = + wst.interpolate(qs[i], refKm, kms, ows, oqs, this) != null; + + int newNumProblems = numProblems(); + + if (success) { + WQKms result = new WQKms(kms, oqs, ows, prefix + origData[i]); + if (oldNumProblems != newNumProblems) { + logger.debug( + qs[i] + " caused " + (newNumProblems-oldNumProblems) + + " new problem(s)."); + result.removeNaNs(); + } + results.add(result); + } + + oldNumProblems = newNumProblems; + } + + return new CalculationResult( + results.toArray(new WQKms[results.size()]), + this); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation2.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,49 @@ +package de.intevation.flys.artifacts.model; + +import java.util.Arrays; + +import org.apache.log4j.Logger; + +public class Calculation2 +extends Calculation +{ + private static Logger logger = Logger.getLogger(Calculation2.class); + + protected double km; + + public Calculation2() { + } + + public Calculation2(double km) { + this.km = km; + } + + public CalculationResult calculate(WstValueTable wst) { + + logger.debug("Calculation2.calculate"); + + double [][] wqs = wst.interpolateWQ(km, this); + + if (wqs == null || wqs[0].length == 0) { + logger.debug("Cannot compute discharge curve data."); + addProblem("Cannot compute discharge curve data."); + return new CalculationResult(new WQKms[0], this); + } + + double [] ws = wqs[0]; + double [] qs = wqs[1]; + double [] kms = new double[ws.length]; + + Arrays.fill(kms, km); + + WQKms wqkms = new WQKms(kms, qs, ws, String.valueOf(km)); + + if (hasProblems()) { + logger.debug("found + "+numProblems()+" problems."); + wqkms.removeNaNs(); + } + + return new CalculationResult(new WQKms[] { wqkms }, this); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation3.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,49 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +public class Calculation3 +extends Calculation +{ + private static Logger logger = Logger.getLogger(Calculation3.class); + + protected double km; + protected int [] days; + protected double [] qs; + + public Calculation3() { + } + + public Calculation3(double km, int [] days, double [] qs) { + this.km = km; + this.days = days; + this.qs = qs; + } + + public CalculationResult calculate(WstValueTable wst) { + + double [] ws = wst.interpolateW(km, qs, new double[qs.length], this); + + if (days == null || days.length == 0) { + addProblem(km, "cannot find Ds"); + } + + if (logger.isDebugEnabled()) { + logger.debug("Calculate duration curve data:"); + logger.debug(" km : " + km); + logger.debug(" num Days : " + (days != null ? days.length : 0)); + logger.debug(" num Qs : " + (qs != null ? qs.length : 0)); + logger.debug(" result Ws: " + (ws != null ? ws.length : 0)); + } + + WQDay wqday = new WQDay(days, ws, qs); + + if (hasProblems()) { + logger.debug("calculation caused "+numProblems()+" problem(s)."); + wqday.removeNaNs(); + } + + return new CalculationResult(wqday, this); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation4.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,309 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Collections; + +import de.intevation.flys.utils.DoubleUtil; + +import de.intevation.flys.model.River; +import de.intevation.flys.model.Gauge; + +import de.intevation.flys.artifacts.model.WstValueTable.QPosition; + +import de.intevation.flys.artifacts.math.Function; +import de.intevation.flys.artifacts.math.Linear; +import de.intevation.flys.artifacts.math.Identity; +import de.intevation.flys.artifacts.math.BackJumpCorrector; + +import org.apache.log4j.Logger; + +public class Calculation4 +extends Calculation +{ + private static Logger logger = Logger.getLogger(Calculation4.class); + + public static final double MINIMAL_STEP_WIDTH = 1e-5; + + public static final Comparator<Segment> REF_CMP = + new Comparator<Segment>() { + public int compare(Segment a, Segment b) { + double d = a.referencePoint - b.referencePoint; + if (d < 0d) return -1; + return d > 0d ? +1 : 0; + } + }; + + protected List<Segment> segments; + + protected boolean isQ; + + public Calculation4() { + } + + public Calculation4(List<Segment> segments, River river, boolean isQ) { + + this.segments = segments; + this.isQ = isQ; + + int numResults = -1; + + // assign reference points + for (Segment segment: segments) { + Gauge gauge = river.maxOverlap(segment.getFrom(), segment.getTo()); + + if (gauge == null) { + logger.warn("no gauge found. Defaults to mid point."); + segment.setReferencePoint( + 0.5*(segment.getFrom()+segment.getTo())); + } + else { + double ref = gauge.getStation().doubleValue(); + logger.debug( + "reference gauge: " + gauge.getName() + + " (km " + ref + ")"); + segment.setReferencePoint(ref); + } + + double [] values = segment.values; + + if (numResults == -1) { + numResults = values.length; + } + else if (numResults != values.length) { + throw new IllegalArgumentException("wrong length of values"); + } + + // convert to Q if needed + if (!isQ && gauge != null) { + double [][] table = new DischargeTables( + river.getName(), gauge.getName()).getFirstTable(); + + // need the original values for naming + segment.backup(); + + for (int i = 0; i < values.length; ++i) { + values[i] = DischargeTables.getQForW(table, values[i]); + } + } + } // for all segments + + Collections.sort(segments, REF_CMP); + } + + public CalculationResult calculate( + WstValueTable table, + double from, double to, double step + ) { + boolean debug = logger.isDebugEnabled(); + + if (debug) { + logger.debug( + "calculate from " + from + " to " + to + " step " + step); + logger.debug("# segments: " + segments.size()); + for (Segment segment: segments) { + logger.debug(" " + segment); + } + } + + if (segments.isEmpty()) { + logger.debug("no segments found"); + // TODO: I18N + addProblem("no segments found"); + return new CalculationResult(new WQKms[0], this); + } + + int numResults = segments.get(0).values.length; + + if (numResults < 1) { + logger.debug("no values given"); + // TODO: I18N + addProblem("no values given"); + return new CalculationResult(new WQKms[0], this); + } + + + WQKms [] results = new WQKms[numResults]; + for (int i = 0; i < results.length; ++i) { + results[i] = new WQKms(); + } + + if (Math.abs(step) < MINIMAL_STEP_WIDTH) { + step = MINIMAL_STEP_WIDTH; + } + + if (from > to) { + step = -step; + } + + QPosition [] qPositions = new QPosition[numResults]; + + Function [] functions = new Function[numResults]; + + double [] out = new double[2]; + + Segment sentinel = new Segment(Double.MAX_VALUE); + Segment s1 = sentinel, s2 = sentinel; + + for (double pos = from; + from < to ? pos <= to : pos >= to; + pos = DoubleUtil.round(pos + step) + ) { + if (pos < s1.referencePoint || pos > s2.referencePoint) { + if (debug) { + logger.debug("need to find new interval for " + pos); + } + // find new interval + if (pos <= segments.get(0).referencePoint) { + // before first segment -> "gleichwertig" + if (debug) { + logger.debug("before first segment -> gleichwertig"); + } + Segment first = segments.get(0); + double [] values = first.values; + double refPos = first.referencePoint; + for (int i = 0; i < qPositions.length; ++i) { + qPositions[i] = table.getQPosition( + refPos, values[i]); + } + sentinel.setReferencePoint(-Double.MAX_VALUE); + s1 = sentinel; + s2 = segments.get(0); + Arrays.fill(functions, Identity.IDENTITY); + } + else if (pos >= segments.get(segments.size()-1).referencePoint) { + // after last segment -> "gleichwertig" + if (debug) { + logger.debug("after last segment -> gleichwertig"); + } + Segment last = segments.get(segments.size()-1); + double [] values = last.values; + double refPos = last.referencePoint; + for (int i = 0; i < qPositions.length; ++i) { + qPositions[i] = table.getQPosition( + refPos, values[i]); + } + sentinel.setReferencePoint(Double.MAX_VALUE); + s1 = last; + s2 = sentinel; + Arrays.fill(functions, Identity.IDENTITY); + } + else { // "ungleichwertig" + // find matching interval + if (debug) { + logger.debug("in segments -> ungleichwertig"); + } + s1 = s2 = null; + for (int i = 1, N = segments.size(); i < N; ++i) { + Segment si1 = segments.get(i-1); + Segment si = segments.get(i); + if (debug) { + logger.debug("check " + pos + " in " + + si1.referencePoint + " - " + si.referencePoint); + } + if (pos >= si1.referencePoint + && pos <= si. referencePoint) { + s1 = si1; + s2 = si; + break; + } + } + + if (s1 == null) { + throw new IllegalStateException("no interval found"); + } + + Segment anchor, free; + + if (from > to) { anchor = s1; free = s2; } + else { anchor = s2; free = s1; } + + // build transforms based on "gleichwertiger" phase + for (int i = 0; i < qPositions.length; ++i) { + QPosition qi = table.getQPosition( + anchor.referencePoint, + anchor.values[i]); + + if ((qPositions[i] = qi) == null) { + // TODO: I18N + addProblem(pos, "cannot find q = " + anchor.values[i]); + functions[i] = Identity.IDENTITY; + } + else { + double qA = table.getQ(qi, anchor.referencePoint); + double qF = table.getQ(qi, free .referencePoint); + + functions[i] = Double.isNaN(qA) || Double.isNaN(qF) + ? Identity.IDENTITY + : new Linear( + qA, qF, + anchor.values[i], free.values[i]); + + if (debug) { + logger.debug( + anchor.referencePoint + ": " + + qA + " -> " + functions[i].value(qA) + + " / " + free.referencePoint + ": " + + qF + " -> " + functions[i].value(qF)); + } + } + } // build transforms + } // "ungleichwertiges" interval + } // find matching interval + + for (int i = 0; i < qPositions.length; ++i) { + QPosition qPosition = qPositions[i]; + + if (qPosition == null) { + continue; + } + + if (table.interpolate(pos, out, qPosition, functions[i])) { + results[i].add(out[0], out[1], pos); + } + else { + // TODO: I18N + addProblem(pos, "cannot interpolate w/q"); + } + } + } + + // Backjump correction + for (int i = 0; i < results.length; ++i) { + BackJumpCorrector bjc = new BackJumpCorrector(); + + double [] ws = results[i].getWs(); + double [] kms = results[i].getKms(); + + if (bjc.doCorrection(kms, ws, this)) { + results[i] = new WQCKms(results[i], bjc.getCorrected()); + } + } + + // name the curves + for (int i = 0; i < results.length; ++i) { + results[i].setName(createName(i)); + } + + return new CalculationResult(results, this); + } + + protected String createName(int index) { + // TODO: I18N + StringBuilder sb = new StringBuilder(isQ ? "Q" : "W"); + sb.append(" benutzerdefiniert ("); + for (int i = 0, N = segments.size(); i < N; ++i) { + if (i > 0) { + sb.append("; "); + } + Segment segment = segments.get(i); + sb.append((segment.backup != null + ? segment.backup + : segment.values)[index]); + } + sb.append(')'); + return sb.toString(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationMessage.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,45 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifacts.Message; + + +public class CalculationMessage implements Message { + + protected String message; + protected int steps; + protected int currentStep; + + + public CalculationMessage() { + } + + + public CalculationMessage(int steps, int currentStep, String message) { + this.steps = steps; + this.currentStep = currentStep; + this.message = message; + } + + + public int getSteps() { + return steps; + } + + + public int getCurrentStep() { + return currentStep; + } + + + public String getMessage() { + return message; + } + + + @Override + public String getText() { + return + String.valueOf(currentStep) + "/" + String.valueOf(steps) + + " - " + getMessage(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,41 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +/** + * Wraps result(s) of a Calculation and eventual error reports. + */ +public class CalculationResult +implements Serializable +{ + protected Object data; + protected Calculation report; + + public CalculationResult() { + } + + /** + * @param report report (e.g. error messages). + */ + public CalculationResult(Object data, Calculation report) { + this.data = data; + this.report = report; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public Calculation getReport() { + return report; + } + + public void setReport(Calculation report) { + this.report = report; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,55 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +/** + * Trival Facet for Cross Sections. + */ +public class CrossSectionFacet +extends DefaultFacet +implements FacetTypes { + + private static Logger logger = Logger.getLogger(CrossSectionFacet.class); + + protected ComputeType type; + + /** Trivial constructor, set (maybe localized) description. */ + public CrossSectionFacet(int idx, String description) { + super(idx, CROSS_SECTION, description); + type = ComputeType.ADVANCE; + } + + + /** + * Gets dummy data. + */ + public Object getData(Artifact artifact, CallContext context) { + logger.debug("Get data for cross section"); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + return winfo.getCrossSectionData(); + } + + + /** Do a deep copy. */ + @Override + public Facet deepCopy() { + CrossSectionFacet copy = new CrossSectionFacet(this.index, this.description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,45 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.CrossSection; +import de.intevation.flys.model.River; + +import org.hibernate.Session; +import org.hibernate.Query; + +/** + * Get Cross Sections. + */ +public class CrossSectionFactory { + + /** + * Get CrossSections for an instantiated River. + * + * @param river river object. + * + * @return List of Cross Sections of river. + */ + public static List<CrossSection> getCrossSections(River river) { + return getCrossSections(river.getName()); + } + + + /** + * Get Cross Sections for a river by name. + * + * @param river name of the river of interest. + * + * @return List of Cross Sections of river. + */ + public static List<CrossSection> getCrossSections(String riverName) { + Session session = SessionHolder.HOLDER.get(); + Query query = session.createQuery( + "from CrossSection where river.name = :rivername"); + query.setParameter("rivername", riverName); + return query.list(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/CrossSectionWaterLineFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,51 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.WINFOArtifact; + + +/** + * Facet for Waterlines in Cross Sections. + */ +public class CrossSectionWaterLineFacet +extends DefaultFacet +implements FacetTypes { + + private static Logger logger = Logger.getLogger(CrossSectionWaterLineFacet.class); + + + /** Trivial constructor, set (maybe localized) description. */ + public CrossSectionWaterLineFacet(int idx, String description) { + super(idx, CROSS_SECTION_WATER_LINE, description); + } + + + /** + * Gets dummy data. + */ + public Object getData(Artifact artifact, CallContext context) { + logger.debug("Get data for cross section water line"); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + return winfo.getWaterLines(this.getIndex()); + } + + + /** Do a deep copy. */ + @Override + public Facet deepCopy() { + CrossSectionWaterLineFacet copy = new CrossSectionWaterLineFacet(this.getIndex(), this.description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DataFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,91 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + +public class DataFacet +extends DefaultFacet +{ + protected ComputeType type; + protected String hash; + protected String stateId; + + + /** Trivial constructor. */ + public DataFacet() { + } + + /** + * Defaults to ADVANCE Compute type. + * @param name Name of the facet. + * @param description maybe localized description of the facet. + */ + public DataFacet(String name, String description) { + this(name, description, ComputeType.ADVANCE); + } + + public DataFacet(String name, String description, ComputeType type) { + this(name, description, type, null); + } + + public DataFacet( + String name, + String description, + ComputeType type, + String hash + ) { + super(name, description); + this.type = type; + this.hash = hash; + } + + + public DataFacet( + String name, + String description, + ComputeType type, + String hash, + String stateId + ) { + super(name, description); + this.type = type; + this.hash = hash; + this.stateId = stateId; + } + + + /** + * Return computation result. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + FLYSArtifact flys = (FLYSArtifact)artifact; + String theHash = (hash != null) ? hash : flys.hash(); + + return (stateId != null && stateId.length() > 0) + ? flys.compute(context, theHash, stateId, type, false) + : flys.compute(context, theHash, type, false); + } + + + /** + * Return a deep copy. + */ + @Override + public Facet deepCopy() { + DataFacet copy = new DataFacet(); + copy.set(this); + copy.type = type; + copy.hash = hash; + copy.stateId = stateId; + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,234 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.Comparator; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.Query; + +import org.apache.log4j.Logger; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.DischargeTable; +import de.intevation.flys.model.DischargeTableValue; + +public class DischargeTables +implements Serializable +{ + private static Logger log = Logger.getLogger(DischargeTables.class); + + public static final double DEFAULT_SCALE = 100.0; + + public static final int MASTER = 0; + + protected List<String> gaugeNames; + + protected String riverName; + + protected double scale; + + protected int kind; + + protected Map<String, double [][]> values; + + public DischargeTables() { + } + + public DischargeTables(String riverName, String gaugeName) { + this(riverName, gaugeName, MASTER); + } + + public DischargeTables(String riverName, String gaugeName, int kind) { + this(riverName, new String [] { gaugeName }, kind); + } + + public DischargeTables(String riverName, String [] gaugeNames) { + this(riverName, gaugeNames, MASTER); + } + + public DischargeTables(String riverName, String [] gaugeNames, int kind) { + this(riverName, Arrays.asList(gaugeNames), kind); + } + + public DischargeTables( + String riverName, + List<String> gaugeNames, + int kind + ) { + scale = Double.NaN; + this.kind = kind; + this.riverName = riverName; + this.gaugeNames = gaugeNames; + } + + public double [][] getFirstTable() { + return getFirstTable(DEFAULT_SCALE); + } + + public double [][] getFirstTable(double scale) { + Map<String, double [][]> values = getValues(scale); + for (double [][] table: values.values()) { + return table; + } + return null; + } + + public Map<String, double [][]> getValues() { + return getValues(DEFAULT_SCALE); + } + + public Map<String, double [][]> getValues(double scale) { + if (values == null || scale != this.scale) { + values = loadValues(scale); + this.scale = scale; + } + return values; + } + + protected Map<String, double [][]> loadValues(double scale) { + Map<String, double [][]> values = new HashMap<String, double [][]>(); + + Session session = SessionHolder.HOLDER.get(); + + Query gaugeQuery = session.createQuery( + "from Gauge where name=:gauge and river.name=:river"); + gaugeQuery.setParameter("river", riverName); + + for (String gaugeName: gaugeNames) { + gaugeQuery.setParameter("gauge", gaugeName); + List<Gauge> gauges = gaugeQuery.list(); + if (gauges.isEmpty()) { + log.warn( + "no gauge '"+gaugeName+"' at river '"+riverName+"'"); + continue; + } + Gauge gauge = gauges.get(0); + + List<DischargeTable> tables = gauge.getDischargeTables(); + + if (tables.isEmpty()) { + log.warn( + "no discharge table for gauge '" + gaugeName + "'"); + continue; + } + + // TODO: Filter by time interval + DischargeTable table = tables.get(0); + + List<DischargeTableValue> dtvs = + table.getDischargeTableValues(); + + final double [][] vs = new double[2][dtvs.size()]; + + boolean qSorted = true; + + double lastQ = -Double.MAX_VALUE; + + int idx = 0; + for (DischargeTableValue dtv: dtvs) { + double q = dtv.getQ().doubleValue(); + vs[0][idx] = q * scale; + vs[1][idx] = dtv.getW().doubleValue() * scale; + ++idx; + + if (qSorted && lastQ > q) { + qSorted = false; + } + lastQ = q; + } + + if (!qSorted) { + log.debug("need to sort by q values"); + // TODO: Do this db level. + // XXX: This is so ugly :-( + Integer [] indices = new Integer[vs[0].length]; + for (int i = 0; i < indices.length; ++i) { + indices[i] = i; + } + + Arrays.sort(indices, new Comparator<Integer>() { + public int compare(Integer a, Integer b) { + double va = vs[1][a]; + double vb = vs[1][b]; + double d = va - vb; + if (d < 0.0) return -1; + if (d > 0.0) return +1; + return 0; + } + }); + + double [][] vs2 = new double[2][vs[0].length]; + for (int i = 0; i < indices.length; ++i) { + vs2[0][i] = vs[0][indices[i]]; + vs2[1][i] = vs[1][indices[i]]; + } + values.put(gaugeName, vs2); + } + else { + values.put(gaugeName, vs); + } + + } + + return values; + } + + public static double getQForW(double [][] values, double w) { + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("calculating getQForW(" + w + ")"); + } + + int index = Arrays.binarySearch(values[1], w); + if (index >= 0) { + return values[0][index]; + } + + index = -index - 1; // insert position + + if (index < 1 || index >= values[0].length) { + // do not extraploate + if (debug) { + log.debug("we do not extrapolate: NaN"); + } + return Double.NaN; + } + + double w1 = values[1][index-1]; + double w2 = values[1][index ]; + double q1 = values[0][index-1]; + double q2 = values[0][index ]; + + // q1 = m*w1 + b + // q2 = m*w2 + b + // q2 - q1 = m*(w2 - w1) + // m = (q2 - q1)/(w2 - w1) # w2 != w1 + // b = q1 - m*w1 + + double q; + if (w1 == w2) { + q = 0.5*(q1 + q2); + if (debug) { + log.debug("same w1 and w1: " + w1); + } + } + else { + double m = (q2 - q1)/(w2 - w1); + double b = q1 - m*w1; + q = w*m + b; + } + if (debug) { + log.debug("Q(" + w + ") = " + q); + } + return q; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,46 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +public class DurationCurveFacet extends DefaultFacet { + + private static Logger logger = Logger.getLogger(DurationCurveFacet.class); + + public DurationCurveFacet() { + } + + public DurationCurveFacet(String name, String description) { + super(0, name, description); + } + + + public Object getData(Artifact artifact, CallContext context) { + logger.debug("Get data for duration curve data"); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult cr = (CalculationResult)winfo.compute( + context, ComputeType.ADVANCE, false); + + return cr.getData(); + } + + @Override + public Facet deepCopy() { + DurationCurveFacet copy = new DurationCurveFacet(); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,47 @@ +package de.intevation.flys.artifacts.model; + +public interface FacetTypes { + + String FLOODMAP_WSPLGEN = "floodmap.wsplgen"; + String FLOODMAP_BARRIERS = "floodmap.barriers"; + String FLOODMAP_RIVERAXIS = "floodmap.riveraxis"; + String FLOODMAP_WMSBACKGROUND = "floodmap.wmsbackground"; + String FLOODMAP_KMS = "floodmap.kms"; + + String DISCHARGE_LONGITUDINAL_W = "discharge_longitudinal_section.w"; + String DISCHARGE_LONGITUDINAL_Q = "discharge_longitudinal_section.q"; + String DISCHARGE_LONGITUDINAL_C = "discharge_longitudinal_section.c"; + + String LONGITUDINAL_W = "longitudinal_section.w"; + String LONGITUDINAL_Q = "longitudinal_section.q"; + String LONGITUDINAL_ANNOTATION = "longitudinal_section.annotations"; + + String W_DIFFERENCES = "w_differences"; + + String COMPUTED_DISCHARGE_Q = "computed_discharge_curve.q"; + String COMPUTED_DISCHARGE_MAINVALUES_Q = "computed_discharge_curve.mainvalues.q"; + String COMPUTED_DISCHARGE_MAINVALUES_W = "computed_discharge_curve.mainvalues.w"; + + String MAINVALUES_Q = "mainvalues.q"; + String MAINVALUES_W = "mainvalues.w"; + + String CROSS_SECTION = "cross_section"; + String CROSS_SECTION_WATER_LINE = "cross_section_water_line"; + + String DISCHARGE_CURVE = "discharge_curve.curve"; + + String DURATION_W = "duration_curve.w"; + String DURATION_Q = "duration_curve.q"; + + String STATIC_WKMS = "other.wkms"; + String STATIC_WQKMS = "other.wqkms"; + + String HEIGHTMARKS_POINTS = "heightmarks_points"; + + String CSV = "csv"; + String WST = "wst"; + String AT = "at"; + + String REPORT = "report"; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.ArrayList; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.Range; + +import org.hibernate.Session; +import org.hibernate.Query; + +public class GaugesFactory +{ + public static List<Gauge> getGauges(River river) { + return getGauges(river.getName()); + } + + + public static Gauge getGauge(String gaugeName) { + Session session = SessionHolder.HOLDER.get(); + Query query = session.createQuery( + "from Gauge where name=:name"); + query.setParameter("name", gaugeName); + + List<Gauge> res = query.list(); + + return res.isEmpty() ? null : res.get(0); + } + + + public static List<Gauge> getGauges(String river) { + Session session = SessionHolder.HOLDER.get(); + Query query = session.createQuery( + "from Gauge where river.name=:name"); + query.setParameter("name", river); + return query.list(); + } + + public static List<Gauge> filterRanges( + List<Gauge> gauges, + List<double []> ranges + ) { + // XXX: Inefficent! + ArrayList<Range> rs = new ArrayList<Range>(); + for (double [] range: ranges) { + double a = range[0]; + double b = range[1]; + rs.add(new Range(Math.min(a, b), Math.max(a, b), null)); + } + return filter(gauges, rs); + } + + public static List<Gauge> filter(List<Gauge> gauges, List<Range> ranges) { + // TODO: Make it an HQL filter! + ArrayList<Gauge> out = new ArrayList<Gauge>(); + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + for (Range cmp: ranges) { + if (range.intersects(cmp)) { + out.add(gauge); + break; + } + } + } + return out; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,121 @@ +package de.intevation.flys.artifacts.model; + + +public class LayerInfo { + + protected String name; + protected String type; + protected String directory; + protected String data; + protected String extent; + protected String group; + protected String groupTitle; + protected String title; + protected String style; + protected String filter; + + + public LayerInfo() { + } + + + public void setName(String name) { + this.name = name; + } + + + public String getName() { + return name; + } + + + public void setType(String type) { + this.type = type; + } + + + public String getType() { + return type; + } + + + public void setDirectory(String directory) { + this.directory = directory; + } + + + public String getDirectory() { + return directory; + } + + + public void setData(String data) { + this.data = data; + } + + + public String getData() { + return data; + } + + + public void setGroup(String group) { + this.group = group; + } + + + public String getGroup() { + return group; + } + + + public void setGroupTitle(String groupTitle) { + this.groupTitle = groupTitle; + } + + + public String getGroupTitle() { + return groupTitle; + } + + + public void setTitle(String title) { + this.title = title; + } + + + public String getTitle() { + return title; + } + + + public void setExtent(String extent) { + this.extent = extent; + } + + + public String getExtent() { + return extent; + } + + + public void setStyle(String style) { + this.style = style; + } + + + public String getStyle() { + return style; + } + + + public void setFilter(String filter) { + this.filter = filter; + } + + + public String getFilter() { + return filter; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,80 @@ +package de.intevation.flys.artifacts.model; + +import java.math.BigDecimal; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.hibernate.Session; +import org.hibernate.Query; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MainValuesFactory { + + private static Logger logger = Logger.getLogger(MainValuesFactory.class); + + public static List<MainValue> getMainValues(Gauge gauge) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from MainValue where gauge=:gauge"); + query.setParameter("gauge", gauge); + + return query.list(); + } + + + /** + * Returns an array of [days, qs] necessary to create duration curves. + * + * @param gauge The selected gauge. + * + * @return a 2dim array of [days, qs] where days is an int[] and qs is + * an double[]. + */ + public static Object[] getDurationCurveData(Gauge gauge) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "select cast(nmv.name as integer) as days, mv.value as q " + + "from MainValue as mv " + + "join mv.mainValue as nmv " + + "join nmv.type mvt " + + "where mvt.name = 'D' and mv.gauge.id = :gauge_id " + + "order by days"); + + query.setParameter("gauge_id", gauge.getId()); + + List<Object> results = query.list(); + int[] days = new int[results.size()]; + double[] qs = new double[results.size()]; + + int idx = 0; + + for (Object obj: results) { + Object[] arr = (Object[]) obj; + + try { + int day = ((Integer) arr[0]).intValue(); + double q = ((BigDecimal) arr[1]).doubleValue(); + + days[idx] = day; + qs[idx++] = q; + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return new Object[] { days, qs }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesQFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; +import java.util.List; + +import org.jfree.chart.annotations.XYTextAnnotation; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; + +import de.intevation.flys.artifacts.MainValuesArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.jfree.FLYSAnnotation; +import de.intevation.flys.jfree.StickyAxisAnnotation; + +/** + * Facet to show Main Q Values. + */ +public class MainValuesQFacet +extends DefaultFacet +implements FacetTypes { + + /** Trivial Constructor. */ + public MainValuesQFacet(String name, String description) { + this.description = description; + this.name = name; + index = 0; + } + + + /** + * Returns the data this facet requires. + * + * @param artifact the owner artifact. + * @param context the CallContext (ignored). + * + * @return the data. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact; + + List<NamedDouble> qs = mvArtifact.getMainValuesQ(); + List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>(); + + for (NamedDouble q: qs) { + xy.add(new StickyAxisAnnotation( + q.getName(), + (float) q.getValue(), + StickyAxisAnnotation.SimpleAxis.X_AXIS)); + } + + return new FLYSAnnotation(description, xy); + } + + + /** + * Create a deep copy of this Facet. + * @return a deep copy. + */ + @Override + public MainValuesQFacet deepCopy() { + MainValuesQFacet copy = new MainValuesQFacet(this.name, description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesWFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,71 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; +import java.util.List; + +import org.jfree.chart.annotations.XYTextAnnotation; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; + +import de.intevation.flys.artifacts.MainValuesArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.jfree.FLYSAnnotation; +import de.intevation.flys.jfree.StickyAxisAnnotation; + +/** + * Facet to show Main W Values. + */ +public class MainValuesWFacet +extends DefaultFacet +implements FacetTypes { + + /** Trivial Constructor. */ + public MainValuesWFacet(String name, String description) { + this.description = description; + this.name = name; + index = 0; + } + + + /** + * Returns the data this facet requires. + * + * @param artifact the owner artifact. + * @param context the CallContext (ignored). + * + * @return the data. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + MainValuesArtifact mvArtifact = (MainValuesArtifact) artifact; + + List<NamedDouble> ws = mvArtifact.getMainValuesW(); + List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>(); + + for (NamedDouble w: ws) { + xy.add(new StickyAxisAnnotation( + w.getName(), + (float) w.getValue(), + StickyAxisAnnotation.SimpleAxis.Y_AXIS)); + + } + + return new FLYSAnnotation(description, xy); + } + + + /** + * Create a deep copy of this Facet. + * @return a deep copy. + */ + @Override + public MainValuesWFacet deepCopy() { + MainValuesWFacet copy = new MainValuesWFacet(this.name, description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedDomFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,179 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactNamespaceContext; + + +/** + * Use an Element (DOM) to store the information about a facet. + * The intent of this facet type is to represent a facet + * stored in an Collection attribute. Different facets can have different + * attributes that we need to parse, but the only thing ManagedFacets need + * to do, is to adjust the attributes "active" and "position". So, those + * values are set directly on the Element, the other attributes aren't + * touched. + */ +public class ManagedDomFacet extends ManagedFacet { + + protected Element facet; + + private static Logger logger = Logger.getLogger(ManagedDomFacet.class); + + + public ManagedDomFacet(Element facet) { + super(null, -1, null, null, -1, -1, -1); + + this.facet = facet; + } + + + @Override + public int getIndex() { + if (this.index < 0) { + String index = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "index"); + + if (index != null && index.length() > 0) { + this.index = Integer.parseInt(index); + } + } + + return this.index; + } + + + @Override + public String getName() { + if (this.name == null || this.name.length() == 0) { + String name = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "facet"); + + this.name = name; + } + + return this.name; + } + + + @Override + public String getDescription() { + if (this.description == null || this.description.length() == 0) { + String description = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "description"); + + this.description = description; + } + + return this.description; + } + + + @Override + public int getPosition() { + if (this.position < 0) { + String position = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, + "pos"); + + if (position != null && position.length() > 0) { + this.position = Integer.parseInt(position); + } + } + + return this.position; + } + + + @Override + public void setPosition(int position) { + this.position = position; + + // TODO Evaluate whether other set/getAttributes also need + // to use the NAMESPACE_PREFIX. + facet.setAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX + ":" + "pos", + String.valueOf(position)); + } + + + @Override + public int getActive() { + if (this.active < 0) { + String active = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "active"); + + if (active != null && active.length() > 0) { + this.active = Integer.parseInt(active); + } + } + + return this.active; + } + + + @Override + public void setActive(int active) { + this.active = active; + + facet.setAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, + "art:active", + String.valueOf(active)); + } + + + @Override + public int getVisible() { + if (this.visible < 0) { + String visible = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "visible"); + + if (visible != null && visible.length() > 0) { + this.visible = Integer.parseInt(visible); + } + } + + return this.visible; + } + + + @Override + public void setVisible(int visible) { + this.visible = visible; + + facet.setAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, + "visible", + String.valueOf(getVisible())); + } + + + @Override + public String getArtifact() { + if (this.uuid == null || this.uuid.length() == 0) { + String uuid = facet.getAttributeNS( + ArtifactNamespaceContext.NAMESPACE_URI, "artifact"); + + this.uuid = uuid; + } + + return this.uuid; + } + + + /** + * Import into document. + * @param doc Document to be imported to. + */ + @Override + public Node toXML(Document doc) { + return doc.importNode(facet, true); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,134 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + + +/** + * Facet with user-supplied theme-control-information (pos in list, + * active/disabled etc) attached. + */ +public class ManagedFacet extends DefaultFacet { + + /** The uuid of the owner artifact. */ + protected String uuid; + + /** A property that determines the position of this facet. */ + protected int position; + + /** A property that determines if this facet is active or not. */ + protected int active; + + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(ManagedFacet.class); + + /** A property that determines if this facet is visible or not. */ + protected int visible; + + + public ManagedFacet() { + } + + public ManagedFacet( + String name, + int index, + String desc, + String uuid, + int pos, + int active, + int visible) + { + super(index, name, desc); + + this.uuid = uuid; + this.position = pos; + this.active = active; + this.visible = visible; + } + + + /** + * Sets position (will be merged to position in ThemeList). + */ + public void setPosition(int pos) { + this.position = pos; + } + + + public int getPosition() { + return position; + } + + + public void setActive(int active) { + this.active = active; + } + + + public int getActive() { + return active; + } + + + public void setVisible(int visible) { + this.visible = visible; + } + + + public int getVisible() { + return visible; + } + + + /** + * Get uuid of related artifact. + * @return uuid of related artifact. + */ + public String getArtifact() { + return uuid; + } + + + public Node toXML(Document doc) { + ElementCreator ec = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element facet = ec.create("theme"); + ec.addAttr(facet, "artifact", getArtifact(), true); + ec.addAttr(facet, "facet", getName(), true); + ec.addAttr(facet, "pos", String.valueOf(getPosition()), true); + ec.addAttr(facet, "active", String.valueOf(getActive()), true); + ec.addAttr(facet, "index", String.valueOf(getIndex()), true); + ec.addAttr(facet, "description", getDescription(), true); + ec.addAttr(facet, "visible", String.valueOf(getVisible()), true); + + return facet; + } + + public void set(ManagedFacet other) { + uuid = other.uuid; + position = other.position; + active = other.active; + } + + @Override + public Facet deepCopy() { + ManagedFacet copy = new ManagedFacet(); + copy.set((DefaultFacet)this); + copy.set((ManagedFacet)this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacetAdapter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,67 @@ +package de.intevation.flys.artifacts.model; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +public class ManagedFacetAdapter extends ManagedFacet { + + protected Facet facet; + + public ManagedFacetAdapter() { + } + + public ManagedFacetAdapter( + Facet facet, + String uuid, + int pos, + int active, + int visible + ) { + super( + facet.getName(), + facet.getIndex(), + facet.getDescription(), + uuid, + pos, + active, + visible); + + this.facet = facet; + } + + + @Override + public Node toXML(Document doc) { + ElementCreator ec = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element e = (Element) facet.toXML(doc); + ec.addAttr(e, "artifact", getArtifact(), true); + ec.addAttr(e, "facet", getName(), true); + ec.addAttr(e, "pos", String.valueOf(getPosition()), true); + ec.addAttr(e, "active", String.valueOf(getActive()), true); + ec.addAttr(e, "visible", String.valueOf(getVisible()), true); + + return e; + } + + @Override + public Facet deepCopy() { + ManagedFacetAdapter copy = new ManagedFacetAdapter(); + copy.set((DefaultFacet)this); + copy.set((ManagedFacet)this); + copy.facet = facet.deepCopy(); + return facet; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MapserverStyle.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,76 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; +import java.util.List; + + +public class MapserverStyle { + + public static class Clazz { + protected String name; + protected String outlinecolor; + protected String symbol; + protected int size; + + public Clazz(String name) { + this.name = name; + } + + public void setOutlineColor(String outlinecolor) { + this.outlinecolor = outlinecolor; + } + + public void setSize(int size) { + this.size = size; + } + + public void setSymbol(String symbol) { + if (symbol != null && symbol.length() > 0) { + this.symbol = symbol; + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("CLASS\n"); + sb.append("NAME \"" + name + "\"\n"); + sb.append("STYLE\n"); + sb.append("SIZE " + String.valueOf(size) + "\n"); + sb.append("OUTLINECOLOR " + outlinecolor + "\n"); + + if (symbol != null) { + sb.append("SYMBOL '" + symbol + "'\n"); + } + + sb.append("END\n"); + sb.append("END\n"); + + return sb.toString(); + } + } // end of Clazz + + + protected List<Clazz> classes; + + + public MapserverStyle() { + classes = new ArrayList<Clazz>(); + } + + public void addClazz(Clazz clazz) { + if (clazz != null) { + classes.add(clazz); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + + for (Clazz clazz: classes) { + sb.append(clazz.toString()); + } + + return sb.toString(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedDouble.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,29 @@ +package de.intevation.flys.artifacts.model; + +/** + * Implementation of a <String,double> pair. + */ +public class NamedDouble +extends NamedObjectImpl +{ + protected double value; + + + /** + * @param name name for the given value. + * @param value value. + */ + public NamedDouble(String name, double value) { + super(name); + this.value = value; + } + + + /** + * Get the value. + * @return the value. + */ + public double getValue() { + return this.value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObject.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,19 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + + +/** + * This class represents an object that has a name. The default case would be to + * inherit from this class. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface NamedObject +extends Serializable +{ + void setName(String name); + + String getName(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/NamedObjectImpl.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,28 @@ +package de.intevation.flys.artifacts.model; + +public class NamedObjectImpl +implements NamedObject +{ + /** The name of this object.*/ + protected String name; + + public NamedObjectImpl() { + } + + public NamedObjectImpl(String name) { + this.name = name; + } + + + @Override + public void setName(String name) { + this.name = name; + } + + + @Override + public String getName() { + return name; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,195 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +import java.util.List; + +import org.apache.log4j.Logger; + +public class QRangeTree +implements Serializable +{ + private static Logger log = Logger.getLogger(QRangeTree.class); + + public static class Node + implements Serializable + { + Node left; + Node right; + Node prev; + Node next; + + double a; + double b; + double q; + + public Node() { + } + + public Node(double a, double b, double q) { + this.a = a; + this.b = b; + this.q = q; + } + + protected final double interpolatePrev(double pos) { + /* + f(prev.b) = prev.q + f(a) = q + + prev.q = m*prev.b + n + q = m*a + n <=> n = q - m*a + + q - prev.q = m*(a - prev.b) + + m = (q - prev.q)/(a - prev.b) # a != prev.b + */ + + if (a == prev.b) { + return 0.5*(q + prev.q); + } + double m = (q - prev.q)/(a - prev.b); + double n = q - m*a; + return m*pos + n; + } + + protected final double interpolateNext(double pos) { + /* + f(next.a) = next.q + f(b) = q + + next.q = m*next.a + n + q = m*b + n <=> n = q - m*b + + q - next.q = m*(b - next.a) + m = (q - next.q)/(b - next.a) # b != next.a + */ + + if (b == next.a) { + return 0.5*(q + next.q); + } + double m = (q - next.q)/(b - next.a); + double n = q - m*b; + return m*pos + n; + } + + public double findQ(double pos) { + + Node current = this; + for (;;) { + if (pos < current.a) { + if (current.left != null) { + current = current.left; + continue; + } + return current.prev != null + ? current.interpolatePrev(pos) + : Double.NaN; + } + if (pos > current.b) { + if (current.right != null) { + current = current.right; + continue; + } + return current.next != null + ? current.interpolateNext(pos) + : Double.NaN; + } + return current.q; + } + } + } // class Node + + protected Node root; + + public QRangeTree() { + } + + /** wstQRanges need to be sorted by range.a */ + public QRangeTree(List<Object []> qRanges, int start, int stop) { + + if (stop <= start) { + return; + } + + Node [] nodes = new Node[stop-start]; + for (int i = 0; i < nodes.length; ++i) { + Object [] qRange = qRanges.get(i + start); + Double q = (Double)qRange[1]; + Double a = (Double)qRange[2]; + Double b = (Double)qRange[3]; + + nodes[i] = new Node( + a != null ? a.doubleValue() : -Double.MAX_VALUE, + b != null ? b.doubleValue() : Double.MAX_VALUE, + q.doubleValue()); + } + + root = wireTree(nodes); + } + + protected static Node wireTree(Node [] nodes) { + for (int i = 0; i < nodes.length; ++i) { + Node node = nodes[i]; + if (i > 0 ) node.prev = nodes[i-1]; + if (i < nodes.length-1) node.next = nodes[i+1]; + } + + return buildTree(nodes, 0, nodes.length-1); + } + + protected static Node buildTree(Node [] nodes, int lo, int hi) { + + if (lo > hi) { + return null; + } + + int mid = (lo + hi) >> 1; + Node parent = nodes[mid]; + + parent.left = buildTree(nodes, lo, mid-1); + parent.right = buildTree(nodes, mid+1, hi); + + return parent; + } + + public double findQ(double pos) { + return root != null ? root.findQ(pos) : Double.NaN; + } + + private static final String name(Object o) { + return String.valueOf(System.identityHashCode(o) & 0xffffffffL); + } + + public String toGraph() { + StringBuilder sb = new StringBuilder(); + sb.append("subgraph c"); + sb.append(name(this)); + sb.append(" {\n"); + if (root != null) { + java.util.Deque<Node> stack = new java.util.ArrayDeque<Node>(); + stack.push(root); + while (!stack.isEmpty()) { + Node current = stack.pop(); + String name = "n" + name(current); + sb.append(name); + sb.append(" [label=\""); + sb.append(current.a).append(", ").append(current.b); + sb.append(": ").append(current.q).append("\"]\n"); + if (current.left != null) { + String leftName = name(current.left); + sb.append(name).append(" -- n").append(leftName).append("\n"); + stack.push(current.left); + } + if (current.right != null) { + String rightName = name(current.right); + sb.append(name).append(" -- n").append(rightName).append("\n"); + stack.push(current.right); + } + } + } + sb.append("}\n"); + return sb.toString(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,41 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RangeWithValues implements Serializable { + + protected double lower; + protected double upper; + protected double[] values; + + + public RangeWithValues() { + } + + + public RangeWithValues(double lower, double upper, double[] values) { + this.lower = lower; + this.upper = upper; + this.values = values; + } + + + public double getLower() { + return lower; + } + + + public double getUpper() { + return upper; + } + + + public double[] getValues() { + return values; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,64 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + +import de.intevation.flys.artifacts.WINFOArtifact; + + +import org.apache.log4j.Logger; + +public class ReportFacet +extends DefaultFacet +implements FacetTypes +{ + private static Logger logger = Logger.getLogger(ReportFacet.class); + + protected ComputeType type; + protected String hash; + protected String stateId; + + public ReportFacet() { + this(ComputeType.ADVANCE); + } + + public ReportFacet(ComputeType type) { + super(0, REPORT, "report"); + this.type = type; + } + + + public ReportFacet(ComputeType type, String hash, String stateId) { + super(0, REPORT, "report"); + this.type = type; + this.hash = hash; + this.stateId = stateId; + } + + public Object getData(Artifact artifact, CallContext context) { + logger.debug("get report data"); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult cr = (CalculationResult)winfo.compute( + context, hash, stateId, type, false); + + return cr.getReport(); + } + + @Override + public Facet deepCopy() { + ReportFacet copy = new ReportFacet(); + copy.set(this); + copy.type = type; + copy.hash = hash; + copy.stateId = stateId; + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,71 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; + +import org.hibernate.Query; +import org.hibernate.Session; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverFactory { + + /** We don't need to instantiate concrete objects of this class. */ + private RiverFactory() { + } + + + /** + * Returns all rivers that were found in the backend. + * + * @return all rivers. + */ + public static List<River> getRivers() { + Session session = SessionHolder.HOLDER.get(); + + return session.createQuery("from River order by name").list(); + } + + + /** + * Returns a River object fetched from database based on its id. + * + * @param river_id The id of the desired river. + * + * @return the river. + */ + public static River getRiver(int river_id) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery("from River where id=:river_id"); + query.setParameter("river_id", river_id); + + List<River> rivers = query.list(); + + return (rivers != null && rivers.size() > 0) ? rivers.get(0) : null; + } + + + /** + * Returns a River object fetched from database based on its name. + * + * @param river The name of a river. + * + * @return the River object. + */ + public static River getRiver(String river) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from River where name =:name"); + query.setParameter("name", river); + + List<River> rivers = query.list(); + + return (rivers != null && rivers.size() > 0) ? rivers.get(0) : null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Segment.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,126 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.ArrayList; + +import java.io.Serializable; + +import org.apache.log4j.Logger; + +import gnu.trove.TDoubleArrayList; + +import de.intevation.flys.utils.DoubleUtil; + +public class Segment +implements Serializable +{ + private static Logger logger = Logger.getLogger(Segment.class); + + protected double from; + protected double to; + protected double [] values; + protected double [] backup; + protected double referencePoint; + + public Segment() { + } + + public Segment(double referencePoint) { + this.referencePoint = referencePoint; + } + + public Segment(double from, double to, double [] values) { + this.from = from; + this.to = to; + this.values = values; + } + + public boolean isUp() { + return from < to; + } + + public String toString() { + StringBuilder sb = new StringBuilder("Segment: ["); + sb.append("from: ").append(from).append("; to: ") + .append(to) + .append("; ref: ").append(referencePoint) + .append("; values: ("); + for (int i = 0; i < values.length; ++i) { + if (i > 0) sb.append(", "); + sb.append(values[i]); + } + sb.append(")]"); + return sb.toString(); + } + + public void setFrom(double from) { + this.from = from; + } + + public void backup() { + backup = (double [])values.clone(); + } + + public double getFrom() { + return from; + } + + public void setTo(double to) { + this.to = to; + } + + public double getTo() { + return to; + } + + public void setValues(double [] values) { + this.values = values; + } + + public double [] getValues() { + return values; + } + + public void setReferencePoint(double referencePoint) { + this.referencePoint = referencePoint; + } + + public double getReferencePoint() { + return referencePoint; + } + + public static List<Segment> parseSegments(String input) { + + ArrayList<Segment> segments = new ArrayList<Segment>(); + + TDoubleArrayList vs = new TDoubleArrayList(); + + for (String segmentStr: input.split(":")) { + String [] parts = segmentStr.split(";"); + if (parts.length < 3) { + logger.warn("invalid segment: '" + segmentStr + "'"); + continue; + } + try { + double from = Double.parseDouble(parts[0].trim()); + double to = Double.parseDouble(parts[1].trim()); + + vs.clear(); + + for (String valueStr: parts[2].split(",")) { + vs.add(DoubleUtil.round( + Double.parseDouble(valueStr.trim()))); + } + + double [] values = vs.toNativeArray(); + segments.add(new Segment(from, to, values)); + } + catch (NumberFormatException nfe) { + logger.warn("invalid segment: '" + segmentStr + "'"); + } + } + + return segments; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWKmsCacheKey.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,33 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +/** + * Caching-Key object for 'static' wst- data. + */ +public final class StaticWKmsCacheKey +implements Serializable +{ + public static final String CACHE_NAME = "wst-value-table-static"; + + private int column; + private int wst_id; + + public StaticWKmsCacheKey(int column, int wst_id) { + this.wst_id = wst_id; + this.column = column; + } + + public int hashCode() { + return (wst_id << 8) | column; + } + + public boolean equals(Object other) { + if (!(other instanceof StaticWKmsCacheKey)) { + return false; + } + StaticWKmsCacheKey o = (StaticWKmsCacheKey) other; + return wst_id == o.wst_id && this.column == o.column; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/StaticWQKmsCacheKey.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,33 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +/** + * Caching-Key object for 'static' wst- data. + */ +public final class StaticWQKmsCacheKey +implements Serializable +{ + public static final String CACHE_NAME = "wst-wq--value-table-static"; + + private int column; + private int wst_id; + + public StaticWQKmsCacheKey(int column, int wst_id) { + this.wst_id = wst_id; + this.column = column; + } + + public int hashCode() { + return (wst_id << 8) | column; + } + + public boolean equals(Object other) { + if (!(other instanceof StaticWQKmsCacheKey)) { + return false; + } + StaticWQKmsCacheKey o = (StaticWQKmsCacheKey) other; + return wst_id == o.wst_id && this.column == o.column; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKms.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,18 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + +public interface WKms +extends NamedObject +{ + int size(); + + double getKm(int index); + + double getW(int index); + + TDoubleArrayList allKms(); + + TDoubleArrayList allWs(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; + +import de.intevation.flys.artifacts.StaticWKmsArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; + +/** + * Facet to show W|km Values. + */ +public class WKmsFacet +extends DefaultFacet +implements FacetTypes { + + /** Trivial Constructor. */ + public WKmsFacet(String description) { + this(STATIC_WKMS, description); + } + + public WKmsFacet(String name, String description) { + this.name = name; + this.description = description; + this.index = 0; + } + + + /** + * Returns the data this facet requires. + * + * @param artifact the owner artifact. + * @param context the CallContext (ignored). + * + * @return the data. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + StaticWKmsArtifact staticData = + (StaticWKmsArtifact) artifact; + return staticData.getWKms(0); + } + + + /** + * Create a deep copy of this Facet. + * @return a deep copy. + */ + @Override + public WKmsFacet deepCopy() { + WKmsFacet copy = new WKmsFacet(description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,135 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import org.apache.log4j.Logger; + +import org.hibernate.Session; + +import org.hibernate.SQLQuery; +import org.hibernate.type.StandardBasicTypes; + +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WKmsImpl; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.backend.SessionHolder; + +/** + * Factory to access ready-made WKms for other (than computed) 'kinds' of + * WST-data. + */ +public class WKmsFactory +{ + private static Logger log = Logger.getLogger(WKmsFactory.class); + + /** Query to get km and ws for wst_id and column_pos. */ + public static final String SQL_SELECT_WS = + "SELECT km, w FROM wst_w_values " + + "WHERE wst_id = :wst_id AND column_pos = :column_pos"; + + /** Query to get name for wst_id and column_pos. */ + public static final String SQL_SELECT_NAME = + "SELECT name " + + "FROM wst_columns "+ + "WHERE wst_id = :wst_id AND position = :column_pos"; + + + private WKmsFactory() { + } + + + /** + * Get WKms for given column and wst_id, caring about the cache. + */ + public static WKms getWKms(int column, int wst_id) { + log.debug("WKmsFactory.getWKms"); + Cache cache = CacheFactory.getCache(StaticWKmsCacheKey.CACHE_NAME); + + StaticWKmsCacheKey cacheKey; + + if (cache != null) { + cacheKey = new StaticWKmsCacheKey(wst_id, column); + Element element = cache.get(cacheKey); + if (element != null) { + log.debug("Got static wst values from cache"); + return (WKms)element.getValue(); + } + } + else { + cacheKey = null; + } + + WKms values = getWKmsUncached(column, wst_id); + + if (values != null && cacheKey != null) { + log.debug("Store static wst values in cache."); + Element element = new Element(cacheKey, values); + cache.put(element); + } + return values; + } + + + /** Get name for a WKms. */ + public static String getWKmsName(int column, int wst_id) { + log.debug("WKmsFactory.getWKmsName c/" + column + ", wst_id/" + wst_id); + + String name = null; + Session session = SessionHolder.HOLDER.get(); + + SQLQuery nameQuery = session.createSQLQuery(SQL_SELECT_NAME) + .addScalar("name", StandardBasicTypes.STRING); + nameQuery.setInteger("wst_id", wst_id); + nameQuery.setInteger("column_pos", column); + + List<String> names = nameQuery.list(); + if (names.size() >= 1) { + name = names.get(0); + } + + return name; + } + + + /** + * Get WKms from db. + * @param column the position columns value + * @param wst_id database id of the wst + * @return according WKms. + */ + public static WKms getWKmsUncached(int column, int wst_id) { + + if (log.isDebugEnabled()) { + log.debug("WKmsFactory.getWKmsUncached c/" + column + ", wst_id/" + wst_id); + } + + WKmsImpl wkms = new WKmsImpl(getWKmsName(column, wst_id)); + + Session session = SessionHolder.HOLDER.get(); + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS) + .addScalar("km", StandardBasicTypes.DOUBLE) + .addScalar("w", StandardBasicTypes.DOUBLE); + sqlQuery.setInteger("wst_id", wst_id); + sqlQuery.setInteger("column_pos", column); + + List<Object []> results = sqlQuery.list(); + + double kms [] = new double[results.size()]; + double ws [] = new double[results.size()]; + + int lastColumn = Integer.MAX_VALUE; + + for (int i = 0, N = results.size(); i < N; i++) { + Object[] row = results.get(i); + wkms.add((Double) row[0], (Double) row[1]); + } + + return wkms; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WKmsImpl.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,88 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + +public class WKmsImpl +extends NamedObjectImpl +implements WKms +{ + protected TDoubleArrayList kms; + protected TDoubleArrayList ws; + + public WKmsImpl() { + super(""); + kms = new TDoubleArrayList(); + ws = new TDoubleArrayList(); + } + + + /** + * Create named, empty WKms. + */ + public WKmsImpl(String name) { + super(name); + kms = new TDoubleArrayList(); + ws = new TDoubleArrayList(); + } + + + public WKmsImpl(int capacity) { + super(""); + kms = new TDoubleArrayList(capacity); + ws = new TDoubleArrayList(capacity); + } + + + public WKmsImpl(TDoubleArrayList kms, TDoubleArrayList ws) { + this(kms, ws, ""); + } + + + public WKmsImpl( + TDoubleArrayList kms, + TDoubleArrayList ws, + String name + ) { + super(name); + this.kms = kms; + this.ws = ws; + } + + + /** + * Add a W (in NN+m) for a km (in km). + */ + public void add(double km, double w) { + kms.add(km); + ws .add(w); + } + + + @Override + public double getW(int index) { + return ws.getQuick(index); + } + + + @Override + public double getKm(int index) { + return kms.getQuick(index); + } + + + @Override + public int size() { + return kms.size(); + } + + @Override + public TDoubleArrayList allKms() { + return kms; + } + + @Override + public TDoubleArrayList allWs() { + return ws; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WMSDBLayerFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,73 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +public class WMSDBLayerFacet extends WMSLayerFacet { + + protected String data; + protected String filter; + protected String geometryType; + + + public WMSDBLayerFacet() { + super(); + } + + + public WMSDBLayerFacet(int index, String name, String description) { + this(index, name, description, ComputeType.FEED, null, null); + } + + + public WMSDBLayerFacet( + int index, + String name, + String description, + ComputeType type, + String stateId, + String hash + + ) { + super(index, name, description, type, stateId, hash); + } + + + public WMSDBLayerFacet( + int index, + String name, + String description, + ComputeType type, + String stateId, + String hash, + String url + ) { + super(index, name, description, type, stateId, hash, url); + } + + + public void setFilter(String filter) { + this.filter = filter; + } + + public String getFilter() { + return filter; + } + + public void setData(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + public void setGeometryType(String geometryType) { + this.geometryType = geometryType; + } + + public String getGeometryType() { + return geometryType; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WMSLayerFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,155 @@ +package de.intevation.flys.artifacts.model; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import com.vividsolutions.jts.geom.Envelope; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; +import de.intevation.flys.utils.GeometryUtils; + + +public class WMSLayerFacet +extends DefaultFacet +{ + protected ComputeType type; + protected List<String> layers; + protected String stateId; + protected String hash; + protected String url; + protected Envelope extent; + protected String srid; + + + private static final Logger logger = Logger.getLogger(WMSLayerFacet.class); + + public WMSLayerFacet() { + } + + + public WMSLayerFacet(int index, String name, String description) { + this(index, name, description, ComputeType.FEED, null, null); + } + + + public WMSLayerFacet( + int index, + String name, + String description, + ComputeType type, + String stateId, + String hash + + ) { + super(index, name, description); + this.layers = new ArrayList<String>(); + this.type = type; + this.stateId = stateId; + this.hash = hash; + } + + + public WMSLayerFacet( + int index, + String name, + String description, + ComputeType type, + String stateId, + String hash, + String url + ) { + this(index, name, description, type, stateId, hash); + this.url = url; + } + + + public void addLayer(String name) { + if (name != null && name.length() > 0) { + layers.add(name); + } + } + + + public void setExtent(Envelope extent) { + if (extent != null) { + this.extent = extent; + } + } + + + public Envelope getExtent() { + return extent; + } + + + public void setSrid(String srid) { + if (srid != null) { + this.srid = srid; + } + } + + + public String getSrid() { + return srid; + } + + + public Object getData(Artifact artifact, CallContext context) { + return null; + } + + + @Override + public Node toXML(Document doc) { + ElementCreator ec = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element facet = ec.create("facet"); + ec.addAttr(facet, "description", description, true); + ec.addAttr(facet, "index", String.valueOf(index), true); + ec.addAttr(facet, "name", name, true); + ec.addAttr(facet, "url", url, true); + ec.addAttr(facet, "layers", layers.get(0), true); + ec.addAttr(facet, "srid", srid != null ? srid : "", true); + ec.addAttr(facet, "extent", extent != null + ? GeometryUtils.jtsBoundsToOLBounds(extent) + : "", true); + + return facet; + } + + @Override + public Facet deepCopy() { + WMSLayerFacet copy = new WMSLayerFacet(); + copy.set(this); + + copy.type = type; + copy.layers = new ArrayList<String>(layers); + copy.stateId = stateId; + copy.hash = hash; + copy.url = url; + copy.extent = extent; + copy.srid = srid; + + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQ.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,172 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.flys.utils.DataUtil; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +public class WQ +extends NamedObjectImpl +{ + private static Logger logger = Logger.getLogger(WQ.class); + + // TODO: s/w/ws/g + protected TDoubleArrayList w; + + // TODO: s/q/qs/g + protected TDoubleArrayList q; + + public WQ() { + this(""); + } + + public WQ(String name) { + w = new TDoubleArrayList(); + q = new TDoubleArrayList(); + } + + public WQ(int capacity) { + this(capacity, ""); + } + + + public WQ(int capacity, String name) { + super(name); + w = new TDoubleArrayList(capacity); + q = new TDoubleArrayList(capacity); + } + + public WQ(double [] qs, double [] ws) { + this(qs, ws, ""); + } + + public WQ(double [] qs, double [] ws, String name) { + super(name); + w = new TDoubleArrayList(ws); + q = new TDoubleArrayList(qs); + } + + public void add(double w, double q) { + this.w.add(w); + this.q.add(q); + } + + public int size() { + return w.size(); + } + + public double getW(int idx) { + return w.getQuick(idx); + } + + public double getQ(int idx) { + return q.getQuick(idx); + } + + public double [] get(int idx) { + return get(idx, new double [2]); + } + + public double [] get(int idx, double [] dst) { + dst[0] = w.getQuick(idx); + dst[1] = q.getQuick(idx); + return dst; + } + + public double [] getWs() { + return w.toNativeArray(); + } + + public double [] getQs() { + return q.toNativeArray(); + } + + public static void removeNaNs(TDoubleArrayList [] arrays) { + + int dest = 0; + + int A = arrays.length; + int N = arrays[0].size(); + + OUTER: for (int i = 0; i < N; ++i) { + for (int j = 0; j < A; ++j) { + TDoubleArrayList a = arrays[j]; + double v = a.getQuick(i); + if (Double.isNaN(v)) { + continue OUTER; + } + a.setQuick(dest, v); + } + ++dest; + } + + if (dest < N) { + for (int i = 0; i < A; ++i) { + arrays[i].remove(dest, N-dest); + } + } + } + + public void removeNaNs() { + removeNaNs(new TDoubleArrayList [] { w, q }); + } + + public boolean guessWaterIncreasing() { + return guessWaterIncreasing(0.05f); + } + + public boolean guessWaterIncreasing(float factor) { + return DataUtil.guessWaterIncreasing(w, factor); + } + + public int [] longestIncreasingWRangeIndices() { + return longestIncreasingWRangeIndices(new int[2]); + } + + public int [] longestIncreasingWRangeIndices(int [] bounds) { + + int N = size(); + int start = 0; + int stop = 0; + + double lastW = Double.MAX_VALUE; + + for (int i = 0; i < N; ++i) { + double v = w.getQuick(i); + if (v <= lastW) { + if (stop-start > bounds[1]-bounds[0]) { + bounds[0] = start; + bounds[1] = stop; + if (logger.isDebugEnabled()) { + logger.debug("new range: " + + bounds[0] + " - " + bounds[1] + " (" + + w.getQuick(bounds[0]) + ", " + + w.getQuick(bounds[1]) + ")"); + + } + } + start = stop = i; + } + else { + stop = i; + } + lastW = v; + } + + if (stop-start > bounds[1]-bounds[0]) { + bounds[0] = start; + bounds[1] = stop; + if (logger.isDebugEnabled()) { + logger.debug("new range @end: " + + bounds[0] + " - " + bounds[1] + " (" + + w.getQuick(bounds[0]) + ", " + + w.getQuick(bounds[1]) + ")"); + + } + } + + return bounds; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQCKms.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,90 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'KM' data with corrected 'W' values computed by a BackJumpCorrector. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQCKms extends WQKms { + + protected TDoubleArrayList cw; + + public WQCKms() { + } + + public WQCKms(WQKms other, double [] cws) { + this.w = other.w; + this.q = other.q; + this.kms = other.kms; + this.cw = new TDoubleArrayList(cws); + } + + + public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) { + super(kms, qs, ws); + + this.cw = new TDoubleArrayList(cws); + } + + @Override + public void removeNaNs() { + removeNaNs(new TDoubleArrayList [] { w, q, cw, kms }); + } + + + /** + * Adds a new row to this data pool with corrected W. + * + * @param w a W. + * @param q a Q. + * @param kms a Kms. + * @param cw The corrected W. + */ + public void add(double w, double q, double kms, double cw) { + add(w, q, kms); + this.cw.add(cw); + } + + + /** + * This method returns a 4dim array of W, Q,Kms and corrected W. + * + * @param idx The position of the triple. + * @param dst destination array + * + * @return a 4dim array of [W, Q, Kms, CW] in dst. + */ + @Override + public double[] get(int idx, double[] dst) { + dst = super.get(idx, dst); + + if (dst.length < 4) { + return dst; + } + + if (cw != null && cw.size() > idx) { + dst[3] = cw.getQuick(idx); + } + + return dst; + } + + public double getC(int idx) { + return cw.getQuick(idx); + } + + + /** + * Returns the double array of corrected W values. + * + * @return the double array of corrected W values. + */ + public double[] getCWs() { + return cw.toNativeArray(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQDay.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,76 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TIntArrayList; + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'Day' data. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQDay +extends WQ +{ + protected TIntArrayList days; + + public WQDay() { + super(""); + days = new TIntArrayList(); + } + + public WQDay(int capacity) { + days = new TIntArrayList(capacity); + } + + public WQDay(int [] days, double [] ws, double [] qs) { + super(qs, ws, ""); + this.days = new TIntArrayList(days); + } + + + public void add(int day, double w, double q) { + super.add(w, q); + days.add(day); + } + + + public boolean isIncreasing() { + int lo = getDay(0); + int hi = getDay(size()-1); + + return lo < hi; + } + + + public int getDay(int idx) { + return days.getQuick(idx); + } + + @Override + public void removeNaNs() { + + int dest = 0; + int N = w.size(); + + for (int i = 0; i < N; ++i) { + double wi = w.getQuick(i); + double qi = q.getQuick(i); + + if (Double.isNaN(wi) || Double.isNaN(qi)) { + continue; + } + + days.setQuick(dest, days.getQuick(i)); + w.setQuick(dest, wi); + q.setQuick(dest, qi); + ++dest; + } + + if (dest < N) { + days.remove(dest, N-dest); + w .remove(dest, N-dest); + q .remove(dest, N-dest); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKms.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,118 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + + +/** + * This class represents a pool of data triples that consists of 'W', 'Q' and + * 'KM' data. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQKms +extends WQ +implements WKms +{ + private static Logger logger = Logger.getLogger(WQKms.class); + + /** The array that contains the 'KMs' values. */ + protected TDoubleArrayList kms; + + + public WQKms() { + this(""); + } + + + public WQKms(String name) { + super(name); + this.kms = new TDoubleArrayList(); + } + + + public WQKms(int capacity) { + this(capacity, ""); + } + + + public WQKms(int capacity, String name) { + super(name); + this.kms = new TDoubleArrayList(capacity); + } + + public WQKms(double [] kms, double [] qs, double [] ws) { + this(kms, qs, ws, ""); + } + + + public WQKms(double [] kms, double [] qs, double [] ws, String name) { + super(qs, ws, name); + this.kms = new TDoubleArrayList(kms); + } + + @Override + public void removeNaNs() { + removeNaNs(new TDoubleArrayList [] { w, q, kms }); + } + + /** + * Adds a new row to this data pool. + * + * @param w a W. + * @param q a Q. + * @param kms a Kms. + */ + public void add(double w, double q, double km) { + super.add(w, q); + kms.add(km); + } + + /** + * This method returns a triple of W, Q and Kms in a single 3dim array. + * + * @param idx The position of the triple. + * @param dst destination array + * + * @return a triple of [W, Q, Kms] in dst. + */ + @Override + public double[] get(int idx, double [] dst) { + dst[0] = w .getQuick(idx); + dst[1] = q .getQuick(idx); + dst[2] = kms.getQuick(idx); + return dst; + } + + @Override + public double getKm(int idx) { + return kms.getQuick(idx); + } + + @Override + public TDoubleArrayList allKms() { + return kms; + } + + @Override + public TDoubleArrayList allWs() { + return w; + } + + public double[] getKms() { + return kms.toNativeArray(); + } + + /** + * Returns a string that consist of the first and last kilometer. + * + * @return a string that consist of the first and last kilometer. + */ + public String toString() { + double from = getKm(0); + double to = getKm(size()-1); + return from + " - " + to; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,57 @@ +package de.intevation.flys.artifacts.model; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; + +import de.intevation.flys.artifacts.StaticWQKmsArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; + +/** + * Facet to show W|Q|km Values. + */ +public class WQKmsFacet +extends DefaultFacet +implements FacetTypes { + + /** Trivial Constructor. */ + public WQKmsFacet(String description) { + this(STATIC_WQKMS, description); + } + + public WQKmsFacet(String name, String description) { + this.name = name; + this.description = description; + this.index = 0; + } + + + /** + * Returns the data this facet requires. + * + * @param artifact the owner artifact. + * @param context the CallContext (ignored). + * + * @return the data. + */ + @Override + public Object getData(Artifact artifact, CallContext context) { + StaticWQKmsArtifact staticData = + (StaticWQKmsArtifact) artifact; + return staticData.getWQKms(0); + } + + + /** + * Create a deep copy of this Facet. + * @return a deep copy. + */ + @Override + public WQKmsFacet deepCopy() { + WQKmsFacet copy = new WQKmsFacet(description); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKmsFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,119 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import org.apache.log4j.Logger; + +import org.hibernate.Session; + +import org.hibernate.SQLQuery; +import org.hibernate.type.StandardBasicTypes; + +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.backend.SessionHolder; + +/** + * Factory to access ready-made WQKms for other (than computed) 'kinds' of + * WST-data. + */ +public class WQKmsFactory +{ + private static Logger log = Logger.getLogger(WQKmsFactory.class); + + /** Query to get km and wqs for wst_id and column_pos. */ + public static final String SQL_SELECT_WQS = + "SELECT position, w, q FROM wst_value_table " + + "WHERE wst_id = :wst_id AND column_pos = :column_pos"; + + /** Query to get name for wst_id and column_pos. */ + public static final String SQL_SELECT_NAME = + "SELECT name " + + "FROM wst_columns "+ + "WHERE wst_id = :wst_id AND position = :column_pos"; + + + /** Hidden constructor, use static methods instead. */ + private WQKmsFactory() { + ; + } + + + /** + * Get WKms for given column and wst_id, caring about the cache. + */ + public static WQKms getWQKms(int column, int wst_id) { + log.debug("WQKmsFactory.getWQKms"); + Cache cache = CacheFactory.getCache(StaticWQKmsCacheKey.CACHE_NAME); + + StaticWQKmsCacheKey cacheKey; + + if (cache != null) { + cacheKey = new StaticWQKmsCacheKey(wst_id, column); + Element element = cache.get(cacheKey); + if (element != null) { + log.debug("Got static wst values from cache"); + return (WQKms)element.getValue(); + } + } + else { + cacheKey = null; + } + + WQKms values = getWQKmsUncached(column, wst_id); + + if (values != null && cacheKey != null) { + log.debug("Store static wst values in cache."); + Element element = new Element(cacheKey, values); + cache.put(element); + } + return values; + } + + + /** + * Get WQKms from db. + * @param column the position columns value + * @param wst_id database id of the wst + * @return respective WQKms. + */ + public static WQKms getWQKmsUncached(int column, int wst_id) { + + if (log.isDebugEnabled()) { + log.debug("WQKmsFactory.getWQKmsUncached, column " + + column + ", wst_id " + wst_id); + } + + WQKms wqkms = new WQKms(WKmsFactory.getWKmsName(column, wst_id)); + + Session session = SessionHolder.HOLDER.get(); + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WQS) + .addScalar("position", StandardBasicTypes.DOUBLE) + .addScalar("w", StandardBasicTypes.DOUBLE) + .addScalar("q", StandardBasicTypes.DOUBLE); + sqlQuery.setInteger("wst_id", wst_id); + sqlQuery.setInteger("column_pos", column); + + List<Object []> results = sqlQuery.list(); + + double kms [] = new double[results.size()]; + double ws [] = new double[results.size()]; + double qs [] = new double[results.size()]; + + int lastColumn = Integer.MAX_VALUE; + + for (int i = 0, N = results.size(); i < N; i++) { + Object[] row = results.get(i); + // add(w, q, km) + wqkms.add((Double) row[1], (Double) row[2], (Double) row[0]); + } + + return wqkms; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WSPLGENCalculation.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,81 @@ +package de.intevation.flys.artifacts.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; + + +public class WSPLGENCalculation extends Calculation { + + private static final Logger log = Logger.getLogger(WSPLGENCalculation.class); + + protected Map<Integer, String> errors; + protected Map<Integer, String> warnings; + + + public WSPLGENCalculation() { + errors = new HashMap<Integer, String>(); + warnings = new HashMap<Integer, String>(); + } + + + public void addError(Integer key, String msg) { + log.debug("New error: (" + key + ") " + msg); + errors.put(key, msg); + } + + + public void addWarning(Integer key, String msg) { + log.debug("New warning: (" + key + ") " + msg); + warnings.put(key, msg); + } + + + public int numErrors() { + return errors.size(); + } + + + public int numWarnings() { + return warnings.size(); + } + + + public void toXML(Document document, CallMeta meta) { + Element root = document.createElement("problems"); + + if (numErrors() > 0) { + Set<Map.Entry<Integer, String>> entrySet = errors.entrySet(); + + for (Map.Entry<Integer, String> entry: entrySet) { + Element problem = document.createElement("problem"); + problem.setAttribute("error", String.valueOf(entry.getKey())); + problem.setTextContent(entry.getValue()); + + root.appendChild(problem); + } + } + + if (numWarnings() > 0) { + Set<Map.Entry<Integer, String>> entrySet = warnings.entrySet(); + + for (Map.Entry<Integer, String> entry: entrySet) { + Element problem = document.createElement("problem"); + problem.setAttribute("error", String.valueOf(entry.getKey())); + problem.setTextContent(entry.getValue()); + + root.appendChild(problem); + } + } + + document.appendChild(root); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,473 @@ +package de.intevation.flys.artifacts.model; + +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.wsplgen.FacetCreator; + + +public class WSPLGENJob { + + public static final String GEL_SPERRE = "SPERRE"; + public static final String GEL_NOSPERRE = "NOSPERRE"; + + + protected FLYSArtifact artifact; + + protected CallContext callContext; + + protected WSPLGENCalculation calculation; + + protected FacetCreator facetCreator; + + protected File workingDir; + + protected String dgm; + protected String pro; + protected String wsp; + protected String wspTag; + protected String axis; + protected String area; + protected String gel; + protected String outFile; + + protected List<String> lin; + + protected int out; + + protected double start; + protected double end; + protected double from; + protected double to; + protected double diff; + protected double dist; + + + + public WSPLGENJob( + FLYSArtifact flys, + File workingDir, + FacetCreator facetCreator, + CallContext context, + WSPLGENCalculation calculation) + { + this.artifact = flys; + this.workingDir = workingDir; + this.facetCreator = facetCreator; + this.callContext = context; + this.calculation = calculation; + + out = -1; + start = Double.NaN; + end = Double.NaN; + from = Double.NaN; + to = Double.NaN; + diff = Double.NaN; + dist = Double.NaN; + lin = new ArrayList<String>(2); + } + + + public File getWorkingDir() { + return workingDir; + } + + + public FLYSArtifact getArtifact() { + return artifact; + } + + + public FacetCreator getFacetCreator() { + return facetCreator; + } + + + public WSPLGENCalculation getCalculation() { + return calculation; + } + + + public CallContext getCallContext() { + return callContext; + } + + + public void setWsp(String wsp) { + this.wsp = wsp; + } + + + public String getWsp() { + return wsp; + } + + + public void setWspTag(String wspTag) { + this.wspTag = wspTag; + } + + + public String getWspTag() { + return wspTag; + } + + + public void addLin(String lin) { + this.lin.add(lin); + } + + + public List<String> getLin() { + return lin; + } + + + public void setAxis(String axis) { + this.axis = axis; + } + + + public String getAxis() { + return axis; + } + + + public void setArea(String area) { + this.area = area; + } + + + public String getArea() { + return area; + } + + + public void setOut(int out) { + this.out = out; + } + + + public int getOut() { + return out; + } + + + public void setOutFile(String outFile) { + this.outFile = outFile; + } + + + public String getOutFile() { + return outFile; + } + + + public void setStart(double start) { + this.start = start; + } + + + public double getStart() { + return start; + } + + + public void setEnd(double end) { + this.end = end; + } + + + public double getEnd() { + return end; + } + + + public void setPro(String pro) { + this.pro = pro; + } + + + public String getPro() { + return pro; + } + + + public void setDgm(String dgm) { + this.dgm = dgm; + } + + + public String getDgm() { + return dgm; + } + + + public void setFrom(double from) { + this.from = from; + } + + + public double getFrom() { + return from; + } + + + public void setTo(double to) { + this.to = to; + } + + + public double getTo() { + return to; + } + + + public void setDiff(double diff) { + this.diff = diff; + } + + + public double getDiff() { + return diff; + } + + + public void setDist(double dist) { + this.dist = dist; + } + + + public double getDist() { + return dist; + } + + + public void setGel(String gel) { + if (gel == null || gel.length() == 0) { + return; + } + + if (gel.equals(GEL_SPERRE) || gel.equals(GEL_NOSPERRE)) { + this.gel = gel; + } + } + + + public String getGel() { + return gel; + } + + + public void toFile(File file) + throws IOException, IllegalArgumentException + { + PrintWriter writer = null; + + try { + writer = + new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file))); + + write(writer); + } + finally { + if (writer != null) { + writer.flush(); + writer.close(); + } + } + } + + + protected void write(PrintWriter writer) + throws IOException, IllegalArgumentException + { + writeWsp(writer); // required + writeWspTag(writer); // required + writeLin(writer); + writeAxis(writer); + writeArea(writer); + writeOut(writer); + writeOutFile(writer); + writeRange(writer); + writeDelta(writer); + writeGel(writer); + writeDist(writer); + writePro(writer); + writeDgm(writer); // required + } + + + protected void writeWsp(PrintWriter writer) + throws IllegalArgumentException + { + String wsp = getWsp(); + + if (wsp != null && wsp.length() > 0) { + writer.println("-WSP=\"" + wsp + "\""); + return; + } + + throw new IllegalArgumentException("Required WSP missing!"); + } + + protected void writeWspTag(PrintWriter writer) + throws IllegalArgumentException + { + String wspTag = getWspTag(); + + if (wspTag != null && wspTag.length() > 0) { + writer.println("-WSPTAG=\"" + wspTag + "\""); + return; + } + + throw new IllegalArgumentException("Required WSPTAG missing!"); + } + + protected void writeLin(PrintWriter writer) + throws IllegalArgumentException + { + List<String> lins = getLin(); + + if (lins != null && !lins.isEmpty()) { + for (String lin: lins) { + writer.println("-LIN=\"" + lin + "\""); + } + } + } + + protected void writeAxis(PrintWriter writer) + throws IllegalArgumentException + { + String axis = getAxis(); + + if (axis != null && axis.length() > 0) { + writer.println("-ACHSE=\"" + axis + "\""); + } + } + + protected void writeGel(PrintWriter writer) + throws IllegalArgumentException + { + if (area != null && area.length() > 0) { + writer.println("-GEL=" + getGel()); + } + } + + protected void writeArea(PrintWriter writer) + throws IllegalArgumentException + { + String area = getArea(); + + if (area != null && area.length() > 0) { + writer.println("-GEBIET=\"" + area + "\""); + } + } + + + protected void writeOut(PrintWriter writer) + throws IllegalArgumentException + { + int out = getOut(); + + if (out >= 0) { + writer.println("-OUTPUT=" + String.valueOf(out)); + } + } + + protected void writeOutFile(PrintWriter writer) + throws IllegalArgumentException + { + String outFile = getOutFile(); + + if (outFile != null && outFile.length() > 0) { + writer.println("-AUSGABE=\""+ outFile + "\""); + } + } + + protected void writeRange(PrintWriter writer) + throws IllegalArgumentException + { + StringBuilder sb = new StringBuilder("-STRECKE="); + + double start = getStart(); + double end = getEnd(); + + if (Double.isNaN(start) && Double.isNaN(end)) { + return; + } + + if (! Double.isNaN(getStart())) { + sb.append(getStart()); + } + + sb.append(","); + + if (! Double.isNaN(getEnd())) { + sb.append(getEnd()); + } + + writer.println(sb.toString()); + } + + protected void writeDelta(PrintWriter writer) + throws IllegalArgumentException + { + StringBuilder sb = new StringBuilder("-DELTA="); + if (! Double.isNaN(from)) { + sb.append(from); + } + + sb.append(","); + + if (! Double.isNaN(to)) { + sb.append(to); + } + + sb.append(","); + + if (! Double.isNaN(diff)) { + sb.append(diff); + } + + writer.println(sb.toString()); + } + + protected void writeDist(PrintWriter writer) + throws IllegalArgumentException + { + if (! Double.isNaN(getDist())) { + writer.println("-DIST=" + String.valueOf(getDist())); + } + } + + protected void writePro(PrintWriter writer) + throws IllegalArgumentException + { + if (pro != null && pro.length() > 0) { + writer.println("-PRO=\"" + getPro() + "\""); + } + } + + protected void writeDgm(PrintWriter writer) + throws IllegalArgumentException + { + if (dgm != null && dgm.length() > 0) { + writer.println("-DGM=\"" + getDgm() + "\""); + return; + } + + throw new IllegalArgumentException("Required DGM missing!"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WSPLGENReportFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,56 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +/** + * This facet is used to provide WSPLGEN reports <b>only</b>. + */ +public class WSPLGENReportFacet extends ReportFacet { + + private static Logger logger = Logger.getLogger(WSPLGENReportFacet.class); + + + protected CalculationResult result; + + + public WSPLGENReportFacet() { + } + + + public WSPLGENReportFacet( + ComputeType type, + String hash, + String stateId, + CalculationResult result + ) { + super(type, hash, stateId); + this.result = result; + } + + + @Override + public Object getData(Artifact artifact, CallContext context) { + return result.getReport(); + } + + + @Override + public Facet deepCopy() { + WSPLGENReportFacet copy = new WSPLGENReportFacet(); + copy.set(this); + copy.type = type; + copy.hash = hash; + copy.stateId = stateId; + copy.result = result; + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,81 @@ +package de.intevation.flys.artifacts.model; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.DefaultFacet; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + +/** + * Facet of a Waterlevel (WQKms). + */ +public class WaterlevelFacet extends DefaultFacet { + + private static Logger logger = Logger.getLogger(WaterlevelFacet.class); + + protected ComputeType type; + protected String stateID; + protected String hash; + + + public WaterlevelFacet(int index, String name, String description) { + this(index, name, description, ComputeType.ADVANCE, null, null); + } + + + public WaterlevelFacet() { + } + + + public WaterlevelFacet( + int index, + String name, + String description, + ComputeType type, + String stateID, + String hash + + ) { + super(index, name, description); + this.type = type; + this.stateID = stateID; + this.hash = hash; + } + + + /** + * Get waterlevel data. + * @return a WQKms at given index. + */ + public Object getData(Artifact artifact, CallContext context) { + logger.debug("Get data for waterlevels at index: " + index); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res = (CalculationResult) + winfo.compute(context, hash, stateID, type, false); + + WQKms [] wqkms = (WQKms [])res.getData(); + + return wqkms[index]; + } + + + /** Copy deeply. */ + @Override + public Facet deepCopy() { + WaterlevelFacet copy = new WaterlevelFacet(); + copy.set(this); + copy.type = type; + copy.stateID = stateID; + copy.hash = hash; + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,48 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; + +import de.intevation.flys.backend.SessionHolder; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import org.hibernate.Query; +import org.hibernate.Session; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WstFactory { + + public static final int DEFAULT_KIND = 0; + + /** We don't need to instantiate concrete objects of this class. */ + private WstFactory() { + } + + + /** + * Returns the Wst object for a given river. + * + * @param river The river. + * + * @return the Wst of <i>river</i>. + */ + public static Wst getWst(River river) { + return getWst(river, DEFAULT_KIND); + } + + public static Wst getWst(River river, int kind) { + Session session = SessionHolder.HOLDER.get(); + + Query query = session.createQuery( + "from Wst where river=:river and kind = :kind"); + query.setParameter("river", river); + query.setInteger("kind", kind); + + List<Wst> wsts = query.list(); + + return wsts.isEmpty() ? null : wsts.get(0); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstLine.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,88 @@ +package de.intevation.flys.artifacts.model; + +import gnu.trove.TDoubleArrayList; + + +/** + * A model class that is used to store a line of a WST. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WstLine { + + /** The kilometer value of the line.*/ + protected double km; + + /** The W values.*/ + protected TDoubleArrayList ws; + + /** The Q values.*/ + protected TDoubleArrayList qs; + + + /** + * A constructor that builds a new WstLine for a specific kilometer. + * + * @param km The kilometer. + */ + public WstLine(double km) { + this.km = km; + this.ws = new TDoubleArrayList(); + this.qs = new TDoubleArrayList(); + } + + + /** + * Adds a pair of W/Q to this line. + * + * @param w The W value. + * @param q The Q value. + */ + public void add(double w, double q) { + ws.add(w); + qs.add(q); + } + + + /** + * Returns the kilometer of this line. + * + * @return the kilomter of this line. + */ + public double getKm() { + return km; + } + + + /** + * Returns the W value at index <i>idx</i> of this line. + * + * @param idx The position of the desired W value. + * + * @return the W at position <i>idx</i>. + */ + public double getW(int idx) { + return ws.size() > idx ? ws.get(idx) : -1d; + } + + + /** + * Returns the Q values of this line. + * + * @return the Q values of this line. + */ + public double[] getQs() { + return qs.toNativeArray(); + } + + + /** + * Returns the number of columns this line consists of. + * + * @return the columns this line consists of. + */ + public int getSize() { + return qs.size(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,657 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +import de.intevation.flys.artifacts.math.Linear; +import de.intevation.flys.artifacts.math.Function; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + +import org.apache.log4j.Logger; + +import org.apache.commons.math.analysis.interpolation.SplineInterpolator; + +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; + +import org.apache.commons.math.ArgumentOutsideDomainException; + +import org.apache.commons.math.exception.MathIllegalArgumentException; + +public class WstValueTable +implements Serializable +{ + private static Logger log = Logger.getLogger(WstValueTable.class); + + public static final int DEFAULT_Q_STEPS = 500; + + /** + * A Column in the table, typically representing one measurement session. + */ + public static final class Column + implements Serializable + { + protected String name; + + protected QRangeTree qRangeTree; + + public Column() { + } + + public Column(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public QRangeTree getQRangeTree() { + return qRangeTree; + } + + public void setQRangeTree(QRangeTree qRangeTree) { + this.qRangeTree = qRangeTree; + } + } // class Column + + /** + * A (weighted) position used for interpolation. + */ + public static final class QPosition { + + protected int index; + protected double weight; + + public QPosition() { + } + + public QPosition(int index, double weight) { + this.index = index; + this.weight = weight; + } + + public QPosition set(int index, double weight) { + this.index = index; + this.weight = weight; + return this; + } + + } // class Position + + /** + * A row, typically a position where measurements whrere taken. + */ + public static final class Row + implements Serializable, Comparable<Row> + { + double km; + double [] ws; + + public Row() { + } + + public Row(double km) { + this.km = km; + } + + public Row(double km, double [] ws) { + this(km); + this.ws = ws; + } + + public int compareTo(Row other) { + double d = km - other.km; + if (d < -0.0001) return -1; + if (d > 0.0001) return +1; + return 0; + } + + public void interpolateW( + Row other, + double km, + double [] iqs, + double [] ows, + WstValueTable table, + Calculation errors + ) { + double kmWeight = Linear.factor(km, this.km, other.km); + + QPosition qPosition = new QPosition(); + + for (int i = 0; i < iqs.length; ++i) { + if (table.getQPosition(km, iqs[i], qPosition) != null) { + double wt = getW(qPosition); + double wo = other.getW(qPosition); + if (Double.isNaN(wt) || Double.isNaN(wo)) { + if (errors != null) { + // TODO: I18N + errors.addProblem( + km, "cannot find w for q = " + iqs[i]); + } + ows[i] = Double.NaN; + } + else { + ows[i] = Linear.weight(kmWeight, wt, wo); + } + } + else { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "cannot find q = " + iqs[i]); + } + ows[i] = Double.NaN; + } + } + } + + public double [][] interpolateWQ( + Row other, + double km, + int steps, + WstValueTable table, + Calculation errors + ) { + int W = Math.min(ws.length, other.ws.length); + + if (W < 1) { + if (errors != null) { + // TODO: I18N + errors.addProblem("no ws found"); + } + return new double[2][0]; + } + + double factor = Linear.factor(km, this.km, other.km); + + double [] splineQ = new double[W]; + double [] splineW = new double[W]; + + double minQ = Double.MAX_VALUE; + double maxQ = -Double.MAX_VALUE; + + for (int i = 0; i < W; ++i) { + double wws = Linear.weight(factor, ws[i], other.ws[i]); + double wqs = Linear.weight( + factor, + table.getQIndex(i, km), + table.getQIndex(i, other.km)); + + if (Double.isNaN(wws) || Double.isNaN(wqs)) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "cannot find w or q"); + } + } + else { + if (wqs < minQ) minQ = wqs; + if (wqs > maxQ) maxQ = wqs; + } + + splineW[i] = wws; + splineQ[i] = wqs; + } + + double stepWidth = (maxQ - minQ)/steps; + + SplineInterpolator interpolator = new SplineInterpolator(); + PolynomialSplineFunction spline; + + try { + spline = interpolator.interpolate(splineQ, splineW); + } + catch (MathIllegalArgumentException miae) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "spline creation failed"); + } + log.error("spline creation failed"); + return new double[2][0]; + } + + double [] outWs = new double[steps]; + double [] outQs = new double[steps]; + + Arrays.fill(outWs, Double.NaN); + Arrays.fill(outQs, Double.NaN); + + try { + double q = minQ; + for (int i = 0; i < outWs.length; ++i, q += stepWidth) { + outWs[i] = spline.value(outQs[i] = q); + } + } + catch (ArgumentOutsideDomainException aode) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "spline interpolation failed"); + } + log.error("spline interpolation failed", aode); + } + + return new double [][] { outWs, outQs }; + } + + + public double [][] interpolateWQ( + int steps, + WstValueTable table, + Calculation errors + ) { + int W = ws.length; + + if (W < 1) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "no ws found"); + } + return new double[2][0]; + } + + double [] splineQ = new double[W]; + + double minQ = Double.MAX_VALUE; + double maxQ = -Double.MAX_VALUE; + + for (int i = 0; i < W; ++i) { + double sq = table.getQIndex(i, km); + if (Double.isNaN(sq)) { + if (errors != null) { + // TODO: I18N + errors.addProblem( + km, "no q found in " + (i+1) + " column"); + } + } + else { + if (sq < minQ) minQ = sq; + if (sq > maxQ) maxQ = sq; + } + splineQ[i] = sq; + } + + double stepWidth = (maxQ - minQ)/steps; + + SplineInterpolator interpolator = new SplineInterpolator(); + + PolynomialSplineFunction spline; + + try { + spline = interpolator.interpolate(splineQ, ws); + } + catch (MathIllegalArgumentException miae) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "spline creation failed"); + } + log.error("spline creation failed"); + return new double[2][0]; + } + + double [] outWs = new double[steps]; + double [] outQs = new double[steps]; + + Arrays.fill(outWs, Double.NaN); + Arrays.fill(outQs, Double.NaN); + + try { + double q = minQ; + for (int i = 0; i < outWs.length; ++i, q += stepWidth) { + outWs[i] = spline.value(outQs[i] = q); + } + } + catch (ArgumentOutsideDomainException aode) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "spline interpolation failed"); + } + log.error("spline interpolation failed.", aode); + } + + return new double [][] { outWs, outQs }; + } + + + public double getW(QPosition qPosition) { + int index = qPosition.index; + double weight = qPosition.weight; + + return weight == 1.0 + ? ws[index] + : Linear.weight(weight, ws[index-1], ws[index]); + } + + public double getW( + Row other, + double km, + QPosition qPosition + ) { + double kmWeight = Linear.factor(km, this.km, other.km); + + int index = qPosition.index; + double weight = qPosition.weight; + + double tw, ow; + + if (weight == 1.0) { + tw = ws[index]; + ow = other.ws[index]; + } + else { + tw = Linear.weight(weight, ws[index-1], ws[index]); + ow = Linear.weight(weight, other.ws[index-1], other.ws[index]); + } + + return Linear.weight(kmWeight, tw, ow); + } + } // class Row + + protected List<Row> rows; + + protected Column [] columns; + + public WstValueTable() { + rows = new ArrayList<Row>(); + } + + public WstValueTable(Column [] columns) { + this(); + this.columns = columns; + } + + public WstValueTable(Column [] columns, List<Row> rows) { + this.columns = columns; + this.rows = rows; + } + + public void sortRows() { + Collections.sort(rows); + } + + public double [] interpolateW(double km, double [] qs, double [] ws) { + return interpolateW(km, qs, ws, null); + } + + + /** + * @param ws (output parameter), gets returned. + * @return output parameter ws. + */ + public double [] interpolateW( + double km, + double [] qs, + double [] ws, + Calculation errors + ) { + int rowIndex = Collections.binarySearch(rows, new Row(km)); + + QPosition qPosition = new QPosition(); + + if (rowIndex >= 0) { // direct row match + Row row = rows.get(rowIndex); + for (int i = 0; i < qs.length; ++i) { + if (getQPosition(km, qs[i], qPosition) == null) { + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "cannot find q = " + qs[i]); + } + ws[i] = Double.NaN; + } + else { + if (Double.isNaN(ws[i] = row.getW(qPosition)) + && errors != null) { + // TODO: I18N + errors.addProblem( + km, "cannot find w for q = " + qs[i]); + } + } + } + } + else { // needs bilinear interpolation + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= rows.size()) { + // do not extrapolate + Arrays.fill(ws, Double.NaN); + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "km not found"); + } + } + else { + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + r1.interpolateW(r2, km, qs, ws, this, errors); + } + } + + return ws; + } + + public double [][] interpolateWQ(double km) { + return interpolateWQ(km, null); + } + + public double [][] interpolateWQ(double km, Calculation errors) { + return interpolateWQ(km, DEFAULT_Q_STEPS, errors); + } + + public double [][] interpolateWQ(double km, int steps, Calculation errors) { + + int rowIndex = Collections.binarySearch(rows, new Row(km)); + + if (rowIndex >= 0) { // direct row match + Row row = rows.get(rowIndex); + return row.interpolateWQ(steps, this, errors); + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex >= rows.size()) { + // do not extrapolate + if (errors != null) { + // TODO: I18N + errors.addProblem(km, "km not found"); + } + return new double[2][0]; + } + + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + + return r1.interpolateWQ(r2, km, steps, this, errors); + } + + public boolean interpolate( + double km, + double [] out, + QPosition qPosition, + Function qFunction + ) { + int R1 = rows.size()-1; + + out[1] = qFunction.value(getQ(qPosition, km)); + + if (Double.isNaN(out[1])) { + return false; + } + + QPosition nPosition = new QPosition(); + if (getQPosition(km, out[1], nPosition) == null) { + return false; + } + + int rowIndex = Collections.binarySearch(rows, new Row(km)); + + if (rowIndex >= 0) { + // direct row match + out[0] = rows.get(rowIndex).getW(nPosition); + return !Double.isNaN(out[0]); + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex > R1) { + // do not extrapolate + return false; + } + + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + out[0] = r1.getW(r2, km, nPosition); + + return !Double.isNaN(out[0]); + } + + + /** + * Look up interpolation of a Q at given positions. + * + * @param q the non-interpolated Q value. + * @param referenceKm the reference km (e.g. gauge position). + * @param kms positions for which to interpolate. + * @param ws (output) resulting interpolated ws. + * @param qs (output) resulting interpolated qs. + * @param errors calculation object to store errors. + */ + public QPosition interpolate( + double q, + double referenceKm, + double [] kms, + double [] ws, + double [] qs, + Calculation errors + ) { + return interpolate( + q, referenceKm, kms, ws, qs, 0, kms.length, errors); + } + + public QPosition interpolate( + double q, + double referenceKm, + double [] kms, + double [] ws, + double [] qs, + int startIndex, + int length, + Calculation errors + ) { + QPosition qPosition = getQPosition(referenceKm, q); + + if (qPosition == null) { + // we cannot locate q at km + Arrays.fill(ws, Double.NaN); + Arrays.fill(qs, Double.NaN); + if (errors != null) { + errors.addProblem(referenceKm, "cannot find q"); + } + return null; + } + + Row kmKey = new Row(); + + int R1 = rows.size()-1; + + for (int i = startIndex, end = startIndex+length; i < end; ++i) { + + if (Double.isNaN(qs[i] = getQ(qPosition, kms[i]))) { + if (errors != null) { + errors.addProblem(kms[i], "cannot find q"); + } + ws[i] = Double.NaN; + continue; + } + + kmKey.km = kms[i]; + int rowIndex = Collections.binarySearch(rows, kmKey); + + if (rowIndex >= 0) { + // direct row match + if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition)) + && errors != null) { + errors.addProblem(kms[i], "cannot find w"); + } + continue; + } + + rowIndex = -rowIndex -1; + + if (rowIndex < 1 || rowIndex > R1) { + // do not extrapolate + if (errors != null) { + errors.addProblem(kms[i], "cannot find km"); + } + ws[i] = Double.NaN; + continue; + } + Row r1 = rows.get(rowIndex-1); + Row r2 = rows.get(rowIndex); + + if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition)) + && errors != null) { + errors.addProblem(kms[i], "cannot find w"); + } + } + + return qPosition; + } + + public QPosition getQPosition(double km, double q) { + return getQPosition(km, q, new QPosition()); + } + + public QPosition getQPosition(double km, double q, QPosition qPosition) { + + if (columns.length == 0) { + return null; + } + + double qLast = columns[0].getQRangeTree().findQ(km); + + if (Math.abs(qLast - q) < 0.00001) { + return qPosition.set(0, 1d); + } + + for (int i = 1; i < columns.length; ++i) { + double qCurrent = columns[i].getQRangeTree().findQ(km); + if (Math.abs(qCurrent - q) < 0.00001) { + return qPosition.set(i, 1d); + } + + double qMin, qMax; + if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; } + else { qMin = qCurrent; qMax = qLast; } + + if (q > qMin && q < qMax) { + double weight = Linear.factor(q, qLast, qCurrent); + return qPosition.set(i, weight); + } + qLast = qCurrent; + } + + return null; + } + + public double getQIndex(int index, double km) { + return columns[index].getQRangeTree().findQ(km); + } + + public double getQ(QPosition qPosition, double km) { + int index = qPosition.index; + double weight = qPosition.weight; + + if (weight == 1d) { + return columns[index].getQRangeTree().findQ(km); + } + double q1 = columns[index-1].getQRangeTree().findQ(km); + double q2 = columns[index ].getQRangeTree().findQ(km); + return Linear.weight(weight, q1, q2); + } + +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,30 @@ +package de.intevation.flys.artifacts.model; + +import java.io.Serializable; + +public final class WstValueTableCacheKey +implements Serializable +{ + public static final String CACHE_NAME = "wst-value-table"; + + private int riverId; + private int kind; + + public WstValueTableCacheKey(int riverId, int kind) { + this.riverId = riverId; + this.kind = kind; + } + + public int hashCode() { + return (riverId << 8) | kind; + } + + public boolean equals(Object other) { + if (!(other instanceof WstValueTableCacheKey)) { + return false; + } + WstValueTableCacheKey o = (WstValueTableCacheKey)other; + return riverId == o.riverId && kind == o.kind; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,247 @@ +package de.intevation.flys.artifacts.model; + +import java.util.List; +import java.util.ArrayList; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.backend.SessionHolder; + +import org.apache.log4j.Logger; + +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import org.hibernate.Session; +import org.hibernate.Query; +import org.hibernate.SQLQuery; + +import org.hibernate.type.StandardBasicTypes; + +public class WstValueTableFactory +{ + private static Logger log = Logger.getLogger(WstValueTableFactory.class); + + public static final int DEFAULT_KIND = 0; + + // TODO: put this into a property file + + public static final String HQL_WST = + "from Wst where river=:river and kind=:kind"; + + public static final String SQL_SELECT_NAMES_POS = + "SELECT position, name FROM wst_columns " + + "WHERE wst_id = :wst_id ORDER BY position"; + + public static final String SQL_SELECT_QS = + "SELECT column_pos, q, a, b FROM wst_q_values " + + "WHERE wst_id = :wst_id"; + + public static final String SQL_SELECT_WS = + "SELECT km, w, column_pos FROM wst_w_values " + + "WHERE wst_id = :wst_id"; + + private WstValueTableFactory() { + } + + public static WstValueTable getTable(River river) { + return getTable(river, DEFAULT_KIND); + } + + public static WstValueTable getTable(River river, int kind) { + + Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME); + + WstValueTableCacheKey cacheKey; + + if (cache != null) { + cacheKey = new WstValueTableCacheKey(river.getId(), kind); + Element element = cache.get(cacheKey); + if (element != null) { + log.debug("got wst value table from cache"); + return (WstValueTable)element.getValue(); + } + } + else { + cacheKey = null; + } + + WstValueTable valueTable = getTableUncached(river, kind); + + if (valueTable != null && cacheKey != null) { + log.debug("store wst value table in cache"); + Element element = new Element(cacheKey, valueTable); + cache.put(element); + } + + return valueTable; + } + + public static WstValueTable getTableUncached(River river) { + return getTableUncached(river, DEFAULT_KIND); + } + + public static WstValueTable getTableUncached(River river, int kind) { + + Session session = SessionHolder.HOLDER.get(); + + Wst wst = loadWst(session, river, kind); + + if (wst == null) { + return null; + } + + WstValueTable.Column [] columns = loadColumns(session, wst); + + loadQRanges(session, columns, wst); + + List<WstValueTable.Row> rows = loadRows(session, wst, columns.length); + + return new WstValueTable(columns, rows); + } + + /** + * @param kind Kind of wst. + */ + protected static Wst loadWst(Session session, River river, int kind) { + Query query = session.createQuery(HQL_WST); + query.setParameter("river", river); + query.setInteger("kind", kind); + + List<Wst> wsts = query.list(); + + return wsts.isEmpty() ? null : wsts.get(0); + } + + protected static List<WstValueTable.Row> loadRows( + Session session, + Wst wst, + int numColumns + ) { + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS) + .addScalar("km", StandardBasicTypes.DOUBLE) + .addScalar("w", StandardBasicTypes.DOUBLE) + .addScalar("column_pos", StandardBasicTypes.INTEGER); + + sqlQuery.setInteger("wst_id", wst.getId()); + + List<Object []> results = sqlQuery.list(); + + int lastColumn = Integer.MAX_VALUE; + double [] ws = null; + + ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>(); + + for (Object [] result: results) { + int column = (Integer)result[2]; + if (column < lastColumn) { + ws = new double[numColumns]; + WstValueTable.Row row = + new WstValueTable.Row((Double)result[0], ws); + rows.add(row); + } + Double w = (Double)result[1]; + ws[column] = w != null ? w : Double.NaN; + lastColumn = column; + } + + rows.trimToSize(); + return rows; + } + + protected static WstValueTable.Column [] loadColumns( + Session session, + Wst wst + ) { + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS) + .addScalar("position", StandardBasicTypes.INTEGER) + .addScalar("name", StandardBasicTypes.STRING); + + sqlQuery.setInteger("wst_id", wst.getId()); + + List<Object []> columnNames = sqlQuery.list(); + + WstValueTable.Column [] columns = + new WstValueTable.Column[columnNames.size()]; + + for (int i = 0; i < columns.length; ++i) { + columns[i] = new WstValueTable.Column( + (String)columnNames.get(i)[1]); + } + return columns; + } + + protected static void loadQRanges( + Session session, + WstValueTable.Column [] columns, + Wst wst + ) { + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS) + .addScalar("column_pos", StandardBasicTypes.INTEGER) + .addScalar("q", StandardBasicTypes.DOUBLE) + .addScalar("a", StandardBasicTypes.DOUBLE) + .addScalar("b", StandardBasicTypes.DOUBLE); + + sqlQuery.setInteger("wst_id", wst.getId()); + + List<Object []> qRanges = sqlQuery.list(); + + int start = -1; + int Q = qRanges.size(); + Integer lastColumn = null; + + for (int i = 0; i < Q; ++i) { + Object [] qRange = qRanges.get(i); + Integer columnId = (Integer)qRange[0]; + if (lastColumn == null) { + lastColumn = columnId; + start = i; + } + else if (!lastColumn.equals(columnId)) { + QRangeTree qRangeTree = new QRangeTree(qRanges, start, i); + columns[lastColumn].setQRangeTree(qRangeTree); + lastColumn = columnId; + start = i; + } + } + + if (start != -1) { + QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q); + columns[lastColumn].setQRangeTree(qRangeTree); + } + + /* This is debug code to visualize the q ranges trees + + java.io.PrintWriter out = null; + try { + out = new java.io.PrintWriter( + new java.io.FileWriter( + "/tmp/qranges" + System.currentTimeMillis() + ".dot")); + + out.println("graph \"Q ranges trees\" {"); + + for (int i = 0; i < columns.length; ++i) { + QRangeTree tree = columns[i].getQRangeTree(); + out.println(tree.toGraph()); + } + + out.println("}"); + + out.flush(); + } + catch (java.io.IOException ioe) { + log.error(ioe); + } + finally { + if (out != null) { + out.close(); + } + } + */ + } + +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/resources/Resources.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,144 @@ +package de.intevation.flys.artifacts.resources; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; + +/** + * This class provides methods for i18n. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class Resources { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(Resources.class); + + /** The singleton instance.*/ + private static Resources INSTANCE; + + /** The locales supported by this server.*/ + protected Locale[] locales; + + /** + * No instance of this class is necessary. + */ + private Resources() { + } + + + /** + * Returns the locales supported by this server. + * + * @return the supported locales. + */ + public Locale[] getLocales() { + if (locales == null) { + readLocales(); + } + + return locales; + } + + + /** + * Read the locales configured for this server. + */ + protected void readLocales() { + // TODO IMPLEMENT ME + + locales = new Locale[2]; + locales[0] = Locale.GERMANY; + locales[1] = Locale.ENGLISH; + } + + + public static Locale getLocale(CallMeta meta) { + if (INSTANCE == null) { + INSTANCE = new Resources(); + } + + Locale[] locales = INSTANCE.getLocales(); + return meta.getPreferredLocale(locales); + } + + + /** + * This method returns the translated value for <i>key</i> or <i>def</i> if + * <i>key</i> is not existing in the resource bundle. + * + * @param meta The CallMeta object of the request that contains the + * preferred locale. + * @param key The key that should be translated. + * @param def A default value that is returned, if <i>key</i> was not found. + * + * @return the translated message. + */ + public static String getMsg(CallMeta meta, String key, String def) { + if (INSTANCE == null) { + INSTANCE = new Resources(); + } + + Locale[] locales = INSTANCE.getLocales(); + Locale locale = meta.getPreferredLocale(locales); + + return getMsg(locale, key, def); + } + + + /** + * Returns a translated message based on a template specified by <i>key</i> + * that has necessary values to be filled in. + * + * @param meta The CallMeta object. + * @param key The key of the template in the resource bundle. + * @param def the default value if no template was found with <i>key</i>. + * @param args The arguments that are necessary for the template. + * + * @return a translated string. + */ + public static String getMsg( + CallMeta meta, + String key, + String def, + Object[] args) + { + String template = getMsg(meta, key, null); + + if (template == null) { + return def; + } + + return MessageFormat.format(template, args); + } + + + /** + * This method returns the translated value for <i>key</i> or <i>def</i> if + * <i>key</i> is not existing in the resource bundle. + * + * @param locale The locale. + * @param key The key that should be translated. + * @param def A default value that is returned, if <i>key</i> was not found. + * + * @return the translated message. + */ + public static String getMsg(Locale locale, String key, String def) { + ResourceBundle bundle = ResourceBundle.getBundle("messages", locale); + + try { + return bundle.getString(key); + } + catch (MissingResourceException mre) { + logger.warn("No message found for key: " + key); + + return def; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/CrossSectionKMService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,196 @@ +package de.intevation.flys.artifacts.services; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import de.intevation.flys.backend.SessionHolder; + +import de.intevation.flys.model.CrossSection; +import de.intevation.flys.model.CrossSectionLine; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; + +import java.util.concurrent.ConcurrentSkipListMap; + +import net.sf.ehcache.Cache; + +import org.apache.log4j.Logger; + +import org.hibernate.Query; +import org.hibernate.Session; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class CrossSectionKMService +extends FLYSService +{ + private static Logger logger = + Logger.getLogger(CrossSectionKMService.class); + + public static final String CACHE_NAME = "cross-section-kms"; + + public CrossSectionKMService() { + } + + @Override + public Document doProcess( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + logger.debug("CrossSectionKMService.doProcess"); + + NodeList crossSectionNodes = + data.getElementsByTagName("cross-section"); + + Cache cache = CacheFactory.getCache(CACHE_NAME); + + Document document = XMLUtils.newDocument(); + + Element all = document.createElement("cross-sections"); + + for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) { + Element crossSectionElement = (Element)crossSectionNodes.item(i); + + String idString = crossSectionElement.getAttribute("id"); + String kmString = crossSectionElement.getAttribute("km"); + String neighborsString = crossSectionElement.getAttribute("n"); + + if (idString.length() == 0 || kmString.length() == 0) { + logger.debug("missing attributes in cross-section element"); + continue; + } + + double km; + Integer crossSectionId; + int N = 2; + + try { + km = Double.parseDouble(kmString); + crossSectionId = Integer.valueOf(idString); + + if (neighborsString.length() > 0) { + N = Integer.parseInt(neighborsString); + } + } + catch (NumberFormatException nfe) { + logger.debug("converting number failed", nfe); + continue; + } + + NavigableMap<Double, Integer> map; + + if (cache == null) { + map = getUncached(crossSectionId); + } + else { + net.sf.ehcache.Element element = cache.get(crossSectionId); + if (element == null) { + map = getUncached(crossSectionId); + if (map != null) { + element = new net.sf.ehcache.Element( + crossSectionId, map); + cache.put(element); + } + } + else { + map = (NavigableMap<Double, Integer>)element.getValue(); + } + } + + if (map == null) { + logger.debug("cannot find cross section " + crossSectionId); + continue; + } + + Deque<Map.Entry<Double, Integer>> result = + nearestNeighbors(map, km, N); + + if (!result.isEmpty()) { + Element csE = document.createElement("cross-section"); + csE.setAttribute("id", idString); + for (Map.Entry<Double, Integer> entry: result) { + Element lineE = document.createElement("line"); + lineE.setAttribute( + "line-id", String.valueOf(entry.getValue())); + lineE.setAttribute( + "km", String.valueOf(entry.getKey())); + csE.appendChild(lineE); + } + all.appendChild(csE); + } + } + + document.appendChild(all); + + return document; + } + + public static Deque<Map.Entry<Double, Integer>> nearestNeighbors( + NavigableMap<Double, Integer> map, + double km, + int N + ) { + Deque<Map.Entry<Double, Integer>> result = + new ArrayDeque<Map.Entry<Double, Integer>>(2*N); + + int i = 0; + for (Map.Entry<Double, Integer> entry: + map.headMap(km, false).descendingMap().entrySet()) { + if (i++ >= N) { + break; + } + result.addFirst(entry); + } + + i = 0; + for (Map.Entry<Double, Integer> entry: + map.tailMap(km, false).entrySet()) { + if (i++ >= N) { + break; + } + result.addLast(entry); + } + + return result; + } + + public static NavigableMap<Double, Integer> getUncached( + Integer crossSectionId + ) { + NavigableMap<Double, Integer> result = + new ConcurrentSkipListMap<Double, Integer>(); + + Session session = SessionHolder.HOLDER.get(); + Query query = session.createQuery( + "from CrossSection where id=:id"); + query.setParameter("id", crossSectionId); + + List<CrossSection> crossSections = query.list(); + if (crossSections.isEmpty()) { + return null; + } + + CrossSection crossSection = crossSections.get(0); + List<CrossSectionLine> lines = crossSection.getLines(); + + for (CrossSectionLine line: lines) { + Double km = line.getKm().doubleValue(); + Integer id = line.getId(); + result.put(km, id); + } + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,213 @@ +package de.intevation.flys.artifacts.services; + +import java.math.BigDecimal; +import java.util.Iterator; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.Annotation; +import de.intevation.flys.model.Attribute; +import de.intevation.flys.model.Position; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.Edge; + +import de.intevation.flys.artifacts.model.AnnotationsFactory; + +import de.intevation.flys.artifacts.cache.CacheFactory; + +import net.sf.ehcache.Cache; + +/** + * This service provides information about distances of a specified river. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DistanceInfoService extends FLYSService { + + private static enum DistanceFilter { + NONE, LOCATIONS, DISTANCES + } + + /** The logger used in this service. */ + private static Logger logger = Logger.getLogger(DistanceInfoService.class); + + public static final String CACHE_NAME = "service-distanceinfo"; + + public static final String RIVER_XPATH = "/art:river/text()"; + + public static final String FILTER_XPATH = "/art:river/art:filter/text()"; + + + /** + * The default constructor. + */ + public DistanceInfoService() { + } + + + @Override + public Document doProcess( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + logger.debug("DistanceInfoService.process"); + + String river = XMLUtils.xpathString( + data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE); + + String filter = XMLUtils.xpathString( + data, FILTER_XPATH, ArtifactNamespaceContext.INSTANCE); + + if (river == null || (river = river.trim()).length() == 0) { + logger.warn("No river specified. Cannot return distance info!"); + return XMLUtils.newDocument(); + } + + logger.debug("Search distances for river: " + river); + + Cache cache = CacheFactory.getCache(CACHE_NAME); + + if (cache == null) { + logger.debug("no cache configured for distance info"); + return getUncached(river, filter); + } + + + String key = getCacheKey(river, filter); + + net.sf.ehcache.Element element = cache.get(key); + + if (element != null) { + logger.debug("distance info found in cache"); + return (Document)element.getValue(); + } + + Document result = getUncached(river, filter); + + element = new net.sf.ehcache.Element(key, result); + + logger.debug("store distance info found into cache"); + + cache.put(element); + + return result; + } + + + protected String getCacheKey(String river, String filtertype) { + return filtertype != null && filtertype.length() > 0 + ? river + "_" + filtertype + : river; + } + + + protected Document getUncached(String river, String filtertype) { + + Document result = XMLUtils.newDocument(); + + Iterator<Annotation> iter = + AnnotationsFactory.getAnnotationsIterator(river); + + Element all = result.createElement("distances"); + + DistanceFilter filter = getDistanceFilter(filtertype); + + while (iter.hasNext()) { + Annotation a = iter.next(); + Element distance = buildDistanceNode(result, a, filter); + + if (distance != null) { + all.appendChild(distance); + } + } + + result.appendChild(all); + + return result; + } + + + protected static DistanceFilter getDistanceFilter(String type) { + if (type.equals("locations")) { + logger.debug("Found 'location' filter."); + return DistanceFilter.LOCATIONS; + } + else if (type.equals("distances")) { + logger.debug("Found 'distances' filter."); + return DistanceFilter.DISTANCES; + } + + logger.debug("Do not use any filter at all."); + + return DistanceFilter.NONE; + } + + + /** + * Builds an Element for a distance info. + * + * @param anno The Annotation that provides information about the distance. + * + * @return an Element that contains information about a distance. + */ + protected static Element buildDistanceNode( + Document document, + Annotation anno, + DistanceFilter filter + ) { + Position pos = anno.getPosition(); + Range range = anno.getRange(); + Attribute attr = anno.getAttribute(); + Edge edge = anno.getEdge(); + BigDecimal a = range.getA(); + BigDecimal b = range.getB(); + + if (b == null && filter == DistanceFilter.DISTANCES) { + return null; + } + + if (b != null && filter == DistanceFilter.LOCATIONS) { + return null; + } + + Element distance = document.createElement("distance"); + + distance.setAttribute("description", pos.getValue()); + + String riverSide = attr.getValue(); + + if (riverSide != null && riverSide.length() > 0) { + distance.setAttribute("riverside", riverSide); + } + + if (a != null) { + distance.setAttribute("from", a.toString()); + } + if (b != null) { + distance.setAttribute("to", b.toString()); + } + if (edge != null) { + BigDecimal bottom = edge.getBottom(); + BigDecimal top = edge.getTop(); + if (bottom != null) { + distance.setAttribute("bottom", bottom.toString()); + } + if (top != null) { + distance.setAttribute("top", top.toString()); + } + } + + return distance; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/FLYSService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,58 @@ +package de.intevation.flys.artifacts.services; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import org.hibernate.Session; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.flys.backend.SessionHolder; + + +public abstract class FLYSService extends DefaultService { + + private static final Logger logger = Logger.getLogger(FLYSService.class); + + + public Document process( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + init(); + + try { + return doProcess(data, globalContext, callMeta); + } + finally { + shutdown(); + } + } + + + protected abstract Document doProcess( + Document data, + GlobalContext globalContext, + CallMeta callMeta); + + + protected void init() { + logger.debug("init"); + SessionHolder.acquire(); + } + + + protected void shutdown() { + logger.debug("shutdown"); + Session session = SessionHolder.HOLDER.get(); + session.close(); + + SessionHolder.release(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,308 @@ +package de.intevation.flys.artifacts.services; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; +import de.intevation.flys.model.MainValueType; +import de.intevation.flys.model.NamedMainValue; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.MainValuesFactory; +import de.intevation.flys.artifacts.model.RiverFactory; + + +/** + * This service returns the main values of a river's gauge based on the start + * and end point of the river. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MainValuesService extends FLYSService { + + /** The logger that is used by this service.*/ + private static Logger logger = Logger.getLogger(MainValuesService.class); + + + /** The XPath that points to the river definition of the incoming request.*/ + public static final String XPATH_RIVER = "/art:mainvalues/art:river/text()"; + + /** The XPath that points to the start definition of the incoming request.*/ + public static final String XPATH_START = "/art:mainvalues/art:start/text()"; + + /** The XPath that points to the end definition of the incoming request.*/ + public static final String XPATH_END = "/art:mainvalues/art:end/text()"; + + /** + * The default constructor. + */ + public MainValuesService() { + } + + + @Override + public Document doProcess( + Document data, + GlobalContext context, + CallMeta callMeta + ) { + logger.debug("MainValuesService.process"); + + try { + River river = getRequestedRiver(data); + double[] minmax = getRequestedStartEnd(data, river); + Gauge gauge = river.determineGauge(minmax[0], minmax[1]); + + logger.debug("Found gauge: " + gauge.getName()); + + List<MainValue> mainValues = getMainValues(river, gauge); + + return buildDocument(river, gauge, mainValues, context); + } + catch (NullPointerException npe) { + logger.error("Could not process the request."); + logger.error(npe, npe); + + return XMLUtils.newDocument(); + } + } + + + /** + * This method extracts the river from the incoming request. If no river + * string was found or no river is found in the database based on this + * string a NullPointerException is thrown. + * + * @param data The incoming request data. + * + * @return the River object. + */ + protected River getRequestedRiver(Document data) + throws NullPointerException + { + logger.debug("MainValuesService.getRiver"); + + String riverStr = XMLUtils.xpathString( + data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE); + + if (riverStr == null || riverStr.trim().length() == 0) { + throw new NullPointerException("No river found in the request."); + } + + River river = RiverFactory.getRiver(riverStr); + + if (river == null) { + throw new NullPointerException("No such river found: " + riverStr); + } + + return river; + } + + + /** + * This method extracts the start and end point from incoming request + * document and returns both values in an array. If no start and end strings + * are found in the document, the min/max values of the <i>river</i> are + * returned. + * + * @param data The incoming request data. + * @param river The river of the request. + * + * @return the start and end point. + */ + protected double[] getRequestedStartEnd(Document data, River river) { + logger.debug("MainValuesService.getStartEnd"); + + String startStr = XMLUtils.xpathString( + data, XPATH_START, ArtifactNamespaceContext.INSTANCE); + + String endStr = XMLUtils.xpathString( + data, XPATH_END, ArtifactNamespaceContext.INSTANCE); + + try { + double start = Double.parseDouble(startStr); + double end = Double.parseDouble(endStr); + + logger.debug("Found start: " + start); + logger.debug("Found end: " + end); + + return new double[] { start, end }; + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + + return river.determineMinMaxDistance(); + } + } + + + /** + * This method creates the result document that includes the main values of + * the specified <i>gauge</i>. + * + * @param river The river. + * @param gauge The gauge. + * + * @return a document that includes the main values of the specified river + * at the specified gauge. + */ + protected List<MainValue> getMainValues(River river, Gauge gauge) + throws NullPointerException + { + if (logger.isDebugEnabled()) { + logger.debug("MainValuesService.buildMainValues"); + logger.debug("River: " + river.getName()); + logger.debug("Gauge: " + gauge.getName()); + } + + List<MainValue> mainValues = MainValuesFactory.getMainValues(gauge); + + if (mainValues == null || mainValues.isEmpty()) { + throw new NullPointerException("No main values found."); + } + + logger.debug(mainValues.size() + " main values found."); + + return mainValues; + } + + + protected Document buildDocument( + River river, + Gauge gauge, + List<MainValue> mainValues, + Object context) + { + logger.debug("MainValuesService.buildDocument"); + + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element rootEl = cr.create("service"); + cr.addAttr(rootEl, "name", "mainvalues"); + + doc.appendChild(rootEl); + + appendMetaInformation(doc, rootEl, river, gauge, context); + appendMainValues(doc, rootEl, mainValues, context); + + return doc; + } + + + /** + * This method appends some meta information to the result document. + * Currently, the river's and gauge's names and the gauge's range are + * appended. + * + * @param root The root element of the result document. + * @param river The river. + * @param gauge The gauge. + * @param context The context object. + */ + protected void appendMetaInformation( + Document doc, + Element root, + River river, + Gauge gauge, + Object context) + { + logger.debug("MainValuesService.appendMetaInformation"); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Range range = gauge.getRange(); + + Element riverEl = cr.create("river"); + cr.addAttr(riverEl, "name", river.getName()); + + Element gaugeEl = cr.create("gauge"); + cr.addAttr(gaugeEl, "name", gauge.getName()); + cr.addAttr(gaugeEl, "from", range.getA().toString()); + cr.addAttr(gaugeEl, "to", range.getB().toString()); + + root.appendChild(riverEl); + root.appendChild(gaugeEl); + } + + + protected void appendMainValues( + Document doc, + Element root, + List<MainValue> mainValues, + Object context) + { + logger.debug("MainValuesService.appendMainValues"); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element list = cr.create("mainvalues"); + + for (MainValue mainValue: mainValues) { + Element newEl = buildMainValueElement(doc, mainValue, context); + + if (newEl != null) { + list.appendChild(newEl); + } + } + + root.appendChild(list); + } + + + /** + * This method builds a concrete mainvalue element. This element consists of + * three attributes: the value, its name and its type. + * + * @param doc The owner document. + * @param mainValue The mainvalue. + * @param context The context object. + * + * @return a mainvalue element. + */ + protected Element buildMainValueElement( + Document doc, + MainValue mainValue, + Object context) + { + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + NamedMainValue namedMainValue = mainValue.getMainValue(); + MainValueType mainValueType = namedMainValue.getType(); + + Element el = cr.create("mainvalue"); + + cr.addAttr(el, "value", mainValue.getValue().toString()); + cr.addAttr(el, "name", namedMainValue.getName()); + cr.addAttr(el, "type", mainValueType.getName()); + + return el; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MapInfoService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,125 @@ +package de.intevation.flys.artifacts.services; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.vividsolutions.jts.geom.Envelope; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.DefaultService; + +import de.intevation.flys.model.River; + +import de.intevation.flys.model.RiverAxis; +import de.intevation.flys.utils.GeometryUtils; + +/** + * This service provides information about the supported rivers by this + * application. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MapInfoService extends DefaultService { + + /** XPath that points to the river.*/ + public static final String XPATH_RIVER = "/mapinfo/river/text()"; + + public static final String XPATH_RIVER_PROJECTION = + "/artifact-database/floodmap/river[@name='%RIVER%']/srid/@value"; + + public static final String XPATH_RIVER_BACKGROUND = + "/artifact-database/floodmap/river[@name='%RIVER%']/background-wms"; + + public static final String XPATH_RIVER_WMS = + "/artifact-database/floodmap/river[@name='%RIVER%']/river-wms/@url"; + + + /** The logger used in this service.*/ + private static Logger logger = Logger.getLogger(MapInfoService.class); + + + /** + * The default constructor. + */ + public MapInfoService() { + } + + + public Document process( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + logger.debug("MapInfoService.process"); + + Document result = XMLUtils.newDocument(); + ElementCreator cr = new ElementCreator(result, null, null); + + Element mapinfo = cr.create("mapinfo"); + result.appendChild(mapinfo); + + String river = extractRiver(data); + if (river == null || river.length() == 0) { + logger.warn("Cannot generate information: river is empty!"); + return result; + } + + Element root = cr.create("river"); + cr.addAttr(root, "name", river); + mapinfo.appendChild(root); + + RiverAxis axis = RiverAxis.getRiverAxis(river); + if (axis != null) { + Envelope env = axis.getGeom().getEnvelopeInternal(); + String bounds = GeometryUtils.jtsBoundsToOLBounds(env); + + logger.debug("River '" + river + "' bounds: " + bounds); + Element bbox = cr.create("bbox"); + cr.addAttr(bbox, "value", bounds); + root.appendChild(bbox); + } + + String xpathS = XPATH_RIVER_PROJECTION.replace("%RIVER%", river); + String sridStr = Config.getStringXPath(xpathS); + if (sridStr != null && sridStr.length() > 0) { + Element srid = cr.create("srid"); + cr.addAttr(srid, "value", sridStr); + root.appendChild(srid); + } + + String xpathB = XPATH_RIVER_BACKGROUND.replace("%RIVER%", river); + Element back = (Element) Config.getNodeXPath(xpathB); + if (back != null) { + Element background = cr.create("background-wms"); + cr.addAttr(background, "url", back.getAttribute("url")); + cr.addAttr(background, "layers", back.getAttribute("layers")); + root.appendChild(background); + } + + String xpathWMS = XPATH_RIVER_WMS.replace("%RIVER%", river); + String wmsStr = Config.getStringXPath(xpathWMS); + if (wmsStr != null && wmsStr.length() > 0) { + Element wms = cr.create("river-wms"); + cr.addAttr(wms, "url", wmsStr); + root.appendChild(wms); + } + + return result; + } + + + protected String extractRiver(Document data) { + return XMLUtils.xpathString( + data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,169 @@ +package de.intevation.flys.artifacts.services; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import java.util.Map; +import java.util.HashMap; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; + +import de.intevation.flys.artifacts.datacage.Recommendations; + +import de.intevation.flys.artifacts.FLYSArtifact; + +public class MetaDataService +extends FLYSService +{ + private static Logger log = Logger.getLogger(MetaDataService.class); + + public static final String XPATH_ARTIFACT_ID = "/art:meta/art:artifact-id/@value"; + public static final String XPATH_USER_ID = "/art:meta/art:user-id/@value"; + public static final String XPATH_OUTS = "/art:meta/art:outs/@value"; + public static final String XPATH_PARAMETERS = "/art:meta/art:parameters/@value"; + + /** The global context key of the artifact database. */ + public static final String ARTIFACT_DATA_BASE_KEY = + "global.artifact.database"; + + public MetaDataService() { + } + + @Override + protected Document doProcess( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + log.debug("MetaDataService.process"); + + String artifactId = XMLUtils.xpathString( + data, XPATH_ARTIFACT_ID, ArtifactNamespaceContext.INSTANCE); + + if (artifactId != null + && (artifactId = artifactId.trim()).length() == 0) { + artifactId = null; + } + + String userId = XMLUtils.xpathString( + data, XPATH_USER_ID, ArtifactNamespaceContext.INSTANCE); + + if (userId != null + && (userId = userId.trim()).length() == 0) { + userId = null; + } + + String outs = XMLUtils.xpathString( + data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE); + + String parameters = XMLUtils.xpathString( + data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE); + + return doService( + artifactId, userId, outs, parameters, globalContext); + } + + protected static Map<String, Object> splitParameters( + String parameters, + Map<String, Object> data + ) { + if (parameters != null) { + String [] parts = parameters.split("\\s*;\\s*"); + for (String part: parts) { + String [] kv = part.split("\\s*:\\s*"); + if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) { + continue; + } + String [] values = kv[1].split("\\s*,\\s*"); + data.put(kv[0], values.length == 1 ? values[0] : values); + } + } + return data; + } + + protected Document doService( + String artifactId, + String userId, + String outsString, + String parameters, + GlobalContext globalContext + ) { + Document result = XMLUtils.newDocument(); + + FLYSArtifact flysArtifact; + + if (log.isDebugEnabled()) { + log.debug("artifact : " + artifactId); + log.debug("user : " + userId); + log.debug("outs : " + outsString); + log.debug("parameters: " + parameters); + } + + if (userId != null && !StringUtils.checkUUID(userId)) { + log.warn("'" + userId + "' is not a UUID"); + return result; + } + + if (artifactId != null) { + if (!StringUtils.checkUUID(artifactId)) { + log.warn("'" + artifactId + "' is not a UUID"); + return result; + } + + Object dbObject = + (ArtifactDatabase)globalContext.get(ARTIFACT_DATA_BASE_KEY); + + if (!(dbObject instanceof ArtifactDatabase)) { + log.error("Cannot find artifact database"); + return result; + } + + ArtifactDatabase db = (ArtifactDatabase)dbObject; + + Artifact artifact; + + try { + artifact = db.getRawArtifact(artifactId); + } + catch (ArtifactDatabaseException adbe) { + log.warn("fetching artifact failed", adbe); + return result; + } + + if (!(artifact instanceof FLYSArtifact)) { + log.warn("artifact is not a FLYS artifact."); + return result; + } + + flysArtifact = (FLYSArtifact)artifact; + } + else { + flysArtifact = null; + } + + + Map<String, Object> data = splitParameters( + parameters, new HashMap<String, Object>()); + + String [] outs = outsString == null + ? new String [0] + : outsString.split("\\s*,\\s*"); + + Recommendations rec = Recommendations.getInstance(); + rec.recommend( + flysArtifact, userId, outs, data, result); + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverService.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,63 @@ +package de.intevation.flys.artifacts.services; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.model.RiverFactory; + + +/** + * This service provides information about the supported rivers by this + * application. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverService extends FLYSService { + + /** The logger used in this service.*/ + private static Logger logger = Logger.getLogger(RiverService.class); + + + protected Document doProcess( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + logger.debug("RiverService.process"); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + List<River> allRivers = RiverFactory.getRivers(); + + Element rivers = ec.create("rivers"); + + for (River river: allRivers) { + Element r = ec.create("river"); + ec.addAttr(r, "name", river.getName(), true); + + rivers.appendChild(r); + } + + result.appendChild(rivers); + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/AnnotationRiverState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,63 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.artifacts.model.AnnotationFacet; +import de.intevation.flys.artifacts.model.FacetTypes; + +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * The only state for an AnnotationArtifact (River is known). + */ +public class AnnotationRiverState +extends DefaultState +implements FacetTypes +{ + /** Developer-centric description of facet. */ + public static final String I18N_DESCRIPTION = "facet.longitudinal_section.annotations"; + + /** The logger that is used in this state. */ + private static final Logger logger = Logger.getLogger(AnnotationRiverState.class); + + + /** + * Add an AnnotationFacet to list of Facets. + * + * @param artifact Ignored. + * @param hash Ignored. + * @param context Ignored. + * @param meta CallMeta to be used for internationalization. + * @param facets List to add AnnotationFacet to. + * + * @return null. + */ + @Override + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets + ) { + logger.debug("AnnotationRiverState.computeInit()"); + + AnnotationFacet facet = new AnnotationFacet( + 0, + LONGITUDINAL_ANNOTATION, + Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION)); + facets.add(facet); + + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,126 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class CalculationSelect extends DefaultState { + + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(CalculationSelect.class); + + + public static final String FIELD_MODE = "calculation_mode"; + + /** Constant value for the reference line calculation. */ + public static final String CALCULATION_SURFACE_CURVE = + "calc.surface.curve"; + + /** Constant value for the differences calculation. */ + public static final String CALCULATION_DURATION_CURVE = + "calc.duration.curve"; + + /** Constant value for the flood map calculation. */ + public static final String CALCULATION_FLOOD_MAP = + "calc.flood.map"; + + /** Constant value for the profile calculation. */ + public static final String CALCULATION_DISCHARGE_LONGITUDINAL_CURVE = + "calc.discharge.longitudinal.section"; + + /** Constant value for the state discharge curve calculation. */ + public static final String CALCULATION_DISCHARGE_CURVE = + "calc.discharge.curve"; + + /** Constant value for the state w differences calculation. */ + public static final String CALCULATION_W_DIFFERENCES = + "calc.w.differences"; + + /** An array that holds all available calculation modes. */ + public static final String[] CALCULATIONS = { + CALCULATION_SURFACE_CURVE, + CALCULATION_FLOOD_MAP, + CALCULATION_DISCHARGE_CURVE, + CALCULATION_DURATION_CURVE, + CALCULATION_DISCHARGE_LONGITUDINAL_CURVE, + CALCULATION_W_DIFFERENCES }; + + + /** Error message that is thrown if no mode has been chosen. */ + public static final String ERROR_NO_CALCULATION_MODE = + "error_feed_no_calculation_mode"; + + /** Error message that is thrown if an invalid calculation mode has been + * chosen. */ + public static final String ERROR_INVALID_CALCULATION_MODE = + "error_feed_invalid_calculation_mode"; + + + public CalculationSelect() { + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + CallMeta meta = context.getMeta(); + Element[] calcs = new Element[CALCULATIONS.length]; + + int i = 0; + + for (String calc: CALCULATIONS) { + calcs[i++] = createItem( + cr, new String[] { + Resources.getMsg(meta, calc, calc), + calc + }); + } + + return calcs; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("CalculationSelect.validate"); + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData data = getData(flys, FIELD_MODE); + String calc = (data != null) ? (String) data.getValue() : null; + + if (calc == null) { + throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE); + } + + calc = calc.trim().toLowerCase(); + + for (String mode: CALCULATIONS) { + if (mode.equals(calc)) { + return true; + } + } + + throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputationRangeState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,269 @@ +package de.intevation.flys.artifacts.states; + +import java.util.Date; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.DischargeTable; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; +import de.intevation.flys.model.TimeInterval; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.GaugesFactory; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.model.WaterlevelFacet; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputationRangeState +extends RangeState +implements FacetTypes +{ + private static Logger logger = + Logger.getLogger(ComputationRangeState.class); + + + /** The name of the 'from' field. */ + public static final String FROM = "ld_from"; + + /** The name of the 'to' field. */ + public static final String TO = "ld_to"; + + /** The name of the 'step' field. */ + public static final String STEP = "ld_step"; + + /** The default step width. */ + public static final int DEFAULT_STEP = 100; + + + + public ComputationRangeState() { + } + + + @Override + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + data.getName(), + data.getName())); + + select.appendChild(label); + + return select; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + double[] minmax = getMinMax(artifact); + + double minVal = Double.MIN_VALUE; + double maxVal = Double.MAX_VALUE; + + if (minmax != null) { + minVal = minmax[0]; + maxVal = minmax[1]; + } + else { + logger.warn("Could not read min/max distance values!"); + } + + if (name.equals("ld_from")) { + Element min = createItem( + cr, + new String[] {"min", new Double(minVal).toString()}); + + return new Element[] { min }; + } + else if (name.equals("ld_to")) { + Element max = createItem( + cr, + new String[] {"max", new Double(maxVal).toString()}); + + return new Element[] { max }; + } + else { + Element step = createItem( + cr, + new String[] {"step", String.valueOf(getDefaultStep())}); + return new Element[] { step }; + } + + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + public Object computeFeed( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + logger.debug("computeFeed"); + + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res = old instanceof CalculationResult + ? (CalculationResult)old + : winfo.getDischargeCurveData(); + + if (facets == null) { + logger.debug("generate no facets"); + return res; + } + + WQKms [] wqkms = (WQKms [])res.getData(); + + logger.debug("generate " + wqkms.length + " facets."); + + String stateID = winfo.getCurrentStateId(); + + for (int i = 0; i < wqkms.length; ++i) { + String name = getSeriesName(context, wqkms[i].getName()); + facets.add(new WaterlevelFacet( + i, DISCHARGE_CURVE, name, ComputeType.FEED, stateID, hash)); + } + + + return res; + } + + protected String getSeriesName(CallContext cc, String gaugeName) { + Gauge gauge = GaugesFactory.getGauge(gaugeName); + + if (gauge == null) { + logger.warn("Cannot determine Gauge for name: " + gaugeName); + return gaugeName; + } + + List<DischargeTable> dts = gauge.getDischargeTables(); + + for (DischargeTable dt: dts) { + if (dt.getKind() == 0) { + TimeInterval ti = dt.getTimeInterval(); + + Date start = ti.getStartTime(); + Date end = ti.getStopTime(); + + String name = gauge.getName(); + + if (end == null) { + Object[] args = new Object[] { name, start }; + return Resources.getMsg( + cc.getMeta(), + "chart.discharge.curve.curve.valid.from", + "", + args); + } + else { + Object[] args = new Object[] { name, start, end }; + return Resources.getMsg( + cc.getMeta(), + "chart.discharge.curve.curve.valid.range", + "", + args); + } + } + } + + return gauge.getName(); + } + + + @Override + protected double[] getMinMax(Artifact artifact) { + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + StateData data = getData(flysArtifact, "river"); + + String name = data != null ? (String) data.getValue() : ""; + + logger.debug("Search for the min/max distances of '" + name + "'"); + + River river = RiverFactory.getRiver(name); + + return river != null ? river.determineMinMaxDistance() : null; + } + + + protected double getDefaultStep() { + return DEFAULT_STEP; + } + + + @Override + protected String getLowerField() { + return FROM; + } + + + @Override + protected String getUpperField() { + return TO; + } + + + @Override + protected String getStepField() { + return STEP; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,87 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WaterlevelFacet; +import de.intevation.flys.artifacts.model.DataFacet; +import de.intevation.flys.artifacts.model.ReportFacet; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.CalculationResult; + +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.utils.FLYSUtils; + +/** + * The final state that will be reached after the discharge curve calculation + * mode has been chosen. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state.*/ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveState.class); + + public ComputedDischargeCurveState() { + } + + + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res = old instanceof CalculationResult + ? (CalculationResult)old + : winfo.getComputedDischargeCurveData(); + + WQKms [] wqkms = (WQKms [])res.getData(); + + if (facets != null && wqkms.length > 0) { + for (int i = 0; i < wqkms.length; ++i) { + + Object[] args = new Object[] { + FLYSUtils.getRiver(winfo).getName(), + wqkms[i].getName() + }; + + String name = Resources.getMsg( + context.getMeta(), + "chart.computed.discharge.curve.curve.label", + "", + args); + + facets.add(new WaterlevelFacet(i, COMPUTED_DISCHARGE_Q, name)); + facets.add(new WaterlevelFacet(i, AT, "AT data")); + } + + facets.add(new DataFacet(CSV, "CSV data")); + + if (res.getReport().hasProblems()) { + facets.add(new ReportFacet()); + } + } + + return res; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DGMSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,70 @@ +package de.intevation.flys.artifacts.states; + +import java.io.File; + +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.model.DGM; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DGMSelect extends DefaultState { + + private static final Logger logger = Logger.getLogger(DGMSelect.class); + + + @Override + protected String getUIProvider() { + return "dgm_datacage_panel"; + } + + + @Override + protected Element createStaticData( + FLYSArtifact flys, + ElementCreator creator, + CallContext cc, + String name, + String value, + String type + ) { + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", type, true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + + creator.addAttr(itemElement, "label", getLabel(cc, value), true); + dataElement.appendChild(itemElement); + + return dataElement; + } + + + public static String getLabel(CallContext cc, String value) { + logger.debug("Create label for value: " + value); + + try { + DGM dgm = DGM.getDGM(Integer.parseInt(value)); + + File file = new File(dgm.getPath()); + return file.getName(); + } + catch (NumberFormatException nfe) { + logger.warn("Cannot parse int value: '" + value + "'"); + } + + return ""; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DefaultState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,396 @@ +package de.intevation.flys.artifacts.states; + +import java.text.NumberFormat; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.ProtocolUtils; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifactdatabase.state.AbstractState; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class DefaultState extends AbstractState { + + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(DefaultState.class); + + /** The three possible compute types. */ + public static enum ComputeType { + FEED, ADVANCE, INIT + } + + + protected StateData getData(FLYSArtifact artifact, String name) { + return artifact.getData(name); + } + + + /** + * Append to a node and return xml description relevant for gui. + */ + public Element describeStatic( + Artifact artifact, + Document document, + Node root, + CallContext context, + String uuid) + { + ElementCreator creator = new ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + CallMeta meta = context.getMeta(); + + String label = Resources.getMsg(meta, getID(), getID()); + Element ui = ProtocolUtils.createArtNode( + creator, "state", + new String[] { "name", "uiprovider", "label" }, + new String[] { getID(), getUIProvider(), label }); + + Map<String, StateData> theData = getData(); + if (theData == null) { + return ui; + } + + Iterator<String> iter = theData.keySet().iterator(); + FLYSArtifact flys = (FLYSArtifact) artifact; + + while (iter.hasNext()) { + String name = iter.next(); + appendStaticData(flys, context, creator, ui, name); + } + + return ui; + } + + + protected void appendStaticData( + FLYSArtifact flys, + CallContext context, + ElementCreator cr, + Element ui, + String name + ) { + StateData data = getData(flys, name); + String value = (data != null) ? (String) data.getValue() : null; + + if (value == null) { + return; + } + + logger.debug("Append element '" + name + "' (" + value + ")"); + + Element e = createStaticData( + flys, cr, context, name, value, data.getType()); + + ui.appendChild(e); + + } + + + /** + * Creates a <i>data</i> element used in the static part of the DESCRIBE + * document. + * + * @param creator The ElementCreator that is used to build new Elements. + * @param meta The CallMeta object used for i18n. + * @param name The name of the data item. + * @param value The value as string. + * + * @return an Element. + */ + protected Element createStaticData( + FLYSArtifact flys, + ElementCreator creator, + CallContext cc, + String name, + String value, + String type + ) { + CallMeta meta = cc.getMeta(); + + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", type, true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + + String attrValue = ""; + try { + // XXX A better way to format the output would be to use the + // 'type' value of the data objects. + double doubleVal = Double.valueOf(value); + Locale l = Resources.getLocale(meta); + NumberFormat nf = NumberFormat.getInstance(l); + + attrValue = nf.format(doubleVal); + } + catch (NumberFormatException nfe) { + attrValue = Resources.getMsg(meta, value, value); + } + + creator.addAttr(itemElement, "label", attrValue, true); + dataElement.appendChild(itemElement); + + return dataElement; + } + + + public Element describe( + Artifact artifact, + Document document, + Node root, + CallContext context, + String uuid) + { + ElementCreator creator = new ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element ui = null; + String uiprovider = getUIProvider(); + if (uiprovider != null) { + ui = ProtocolUtils.createArtNode( + creator, "dynamic", + new String[] { "uiprovider" }, + new String[] { uiprovider }); + } + else { + ui = ProtocolUtils.createArtNode(creator, "dynamic", null, null); + } + + Map<String, StateData> theData = getData(); + if (theData == null) { + return ui; + } + + Iterator<String> iter = theData.keySet().iterator(); + FLYSArtifact flys = (FLYSArtifact) artifact; + + while (iter.hasNext()) { + String name = iter.next(); + StateData data = getData(flys, name); + + data = data != null ? data : getData(name); + + Element select = createData(creator, artifact, data, context); + + String defValue = (String) data.getValue(); + String defDesc = null; + + if (defValue != null && defValue.length() > 0) { + defDesc = Resources.getMsg( + context.getMeta(), + defValue, + defValue); + } + + if (defValue != null && defDesc != null) { + creator.addAttr(select, "defaultValue", defValue, true); + creator.addAttr(select, "defaultLabel", defDesc, true); + } + + Element choices = ProtocolUtils.createArtNode( + creator, "choices", null, null); + + select.appendChild(choices); + ui.appendChild(select); + + Element[] items = createItems(creator, artifact, name, context); + if (items != null) { + for (Element item: items) { + choices.appendChild(item); + } + } + } + + return ui; + } + + + /** + * This method creates the root node that contains the list of selectable + * items. + * + * @param cr The ElementCreator. + * @param name The name of the amount of data. + * + * @return the root node of the item list. + */ + protected Element createData( + ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + select.appendChild(label); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + getID(), + getID())); + + return select; + } + + + /** + * This method creates a list of items. These items represent the amount of + * input data that is possible for this state. + * + * @param cr The ElementCreator. + * @param name The name of the amount of data. + * + * @return a list of items. + */ + protected Element[] createItems( + ElementCreator cr, + Artifact artifact, + String name, + CallContext context + ) { + return null; + } + + + /** + * This method is used to create an <i>item</i> Element that contains two + * further elements <i>label</i> and <i>value</i>. The label and value + * elements both have text nodes. + * + * @param cr The ElementCreator used to build new Elements. + * @param obj This implementation awaits a String array with [0] = label and + * [1] = value. + * + * @return an Element. + */ + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + /** + * This method transform a given value into a StateData object. + * + * @param flys The FLYSArtifact. + * @param name The name of the data object. + * @param val The value of the data object. + * + * @return a StateData object with <i>name</i> and <i>val</i>ue. + */ + public StateData transform( + FLYSArtifact flys, + CallContext cc, + String name, + String val + ) { + logger.debug("Transform data ('" + name + "','" + val + "')"); + return new DefaultStateData(name, null, null, val); + } + + + /** + * This method validates the inserted data and returns true, if everything + * was correct, otherwise an exception is thrown. + * + * @param artifact A reference to the owner artifact. + * + * @return true, if everything was fine. + */ + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + return true; + } + + + /** + * Returns which UIProvider shall be used to aid user input. + */ + protected String getUIProvider() { + return null; + } + + + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return null; + } + + + public Object computeFeed( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return null; + } + + + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets) + { + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,99 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.ReportFacet; +import de.intevation.flys.artifacts.model.WaterlevelFacet; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.CalculationResult; + +import de.intevation.flys.artifacts.model.DataFacet; + +public class DischargeLongitudinalSection +extends DefaultState +implements FacetTypes +{ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSection.class); + + + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res = old instanceof CalculationResult + ? (CalculationResult)old + : winfo.getDischargeLongitudinalSectionData(); + + if (facets == null) { + return res; + } + + WQKms [] wqkms = (WQKms [])res.getData(); + + for (int i = 0; i < wqkms.length; i++) { + String nameW = null; + String nameQ = null; + + if (winfo.isQ()) { + nameQ = wqkms[i].getName(); + nameW = "W(" + nameQ + ")"; + } + else { + nameW = wqkms[i].getName(); + nameQ = "Q(" + nameQ + ")"; + } + + Facet w = new WaterlevelFacet( + i, DISCHARGE_LONGITUDINAL_W, nameW); + + Facet q = new WaterlevelFacet( + i, DISCHARGE_LONGITUDINAL_Q, nameQ); + + facets.add(w); + facets.add(q); + + if (wqkms[i] instanceof WQCKms) { + // TODO DO i18n + + String nameC = nameW.replace( + "benutzerdefiniert", + "benutzerdefiniert [korrigiert]"); + + Facet c = new WaterlevelFacet( + i, DISCHARGE_LONGITUDINAL_C, nameC); + + facets.add(c); + } + } + + if (wqkms.length > 0) { + facets.add(new DataFacet(CSV, "CSV data")); + facets.add(new DataFacet(WST, "WST data")); + } + + if (res.getReport().hasProblems()) { + facets.add(new ReportFacet()); + } + + return res; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceOnlySelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,51 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +public class DistanceOnlySelect extends DistanceSelect { + + private static Logger logger = Logger.getLogger(DistanceOnlySelect.class); + + @Override + protected String getUIProvider() { + return "distance_only_panel"; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData dFrom = getData(flys, getLowerField()); + StateData dTo = getData(flys, getUpperField()); + + String fromStr = dFrom != null ? (String) dFrom.getValue() : null; + String toStr = dTo != null ? (String) dTo.getValue() : null; + + if (fromStr == null || toStr == null) { + throw new IllegalArgumentException("error_empty_state"); + } + + try { + double from = Double.parseDouble(fromStr); + double to = Double.parseDouble(toStr); + + double[] minmax = getMinMax(flys); + + return validateBounds(minmax[0], minmax[1], from, to); + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException("error_invalid_double_value"); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DistanceSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,24 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DistanceSelect extends ComputationRangeState { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(DistanceSelect.class); + + + public DistanceSelect() { + } + + + @Override + protected String getUIProvider() { + return "distance_panel"; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,98 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.DurationCurveFacet; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQDay; + +import de.intevation.flys.artifacts.model.DataFacet; +import de.intevation.flys.artifacts.model.ReportFacet; +import de.intevation.flys.artifacts.model.CalculationResult; + +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.utils.FLYSUtils; + + +/** + * The final state that will be reached after the duration curve calculation + * mode has been chosen. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(DurationCurveState.class); + + public DurationCurveState() { + } + + + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + WINFOArtifact winfo = (WINFOArtifact)artifact; + + CalculationResult res; + + if (old instanceof CalculationResult) { + res = (CalculationResult)old; + } + else { + res = winfo.getDurationCurveData(); + } + + WQDay wqday = (WQDay)res.getData(); + + if (wqday != null && facets != null) { + Object[] args = new Object[] { + FLYSUtils.getRiver(winfo).getName() + }; + + String nameW = Resources.getMsg( + context.getMeta(), + "chart.duration.curve.curve.w", + "", + args); + + String nameQ = Resources.getMsg( + context.getMeta(), + "chart.duration.curve.curve.q", + "", + args); + + Facet w = new DurationCurveFacet(DURATION_W, nameW); + Facet q = new DurationCurveFacet(DURATION_Q, nameQ); + + facets.add(w); + facets.add(q); + + facets.add(new DataFacet(CSV, "CSV data")); + + if (res.getReport().hasProblems()) { + facets.add(new ReportFacet()); + } + } + + return res; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,709 @@ +package de.intevation.flys.artifacts.states; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.MultiPolygon; +import com.vividsolutions.jts.geom.Polygon; + +import org.apache.log4j.Logger; + +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; + +import org.geotools.feature.FeatureCollection; +import org.geotools.feature.FeatureCollections; +import org.geotools.feature.simple.SimpleFeatureBuilder; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.FileTools; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.CrossSectionTrack; +import de.intevation.flys.model.DGM; +import de.intevation.flys.model.Floodplain; +import de.intevation.flys.model.RiverAxis; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.CalculationMessage; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WSPLGENCalculation; +import de.intevation.flys.artifacts.model.WSPLGENJob; +import de.intevation.flys.artifacts.model.WSPLGENReportFacet; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; +import de.intevation.flys.exports.WstWriter; +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.MapfileGenerator; +import de.intevation.flys.utils.GeometryUtils; +import de.intevation.flys.wsplgen.FacetCreator; +import de.intevation.flys.wsplgen.JobObserver; +import de.intevation.flys.wsplgen.Scheduler; + + +public class FloodMapState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(FloodMapState.class); + + + public static final String KEEP_ARTIFACT_DIR = + System.getProperty("flys.uesk.keep.artifactsdir", "false"); + + + public static final String WSP_ARTIFACT = "wsp"; + + public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel"; + + public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par"; + public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp"; + public static final String WSPLGEN_BARRIERS_POLY = "barrier_polygons.shp"; + public static final String WSPLGEN_AXIS = "axis.shp"; + public static final String WSPLGEN_QPS = "qps.shp"; + public static final String WSPLGEN_FLOODPLAIN = "talaue.shp"; + public static final String WSPLGEN_WSP_FILE = "waterlevel.wst"; + public static final String WSPLGEN_OUTPUT_FILE = "wsplgen.shp"; + + public static final int WSPLGEN_DEFAULT_OUTPUT = 0; + + + + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + logger.debug("FloodMapState.computeAdvance"); + + File artifactDir = getDirectory(artifact); + + if (artifactDir == null) { + logger.error("Could not create directory for WSPLGEN results!"); + return null; + } + + WSPLGENCalculation calculation = new WSPLGENCalculation(); + + FacetCreator facetCreator = new FacetCreator( + artifact, context, hash, getID(), facets); + + WSPLGENJob job = prepareWSPLGENJob( + artifact, + facetCreator, + artifactDir, + context, + calculation); + + CalculationResult res = new CalculationResult(null, calculation); + WSPLGENReportFacet report= new WSPLGENReportFacet( + ComputeType.ADVANCE, hash, getID(), res); + + facets.add(report); + + if (job == null) { + if (KEEP_ARTIFACT_DIR.equals("false")) { + removeDirectory(artifact); + } + + calculation.addError(-1, Resources.getMsg( + context.getMeta(), + "wsplgen.job.error", + "wsplgen.job.error")); + + logger.error("No WSPLGEN processing has been started!"); + + return null; + } + + context.afterCall(CallContext.BACKGROUND); + context.addBackgroundMessage(new CalculationMessage( + JobObserver.STEPS.length, + 0, + Resources.getMsg( + context.getMeta(), + "wsplgen.job.queued", + "wsplgen.job.queued") + )); + + Scheduler scheduler = Scheduler.getInstance(); + scheduler.addJob(job); + + return null; + } + + + /** + * Returns (and creates if not existing) the directory for storing WSPLEN + * data for the owner artifact. + * + * @param artifact The owner Artifact. + * + * @return the directory for WSPLEN data. + */ + protected File getDirectory(FLYSArtifact artifact) { + String shapePath = FLYSUtils.getXPathString( + FLYSUtils.XPATH_SHAPEFILE_DIR); + + File artifactDir = FileTools.getDirectory( + shapePath, artifact.identifier()); + + return artifactDir; + } + + + /** + * Removes the directory and all its content where the required data and the + * results of WSPLGEN are stored. Should be called in endOfLife(). + */ + protected void removeDirectory(FLYSArtifact artifact) { + String shapePath = FLYSUtils.getXPathString( + FLYSUtils.XPATH_SHAPEFILE_DIR); + + File artifactDir = new File(shapePath, artifact.identifier()); + + if (artifactDir.exists()) { + logger.info("Delete directory: " + artifactDir.getAbsolutePath()); + boolean success = FileTools.deleteRecursive(artifactDir); + } + else { + logger.debug("There is no directory to remove."); + } + } + + + @Override + public void endOfLife(Artifact artifact, Object callContext) { + logger.info("FloodMapState.endOfLife: " + artifact.identifier()); + + FLYSArtifact flys = (FLYSArtifact) artifact; + removeDirectory(flys); + + MapfileGenerator.getInstance().update(); + } + + + protected WSPLGENJob prepareWSPLGENJob( + FLYSArtifact artifact, + FacetCreator facetCreator, + File artifactDir, + CallContext context, + WSPLGENCalculation calculation + ) { + logger.debug("FloodMapState.prepareWSPLGENJob"); + + WSPLGENJob job = new WSPLGENJob( + artifact, + artifactDir, + facetCreator, + context, + calculation); + + File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE); + + setOut(artifact, job); + setRange(artifact, job); + setDelta(artifact, job); + setGel(artifact, job); + setDist(artifact, job); + setLine(artifact, facetCreator, artifactDir, job); + setAxis(artifact, artifactDir, job); + setPro(artifact, artifactDir, job); + setDgm(artifact, job); + setArea(artifact, artifactDir, job); + setOutFile(artifact, job); + setWsp(artifact, context, artifactDir, job); // WSP + + // TODO + // setWspTag(artifact, job); + + try { + job.toFile(paraFile); + + return job; + } + catch (IOException ioe) { + logger.warn("Cannot write PAR file: " + ioe.getMessage()); + } + catch (IllegalArgumentException iae) { + logger.warn("Cannot write PAR file: " + iae.getMessage()); + } + + return null; + } + + + protected void setOut(FLYSArtifact artifact, WSPLGENJob job) { + job.setOut(WSPLGEN_DEFAULT_OUTPUT); + } + + + protected void setRange(FLYSArtifact artifact, WSPLGENJob job) { + double[] range = FLYSUtils.getKmRange(artifact); + + job.setStart(range[0]); + job.setEnd(range[1]); + } + + + protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) { + String from = artifact.getDataAsString("diff_from"); + String to = artifact.getDataAsString("diff_to"); + String diff = artifact.getDataAsString("diff_diff"); + + try { + job.setFrom(Double.parseDouble(from)); + } + catch (NumberFormatException nfe) { + } + + try { + job.setTo(Double.parseDouble(to)); + } + catch (NumberFormatException nfe) { + } + + try { + job.setDiff(Double.parseDouble(diff)); + } + catch (NumberFormatException nfe) { + } + } + + + protected void setGel(FLYSArtifact artifact, WSPLGENJob job) { + String gel = artifact.getDataAsString("use_floodplain"); + + if (gel != null && gel.length() > 0) { + boolean use = Boolean.parseBoolean(gel); + + if (use) { + job.setGel(WSPLGENJob.GEL_SPERRE); + } + else { + job.setGel(WSPLGENJob.GEL_NOSPERRE); + } + } + } + + + protected void setDist(FLYSArtifact artifact, WSPLGENJob job) { + String dist = artifact.getDataAsString("profile_distance"); + + try { + job.setDist(Double.parseDouble(dist)); + } + catch (NumberFormatException nfe) { + // nothing to do here + } + } + + + protected void setLine( + FLYSArtifact artifact, + FacetCreator facetCreator, + File dir, + WSPLGENJob job + ) { + String geoJSON = artifact.getDataAsString("uesk.barriers"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + if (geoJSON == null || geoJSON.length() == 0) { + logger.debug("No barrier features in parameterization existing."); + return; + } + + SimpleFeatureType ft = getBarriersFeatureType( + "barriers", srs, Geometry.class); + + List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft); + if (features == null || features.size() == 0) { + logger.debug("No barrier features extracted."); + return; + } + + FeatureCollection[] fcs = splitLinesAndPolygons(features); + + File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES); + File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY); + + Object[][] obj = new Object[][] { + new Object[] { "typ", String.class } + }; + + boolean l = GeometryUtils.writeShapefile( + shapeLines, + GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj), + fcs[0]); + + if (l) { + logger.debug( + "Successfully created barrier line shapefile. " + + "Write shapefile path into WSPLGEN job."); + job.addLin(shapeLines.getAbsolutePath()); + } + + boolean p = GeometryUtils.writeShapefile( + shapePolys, + GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj), + fcs[1]); + + if (p) { + logger.debug( + "Successfully created barrier polygon shapefile. " + + "Write shapefile path into WSPLGEN job."); + job.addLin(shapePolys.getAbsolutePath()); + } + + if (p || l) { + facetCreator.createBarrierFacet(); + } + } + + + protected SimpleFeatureType getBarriersFeatureType( + String name, + String srs, + Class type + ) { + Object[][] attrs = new Object[3][]; + attrs[0] = new Object[] { "typ", String.class }; + attrs[1] = new Object[] { "elevation", Double.class }; + attrs[2] = new Object[] { "mark.selected", Integer.class }; + + return GeometryUtils.buildFeatureType(name, srs, type, attrs); + } + + + protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) { + FeatureCollection lines = FeatureCollections.newCollection(); + FeatureCollection polygons = FeatureCollections.newCollection(); + + for (SimpleFeature feature: f) { + Geometry geom = (Geometry) feature.getDefaultGeometry(); + + + if (geom instanceof LineString) { + geom = applyElevationAttribute(feature, (LineString) geom); + lines.add(feature); + } + else if (geom instanceof Polygon) { + geom = applyElevationAttribute(feature, (Polygon) geom); + polygons.add(feature); + } + else { + logger.warn("Feature not supported: " + geom.getClass()); + } + } + + logger.debug("Found " + lines.size() + " barrier lines."); + logger.debug("Found " + polygons.size() + " barrier polygons."); + + return new FeatureCollection[] { lines, polygons }; + } + + + protected static Geometry applyElevationAttribute( + SimpleFeature feature, + Geometry geom + ) { + logger.debug("Apply elevations for: " + geom.getClass()); + + List<Double> elevations = extractElevations(feature); + int numPoints = geom.getNumPoints(); + int numElevation = elevations.size(); + + String typ = (String) feature.getAttribute("typ"); + + if (numPoints > numElevation) { + logger.warn("More vertices in Geometry than elevations given."); + } + + Coordinate[] c = geom.getCoordinates(); + for (int i = 0; i < numPoints; i++) { + if (i < numElevation) { + c[i].z = elevations.get(i); + } + else if (typ != null && typ.equals("Graben")) { + c[i].z = -9999d; + } + else { + c[i].z = 9999d; + } + } + + return geom; + } + + + protected static List<Double> extractElevations(SimpleFeature feature) { + String tmp = (String) feature.getAttribute("elevation"); + String typ = (String) feature.getAttribute("typ"); + + String[] elevations = tmp == null ? null : tmp.split(" "); + + int num = elevations != null ? elevations.length : 0; + + List<Double> list = new ArrayList<Double>(num); + + for (int i = 0; i < num; i++) { + try { + list.add(Double.parseDouble(elevations[i])); + } + catch (NumberFormatException nfe) { + logger.warn("Error while parsing elevation at pos: " + i); + if (typ != null && typ.equals("Graben")) { + list.add(new Double(-9999.0)); + } + else { + list.add(new Double(9999.0)); + } + } + } + + return list; + } + + + protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + RiverAxis axis = RiverAxis.getRiverAxis(river); + if (axis == null) { + logger.warn("Could not find river axis for: '" + river + "'"); + return; + } + + Geometry geom = axis.getGeom(); + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "axis", srs, LineString.class); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + builder.add(geom); + + FeatureCollection collection = FeatureCollections.newCollection(); + collection.add(builder.buildFeature("0")); + + File axisShape = new File(dir, WSPLGEN_AXIS); + + boolean a = GeometryUtils.writeShapefile( + axisShape, + GeometryUtils.buildFeatureType("axis", srs, LineString.class), + collection); + + if (a) { + job.setAxis(axisShape.getAbsolutePath()); + } + } + + + protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + List<CrossSectionTrack> cst = + CrossSectionTrack.getCrossSectionTrack(river); + + logger.debug("Found " + cst.size() + " CrossSectionTracks."); + + Object[][] attrs = new Object[2][]; + attrs[0] = new Object[] { "ELEVATION", Double.class }; + attrs[1] = new Object[] { "KILOMETER", Double.class }; + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "qps", srs, LineString.class, attrs); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + FeatureCollection collection = FeatureCollections.newCollection(); + + int i = 0; + for (CrossSectionTrack track: cst) { + builder.reset(); + builder.add(track.getGeom()); + builder.add(track.getZ().doubleValue()); + builder.add(track.getKm().doubleValue()); + + collection.add(builder.buildFeature(String.valueOf(i++))); + } + + File qpsShape = new File(dir, WSPLGEN_QPS); + + boolean q = GeometryUtils.writeShapefile( + qpsShape, + GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs), + collection); + + if (q) { + job.setPro(qpsShape.getAbsolutePath()); + } + } + + + protected void setDgm(FLYSArtifact artifact, WSPLGENJob job) { + String dgm_id = artifact.getDataAsString("dgm"); + + int id = -1; + try { + id = Integer.parseInt(dgm_id); + } + catch (NumberFormatException nfe) { /* do nothing */ } + + DGM dgm = DGM.getDGM(id); + + if (dgm == null) { + logger.warn("Could not find specified DGM."); + + return; + } + + job.setDgm(dgm.getPath()); + } + + + protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + Floodplain plain = Floodplain.getFloodplain(river); + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "talaue", srs, MultiPolygon.class); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + builder.add(plain.getGeom()); + + FeatureCollection collection = FeatureCollections.newCollection(); + collection.add(builder.buildFeature("0")); + + File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN); + + boolean t = GeometryUtils.writeShapefile( + talaueShape, + GeometryUtils.buildFeatureType("talaue", srs, MultiPolygon.class), + collection); + + if (t) { + job.setArea(talaueShape.getAbsolutePath()); + } + } + + + protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) { + job.setOutFile(WSPLGEN_OUTPUT_FILE); + } + + + protected WQKms getWQKms(FLYSArtifact flys, CallContext cc) { + String wspString = flys.getDataAsString(WSP_ARTIFACT); + String[] parts = wspString.split(";"); + + String otherArtifact = parts[0]; + + int idx = -1; + try { + idx = Integer.parseInt(parts[2]); + } + catch (NumberFormatException nfe) { /* do nothing */ } + + FLYSArtifact src = otherArtifact != null + ? FLYSUtils.getArtifact(otherArtifact, cc) + : flys; + + logger.debug("Use waterlevel provided by Artifact: " + src.identifier()); + + CalculationResult rawData = (CalculationResult) src.compute( + cc, + null, + WINFO_WSP_STATE_ID, + ComputeType.ADVANCE, + false); + + WQKms[] wqkms = (WQKms[]) rawData.getData(); + + return wqkms == null || idx == -1 || idx >= wqkms.length + ? null + : wqkms[idx]; + } + + + protected void setWsp( + FLYSArtifact artifact, + CallContext context, + File dir, + WSPLGENJob job) + { + logger.debug("FloodMapState.setWsp"); + + WQKms data = getWQKms(artifact, context); + + if (data == null) { + logger.warn("No WST data found!"); + return; + } + + WstWriter writer = new WstWriter(1); + + // TODO REMOVE job.setWspTag(...) This is only used until the user is + // able to select the WSP column himself! + boolean writeWspTag = true; + + double[] buf = new double[4]; + logger.debug("Add WST column: " + data.getName()); + writer.addColumn(data.getName()); + + if (writeWspTag) { + job.setWspTag(data.getName()); + writeWspTag = false; + } + + for (int i = 0, num = data.size(); i < num; i++) { + data.get(i, buf); + writer.add(buf); + } + + FileOutputStream fout = null; + + try { + File wspFile = new File(dir, WSPLGEN_WSP_FILE); + fout = new FileOutputStream(wspFile); + + writer.write(fout); + + job.setWsp(wspFile.getAbsolutePath()); + } + catch (FileNotFoundException fnfe) { + logger.warn("Error while writing wsp file: " + fnfe.getMessage()); + } + finally { + if (fout != null) { + try { + fout.close(); + } + catch (IOException ioe) { /* do nothing */ } + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodplainChoice.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,63 @@ +package de.intevation.flys.artifacts.states; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; + +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FloodplainChoice extends DefaultState { + + public static final String OPTION = "floodplain.option"; + + + @Override + protected String getUIProvider() { + return "boolean_panel"; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + CallMeta meta = context.getMeta(); + + Element option = createItem( + cr, + new String[] { Resources.getMsg(meta, OPTION, OPTION), "true" }); + + return new Element[] { option }; + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,146 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import gnu.trove.TDoubleArrayList; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.WINFOArtifact; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LocationDistanceSelect +extends ComputationRangeState +{ + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(LocationDistanceSelect.class); + + + /** The name of the 'mode' field. */ + public static final String MODE = "ld_mode"; + + /** The name of the 'locations' field.*/ + public static final String LOCATIONS = "ld_locations"; + + + /** + * The default constructor that initializes an empty State object. + */ + public LocationDistanceSelect() { + } + + + @Override + protected String getUIProvider() { + return "location_distance_panel"; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("LocationDistanceSelect.validate"); + + WINFOArtifact flys = (WINFOArtifact) artifact; + + if (flys.isRange()) { + return super.validate(flys); + } + else { + return validateLocations(flys); + } + } + + + protected boolean validateLocations(WINFOArtifact flys) + throws IllegalArgumentException + { + StateData dValues = getData(flys, LOCATIONS); + String values = dValues != null ? (String)dValues.getValue() : null; + + if (values == null || values.length() == 0) { + throw new IllegalArgumentException("error_empty_state"); + } + + double[] absMinMax = getMinMax(flys); + double[] relMinMax = getMinMaxFromString(values); + + if (relMinMax[0] < absMinMax[0] || relMinMax[0] > absMinMax[1]) { + throw new IllegalArgumentException("error_feed_from_out_of_range"); + } + + if (relMinMax[1] > absMinMax[1] || relMinMax[1] < absMinMax[0]) { + throw new IllegalArgumentException("error_feed_to_out_of_range"); + } + + return true; + } + + + /** + * Extracts the min/max values from String <i>s</i>. An + * IllegalArgumentException is thrown if there is a value that throws a + * NumberFormatException. + * + * @param s String that contains whitespace separated double values. + * + * @return a 2dmin array [min,max]. + */ + public static double[] getMinMaxFromString(String s) + throws IllegalArgumentException + { + String[] values = s.split(" "); + + double[] minmax = new double[] { + Double.MAX_VALUE, + -Double.MAX_VALUE }; + + for (String v: values) { + try { + double value = Double.valueOf(v); + + minmax[0] = minmax[0] < value ? minmax[0] : value; + minmax[1] = minmax[1] > value ? minmax[1] : value; + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "error_invalid_double_value"); + } + } + + return minmax; + } + + + public static double[] getLocations(WINFOArtifact flys) { + StateData data = flys.getData("ld_locations"); + String value = data != null ? (String) data.getValue() : null; + + if (value == null || value.length() == 0) { + logger.warn("No location data given."); + return null; + } + + String[] splitted = value.split(" "); + TDoubleArrayList values = new TDoubleArrayList(); + + for (String split: splitted) { + try { + values.add(Double.valueOf(split)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return values.toNativeArray(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,134 @@ +package de.intevation.flys.artifacts.states; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +/** + * This state is used to realize the input of multiple locations as string. The + * string should be a whitespace separated list of double values where each + * double value represents a location. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LocationSelect extends LocationDistanceSelect { + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(LocationSelect.class); + + + /** The name of the StateData object that stores the location string.*/ + public static final String FIELD_LOCATIONS = "ld_locations"; + + + public LocationSelect() { + } + + + @Override + protected String getUIProvider() { + return "location_panel"; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + double[] minmax = getMinMax(artifact); + + double minVal = Double.MIN_VALUE; + double maxVal = Double.MAX_VALUE; + + if (minmax != null) { + minVal = minmax[0]; + maxVal = minmax[1]; + } + else { + logger.warn("Could not read min/max distance values!"); + } + + if (name.equals(FIELD_LOCATIONS)) { + Element min = createItem( + cr, + new String[] {"min", new Double(minVal).toString()}); + + Element max = createItem( + cr, + new String[] {"max", new Double(maxVal).toString()}); + + return new Element[] { min, max }; + } + + return null; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("LocationSelect.validate"); + + FLYSArtifact flys = (FLYSArtifact) artifact; + StateData data = getData(flys, FIELD_LOCATIONS); + + String locationStr = data != null ? (String) data.getValue() : null; + + if (locationStr == null || locationStr.length() == 0) { + logger.error("No locations given."); + throw new IllegalArgumentException("error_empty_state"); + } + + double[] minmax = getMinMax(artifact); + double[] mm = extractLocations(locationStr); + + logger.debug("Inserted min location: " + mm[0]); + logger.debug("Inserted max location: " + mm[mm.length-1]); + + return validateBounds(minmax[0], minmax[1], mm[0], mm[mm.length-1], 0d); + } + + + /** + * This method takes a string that consist of whitespace separated double + * values and returns the double values as array. + * + * @param locationStr The locations inserted in this state. + * + * @return the locations as array. + */ + protected double[] extractLocations(String locationStr) { + String[] tmp = locationStr.split(" "); + TDoubleArrayList locations = new TDoubleArrayList(); + + for (String l: tmp) { + try { + locations.add(Double.parseDouble(l)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + locations.sort(); + + return locations.toNativeArray(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/OutputState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,43 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.FacetTypes; + + +public class OutputState extends DefaultState implements FacetTypes { + + private static final Logger logger = Logger.getLogger(OutputState.class); + + + @Override + public Element describeStatic( + Artifact artifact, + Document document, + Node root, + CallContext context, + String uuid) + { + return null; + } + + + @Override + public Element describe( + Artifact artifact, + Document document, + Node root, + CallContext context, + String uuid) + { + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ProfileDistanceSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,13 @@ +package de.intevation.flys.artifacts.states; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ProfileDistanceSelect extends DefaultState { + + @Override + protected String getUIProvider() { + return "auto_integer"; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RangeState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,106 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class RangeState extends DefaultState { + + /** The logger that is used in this class. */ + private Logger logger = Logger.getLogger(RangeState.class); + + + public RangeState() { + } + + + protected abstract String getLowerField(); + protected abstract String getUpperField(); + protected abstract String getStepField(); + protected abstract double[] getMinMax(Artifact artifact); + + + protected boolean validateBounds( + double fromValid, double toValid, + double from, double to) + throws IllegalArgumentException + { + if (from < fromValid) { + logger.error( + "Invalid 'from'. " + from + " is smaller than " + fromValid); + throw new IllegalArgumentException("error_feed_from_out_of_range"); + } + else if (to > toValid) { + logger.error( + "Invalid 'to'. " + to + " is bigger than " + toValid); + throw new IllegalArgumentException("error_feed_to_out_of_range"); + } + + return true; + } + + + /** + * Validates a given range with a given valid range. + * + * @param fromValid Valid lower value of the range. + * @param toValid Valid upper value of the range. + * @param from The lower value. + * @param to The upper value. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateBounds( + double fromValid, double toValid, + double from, double to, double step) + throws IllegalArgumentException + { + logger.debug("RangeState.validateRange"); + + // XXX The step width is not validated at the moment! + return validateBounds(fromValid, toValid, from, to); + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData dFrom = getData(flys, getLowerField()); + StateData dTo = getData(flys, getUpperField()); + StateData dStep = getData(flys, getStepField()); + + String fromStr = dFrom != null ? (String) dFrom.getValue() : null; + String toStr = dTo != null ? (String) dTo.getValue() : null; + String stepStr = dStep != null ? (String) dStep.getValue() : null; + + if (fromStr == null || toStr == null || stepStr == null) { + throw new IllegalArgumentException("error_empty_state"); + } + + try { + double from = Double.parseDouble(fromStr); + double to = Double.parseDouble(toStr); + double step = Double.parseDouble(stepStr); + + double[] minmax = getMinMax(flys); + + return validateBounds(minmax[0], minmax[1], from, to, step); + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException("error_invalid_double_value"); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverAxisState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,79 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.WMSDBLayerFacet; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.GeometryUtils; + + +public class RiverAxisState extends OutputState { + + public static final String I18N_DESCRIPTION = "floodmap.riveraxis"; + + + private static final Logger logger = Logger.getLogger(RiverAxisState.class); + + + @Override + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets + ) { + logger.debug("RiverAxisState.computeInit()"); + + FLYSContext flysContext = null; + + if (context instanceof FLYSContext) { + flysContext = (FLYSContext) context; + } + else { + flysContext = (FLYSContext) ((CallContext) context).globalContext(); + } + + String river = artifact.getDataAsString("river"); + + if(river == null || river.length() == 0) { + logger.warn("No river found in the current parameterization."); + return null; + } + + String url = FLYSUtils.getUserWMSUrl(artifact.identifier()); + + WMSDBLayerFacet facet = new WMSDBLayerFacet( + 0, + FLOODMAP_RIVERAXIS, + Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION), + ComputeType.INIT, + getID(), hash, + url); + + String name = artifact.identifier() + "-" + FLOODMAP_RIVERAXIS; + + facet.addLayer(name); + facet.setExtent(GeometryUtils.getRiverBoundary(river)); + facet.setSrid(FLYSUtils.getRiverSrid(artifact)); + facet.setData("geom FROM river_axes USING UNIQUE id USING SRID 31466"); + facet.setFilter("river_id=1"); + facet.setGeometryType("LINE"); + + facets.add(facet); + + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,163 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class RiverSelect extends DefaultState { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(RiverSelect.class); + + + /** Error message that is thrown if no river was found based on a given + * name.*/ + public static final String ERROR_NO_SUCH_RIVER = + "error_feed_no_such_river"; + + /** Error message that is thrown if no river was found based on a given + * name.*/ + public static final String ERROR_NO_RIVER_SELECTED = + "error_feed_no_river_selected"; + + + /** + * The default constructor that initializes an empty State object. + */ + public RiverSelect() { + } + + + /** + * Initialize the state based on the state node in the configuration. + * + * @param config The state configuration node. + */ + public void setup(Node config) { + super.setup(config); + } + + + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", + new String[] { "uiprovider" }, + new String[] { "select_with_map" }); + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + select.appendChild(label); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + getID(), + getID())); + + return select; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + List<River> rivers = RiverFactory.getRivers(); + Element[] items = new Element[rivers.size()]; + + int idx = 0; + for (River river: rivers) { + items[idx++] = createRiverItem(cr, river); + } + + return items; + } + + + /** + * This method creates a node that represents a river item. This node + * contains the label and the value that describe the river. + * + * @param cr The ElementCreator. + * @param river The river. + * + * @return the element that contains the information about the river. + */ + protected Element createRiverItem(XMLUtils.ElementCreator cr, River river) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + label.setTextContent(river.getName()); + value.setTextContent(river.getName()); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("RiverSelect.validate"); + + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData dRiver = getData(flys, "river"); + + if (dRiver == null || dRiver.getValue() == null) { + throw new IllegalArgumentException(ERROR_NO_RIVER_SELECTED); + } + + River river = RiverFactory.getRiver((String) dRiver.getValue()); + + if (river == null) { + throw new IllegalArgumentException(ERROR_NO_SUCH_RIVER); + } + + return true; + } + + + @Override + protected String getUIProvider() { + return "river_panel"; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ScenarioSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,120 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.ProtocolUtils; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.resources.Resources; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ScenarioSelect extends DefaultState { + + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(ScenarioSelect.class); + + + public static final String FIELD_MODE = "scenario"; + public static final String FIELD_BARRIERS = "uesk.barriers"; + + public static final String SCENARIO_CURRENT = "scenario.current"; + public static final String SCENARIO_POTENTIEL = "scenario.potentiel"; + public static final String SCENARIO_SCENRAIO = "scenario.scenario"; + + public static final String[] SCENARIOS = { + SCENARIO_CURRENT, + SCENARIO_POTENTIEL, + SCENARIO_SCENRAIO }; + + + + @Override + protected String getUIProvider() { + return "map_digitize"; + } + + + @Override + protected void appendStaticData( + FLYSArtifact flys, + CallContext cc, + ElementCreator creator, + Element ui, + String name + ) { + if (name != null && name.equals(FIELD_BARRIERS)) { + return; + } + else { + super.appendStaticData(flys, cc, creator, ui, name); + } + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + CallMeta meta = context.getMeta(); + + if (name.equals(FIELD_MODE)) { + Element[] scenarios = new Element[SCENARIOS.length]; + + int i = 0; + + for (String scenario: SCENARIOS) { + scenarios[i++] = createItem( + cr, new String[] { + Resources.getMsg(meta, scenario, scenario), + scenario + }); + } + + return scenarios; + } + else { + FLYSArtifact flys = (FLYSArtifact) artifact; + String data = flys.getDataAsString(name); + + return new Element[] { createItem( + cr, + new String[] { + Resources.getMsg(meta, name, name), + data + } + )}; + } + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StateFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,121 @@ +package de.intevation.flys.artifacts.states; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class StateFactory { + + /** The logger used in this class */ + private static Logger logger = Logger.getLogger(StateFactory.class); + + /** The XPath to the classname of the state */ + public static final String XPATH_STATE = "@state"; + + /** The XPath to the data items of the state relative to the state node. */ + public static final String XPATH_DATA = "data"; + + /** The XPath to the data name relative to the data node.*/ + public static final String XPATH_DATA_NAME = "@name"; + + /** The XPath to the data type relative to the data node.*/ + public static final String XPATH_DATA_TYPE = "@type"; + + /** The XPath to the data description relative to the data node.*/ + public static final String XPATH_DATA_DESCRIPTION = "@description"; + + + /** + * Creates a new State based on the configured class provided by + * <code>stateConf</code>. + * + * @param stateConf The configuration of the state. + * + * @return a State. + */ + public static State createState(Node stateConf) { + String clazz = (String) XMLUtils.xpath( + stateConf, XPATH_STATE, XPathConstants.STRING); + + State state = null; + + try { + logger.debug("Create a new State for class: " + clazz); + state = (State) Class.forName(clazz).newInstance(); + state.setup(stateConf); + + initializeStateData(state, stateConf); + } + catch (InstantiationException ie) { + logger.error(ie, ie); + } + catch (IllegalAccessException iae) { + logger.error(iae, iae); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe, cnfe); + } + + return state; + } + + + /** + * This method extracts the configured input data of a state and adds new + * StateData objects to the State. + * + * @param state The state. + * @param stateConf The state configuration node. + */ + protected static void initializeStateData(State state, Node stateConf) { + NodeList dataList = (NodeList) XMLUtils.xpath( + stateConf, XPATH_DATA, XPathConstants.NODESET); + + if (dataList == null || dataList.getLength() == 0) { + logger.debug("The state has no input data configured."); + + return; + } + + int items = dataList.getLength(); + + logger.debug("The state has " + items + " data items configured."); + + for (int i = 0; i < items; i++) { + Node data = dataList.item(i); + + String name = (String) XMLUtils.xpath( + data, XPATH_DATA_NAME, XPathConstants.STRING); + String type = (String) XMLUtils.xpath( + data, XPATH_DATA_TYPE, XPathConstants.STRING); + String desc = (String) XMLUtils.xpath( + data, XPATH_DATA_DESCRIPTION, XPathConstants.STRING); + + if (name == null || name.equals("")) { + logger.warn("No name for data item at pos " + i + " found."); + continue; + } + + if (type == null || type.equals("")) { + logger.warn("No type for data item at pos " + i + " found."); + logger.warn("Default type 'string' used."); + type = "string"; + } + + state.addData(name, new DefaultStateData(name, type, desc)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,32 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.FacetTypes; + +/** + * Yet, a non-abstract DefaultState. + */ +public class StaticState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static final Logger logger = Logger.getLogger(StaticState.class); + + + /** + * Trivial constructor, sets id and description. + * @param id String used for both id and description. + */ + public StaticState(String id) { + this(id, id); + } + + + public StaticState(String id, String description) { + super(); + setID(id); + setDescription(description); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WDifferencesState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,178 @@ +package de.intevation.flys.artifacts.states; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.Artifact; +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.StaticWKmsArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.math.WKmsOperation; + +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.DataFacet; +import de.intevation.flys.artifacts.model.DifferenceCurveFacet; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.StringUtil; + + +public class WDifferencesState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(WDifferencesState.class); + + + public WDifferencesState() { + } + + + /** Specify to display nothing (this is kind of a "final" state). */ + @Override + protected String getUIProvider() { + return "noinput"; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData data = flys.getData("diffids"); + + if (data == null) { + throw new IllegalArgumentException("diffids is empty"); + } + + // TODO: Also validate format. + + return true; + } + + + protected WKms getWKms(String mingle, CallContext context) { + String[] def = mingle.split(";"); + String uuid = def[0]; + String name = def[1]; + int idx = Integer.parseInt(def[2]); + + if (name.startsWith("staticwkms")) { + StaticWKmsArtifact staticWKms = + (StaticWKmsArtifact) FLYSUtils.getArtifact( + uuid, + context); + logger.debug("WDifferencesState obtain data from StaticWKms"); + WKms wkms = staticWKms.getWKms(idx); + if (wkms == null) + logger.error("No WKms from artifact."); + return wkms; + } + + WINFOArtifact flys = (WINFOArtifact) FLYSUtils.getArtifact( + uuid, + context); + + if (flys == null) { + logger.warn("One of the artifacts (1) for diff calculation could not be loaded"); + return null; + } + else{ + WQKms[] wqkms = (WQKms[]) flys.getWaterlevelData(). + getData(); + if (wqkms == null) + logger.warn("not waterlevels in artifact"); + else if (wqkms.length < idx) + logger.warn("not enough waterlevels in artifact"); + return wqkms[idx]; + } + } + + + /** + * Return CalculationResult with Array of WKms that are difference of + * Waterlevels. Add respective facets (DifferencesCurveFacet, DataFacet). + */ + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + WINFOArtifact winfo = (WINFOArtifact) artifact; + String id = getID(); + + // Load the Artifacts/facets that we want to subtract and display. + // Expected format is: + // [42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;0]#[1231f2-....] + String diffids = winfo.getDataAsString("diffids"); + logger.debug("WDifferencesState has: " + diffids); + String datas[] = diffids.split("#"); + + // Validate the Data-Strings. + for (String s: datas) { + if (!WaterlevelSelectState.isValueValid(winfo.getDataAsString("diffids"))) { + // TODO: escalate. + } + } + + if (datas.length < 2) { + // TODO crash with style + ; + } + + List<WKms> wkmss = new ArrayList<WKms>(); + + for(int i = 0; i < datas.length; i+=2) { + // e.g.: + // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1 + WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]), + context); + WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]), + context); + + String facetName = "diff ()"; + + if (minuendWKms != null && subtrahendWKms != null) { + facetName = StringUtil.wWrap(minuendWKms.getName()) + + " - " + StringUtil.wWrap(subtrahendWKms.getName()); + WKms wkms = WKmsOperation.SUBTRACTION.operate(minuendWKms, + subtrahendWKms); + wkms.setName(facetName); + wkmss.add(wkms); + logger.debug("WKMSSubtraction happened"); + } + + if (facets != null) { + facets.add(new DifferenceCurveFacet(i/2, W_DIFFERENCES, facetName, + ComputeType.ADVANCE, id, hash)); + facets.add(new DataFacet(CSV, "CSV data")); + logger.debug("Adding facets in WDifferencesState."); + } + else { + logger.debug("Not adding facets in WDifferencesState."); + } + } + + // TODO Evaluate whether null is okay as reports. + WKms[] diffs = wkmss.toArray(new WKms[wkmss.size()]); + CalculationResult result = new CalculationResult(diffs, null); + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WMSBackgroundState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,120 @@ +package de.intevation.flys.artifacts.states; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.WMSLayerFacet; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; + + +public class WMSBackgroundState extends OutputState { + + public static final String I18N_DESCRIPTION = "floodmap.wmsbackground"; + + public static final String XPATH_SRID = + "/artifact-database/floodmap/river[@name=$name]/srid/@value"; + + public static final String XPATH_WMS_URL = + "/artifact-database/floodmap/river[@name=$name]/background-wms/@url"; + + public static final String XPATH_WMS_LAYER = + "/artifact-database/floodmap/river[@name=$name]/background-wms/@layers"; + + + protected String url; + protected String layer; + protected String srid; + + + private static final Logger logger = Logger.getLogger(WMSBackgroundState.class); + + + @Override + public void setup(Node config) { + super.setup(config); + + logger.debug("WMSBackgroundState.setup()"); + } + + + @Override + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets + ) { + logger.debug("WMSBackgroundState.computeInit()"); + + if (url == null || layer == null) { + Document cfg = Config.getConfig(); + + String river = artifact.getDataAsString("river"); + + Map<String, String> variables = new HashMap<String, String>(); + variables.put("name", river); + + srid = (String) XMLUtils.xpath( + cfg, + XPATH_SRID, + XPathConstants.STRING, + null, + variables); + + url = (String) XMLUtils.xpath( + cfg, + XPATH_WMS_URL, + XPathConstants.STRING, + null, + variables); + + layer = (String) XMLUtils.xpath( + cfg, + XPATH_WMS_LAYER, + XPathConstants.STRING, + null, + variables); + } + + if (url == null || layer == null) { + logger.warn("No background layers currently configured:"); + logger.warn("... add config for WMS url: " + XPATH_WMS_URL); + logger.warn("... add config for WMS layer: " + XPATH_WMS_LAYER); + return null; + } + + WMSLayerFacet facet = new WMSLayerFacet( + 0, + FLOODMAP_WMSBACKGROUND, + Resources.getMsg(meta, I18N_DESCRIPTION, I18N_DESCRIPTION), + ComputeType.INIT, + getID(), hash, + url); + + facet.addLayer(layer); + facet.setSrid(srid); + + facets.add(facet); + + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,456 @@ +package de.intevation.flys.artifacts.states; + +import java.util.ArrayList; +import java.util.List; +import java.util.Comparator; +import java.util.Collections; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.Range; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.RangeWithValues; +import de.intevation.flys.artifacts.model.WstFactory; +import de.intevation.flys.utils.FLYSUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQAdapted extends DefaultState { + + /** The logger used in this state.*/ + private static Logger logger = Logger.getLogger(WQAdapted.class); + + + public static final String FIELD_WQ_MODE = "wq_mode"; + + public static final String FIELD_WQ_VALUES = "wq_values"; + + public static final class GaugeOrder implements Comparator<Gauge> { + private int order; + + public GaugeOrder(boolean up) { + order = up ? 1 : -1; + } + + public int compare(Gauge a, Gauge b) { + return order * a.getRange().getA().compareTo(b.getRange().getA()); + } + } // class GaugeOrder + + public static final GaugeOrder GAUGE_UP = new GaugeOrder(true); + public static final GaugeOrder GAUGE_DOWN = new GaugeOrder(false); + + public WQAdapted() { + } + + /** + * This method creates one element for each gauge of the selected river that + * is intersected by the given kilometer range. Each element is a tuple of + * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower + * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper + * kilometer range. + * + * @param cr The ElementCreator. + * @param artifact The FLYS artifact. + * @param name The name of the data item. + * @param context The CallContext. + * + * @return a list of elements that consist of tuples of the intersected + * gauges of the selected river. + */ + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createItems"); + + if (name != null && name.equals(FIELD_WQ_MODE)) { + return createModeItems(cr, artifact, name, context); + } + else if (name != null && name.equals(FIELD_WQ_VALUES)) { + return createValueItems(cr, artifact, name, context); + } + else { + logger.warn("Unknown data object: " + name); + return null; + } + } + + + protected Element[] createModeItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createModeItems"); + + Element w = createItem(cr, new String[] { "w", "W" }); + Element q = createItem(cr, new String[] { "q", "Q" }); + + return new Element[] { w, q }; + } + + + protected Element[] createValueItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("WQAdapted.createValueItems"); + + WINFOArtifact flysArtifact = (WINFOArtifact) artifact; + + double[] dist = FLYSUtils.getKmRange(flysArtifact); + River river = FLYSUtils.getRiver(flysArtifact); + Wst wst = WstFactory.getWst(river); + List<Gauge> gauges = flysArtifact.getGauges(); + + int num = gauges != null ? gauges.size() : 0; + + if (num == 0) { + logger.warn("Selected distance matches no gauges."); + return null; + } + + Element[] elements = new Element[num]; + + double rangeFrom = dist[0]; + double rangeTo = dist[1]; + + int idx = 0; + + if (rangeFrom < rangeTo) { + Collections.sort(gauges, GAUGE_UP); + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + double from = lower < rangeFrom ? rangeFrom : lower; + double to = upper > rangeTo ? rangeTo : upper; + + double[] mmQ = determineMinMaxQ(gauge, wst); + double[] mmW = gauge.determineMinMaxW(); + + elements[idx++] = createItem( + cr, new String[] { from + ";" + to, ""}, mmQ, mmW); + } + } + else { + Collections.sort(gauges, GAUGE_DOWN); + rangeFrom = dist[1]; + rangeTo = dist[0]; + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + double from = lower < rangeFrom ? rangeFrom : lower; + double to = upper > rangeTo ? rangeTo : upper; + + double[] mmQ = determineMinMaxQ(gauge, wst); + double[] mmW = gauge.determineMinMaxW(); + + elements[idx++] = createItem( + cr, new String[] { to + ";" + from, ""}, mmQ, mmW); + } + } + + return elements; + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + return createItem(cr, obj, null, null); + } + + + protected Element createItem( + XMLUtils.ElementCreator cr, + Object obj, + double[] q, + double[] w) + { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + if (q != null) { + Element qRange = createRangeElement(cr, q, "Q"); + item.appendChild(qRange); + } + + if (w != null) { + Element wRange = createRangeElement(cr, w, "W"); + item.appendChild(wRange); + } + + return item; + } + + + protected Element createRangeElement( + XMLUtils.ElementCreator cr, + double[] mm, + String type) + { + Element range = ProtocolUtils.createArtNode( + cr, "range", + new String[] {"type"}, + new String[] {type}); + + Element min = ProtocolUtils.createArtNode(cr, "min", null, null); + min.setTextContent(String.valueOf(mm[0])); + + Element max = ProtocolUtils.createArtNode(cr, "max", null, null); + max.setTextContent(String.valueOf(mm[1])); + + range.appendChild(min); + range.appendChild(max); + + return range; + } + + + /** + * Determines the min and max Q value for the given gauge. If no min and + * max values could be determined, this method will return + * [Double.MIN_VALUE, Double.MAX_VALUE]. + * + * @param gauge + * @param wst + * + * @return the min and max Q values for the given gauge. + */ + protected double[] determineMinMaxQ(Gauge gauge, Wst wst) { + logger.debug("WQAdapted.determineMinMaxQ"); + + double[] minmaxQ = gauge != null + ? wst.determineMinMaxQ(gauge.getRange()) + : null; + + double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE; + double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE; + + return new double[] { minQ, maxQ }; + } + + + @Override + protected String getUIProvider() { + return "wq_panel_adapted"; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validate"); + + WINFOArtifact flys = (WINFOArtifact) artifact; + StateData data = getData(flys, FIELD_WQ_MODE); + + String mode = data != null ? (String) data.getValue() : null; + + if (mode != null && mode.equals("W")) { + return validateW(artifact); + } + else if (mode != null && mode.equals("Q")) { + return validateQ(artifact); + } + else { + throw new IllegalArgumentException("error_feed_no_wq_mode_selected"); + } + } + + + protected boolean validateW(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validateW"); + WINFOArtifact flys = (WINFOArtifact) artifact; + + RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values")); + + if (rwvs == null) { + throw new IllegalArgumentException("error_missing_wq_data"); + } + + List<Gauge> gauges = ((WINFOArtifact) artifact).getGauges(); + + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + for (RangeWithValues rwv: rwvs) { + if (lower <= rwv.getLower() && upper >= rwv.getUpper()) { + compareWsWithGauge(gauge, rwv.getValues()); + } + } + } + + return true; + } + + + protected boolean validateQ(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQAdapted.validateQ"); + WINFOArtifact flys = (WINFOArtifact) artifact; + + RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values")); + + if (rwvs == null) { + throw new IllegalArgumentException("error_missing_wq_data"); + } + + List<Gauge> gauges = flys.getGauges(); + River river = FLYSUtils.getRiver(flys); + Wst wst = WstFactory.getWst(river); + + for (Gauge gauge: gauges) { + Range range = gauge.getRange(); + double lower = range.getA().doubleValue(); + double upper = range.getB().doubleValue(); + + for (RangeWithValues rwv: rwvs) { + if (lower <= rwv.getLower() && upper >= rwv.getUpper()) { + compareQsWithGauge(wst, gauge, rwv.getValues()); + } + } + } + + return true; + } + + + protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs) + throws IllegalArgumentException + { + double[] minmax = gauge != null + ? wst.determineMinMaxQ(gauge.getRange()) + : null; + + if (minmax == null) { + logger.warn("Could not determine min/max Q of gauge."); + return true; + } + + if (logger.isDebugEnabled()) { + logger.debug("Validate Qs with:"); + logger.debug("-- Gauge: " + gauge.getName()); + logger.debug("-- Gauge min: " + minmax[0]); + logger.debug("-- Gauge max: " + minmax[1]); + } + + for (double q: qs) { + if (q < minmax[0] || q > minmax[1]) { + throw new IllegalArgumentException( + "error_feed_q_values_invalid"); + } + } + + return true; + } + + + protected boolean compareWsWithGauge(Gauge gauge, double[] ws) + throws IllegalArgumentException + { + double[] minmax = gauge != null + ? gauge.determineMinMaxW() + : null; + + if (minmax == null) { + logger.warn("Could not determine min/max W of gauge."); + return true; + } + + if (logger.isDebugEnabled()) { + logger.debug("Validate Ws with:"); + logger.debug("-- Gauge: " + gauge.getName()); + logger.debug("-- Gauge min: " + minmax[0]); + logger.debug("-- Gauge max: " + minmax[1]); + } + + for (double w: ws) { + if (w < minmax[0] || w > minmax[1]) { + throw new IllegalArgumentException( + "error_feed_w_values_invalid"); + } + } + + return true; + } + + + protected RangeWithValues[] extractInput(StateData data) { + if (data == null) { + return null; + } + + String dataString = (String) data.getValue(); + String[] ranges = dataString.split(":"); + + List<RangeWithValues> rwv = new ArrayList<RangeWithValues>(); + + for (String range: ranges) { + String[] parts = range.split(";"); + + double lower = Double.parseDouble(parts[0]); + double upper = Double.parseDouble(parts[1]); + + String[] values = parts[2].split(","); + + int num = values.length; + double[] res = new double[num]; + + for (int i = 0; i < num; i++) { + try { + res[i] = Double.parseDouble(values[i]); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + rwv.add(new RangeWithValues(lower, upper, res)); + } + + return (RangeWithValues[]) rwv.toArray(new RangeWithValues[rwv.size()]); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQSelect.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,476 @@ +package de.intevation.flys.artifacts.states; + +import java.text.NumberFormat; + +import gnu.trove.TDoubleArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; +import de.intevation.flys.model.Wst; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.WstFactory; +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.utils.FLYSUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WQSelect extends DefaultState { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(WQSelect.class); + + + /** The default step width for Qs. */ + public static final String DEFAULT_STEP_Q = "50"; + + /** The default step width for Qs. */ + public static final String DEFAULT_STEP_W = "30"; + + /** The name of the 'mode' field. */ + public static final String WQ_MODE = "wq_mode"; + + /** Them name fo the 'free' field. */ + public static final String WQ_FREE = "wq_free"; + + /** The name of the 'selection' field. */ + public static final String WQ_SELECTION = "wq_selection"; + + /** The name of the 'from' field. */ + public static final String WQ_FROM = "wq_from"; + + /** The name of the 'to' field. */ + public static final String WQ_TO = "wq_to"; + + /** The name of the 'step' field. */ + public static final String WQ_STEP = "wq_step"; + + /** The name of the 'single' field. */ + public static final String WQ_SINGLE = "wq_single"; + + + /** + * The default constructor that initializes an empty State object. + */ + public WQSelect() { + } + + + @Override + protected Element createStaticData( + FLYSArtifact flys, + ElementCreator creator, + CallContext cc, + String name, + String value, + String type + ) { + if (!name.equals(WQ_SINGLE)) { + return super.createStaticData(flys, creator, cc, name, value, type); + } + + String mode = flys.getDataAsString(WQ_MODE); + String free = flys.getDataAsString(WQ_FREE); + if (mode == null || mode.equals("W") || Boolean.valueOf(free)) { + return super.createStaticData(flys, creator, cc, name, value, type); + } + + WINFOArtifact winfo = (WINFOArtifact) flys; + + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", type, true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + creator.addAttr(itemElement, "label", getLabel(winfo, cc, value), true); + + dataElement.appendChild(itemElement); + + return dataElement; + } + + + protected static String getLabel( + WINFOArtifact winfo, + CallContext cc, + String raw + ) { + String[] values = raw.split(" "); + String label = null; + + NumberFormat nf = NumberFormat.getInstance( + Resources.getLocale(cc.getMeta())); + + for (String value: values) { + try { + double v = Double.valueOf(value.trim()); + + String tmp = nf.format(v); + String mv = FLYSUtils.getNamedMainValue(winfo.getGauge(),v); + + if (mv != null && mv.length() > 0) { + String add = mv + ": " + tmp; + label = label != null ? label + ", " + add : add; + } + else { + label = label != null ? label + ", " + tmp : tmp; + } + } + catch (NumberFormatException nfe) { + // do nothing here + } + } + + return label; + } + + + @Override + protected Element createData( + XMLUtils.ElementCreator cr, + Artifact artifact, + StateData data, + CallContext context) + { + Element select = ProtocolUtils.createArtNode( + cr, "select", null, null); + + cr.addAttr(select, "name", data.getName(), true); + + Element label = ProtocolUtils.createArtNode( + cr, "label", null, null); + + Element choices = ProtocolUtils.createArtNode( + cr, "choices", null, null); + + label.setTextContent(Resources.getMsg( + context.getMeta(), + data.getName(), + data.getName())); + + select.appendChild(label); + + return select; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + // TODO Insert correct min/max values! + double[] minmaxW = determineMinMaxW(artifact); + double[] minmaxQ = determineMinMaxQ(artifact); + + if (name.equals("wq_from")) { + Element minW = createItem( + cr, new String[] {"minW", new Double(minmaxW[0]).toString()}); + Element minQ = createItem( + cr, new String[] {"minQ", new Double(minmaxQ[0]).toString()}); + return new Element[] { minW, minQ }; + } + else if (name.equals("wq_to")) { + Element maxW = createItem( + cr, new String[] {"maxW", new Double(minmaxW[1]).toString()}); + Element maxQ = createItem( + cr, new String[] {"maxQ", new Double(minmaxQ[1]).toString()}); + return new Element[] { maxW, maxQ }; + } + else { + Element stepW = createItem( + cr, new String[] {"stepW", DEFAULT_STEP_W}); + Element stepQ = createItem( + cr, new String[] {"stepQ", DEFAULT_STEP_Q}); + return new Element[] { stepW, stepQ }; + } + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } + + + @Override + protected String getUIProvider() { + return "wq_panel"; + } + + + /** + * Determines the min and max W value for the current gauge. If no min and + * max values could be determined, this method will return + * [Double.MIN_VALUE, Double.MAX_VALUE]. + * + * @param artifact The FLYSArtifact. + * + * @return the min and max W values for the current gauge. + */ + protected double[] determineMinMaxW(Artifact artifact) { + logger.debug("WQSelect.determineCurrentGauge"); + + Gauge gauge = ((WINFOArtifact) artifact).getGauge(); + double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null; + + double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE; + double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE; + + return new double[] { minW, maxW }; + } + + + /** + * Determines the min and max Q value for the current gauge. If no min and + * max values could be determined, this method will return + * [Double.MIN_VALUE, Double.MAX_VALUE]. + * + * @param artifact The FLYSArtifact. + * + * @return the min and max Q values for the current gauge. + */ + protected double[] determineMinMaxQ(Artifact artifact) { + logger.debug("WQSelect.determineMinMaxQ"); + + WINFOArtifact flysArtifact = (WINFOArtifact) artifact; + + River river = FLYSUtils.getRiver(flysArtifact); + Gauge gauge = flysArtifact.getGauge(); + Wst wst = WstFactory.getWst(river); + + double[] minmaxQ = gauge != null + ? wst.determineMinMaxQ(gauge.getRange()) + : null; + + double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE; + double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE; + + return new double[] { minQ, maxQ }; + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQSelect.validate"); + + WINFOArtifact flys = (WINFOArtifact) artifact; + + StateData data = getData(flys, WQ_SELECTION); + String selectionMode = data != null ? (String) data.getValue() : null; + + if (selectionMode == null || selectionMode.equals("single")) { + return validateSingle(artifact); + } + else { + return validateRange(artifact); + } + } + + + protected boolean validateBounds( + double fromValid, double toValid, + double from, double to, double step) + throws IllegalArgumentException + { + logger.debug("RangeState.validateRange"); + + if (from < fromValid) { + logger.error( + "Invalid 'from'. " + from + " is smaller than " + fromValid); + throw new IllegalArgumentException("error_feed_from_out_of_range"); + } + else if (to > toValid) { + logger.error( + "Invalid 'to'. " + to + " is bigger than " + toValid); + throw new IllegalArgumentException("error_feed_to_out_of_range"); + } + + return true; + } + + + protected boolean validateSingle(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateSingle"); + + WINFOArtifact flys = (WINFOArtifact) artifact; + StateData data = getData(flys, WQ_SINGLE); + + String tmp = data != null ? (String) data.getValue() : null; + + if (tmp == null || tmp.length() == 0) { + throw new IllegalArgumentException("error_empty_state"); + } + + String[] strValues = tmp.split(" "); + TDoubleArrayList all = new TDoubleArrayList(); + + for (String strValue: strValues) { + try { + all.add(Double.parseDouble(strValue)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + all.sort(); + + StateData dMode = getData(flys, WQ_MODE); + String mode = dMode != null ? (String) data.getValue() : null; + + logger.debug("WQ Mode: " + mode); + + double[] minmax = null; + + if (mode != null && mode.trim().toLowerCase().equals("w")) { + minmax = determineMinMaxW(artifact); + } + else { + minmax = determineMinMaxQ(artifact); + } + + double min = all.get(0); + double max = all.get(all.size()-1); + + logger.debug("Inserted min value = " + min); + logger.debug("Inserted max value = " + max); + + return validateBounds(minmax[0], minmax[1], min, max, 0d); + } + + + protected boolean validateRange(Artifact artifact) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateRange"); + WINFOArtifact flys = (WINFOArtifact) artifact; + + StateData data = flys.getData(WQ_MODE); + String mode = data != null ? (String) data.getValue() : null; + logger.debug("WQ Mode: " + mode); + + if (mode == null || mode.length() == 0) { + throw new IllegalArgumentException("error_feed_invalid_wq_mode"); + } + + StateData dFrom = flys.getData(WQ_FROM); + StateData dTo = flys.getData(WQ_TO); + StateData dStep = flys.getData(WQ_STEP); + + String fromStr = dFrom != null ? (String) dFrom.getValue() : null; + String toStr = dTo != null ? (String) dTo.getValue() : null; + String stepStr = dStep != null ? (String) dStep.getValue() : null; + + if (fromStr == null || toStr == null || stepStr == null) { + throw new IllegalArgumentException("error_empty_state"); + } + + try { + double from = Double.parseDouble(fromStr); + double to = Double.parseDouble(toStr); + double step = Double.parseDouble(stepStr); + + if (mode != null && mode.trim().toLowerCase().equals("w")) { + return validateW(artifact, from, to, step); + } + else if (mode != null && mode.trim().toLowerCase().equals("q")) { + return validateQ(artifact, from, to, step); + } + else { + throw new IllegalArgumentException( + "error_feed_invalid_wq_mode"); + } + } + catch (NumberFormatException nfe) { + throw new IllegalArgumentException("error_feed_number_format"); + } + } + + + /** + * Validates the inserted W values. + * + * @param artifact The owner artifact. + * @param from The lower value of the W range. + * @param to The upper value of the W range. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateW( + Artifact artifact, + double from, + double to, + double step) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateW"); + + double[] minmaxW = determineMinMaxW(artifact); + + return validateBounds(minmaxW[0], minmaxW[1], from, to, step); + } + + + /** + * Validates the inserted Q values. + * + * @param artifact The owner artifact. + * @param from The lower value of the Q range. + * @param to The upper value of the Q range. + * @param step The step width. + * + * @return true, if everything was fine, otherwise an exception is thrown. + */ + protected boolean validateQ( + Artifact artifact, + double from, + double to, + double step) + throws IllegalArgumentException + { + logger.debug("WQSelect.validateQ"); + + double[] minmaxQ = determineMinMaxQ(artifact); + + return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelGroundDifferences.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,126 @@ +package de.intevation.flys.artifacts.states; + +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ProtocolUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WaterlevelGroundDifferences extends RangeState { + + public static final String LOWER_FIELD = "diff_from"; + public static final String UPPER_FIELD = "diff_to"; + public static final String DIFF_FIELD = "diff_diff"; + + public static final double DEFAULT_STEP = 0d; + + + private static Logger logger = + Logger.getLogger(WaterlevelGroundDifferences.class); + + + + @Override + protected String getLowerField() { + return LOWER_FIELD; + } + + + @Override + protected String getUpperField() { + return UPPER_FIELD; + } + + + @Override + protected String getStepField() { + return DIFF_FIELD; + } + + + @Override + protected double[] getMinMax(Artifact artifact) { + return new double[] { -Double.MAX_VALUE, Double.MAX_VALUE }; + } + + + @Override + protected String getUIProvider() { + return "waterlevel_ground_panel"; + } + + + protected double getDefaultStep() { + return DEFAULT_STEP; + } + + + @Override + protected Element[] createItems( + XMLUtils.ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + double[] minmax = getMinMax(artifact); + + double minVal = Double.MIN_VALUE; + double maxVal = Double.MAX_VALUE; + + if (minmax != null) { + minVal = minmax[0]; + maxVal = minmax[1]; + } + else { + logger.warn("Could not read min/max distance values!"); + } + + if (name.equals(LOWER_FIELD)) { + Element min = createItem( + cr, + new String[] {"min", new Double(minVal).toString()}); + + return new Element[] { min }; + } + else if (name.equals(UPPER_FIELD)) { + Element max = createItem( + cr, + new String[] {"max", new Double(maxVal).toString()}); + + return new Element[] { max }; + } + else { + Element step = createItem( + cr, + new String[] {"step", String.valueOf(getDefaultStep())}); + return new Element[] { step }; + } + } + + + protected Element createItem(XMLUtils.ElementCreator cr, Object obj) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + + String[] arr = (String[]) obj; + + label.setTextContent(arr[0]); + value.setTextContent(arr[1]); + + item.appendChild(label); + item.appendChild(value); + + return item; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelInfoState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,151 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.ReportFacet; +import de.intevation.flys.artifacts.model.WaterlevelFacet; +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.artifacts.model.DataFacet; +import de.intevation.flys.artifacts.model.CrossSectionFacet; +import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet; +import de.intevation.flys.artifacts.model.CalculationResult; + + +public class WaterlevelInfoState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(WaterlevelInfoState.class); + + + @Override + protected String getUIProvider() { + return "noinput"; + } + + + public Object computeInit( + FLYSArtifact artifact, + String hash, + Object context, + CallMeta meta, + List<Facet> facets + ) { + return compute((WINFOArtifact) artifact, hash, facets, null); + } + + + protected Object compute( + WINFOArtifact winfo, + String hash, + List<Facet> facets, + Object old + ) { + logger.debug("WaterlevelInfoState.compute"); + String id = getID(); + + CalculationResult res = old instanceof CalculationResult + ? (CalculationResult)old + : winfo.getWaterlevelData(); + + if (facets == null) { + return res; + } + + WQKms [] wqkms = (WQKms [])res.getData(); + + for (int i = 0; i < wqkms.length; i++) { + String nameW = null; + String nameQ = null; + + if (winfo.isQ()) { + nameQ = wqkms[i].getName(); + nameW = "W(" + nameQ + ")"; + } + else { + nameW = wqkms[i].getName(); + nameQ = "Q(" + nameQ + ")"; + } + + logger.debug("WaterlevelInfoState Create facet: " + nameW); + logger.debug("WaterlevelInfoState Create facet: " + nameQ); + + Facet w = new WaterlevelFacet( + i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash); + Facet q = new WaterlevelFacet( + i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash); + + facets.add(w); + facets.add(q); + } + + if (wqkms.length > 0) { + Facet wst = new DataFacet( + WST, "WST data", ComputeType.ADVANCE, hash, id); + Facet csv = new DataFacet( + CSV, "CSV data", ComputeType.ADVANCE, hash, id); + + facets.add(wst); + facets.add(csv); + } + + if (res.getReport().hasProblems()) { + facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id)); + } + + // Also register the CrossSectionFacets (added to respective out). + int idx = 0; + for (String name: winfo.getCrossSectionNames()) { + facets.add(new CrossSectionFacet(idx++, name)); + } + + // TODO Adjust to WaterlevelState - implementation. + facets.add(new CrossSectionWaterLineFacet(0, "Q=" + winfo.getDataAsString("wq_single"))); + + // Assume to be in wq_single mode. + return res; + } + + + /** + * @param context Ignored. + */ + @Override + public Object computeFeed( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return compute((WINFOArtifact) artifact, hash, facets, old); + } + + + /** + * @param context Ignored. + */ + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return compute((WINFOArtifact) artifact, hash, facets, old); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelPairSelectState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,162 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.ProtocolUtils; +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.utils.StringUtil; + +/** + * State in which the user selects 1 to n pairs of Waterlevels and alikes. + */ +public class WaterlevelPairSelectState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger( + WaterlevelPairSelectState.class); + + + /** Trivial constructor. */ + public WaterlevelPairSelectState() { + } + + + /** Specify to display a datacage_twin_panel. */ + @Override + protected String getUIProvider() { + return "datacage_twin_panel"; + } + + + /** + * Overridden to do nothing. + */ + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + //Get data and do stuff, do not calculate + return ""; + } + + + /** + * Create elements for document (prepopulated with data, if any). + * @param artifact FLYSArtifact to get data from. + * @param name DataName, expceted to be "diffids". + */ + @Override + protected Element[] createItems( + ElementCreator cr, + Artifact artifact, + String name, + CallContext context) + { + logger.debug("createItems: " + name); + if (name.equals("diffids")) { + Element item = ProtocolUtils.createArtNode(cr, "item", null, null); + Element label = ProtocolUtils.createArtNode(cr, "label", null, null); + Element value = ProtocolUtils.createArtNode(cr, "value", null, null); + FLYSArtifact flys = (FLYSArtifact) artifact; + String s = flys.getDataAsString("diffids"); + value.setTextContent(s); + item.appendChild(label); + item.appendChild(value); + return new Element[] { item }; + } + return new Element[] {}; + } + + + /** + * Creats the data element used for the static part of DESCRIBE document. + */ + @Override + protected Element createStaticData( + FLYSArtifact flys, + ElementCreator creator, + CallContext cc, + String name, + String value, + String type + ) { + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", type, true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + + String[] labels = getLabels(cc, value); + Object[] obj = new Object[] { labels[0] }; + + // TODO own i18n + String attrValue = Resources.getMsg( + cc.getMeta(), "wsp.selected.string", "wsp.selected.string", obj); + //I18N_STATIC_KEY, I18N_STATIC_KEY, obj); + + creator.addAttr(itemElement, "label", attrValue, true); + dataElement.appendChild(itemElement); + + return dataElement; + } + + + /** + * Get name to display for selected watelerlevels (for example "Q=123") + * from the CalculationResult. + */ + public static String[] getLabels(CallContext cc, String value) { + String[] recommendations = value.split("#"); + String displayString = ""; + + // Walk over all selected recommendations and create label + // like "W (Q=1) - W (Q=2)". + for (int i = 0; i < recommendations.length; i+=2) { + String[] minuendParts = StringUtil + .unbracket(recommendations[i+0]) + .split(";"); + if(minuendParts.length >= 4) { + displayString += "(" + minuendParts[3]; + } + else { + displayString += "([error]"; + } + + displayString += " - "; + + String[] subtrahendParts = StringUtil + .unbracket(recommendations[i+1]) + .split(";"); + if(subtrahendParts.length >= 4) { + displayString += subtrahendParts[3] + ") "; + } + else { + displayString += "[error])"; + } + } + + return new String[] { displayString }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelSelectState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,183 @@ +package de.intevation.flys.artifacts.states; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Element; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.data.DefaultStateData; +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.StringUtil; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WaterlevelSelectState extends DefaultState { + + private static final Logger logger = + Logger.getLogger(WaterlevelSelectState.class); + + public static final String SPLIT_CHAR = ";"; + + public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel"; + + public static final String I18N_STATIC_KEY = "wsp.selected.string"; + + + @Override + protected String getUIProvider() { + return "wsp_datacage_panel"; + } + + + /** + * @param flys ignored + * @param cc ignrored + */ + @Override + public StateData transform( + FLYSArtifact flys, + CallContext cc, + String name, + String val + ) { + if (!isValueValid(val)) { + logger.error("The given input string is not valid: '" + val + "'"); + return null; + } + + return new DefaultStateData(name, null, null, StringUtil.unbracket(val)); + } + + + @Override + public boolean validate(Artifact artifact) + throws IllegalArgumentException + { + FLYSArtifact flys = (FLYSArtifact) artifact; + + StateData data = flys.getData("wsp"); + + if (data == null) { + throw new IllegalArgumentException("WSP is empty"); + } + + return true; + } + + + @Override + protected Element createStaticData( + FLYSArtifact flys, + ElementCreator creator, + CallContext cc, + String name, + String value, + String type + ) { + Element dataElement = creator.create("data"); + creator.addAttr(dataElement, "name", name, true); + creator.addAttr(dataElement, "type", type, true); + + Element itemElement = creator.create("item"); + creator.addAttr(itemElement, "value", value, true); + + String[] labels = getLabels(cc, value); + Object[] obj = new Object[] { labels[0] }; + + String attrValue = Resources.getMsg( + cc.getMeta(), I18N_STATIC_KEY, I18N_STATIC_KEY, obj); + + creator.addAttr(itemElement, "label", attrValue, true); + dataElement.appendChild(itemElement); + + return dataElement; + } + + + /** + * Get name to display for selected watelerlevel (for example "Q=123") + * from the CalculationResult. + */ + public static String[] getLabels(CallContext cc, String value) { + String[] parts = value.split(SPLIT_CHAR); + + FLYSArtifact artifact = FLYSUtils.getArtifact(parts[0], cc); + + CalculationResult rawData = (CalculationResult) artifact.compute( + cc, + null, + WINFO_WSP_STATE_ID, + ComputeType.ADVANCE, + false); + + WQKms[] wqkms = (WQKms[]) rawData.getData(); + + int idx = -1; + try { + idx = Integer.parseInt(parts[2]); + } + catch (NumberFormatException nfe) { /* do nothing */ } + + String name = wqkms[idx].getName(); + + return new String[] { StringUtil.wWrap(name) }; + } + + + /** + * Validates the given String. A valid string for this state requires the + * format: "UUID;FACETNAME;FACETINDEX". + * + * @param value The string value requires validation. + * + * @return true, if the string applies the specified format, otherwise + * false. + */ + public static boolean isValueValid(String value) { + logger.debug("Validate string: '" + value + "'"); + + value = StringUtil.unbracket(value); + + logger.debug("Validate substring: '" + value + "'"); + + if (value == null || value.length() == 0) { + return false; + } + + String[] parts = value.split(SPLIT_CHAR); + + if (parts == null || parts.length < 3) { + return false; + } + + if (parts[0] == null || parts[0].length() == 0) { + return false; + } + + if (parts[1] == null || parts[1].length() == 0) { + return false; + } + + try { + Integer.parseInt(parts[2]); + } + catch (NumberFormatException nfe) { + logger.error("Index is not a valid integer!", nfe); + } + + return true; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,133 @@ +package de.intevation.flys.artifacts.states; + +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.DataFacet; +import de.intevation.flys.artifacts.model.CrossSectionFacet; +import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.ReportFacet; +import de.intevation.flys.artifacts.model.WaterlevelFacet; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.utils.FLYSUtils; + + +public class WaterlevelState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(WaterlevelState.class); + + + /** + * From this state can only be continued trivially. + */ + @Override + protected String getUIProvider() { + return "continue"; + } + + + protected Object compute( + WINFOArtifact winfo, + CallContext cc, + String hash, + List<Facet> facets, + Object old + ) { + String id = getID(); + + CalculationResult res = old instanceof CalculationResult + ? (CalculationResult)old + : winfo.getWaterlevelData(); + + if (facets == null) { + return res; + } + + WQKms [] wqkms = (WQKms [])res.getData(); + + for (int i = 0; i < wqkms.length; i++) { + String name = wqkms[i].getName(); + + String nameW = FLYSUtils.createWspWTitle(winfo, cc, name); + String nameQ = FLYSUtils.createWspQTitle(winfo, cc, name); + + logger.debug("Create facet: " + nameW); + logger.debug("Create facet: " + nameQ); + + Facet w = new WaterlevelFacet( + i, LONGITUDINAL_W, nameW, ComputeType.ADVANCE, id, hash); + Facet q = new WaterlevelFacet( + i, LONGITUDINAL_Q, nameQ, ComputeType.ADVANCE, id, hash); + + facets.add(new CrossSectionWaterLineFacet(i, nameQ)); + + facets.add(w); + facets.add(q); + } + + if (wqkms.length > 0) { + Facet wst = new DataFacet( + WST, "WST data", ComputeType.ADVANCE, hash, id); + Facet csv = new DataFacet( + CSV, "CSV data", ComputeType.ADVANCE, hash, id); + + facets.add(wst); + facets.add(csv); + // Also register the CrossSectionFacets. + int idx = 0; + for (String name: winfo.getCrossSectionNames()) { + facets.add(new CrossSectionFacet(idx++, name)); + } + } + + if (res.getReport().hasProblems()) { + facets.add(new ReportFacet(ComputeType.ADVANCE, hash, id)); + } + + return res; + } + + + /** + * @param context Ignored. + */ + @Override + public Object computeFeed( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return compute((WINFOArtifact) artifact, context, hash, facets, old); + + } + + + /** + * @param context Ignored. + */ + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + return compute((WINFOArtifact) artifact, context, hash, facets, old); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,104 @@ +package de.intevation.flys.artifacts.transitions; + +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.transition.Transition; + + +/** + * The default implementation of a <code>Transition</code>. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultTransition implements Transition { + + /** The ID of the current state */ + protected String from; + + /** The ID of the target state */ + protected String to; + + + /** + * The default constructor. + */ + public DefaultTransition() { + } + + + /** + * The default constructor. + * + * @param from The current state. + * @param to The target state. + */ + public DefaultTransition(String from, String to) { + this.from = from; + this.to = to; + } + + + public void init(Node config) { + // nothing to do in the default transition + } + + + /** + * Returns the current state ID. + * + * @return the current state ID. + */ + public String getFrom() { + return from; + } + + + /** + * Returns the target state ID. + * + * @return the target state ID. + */ + public String getTo() { + return to; + } + + + /** + * Set the current state ID. + * + * @param to the current state ID. + */ + public void setFrom(String from) { + this.from = from; + } + + + /** + * Set the target state ID. + * + * @param to the target state ID. + */ + public void setTo(String to) { + this.to = to; + } + + + /** + * Determines if its valid to step from state <i>a</i> of an artifact + * <i>artifact</i> to state <i>b</i>. This method always returns true - no + * validation takes place. + * + * @param artifact The owner artifact of state a and b. + * @param a The current state. + * @param b The target state. + * + * @return true, if it is valid to step from a to b, otherwise false. + */ + public boolean isValid(Artifact artifact, State a, State b) { + return true; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,71 @@ +package de.intevation.flys.artifacts.transitions; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; + +import de.intevation.artifactdatabase.transition.Transition; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class TransitionFactory { + + /** The logger used in this class */ + private static Logger logger = Logger.getLogger(TransitionFactory.class); + + /** The XPath to the classname of the transition */ + public static final String XPATH_TRANSITION = "@transition"; + + /** The XPath to the current state ID */ + public static final String XPATH_CURRENT_STATE = "from/@state"; + + /** The XPath to the target state ID */ + public static final String XPATH_TARGET_STATE = "to/@state"; + + + /** + * Creates a new Transition based on the configured class provided by + * <code>transitionConf</code>. + * + * @param transitionConf The configuration of the transition. + * + * @return a Transition. + */ + public static Transition createTransition(Node transitionConf) { + String clazz = (String) XMLUtils.xpath( + transitionConf, XPATH_TRANSITION, XPathConstants.STRING); + + Transition transition = null; + + try { + transition = (Transition) Class.forName(clazz).newInstance(); + + String from = (String) XMLUtils.xpath( + transitionConf, XPATH_CURRENT_STATE, XPathConstants.STRING); + String to = (String) XMLUtils.xpath( + transitionConf, XPATH_TARGET_STATE, XPathConstants.STRING); + + transition.init(transitionConf); + transition.setFrom(from); + transition.setTo(to); + } + catch (InstantiationException ie) { + logger.error(ie, ie); + } + catch (IllegalAccessException iae) { + logger.error(iae, iae); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe, cnfe); + } + + return transition; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,118 @@ +package de.intevation.flys.artifacts.transitions; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.data.StateData; +import de.intevation.artifactdatabase.state.State; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +/** + * This transition compares data objects with a <i>equal</i> or <i>notequal</i> + * operator. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ValueCompareTransition extends DefaultTransition { + + /** The logger that is used in this transition.*/ + private static Logger log = Logger.getLogger(ValueCompareTransition.class); + + + /** XPath that points to the condition's operator.*/ + public static final String XPATH_OPERATOR = "condition/@operator"; + + /** XPath that points to the condition's value.*/ + public static final String XPATH_VALUE = "condition/@value"; + + /** XPath that points to the condition's dataname.*/ + public static final String XPATH_DATANAME = "condition/@data"; + + /** The value of the 'equal' operator.*/ + public static final String OPERATOR_EQUAL = "equal"; + + /** The value of the 'not equal' operator.*/ + public static final String OPERATOR_NOTEQUAL = "notequal"; + + + /** The operator.*/ + protected String operator; + + /** The value used for comparison.*/ + protected String value; + + /** The name of the data used for the comparison.*/ + protected String dataname; + + + + public ValueCompareTransition() { + } + + + public ValueCompareTransition(String from, String to) { + super(from, to); + } + + + @Override + public void init(Node config) { + log.debug("ValueCompareTransition.init"); + + String tmp = (String) XMLUtils.xpath( + config, + XPATH_OPERATOR, + XPathConstants.STRING); + operator = tmp.trim().toLowerCase(); + + value = (String) XMLUtils.xpath( + config, + XPATH_VALUE, + XPathConstants.STRING); + + dataname = (String) XMLUtils.xpath( + config, + XPATH_DATANAME, + XPathConstants.STRING); + } + + + @Override + public boolean isValid(Artifact artifact, State a, State b) { + log.debug("ValueCompareTransition.isValid"); + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + StateData dataObj = flysArtifact.getData(dataname); + String dataVal = dataObj != null ? (String) dataObj.getValue() : ""; + + if (log.isDebugEnabled()) { + log.debug("Compare settings:"); + log.debug("-- dataname: " + dataname); + log.debug("-- operator: " + operator); + log.debug("-- compare value: " + value); + log.debug("-- state value: " + dataVal); + } + + if (operator.equals(OPERATOR_EQUAL)) { + return value.equals(dataVal); + } + else if (operator.equals(OPERATOR_NOTEQUAL)) { + return !(value.equals(dataVal)); + } + + log.error("Wrong operator set! No comparison takes place."); + + return false; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeParser.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,134 @@ +package de.intevation.flys.collections; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.model.ManagedFacet; +import de.intevation.flys.artifacts.model.ManagedDomFacet; + +/** + * Access parts of the Attribute parts of a FLYSCollections description + * document. + */ +public class AttributeParser { + + /** Constant XPath that points to the outputmodes of an artifact. */ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:attribute/art:outputs/art:output"; + + private static Logger logger = Logger.getLogger(AttributeParser.class); + + protected Map<String, Output> outs; + + /** List of facets. */ + protected List<Facet> facets; + + + public AttributeParser() { + this.outs = new HashMap<String, Output>(); + this.facets = new ArrayList<Facet>(); + } + + + public void parse(Document doc) { + logger.debug("AttributeParser.parse"); + + NodeList outs = (NodeList) XMLUtils.xpath( + doc, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = outs != null ? outs.getLength() : 0; + + logger.debug("Attribute has " + num + " outputs."); + + for (int i = 0; i < num; i++) { + Node out = outs.item(i); + + parseOutput(out); + } + } + + + public Map<String, Output> getOuts() { + return outs; + } + + + /** + * Adds item (a ManagedFacet) to an out. + */ + protected void addItem(String out, ManagedFacet item) { + this.facets.add(item); + Output o = outs.get(out); + + if (o != null) { + o.addFacet(item); + } + } + + + protected void parseOutput(Node out) { + String name = XMLUtils.xpathString( + out, "@name", ArtifactNamespaceContext.INSTANCE); + + if (outs.get(name) == null) { + logger.debug("Create new output: " + name); + + Output o = new DefaultOutput(name, null, null); + outs.put(name, o); + } + + parseItems(out, name); + } + + + /** + * Access all facets. + * @return list of all facets. + */ + public List<Facet> getFacets() { + return this.facets; + } + + + protected void parseItems(Node out, String outname) { + NodeList themes = (NodeList) XMLUtils.xpath( + out, "art:facet", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = themes != null ? themes.getLength() : 0; + + logger.debug("Output has " + num + " themes."); + + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + for (int i = 0; i < num; i++) { + Element theme = (Element) themes.item(i); + + addItem(outname, new ManagedDomFacet(theme)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeWriter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,294 @@ +package de.intevation.flys.collections; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.ManagedFacet; + +/** + * Create attribute- element of describe document of an ArtifactCollection. + * The attribute-element contains the merged output of all outputmodes and + * facets that are part of the collection. + */ +public class AttributeWriter { + + /** ArtifactDatabase used to fetch Artifacts. */ + protected ArtifactDatabase db = null; + + protected Map<String, Output> oldAttr; + + protected Map<String, Output> newAttr; + + /** List of already seen facets. */ + protected List<Facet> oldFacets; + + /** List of "new" facets. */ + protected List<Facet> newFacets; + + /** + * "Compatibility matrix", mapws list of facet names to output names. + * Any facet that is not found in the list for a specific output will + * not be added to the resulting document. + */ + protected Map<String, List<String>> compatibilities; + + private static Logger logger = Logger.getLogger(AttributeWriter.class); + + + /** + * Create a AttributeWriter. + * Attributes not present in newAttr will not be included in the document. + * @param db Database to fetch artifacts. + * @param oldAttr "Old" (possibly user-changed) outputs. + * @param newAttr "New" (eventually re-read in its original, unchanged + * form) outputs. + */ + public AttributeWriter( + ArtifactDatabase db, + Map<String, Output> oldAttr, + List<Facet> oldFacets, + Map<String, Output> newAttr, + List<Facet> newFacets, + Map<String, List<String>> matrix) + { + this.db = db; + this.oldAttr = oldAttr; + this.newAttr = newAttr; + this.oldFacets = oldFacets; + this.newFacets = newFacets; + this.compatibilities = matrix; + } + + + /** + * Create document by merging outputs given in + * constructor. + * + * The "new" set rules about existance of attributes, so anything not + * present in it will not be included in the resulting document. + * The "old" set rules about the content of attributes (as user changes + * are recorded here and not in the new set). + * + * @return document with merged outputs as described. + */ + protected Document write() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element attribute = cr.create("attribute"); + Element outs = cr.create("outputs"); + + attribute.appendChild(outs); + doc.appendChild(attribute); + + for (String outName: newAttr.keySet()) { + Output a = newAttr.get(outName); + Output b = oldAttr.get(outName); + + writeOutput(doc, outs, cr, a.getName(), newFacets, oldFacets); + } + + return doc; + } + + + /** + * @param doc Document to add output nodes to + * @param outs Node in Document to add output nodes to + * @param cr ElementCreator in use to modify doc/outs + * @param outputName the "new" outputs name + * @param newOutFacets Facets of the new outputs + * @param oldOutFacets Facets of the old outputs (can be null) + */ + protected void writeOutput( + Document doc, + Node outs, + ElementCreator cr, + String outputName, + List<Facet> newOutFacets, + List<Facet> oldOutFacets) + { + Element output = cr.create("output"); + cr.addAttr(output, "name", outputName); + + outs.appendChild(output); + + List<String> compatibleFacets = this.compatibilities.get(outputName); + try { + writeFacets(doc, cr, output, newOutFacets, oldOutFacets, compatibleFacets); + } + catch (ArtifactDatabaseException ade) { + logger.error(ade, ade); + } + } + + + /** + * @param doc Document to add facet nodes to + * @param cr ElementCreator to use with output/doc + * @param output Node in Document to add facet nodes to + * @param newFacets the new facets + * @param oldFacets the old facets + * @param compatibleFacets List of facets to accept + */ + protected void writeFacets( + Document doc, + ElementCreator cr, + Element output, + List<Facet> newFacets, + List<Facet> oldFacets, + List<String> compatibleFacets) + throws ArtifactDatabaseException + { + if (compatibleFacets == null) { + logger.warn("No compatible facets, not generating out."); + return; + } + + int num = newFacets.size(); + + // Add all new Facets either in their old state or (if really + // new) as they are. + List<ManagedFacet> currentFacets = new ArrayList<ManagedFacet>(); + List<ManagedFacet> genuinelyNewFacets = new ArrayList<ManagedFacet>(); + + for (int i = 0; i < num; i++) { + ManagedFacet facet = (ManagedFacet) newFacets.get(i); + if (!compatibleFacets.contains(facet.getName())) { + //logger.debug("Have incompatible facet, skip: " + facet.getName()); + continue; + } + //else logger.debug("Have compatible facet: " + facet.getName()); + + ManagedFacet picked = pickFacet(facet, oldFacets); + + if (facet.equals(picked)) { + genuinelyNewFacets.add(picked); + } + else { + currentFacets.add(picked); + } + } + + // With each genuinely new Facet, ask Artifact whether it comes to live + // in/activate. + for (ManagedFacet newMF: genuinelyNewFacets) { + FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(newMF.getArtifact()); + newMF.setActive(flys.getInitialFacetActivity( + output.getAttribute("name"), + newMF.getName(), + newMF.getIndex())); + } + + // For each genuinely new Facet check positional conflicts. + for (ManagedFacet newMF: genuinelyNewFacets) { + boolean conflicts = true; + // Loop until all conflicts resolved. + while (conflicts) { + conflicts = false; + for (ManagedFacet oldMF: currentFacets) { + if (newMF.getPosition() == oldMF.getPosition()) { + conflicts = true; + logger.debug("Positional conflict while merging " + + "facets, pushing newest facet 1 up (" + newMF.getPosition() + ")"); + newMF.setPosition(newMF.getPosition() + 1); + break; + } + } + } + currentFacets.add(newMF); + } + + // Fill/correct "gaps" (e.g. position 1,2,5 are taken, after gap filling + // expect positions 1,2,3 [5->3]) + // Preparations to be able to detect gaps. + Map<Integer, ManagedFacet> mfmap = new HashMap<Integer, ManagedFacet>(); + int max = 0; + for (ManagedFacet mf: currentFacets) { + int pos = mf.getPosition(); + mfmap.put(Integer.valueOf(pos), mf); + if (pos > max) max = pos; + } + + // Finally do gap correction. + if (max != currentFacets.size()) { + int gap = 0; + for (int i = 1; i <= max; i++) { + ManagedFacet mf = mfmap.get(Integer.valueOf(i)); + if (mf == null) { + gap++; + continue; + } + mf.setPosition(mf.getPosition() - gap); + } + } + + // Now add all facets. + for (ManagedFacet oldMF: currentFacets) { + Node node = oldMF.toXML(doc); + if (node != null) { + output.appendChild(node); + } + } + } + + + /** + * Returns the facet to be added to Document. + * Return the new facet only if the "same" facet was not present before. + * Return the "old" facet otherwise (user-defined information sticks + * to it). + * @param facet the new facet. + * @param oldFacets the old facets, new facet is compared against each of + * these. + * @return facet if genuinely new, matching old facet otherwise. + */ + protected ManagedFacet pickFacet(ManagedFacet facet, + List<Facet> oldFacets) + { + if (oldFacets == null) { + logger.debug("No old facets to compare a new to found."); + return facet; + } + + String hash = facet.getName() + facet.getIndex() + facet.getArtifact(); + + // Compare "new" facet with all old facets. + // Take oldFacet if that facet was already present (otherwise + // information is lost, the new one otherwise. + for (Facet oFacet: oldFacets) { + ManagedFacet oldFacet = (ManagedFacet) oFacet; + String oldHash = oldFacet.getName() + + oldFacet.getIndex() + + oldFacet.getArtifact(); + if (hash.equals(oldHash)) { + return oldFacet; + } + } + return facet; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,873 @@ +package de.intevation.flys.collections; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.ClientProtocolUtils; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.Backend; +import de.intevation.artifactdatabase.Backend.PersistentArtifact; +import de.intevation.artifactdatabase.DefaultArtifactCollection; +import de.intevation.artifactdatabase.state.StateEngine; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.ManagedFacet; +import de.intevation.flys.artifacts.model.ManagedDomFacet; +import de.intevation.flys.exports.OutGenerator; +import de.intevation.flys.themes.Theme; +import de.intevation.flys.themes.ThemeFactory; + +import de.intevation.flys.utils.FLYSUtils; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSArtifactCollection extends DefaultArtifactCollection { + + /** The logger used in this class. */ + private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); + + /** Constant XPath that points to the outputmodes of an artifact. */ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:result/art:outputmodes"; + + public static final String XPATH_COLLECTION_ITEMS = + "/art:result/art:artifact-collection/art:collection-item"; + + public static final String XPATH_OUT_NAME = "/art:action/@art:name"; + + public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; + + /** Xpath to master artifacts uuid. */ + public static final String XPATH_MASTER_UUID = + "/art:artifact-collection/art:artifact/@art:uuid"; + + public static final String XPATH_LOADED_RECOMMENDATIONS = + "/art:attribute/art:loaded-recommendations"; + + + /** + * Return description Document for this collection. + */ + @Override + public Document describe(CallContext context) { + log.debug("FLYSArtifactCollection.describe: " + identifier); + + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Date creationTime = getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + Element collection = ec.create("artifact-collection"); + Element artifacts = ec.create("artifacts"); + + ec.addAttr(collection, "name", getName(), true); + ec.addAttr(collection, "uuid", identifier(), true); + ec.addAttr(collection, "creation", creation, true); + ec.addAttr(collection, "ttl", String.valueOf(getTTL()), true); + + collection.appendChild(artifacts); + doc.appendChild(collection); + + ArtifactDatabase db = context.getDatabase(); + Document oldAttrs = getAttribute(); + + try { + String[] aUUIDs = getArtifactUUIDs(context); + Node newAttr = mergeAttributes(db, context, oldAttrs, aUUIDs); + + collection.appendChild(doc.importNode(newAttr, true)); + + // Make it an empty array if null. + if (aUUIDs == null) { + aUUIDs = new String[] {}; + } + + for (String uuid: aUUIDs) { + try { + artifacts.appendChild( + buildArtifactNode(db, uuid, context, ec)); + } + catch (ArtifactDatabaseException dbe) { + log.warn(dbe, dbe); + } + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + + collection.appendChild( + doc.importNode(oldAttrs.getFirstChild(), true)); + } + + return doc; + } + + + /** + * Merge the current art:outputs nodes with the the outputs provided by the + * artifacts in the Collection. + * + * @param uuids Artifact uuids. + */ + protected Node mergeAttributes( + ArtifactDatabase db, + CallContext context, + Document oldAttrs, + String[] uuids) + { + Document doc = buildOutAttributes(db, context, oldAttrs, uuids); + Node newAttr = doc.getFirstChild(); + + newAttr = mergeLoadedRecommendations(oldAttrs, newAttr); + + try { + // Save the merged document into database. + db.setCollectionAttribute(identifier(), context.getMeta(), doc); + } + catch (ArtifactDatabaseException adb) { + log.error(adb, adb); + } + + return newAttr; + } + + + /** + * Merge the recommendations which have already been loaded from the old + * attribute document into the new attribute document. This is necessary, + * because mergeAttributes() only merges the art:outputs nodes - all + * other nodes are skiped. + */ + protected Node mergeLoadedRecommendations(Document oldAttrs, Node newAttrs){ + Element loadedRecoms = (Element) XMLUtils.xpath( + oldAttrs, + XPATH_LOADED_RECOMMENDATIONS, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (loadedRecoms != null) { + Document doc = newAttrs.getOwnerDocument(); + newAttrs.appendChild(doc.importNode(loadedRecoms, true)); + } + + return newAttrs; + } + + + @Override + public void out( + String type, + Document format, + OutputStream out, + CallContext context) + throws IOException + { + log.info("FLYSArtifactCollection.out"); + + String name = XMLUtils.xpathString( + format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); + + String subtype = XMLUtils.xpathString( + format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); + + log.info("-> Output name = " + name); + log.info("-> Output type = " + type); + log.info("-> Output subtype = " + subtype); + + OutGenerator generator = null; + if (type != null + && type.length() > 0 + && type.indexOf("chartinfo") > 0) + { + generator = getOutGenerator(context, type, subtype); + } + else { + generator = getOutGenerator(context, name, subtype); + } + + if (generator == null) { + log.error("There is no generator specified for output: " + name); + // TODO Throw an exception. + + return; + } + + generator.init(format, out, context); + + // Get master artifact. + FLYSArtifact master = getMasterArtifact(context); + if (master != null) { + log.debug("Will set master Artifact to uuid: " + master.identifier()); + generator.setMasterArtifact(master); + } + else { + log.warn("Could not set master artifact."); + } + + try { + Document attr = getAttribute(context, name); + doOut(generator, name, subtype, attr, context); + } + catch (ArtifactDatabaseException adbe) { + log.error(adbe, adbe); + } + } + + + /** + * Creates the concrete output. + * + * @param generator The OutGenerator that creates the output. + * @param outputName The name of the requested output. + * @param attributes The collection's attributes for this concrete output + * type. + * @param context The context object. + */ + protected void doOut( + OutGenerator generator, + String outName, + String facet, + Document attributes, + CallContext context) + throws IOException + { + log.info("FLYSArtifactCollection.doOut: " + outName); + + ThemeList themeList = new ThemeList(attributes); + + int size = themeList.size(); + log.debug("Output will contain " + size + " elements."); + + try { + for (int i = 0; i < size; i++) { + ManagedFacet theme = themeList.get(i); + + if (theme == null) { + log.debug("Theme is empty - no output is generated."); + continue; + } + + String art = theme.getArtifact(); + + if (log.isDebugEnabled()) { + log.debug("Do output for..."); + log.debug("... artifact: " + art); + log.debug("... facet: " + theme.getName()); + } + + String facetName = theme.getName(); + + if (outName.equals("export") && !facetName.equals(facet)) { + continue; + } + + Artifact artifact = getArtifact(art, context); + + generator.doOut( + artifact, + theme, + getFacetThemeFromAttribute( + art, + outName, + facetName, + theme.getDescription(), + theme.getIndex(), + context), + theme.getActive() == 1); + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + + generator.generate(); + } + + + /** + * @return masterartifact or null if exception/not found. + */ + protected FLYSArtifact getMasterArtifact(CallContext context) + { + try { + ArtifactDatabase db = context.getDatabase(); + CallMeta callMeta = context.getMeta(); + Document document = db.getCollectionsMasterArtifact( + identifier(), callMeta); + + String masterUUID = XMLUtils.xpathString( + document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE); + FLYSArtifact masterArtifact = + (FLYSArtifact) getArtifact(masterUUID, context); + return masterArtifact; + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + return null; + } + + + /** + * Return merged output document. + * @param uuids List of artifact uuids. + */ + protected Document buildOutAttributes( + ArtifactDatabase db, + CallContext context, + Document oldAttr, + String[] uuids) + { + Document doc = XMLUtils.newDocument(); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + FLYSArtifact masterArtifact = getMasterArtifact(context); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + AttributeParser aParser = new AttributeParser(); + OutputParser oParser = new OutputParser(db, context); + + if (uuids != null) { + for (String uuid: uuids) { + try { + oParser.parse(uuid); + } + catch (ArtifactDatabaseException ade) { + log.warn(ade, ade); + } + } + } + + aParser.parse(oldAttr); + + return new AttributeWriter( + db, + aParser.getOuts(), + aParser.getFacets(), + oParser.getOuts(), + oParser.getFacets(), + engine.getCompatibleFacets(masterArtifact.getStateHistoryIds()) + ).write(); + } + + + /** + * Returns the "attribute" (part of description document) for a specific + * output type. + * + * @param output The name of the desired output type. + * + * @return the attribute for the desired output type. + */ + protected Document getAttribute(CallContext context, String output) + throws ArtifactDatabaseException + { + Document attr = buildOutAttributes( + context.getDatabase(), + context, + getAttribute(), + getArtifactUUIDs(context)); + + Node out = (Node) XMLUtils.xpath( + attr, + "art:attribute/art:outputs/art:output[@name='" + output + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + + if (out != null) { + Document o = XMLUtils.newDocument(); + + o.appendChild(o.importNode(out, true)); + + return o; + } + + return null; + } + + + /** + * This method returns the list of artifact UUIDs that this collections + * contains. + * + * @param context The CallContext that is necessary to get information about + * the ArtifactDatabase. + * + * @return a list of uuids. + */ + protected String[] getArtifactUUIDs(CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifactUUIDs"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + Document itemList = db.listCollectionArtifacts(identifier(), meta); + NodeList items = (NodeList) XMLUtils.xpath( + itemList, + XPATH_COLLECTION_ITEMS, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (items == null || items.getLength() == 0) { + log.debug("No artifacts found in this collection."); + return null; + } + + int num = items.getLength(); + + List<String> uuids = new ArrayList<String>(num); + + for (int i = 0; i < num; i++) { + String uuid = XMLUtils.xpathString( + items.item(i), + "@art:uuid", + ArtifactNamespaceContext.INSTANCE); + + if (uuid != null && uuid.trim().length() != 0) { + uuids.add(uuid); + } + } + + return (String[]) uuids.toArray(new String[uuids.size()]); + } + + + /** + * Returns a concrete Artifact of this collection specified by its uuid. + * + * @param uuid The Artifact's uuid. + * @param context The CallContext. + * + * @return an Artifact. + */ + protected Artifact getArtifact(String uuid, CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifact"); + + Backend backend = Backend.getInstance(); + PersistentArtifact persistent = backend.getArtifact(uuid); + + return persistent != null ? persistent.getArtifact() : null; + } + + + /** + * Returns the attribute that belongs to an artifact and facet stored in + * this collection. + * + * @param uuid The Artifact's uuid. + * @param outname The name of the requested output. + * @param facet The name of the requested facet. + * @param context The CallContext. + * + * @return an attribute in form of a document. + */ + protected Document getFacetThemeFromAttribute( + String uuid, + String outName, + String facet, + String pattern, + int index, + CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta); + + if (attr == null) { + attr = initItemAttribute(uuid, facet, pattern, index, outName, context); + + if (attr == null) { + return null; + } + } + + log.debug("Search attribute of collection item: " + uuid); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + log.warn("No attribute found. Operation failed."); + return null; + } + + log.debug("Search theme for facet '" + facet + "' in attribute."); + + Node theme = (Node) XMLUtils.xpath( + tmp, + "art:themes/theme[@facet='" + facet + + "' and @index='" + String.valueOf(index) + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (theme == null) { + log.warn("Could not find the theme in attribute of: " + uuid); + + Theme t = getThemeForFacet( + uuid, facet, pattern, index, outName, context); + + if (t == null) { + log.warn("No theme found for facet: " + facet); + return null; + } + + addThemeToAttribute(uuid, attr, t, context); + theme = t.toXML().getFirstChild(); + } + + Document doc = XMLUtils.newDocument(); + doc.appendChild(doc.importNode(theme, true)); + + return doc; + } + + + /** + * Adds the theme of a facet to a CollectionItem's attribute. + * + * @param uuid The uuid of the artifact. + * @param attr The current attribute of an artifact. + * @param t The theme to add. + * @param context The CallContext. + */ + protected void addThemeToAttribute( + String uuid, + Document attr, + Theme t, + CallContext context) + { + log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid); + + if (t == null) { + log.warn("Theme is empty - cancel adding it to attribute!"); + return; + } + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + attr, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + tmp = ec.create("attribute"); + attr.appendChild(tmp); + } + + Node themes = (Node) XMLUtils.xpath( + tmp, + "art:themes", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (themes == null) { + themes = ec.create("themes"); + tmp.appendChild(themes); + } + + themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true)); + + try { + setCollectionItemAttribute(uuid, attr, context); + + log.debug("Successfully added theme to item attribute."); + } + catch (ArtifactDatabaseException e) { + // do nothing + log.warn("Cannot set attribute of item: " + uuid); + } + } + + + /** + * Initializes the attribute of an collection item with the theme of a + * specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of a facet. + * @param context The CallContext. + * + * @param the new attribute. + */ + protected Document initItemAttribute( + String uuid, + String facet, + String pattern, + int index, + String outName, + CallContext context) + { + log.info("FLYSArtifactCollection.initItemAttribute"); + + Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context); + + if (t == null) { + log.info("Could not find theme for facet. Cancel initialization."); + return null; + } + + Document attr = XMLUtils.newDocument(); + + addThemeToAttribute(uuid, attr, t, context); + + return attr; + } + + + /** + * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new + * value <i>attr</i>. + * + * @param uuid The uuid of the CollectionItem. + * @param attr The new attribute for the CollectionItem. + * @param context The CallContext. + */ + public void setCollectionItemAttribute( + String uuid, + Document attr, + CallContext context) + throws ArtifactDatabaseException + { + Document doc = ClientProtocolUtils.newSetItemAttributeDocument( + uuid, + attr); + + if (doc == null) { + log.warn("Cannot set item attribute: No attribute found."); + return; + } + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + db.setCollectionItemAttribute(identifier(), uuid, doc, meta); + } + + + /** + * Returns the theme of a specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of the facet. + * @param context The CallContext object. + * + * @return the desired theme. + */ + protected Theme getThemeForFacet( + String uuid, + String facet, + String pattern, + int index, + String outName, + CallContext context) + { + log.info("FLYSArtifactCollection.getThemeForFacet: " + facet); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + // Push artifact in flysContext. + ArtifactDatabase db = context.getDatabase(); + try { + FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid); + log.debug("Got raw artifact"); + flysContext.put(flysContext.ARTIFACT_KEY, artifact); + } + catch (Exception e) { + log.error("Exception caught when trying to get art.", e); + } + + Theme t = ThemeFactory.getTheme(flysContext, facet, pattern, outName); + + if (t != null) { + t.setFacet(facet); + t.setIndex(index); + } + + return t; + } + + + /** + * Returns the OutGenerator for a specified <i>type</i>. + * + * @param name The name of the output type. + * @param type Defines the type of the desired OutGenerator. + * + * @return Instance of an OutGenerator for specified <i>type</i>. + */ + protected OutGenerator getOutGenerator( + CallContext context, + String name, + String type) + { + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Map<String, Class> generators = (Map<String, Class>) + flysContext.get(FLYSContext.OUTGENERATORS_KEY); + + if (generators == null) { + log.error("No output generators found in the running application!"); + return null; + } + + Class clazz = generators.get(name); + + try { + return clazz != null ? (OutGenerator) clazz.newInstance() : null; + } + catch (InstantiationException ie) { + log.error(ie, ie); + } + catch (IllegalAccessException iae) { + log.error(iae, iae); + } + + return null; + } + + + protected Element buildArtifactNode( + ArtifactDatabase database, + String uuid, + CallContext context, + XMLUtils.ElementCreator ec) + throws ArtifactDatabaseException + { + log.debug("Append artifact '" + uuid + "' to collection description"); + + // TODO + String hash = "MYHASH"; + + Element ci = ec.create("artifact"); + ec.addAttr(ci, "uuid", uuid, true); + ec.addAttr(ci, "hash", hash, true); + + // XXX I am not sure if it works well every time with an empty document + // in the describe operation of an artifact. + Document description = database.describe(uuid, null, context.getMeta()); + + Node outputModes = (Node) XMLUtils.xpath( + description, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (outputModes != null) { + Document doc = ci.getOwnerDocument(); + ci.appendChild(doc.importNode(outputModes, true)); + } + + return ci; + } + + + /** + * Inner class to structure/order the themes of a chart. + */ + private static class ThemeList { + private Logger logger = Logger.getLogger(ThemeList.class); + protected Map<Integer, ManagedFacet> themes; + + public ThemeList(Document output) { + themes = new HashMap<Integer, ManagedFacet>(); + parse(output); + } + + protected void parse(Document output) { + NodeList themeList = (NodeList) XMLUtils.xpath( + output, + "art:output/art:facet", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = themeList != null ? themeList.getLength() : 0; + + logger.debug("Output has " + num + " elements."); + + if (num == 0) { + return; + } + + String uri = ArtifactNamespaceContext.NAMESPACE_URI; + + for (int i = 0; i < num; i++) { + Element theme = (Element) themeList.item(i); + + ManagedDomFacet facet = new ManagedDomFacet(theme); + themes.put(Integer.valueOf(facet.getPosition()-1), facet); + } + } + + public ManagedFacet get(int idx) { + return themes.get(Integer.valueOf(idx)); + } + + public int size() { + return themes.size(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/OutputParser.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,130 @@ +package de.intevation.flys.collections; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.state.DefaultOutput; +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.ManagedFacetAdapter; + + +public class OutputParser { + + /** Constant XPath that points to the outputmodes of an artifact. */ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:result/art:outputmodes/art:output"; + + private static Logger logger = Logger.getLogger(OutputParser.class); + + protected ArtifactDatabase db; + protected CallMeta meta; + protected CallContext context; + + /** Map outputs name to Output. */ + protected Map<String, Output> outs; + + /** Map facets name to list of Facets. */ + protected List<Facet> facets; + + + /** + * @param db Database used to fetch artifacts, outputs and facets. + */ + public OutputParser(ArtifactDatabase db, CallContext context) { + this.db = db; + this.meta = context.getMeta(); + this.context = context; + this.outs = new HashMap<String, Output>(); + this.facets = new ArrayList<Facet>(); + } + + + /** + * Gets raw artifact with given id and sorts outputs in mapping. + * Converts Facets to ManagedFacets on the way. + * @param uuid uuid of artifact to load from database. + */ + public void parse(String uuid) + throws ArtifactDatabaseException + { + logger.debug("OutputParser.parse: " + uuid); + + FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(uuid); + + List<Output> outList = flys.getOutputs(context); + + for (Output out: outList) { + String name = out.getName(); + + Output o = outs.get(name); + int pos = 1; + + if (o == null) { + o = new DefaultOutput( + out.getName(), + out.getDescription(), + out.getMimeType(), + new ArrayList<Facet>(), + out.getType()); + outs.put(name, o); + } + else { + logger.debug("OutputParser.parse: Use 'old' Output"); + pos = o.getFacets().size() + 1; + } + + List<Facet> mfacets = facet2ManagedFacet(uuid, out.getFacets(), pos); + o.addFacets(mfacets); + this.facets.addAll(mfacets); + } + } + + + /** + * Access mapping of Outputname to Output. + */ + public Map<String, Output> getOuts() { + return outs; + } + + + /** + * Access all facets. + */ + public List<Facet> getFacets() { + return this.facets; + } + + + /** + * Creates a list of ManagedFacets from list of Facets. + * @param pos Position of first facet (for each other the positions + * will be increased). + */ + protected List<Facet> facet2ManagedFacet( + String uuid, + List<Facet> old, + int pos) + { + List<Facet> newFacets = new ArrayList<Facet>(old.size()); + + for (Facet f: old) { + newFacets.add(new ManagedFacetAdapter(f, uuid, pos++, 1, 1)); + } + + return newFacets; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ATExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,94 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.model.WQ; + +import de.intevation.flys.utils.FLYSUtils; + + +public class ATExporter +implements OutGenerator +{ + private static Logger logger = Logger.getLogger(ATExporter.class); + + public static final String DEFAULT_ENCODING = "UTF-8"; + + protected WQ data; + protected CallContext context; + protected OutputStream out; + protected FLYSArtifact master; + + public ATExporter() { + } + + @Override + public void init(Document request, OutputStream out, CallContext context) { + this.context = context; + this.out = out; + } + + @Override + public void setMasterArtifact(Artifact master) { + this.master = (FLYSArtifact) master; + } + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + + FLYSArtifact flys = (FLYSArtifact)artifact; + + if ((facet = flys.getNativeFacet(facet)) == null) { + logger.debug("native facet not found."); + return; + } + + data = (WQ)facet.getData(flys, context); + } + + @Override + public void generate() throws IOException { + + if (data == null) { + logger.debug("no W/Q data"); + return; + } + + ATWriter at; + try { + at = new ATWriter(data); + } + catch (IllegalArgumentException iae) { + logger.error("creating ATWriter failed", iae); + throw new IOException(iae); + } + + String river = FLYSUtils.getRiver(master).getName(); + double[] kms = FLYSUtils.getLocations(master); + + at.write( + new OutputStreamWriter(out, DEFAULT_ENCODING), + context.getMeta(), + river, + kms[0]); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ATWriter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,166 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.Writer; +import java.io.PrintWriter; + +import java.util.Locale; + +import de.intevation.artifacts.CallMeta; + +import de.intevation.flys.artifacts.model.WQ; +import de.intevation.flys.artifacts.resources.Resources; + +import org.apache.commons.math.analysis.UnivariateRealFunction; + +import org.apache.commons.math.analysis.interpolation.SplineInterpolator; +import org.apache.commons.math.analysis.interpolation.LinearInterpolator; + +import org.apache.commons.math.analysis.polynomials.PolynomialFunction; + +import org.apache.commons.math.FunctionEvaluationException; + +import org.apache.log4j.Logger; + +public class ATWriter +{ + private static Logger logger = Logger.getLogger(ATWriter.class); + + public static final int COLUMNS = 10; + + public static final String I18N_AT_HEADER = + "export.discharge.curve.at.header"; + + public static final String EMPTY = " "; + + protected double minW; + protected double maxW; + protected double minQ; + protected double maxQ; + + protected UnivariateRealFunction qFunc; + + public ATWriter() { + } + + public ATWriter(WQ wq) throws IllegalArgumentException { + + int [] bounds = wq.longestIncreasingWRangeIndices(); + + if (logger.isDebugEnabled()) { + logger.debug("exporting w between indices " + + bounds[0] + " and " + bounds[1] + " (" + + wq.getW(bounds[0]) + ", " + wq.getW(bounds[1])); + } + + if (bounds[1]-bounds[0] < 1) { // Only first w can be written out. + minW = maxW = wq.getW(bounds[0]); + minQ = maxQ = wq.getQ(bounds[0]); + // constant function + qFunc = new PolynomialFunction(new double [] { minQ }); + return; + } + + double [] ws = new double[bounds[1]-bounds[0]]; + double [] qs = new double[ws.length]; + + for (int i = 0; i < ws.length; ++i) { + int idx = bounds[0]+i; + ws[i] = wq.getW(idx); + qs[i] = wq.getQ(idx); + } + + qFunc = ws.length < 3 + ? new LinearInterpolator().interpolate(ws, qs) + : new SplineInterpolator().interpolate(ws, qs); + + minW = wq.getW(bounds[0]); + maxW = wq.getW(bounds[1]); + minQ = wq.getQ(bounds[0]); + maxQ = wq.getQ(bounds[1]); + } + + public double getQ(double w) { + + try { + return qFunc.value(w); + } + catch (FunctionEvaluationException aode) { + // should not happen + logger.warn("spline interpolation failed", aode); + return w <= minW ? minQ : maxQ; + } + } + + protected static void printQ(PrintWriter out, double q) { + String format; + if (q < 1d) format = " % 8.3f"; + else if (q < 10d) format = " % 8.2f"; + else if (q < 100d) format = " % 8.1f"; + else { + format = " % 8.0f"; + if (q > 1000d) q = Math.rint(q/10d)*10d; + } + out.printf(Locale.US, format, q); + } + + + protected static void printHeader( + PrintWriter out, + CallMeta callMeta, + String river, + double km + ) { + out.println(Resources.getMsg( + callMeta, + I18N_AT_HEADER, + I18N_AT_HEADER, + new Object[] { river, km } )); + } + + + public void write(Writer writer, CallMeta meta, String river, double km) + throws IOException + { + PrintWriter out = new PrintWriter(writer); + + // a header is required, because the desktop version of FLYS will skip + // the first row. + printHeader(out, meta, river, km); + + double rest = (minW * 100.0) % 10.0; + + double startW = Math.rint((minW - rest*0.01)*10.0)*0.1; + + if (logger.isDebugEnabled()) { + logger.debug("startW: " + startW); + logger.debug("rest: " + rest); + } + + int col = 0; + for (double w = startW; w <= maxW; w += 0.01) { + if (col == 0) { + out.printf(Locale.US, "%8d", (int)Math.round(w*100.0)); + } + + if (w < minW) { + out.print(EMPTY); + } + else { + printQ(out, getQ(w)); + } + + if (++col >= COLUMNS) { + out.println(); + col = 0; + } + } + + if (col > 0) { + out.println(); + } + + out.flush(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/AbstractExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,232 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.resources.Resources; + +import de.intevation.flys.artifacts.FLYSArtifact; + + +/** + * An abstract exporter that implements some basic methods for exporting data of + * artifacts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class AbstractExporter implements OutGenerator { + + /** The logger used in this exporter.*/ + private static Logger logger = Logger.getLogger(AbstractExporter.class); + + + /** The name of the CSV facet which triggers the CSV creation. */ + public static final String FACET_CSV = "csv"; + + /** The default charset for the CSV export. */ + public static final String DEFAULT_CSV_CHARSET = "UTF-8"; + + /** The default separator for the CSV export. */ + public static final char DEFAULT_CSV_SEPARATOR = ','; + + /** XPath that points to the desired export facet. */ + public static final String XPATH_FACET = "/art:action/@art:type"; + + + /** 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; + + /** The selected facet. */ + protected String facet; + + /** The master artifact. */ + protected Artifact master; + + + /** + * Concrete subclasses need to use this method to write their special data + * objects into the CSV document. + * + * @param writer The CSVWriter. + */ + protected abstract void writeCSVData(CSVWriter writer); + + + /** + * This method enables concrete subclasses to collected its own special + * data. + * + * @param artifacts The artifact that stores the data that has to be + * exported. + */ + protected abstract void addData(Object data); + + @Override + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("AbstractExporter.init"); + + this.request = request; + this.out = out; + this.context = context; + } + + + @Override + public void setMasterArtifact(Artifact master) { + this.master = master; + } + + + /** + * This doOut() just collects the data of multiple artifacts. Therefore, it + * makes use of the addData() method which enables concrete subclasses to + * store its data on its own. The real output creation takes place in the + * concrete generate() methods. + * + * @param artifact The artifact. + * @param facet The facet to add - NOTE: the facet needs to fit to the first + * facet inserted into this exporter. Otherwise this artifact/facet is + * skipped. + * @param attr The attr document. + */ + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = facet.getName(); + + logger.debug("AbstractExporter.doOut: " + name); + + if (!isFacetValid(name)) { + logger.warn("Facet '" + name + "' not valid. No output created!"); + return; + } + + FLYSArtifact flys = (FLYSArtifact)artifact; + + Facet nativeFacet = flys.getNativeFacet(facet); + + if (nativeFacet != null) { + addData(nativeFacet.getData(flys, context)); + } + } + + + /** + * Generates an export based on a specified facet. + */ + @Override + public void generate() + throws IOException + { + logger.debug("AbstractExporter.generate"); + + if (facet != null && facet.equals(FACET_CSV)) { + generateCSV(); + } + else { + throw new IOException("invalid facet for exporter."); + } + } + + + /** + * Determines if the desired facet is valid for this exporter. If no facet + * is currently set, <i>facet</i> is set. + * + * @param facet The desired facet. + * + * @return true, if <i>facet</i> is valid, otherwise false. + */ + protected boolean isFacetValid(String facet) { + logger.debug("AbstractExporter.isFacetValid"); + + String thisFacet = getFacet(); + + if (thisFacet == null || thisFacet.length() == 0) { + return false; + } + else if (facet == null || facet.length() == 0) { + return false; + } + else { + return thisFacet.equals(facet); + } + } + + + /** + * Returns the name of the desired facet. + * + * @return the name of the desired facet. + */ + protected String getFacet() { + if (facet == null) { + facet = getFacetFromRequest(); + } + + return facet; + } + + + /** + * Extracts the name of the requested facet from request document. + * + * @return the name of the requested facet. + */ + protected String getFacetFromRequest() { + return XMLUtils.xpathString( + request, XPATH_FACET, ArtifactNamespaceContext.INSTANCE); + } + + + protected String msg(String key, String def) { + return Resources.getMsg(context.getMeta(), key, def); + } + + + /** + * This method starts CSV creation. It makes use of writeCSVData() which has + * to be implemented by concrete subclasses. + */ + protected void generateCSV() + throws IOException + { + logger.info("AbstractExporter.generateCSV"); + + CSVWriter writer = new CSVWriter( + new OutputStreamWriter( + out, + DEFAULT_CSV_CHARSET), + DEFAULT_CSV_SEPARATOR); + + writeCSVData(writer); + + writer.close(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartExportHelper.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.flys.exports; + +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.PageSize; +import com.lowagie.text.Rectangle; + +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdf.PdfWriter; + +import java.awt.Graphics2D; +import java.awt.Transparency; + +import java.awt.geom.Rectangle2D.Double; + +import java.awt.geom.Rectangle2D; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import org.jfree.chart.ChartRenderingInfo; + +import javax.imageio.ImageIO; + +import org.apache.batik.svggen.SVGGraphics2D; +import org.apache.batik.svggen.SVGGraphics2DIOException; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * This class is a helper class which supports some methods to export charts + * into specific formats. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ChartExportHelper { + + public static final String FORMAT_PNG = "png"; + + public static final String FORMAT_PDF = "pdf"; + + public static final String FORMAT_SVG = "svg"; + + + /** + * Constant field to define A4 as default page size. + */ + public static final String DEFAULT_PAGE_SIZE = "A4"; + + /** + * Constant field to define UTF-8 as default encoding. + */ + public static final String DEFAULT_ENCODING = "UTF-8"; + + /** + * Logger used for logging with log4j. + */ + private static Logger log = Logger.getLogger(ChartExportHelper.class); + + + /** + * A method to export a <code>JFreeChart</code> as image to an + * <code>OutputStream</code> with a given format, width and height. + * + * @param out OutputStream + * @param chart JFreeChart object to be exported. + * @param format Format (e.g. png, gif, jpg) + * @param width Width, the image used to be + * @param height Height, the image used to be + * + * @throws IOException if writing image to OutputStream failed. + */ + public static void exportImage( + OutputStream out, + JFreeChart chart, + CallContext cc + ) + throws IOException + { + log.info("export chart as png"); + + ChartRenderingInfo info = new ChartRenderingInfo(); + + String format = (String) cc.getContextValue("chart.image.format"); + + int[] size = getSize(cc); + + ImageIO.write( + chart.createBufferedImage( + size[0], size[1], Transparency.BITMASK, info + ), + format, + out + ); + } + + + /** + * A method to export a <code>JFreeChart</code> as SVG to an + * <code>OutputStream</code>. + * + * @param out OutputStream + * @param chart JFreeChart to be exported + * @param context The CallContext object that contains extra chart + * parameters. + */ + public static void exportSVG( + OutputStream out, + JFreeChart chart, + CallContext context + ) { + String encoding = (String) context.getContextValue("chart.encoding"); + + log.info("export chart as svg"); + + if (encoding == null) + encoding = DEFAULT_ENCODING; + + org.w3c.dom.Document document = XMLUtils.newDocument(); + SVGGraphics2D graphics = new SVGGraphics2D(document); + + int[] size = getSize(context); + + chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,size[0],size[1])); + + try { + graphics.stream(new OutputStreamWriter(out, encoding)); + } + catch (SVGGraphics2DIOException svge) { + log.error("Error while writing svg export to output stream.", svge); + } + catch (UnsupportedEncodingException uee) { + log.error("Unsupported encoding: " + encoding, uee); + } + } + + + /** + * A method to export a <code>JFreeChart</code> as PDF to an + * <code>OutputStream</code>. + * + * @param out OutputStream + * @param chart JFreeChart + * @param pageFormat String to specify a page format, {@link + * #DEFAULT_PAGE_SIZE} is used if no pageFormat is given + * @param landscape If this is true, the pdf is delivered in landscape + * format + * @param marginLeft Space to left border + * @param marginRight Space to right border + * @param marginTop Space to upper border + * @param marginBottom Space to lower border + */ + public static void exportPDF( + OutputStream out, + JFreeChart chart, + CallContext cc + ) { + log.info("export chart as pdf."); + + String pageFormat = (String) cc.getContextValue("chart.page.format"); + + if (pageFormat == null) + pageFormat = DEFAULT_PAGE_SIZE; + + // max size of the chart + Rectangle page = PageSize.getRectangle(pageFormat); + float pageWidth = page.getWidth(); + float pageHeight = page.getHeight(); + + // the chart width + int[] size = getSize(cc); + + boolean landscape = size[0] > size[1]; + + float width = 0; + float height = 0; + if (landscape) { + width = pageHeight; + height = pageWidth; + } + else { + width = pageWidth; + height = pageHeight; + } + + float marginLeft = (Float) cc.getContextValue( + "chart.marginLeft"); + + float marginRight = (Float) cc.getContextValue( + "chart.marginRight"); + + float marginTop = (Float) cc.getContextValue( + "chart.marginTop"); + + float marginBottom = (Float) cc.getContextValue( + "chart.marginBottom"); + + float spaceX = width - marginLeft - marginRight; + if (size[0] > spaceX) { + log.warn("Width of the chart is too big for pdf -> resize it now."); + double ratio = ((double)spaceX) / size[0]; + size[0] *= ratio; + size[1] *= ratio; + log.debug("Resized chart to " + size[0] + "x" + size[1]); + } + + float spaceY = height - marginTop - marginBottom; + if (size[1] > spaceY) { + log.warn("Height of the chart is too big for pdf -> resize it now."); + double ratio = ((double)spaceY) / size[1]; + size[0] *= ratio; + size[1] *= ratio; + log.debug("Resized chart to " + size[0] + "x" + size[1]); + } + + Document document = null; + if (landscape) { + document = new Document(page.rotate()); + log.debug("Create landscape pdf."); + } + else + document = new Document(page); + + try { + PdfWriter writer = PdfWriter.getInstance(document, out); + + document.addSubject(chart.getTitle().getText()); + document.addCreationDate(); + document.open(); + + PdfContentByte content = writer.getDirectContent(); + + PdfTemplate template = content.createTemplate(width, height); + Graphics2D graphics = template.createGraphics(width, height); + + double[] origin = getCenteredAnchor( + marginLeft, marginRight, marginBottom, marginTop, + width, height, + size[0], size[1]); + + Rectangle2D area = new Rectangle2D.Double( + origin[0], origin[1], size[0], size[1]); + + chart.draw(graphics, area); + graphics.dispose(); + content.addTemplate(template, 0f, 0f); + } + catch (DocumentException de) { + log.error("Error while exporting chart to pdf.", de); + } + finally { + document.close(); + } + } + + + public static int[] getSize(CallContext cc) { + int[] size = new int[2]; + + size[0] = (Integer) cc.getContextValue("chart.width"); + size[1] = (Integer) cc.getContextValue("chart.height"); + + return size; + } + + + /** + * This method returns the anchor of the chart so that the chart is centered + * according to the given parameters. + * + * @param mLeft Left margin + * @param mRight Right margin + * @param mBottom Bottom margin + * @param mTop Top margin + * @param width The complete width of the drawing area. + * @param height The complete height of the drawing area. + * @param chartWidth The width of the chart. + * @param chartHeight The height of the chart. + * + * @return an array that contains the anchor for a chart with the given + * parameters. The first value is the x point, the second value is the y + * point. + */ + public static double[] getCenteredAnchor( + double mLeft, double mRight, double mBottom, double mTop, + double width, double height, + double chartWidth, double chartHeight + ) { + if (log.isDebugEnabled()) { + log.debug("Calculate centered origin..."); + log.debug("-> PDF width : " + width); + log.debug("-> PDF height : " + height); + log.debug("-> Chart width : " + chartWidth); + log.debug("-> Chart height : " + chartHeight); + log.debug("-> margin left : " + mLeft); + log.debug("-> margin right : " + mRight); + log.debug("-> margin bottom: " + mBottom); + log.debug("-> margin top : " + mTop); + } + + double[] origin = new double[2]; + + double centerX = width / 2; + double centerY = height / 2; + + origin[0] = centerX - chartWidth / 2; + origin[1] = centerY - chartHeight / 2; + + origin[0] = origin[0] >= mLeft ? origin[0] : mLeft; + origin[1] = origin[1] >= mTop ? origin[1] : mTop; + + if (log.isDebugEnabled()) { + log.debug("==> centered left origin: " + origin[0]); + log.debug("==> centered top origin: " + origin[1]); + } + + return origin; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,292 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.jfree.data.Range; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.PreferredLocale; + +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.utils.FLYSUtils; + + +/** + * The base class for chart creation. It should provide some basic things that + * equal in all chart types. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class ChartGenerator implements OutGenerator { + + private static Logger logger = Logger.getLogger(ChartGenerator.class); + + /** The default chart width, if no other width is set. */ + public static final int DEFAULT_CHART_WIDTH = 600; + + /** The default chart height, if no other height is set.*/ + public static final int DEFAULT_CHART_HEIGHT = 400; + + /** The default chart format, if no other height is set.*/ + public static final String DEFAULT_CHART_FORMAT = "png"; + + /** The XPath that points to the chart size of the incoming request + * document.*/ + 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; + + /** The artifact that is used to decorate the chart with meta information.*/ + protected Artifact master; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("ChartGenerator.init"); + + this.request = request; + this.out = out; + this.context = context; + } + + + public void setMasterArtifact(Artifact master) { + this.master = master; + } + + + 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); + } + + + protected String msg(String key, String def) { + return Resources.getMsg(context.getMeta(), key, def); + } + + + protected String msg(String key, String def, Object[] args) { + return Resources.getMsg(context.getMeta(), key, def, args); + } + + + protected String getRiverName() { + WINFOArtifact flys = (WINFOArtifact) master; + + River river = FLYSUtils.getRiver(flys); + return (river != null) ? river.getName() : ""; + } + + + protected double[] getRange() { + WINFOArtifact flys = (WINFOArtifact) master; + + return FLYSUtils.getKmRange(flys); + } + + + /** + * 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 the result of + * getDefaultSize() 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 : getDefaultSize(); + } + + + 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; + } + + + protected Range getDomainAxisRange() { + 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"); + + logger.debug("FOUND X RANGE: " + lower + " -> " + upper); + + if (lower.length() > 0 && upper.length() > 0) { + try { + double from = Double.parseDouble(lower); + double to = Double.parseDouble(upper); + + 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() { + 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"); + + if (lower.length() > 0 && upper.length() > 0) { + try { + double from = Double.parseDouble(lower); + double to = Double.parseDouble(upper); + + if (from == 0 && to == 0) { + logger.debug("No range specified. Lower and upper Y == 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 value axis range."); + } + } + + return null; + } + + + /** + * 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 }; + } + + + public abstract void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible); + + public abstract void generate() throws IOException; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,156 @@ +package de.intevation.flys.exports; + +import de.intevation.flys.java2d.NOPGraphics2D; + +import java.io.IOException; +import java.io.OutputStream; + +import java.awt.Transparency; +import java.awt.Graphics2D; + +import java.awt.geom.Rectangle2D; + +import java.awt.image.BufferedImage; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.JFreeChart; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * An OutGenerator that generates meta information for charts. A concrete + * ChartInfoGenerator need to instantiate a concrete ChartGenerator and dispatch + * the methods to that instance. The only thing this ChartInfoGenerator needs + * to, is to overrite the generate() method which doesn't write the chart image + * to the OutputStream but a Document that contains some meta information of the + * created chart. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class ChartInfoGenerator implements OutGenerator { + + public static final boolean USE_NOP_GRAPHICS = + Boolean.getBoolean("info.rendering.nop.graphics"); + + /** The logger used in this generator.*/ + private static Logger logger = + Logger.getLogger(ChartInfoGenerator.class); + + + /** The OutGenerator that creates the charts.*/ + protected XYChartGenerator generator; + + protected OutputStream out; + + + + public ChartInfoGenerator(XYChartGenerator generator) { + this.generator = generator; + } + + + /** + * Dispatches the operation to the instantiated generator. + * + * @param request + * @param out + * @param context + */ + public void init(Document request, OutputStream out, CallContext context) { + this.out = out; + + generator.init(request, out, context); + } + + + /** + * Dispatches the operation to the instantiated generator. + * + * @param master + */ + public void setMasterArtifact(Artifact master) { + generator.setMasterArtifact(master); + } + + + /** + * Dispatches the operation to the instantiated generator. + * + * @param artifacts + * @param facet + * @param attr + */ + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + generator.doOut(artifact, facet, attr, visible); + } + + + /** + * This method generates the chart using a concrete ChartGenerator but + * doesn't write the chart itself to the OutputStream but a Document that + * contains meta information of the created chart. + */ + @Override + public void generate() + throws IOException + { + logger.debug("ChartInfoGenerator.generate"); + + JFreeChart chart = generator.generateChart(); + + int[] size = generator.getSize(); + + 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"); + } + + + InfoGeneratorHelper helper = new InfoGeneratorHelper(generator); + Document doc = helper.createInfoDocument(chart, info); + + XMLUtils.toStream(doc, out); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,120 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.utils.Formatter; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveExporter extends AbstractExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveExporter.class); + + + public static final String CSV_W_HEADER = + "export.computed.discharge.curve.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.computed.discharge.curve.csv.header.q"; + + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]"; + + + protected List<WQKms> data; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("ComputedDischargeCurveExporter.init"); + + super.init(request, out, context); + + this.data = new ArrayList<WQKms>(); + } + + + @Override + protected void addData(Object d) { + if (d instanceof CalculationResult) { + d = ((CalculationResult)d).getData(); + if (d instanceof WQKms []) { + data.addAll(Arrays.asList((WQKms [])d)); + } + } + } + + + protected void writeCSVData(CSVWriter writer) { + logger.info("ComputedDischargeCurveExporter.writeData"); + + writeCSVHeader(writer); + + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + double[] res = new double[3]; + + for (WQKms wqkms: data) { + int size = wqkms.size(); + + for (int i = 0; i < size; i++) { + res = wqkms.get(i, res); + + writer.writeNext(new String[] { + wf.format(res[0]), + qf.format(res[1]) + }); + } + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.debug("ComputedDischargeCurveExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return Formatter.getComputedDischargeW(context); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return Formatter.getComputedDischargeQ(context); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,143 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.xy.XYSeries; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveGenerator +extends DischargeCurveGenerator +implements FacetTypes +{ + /** The logger used in this generator. */ + private static Logger logger = + Logger.getLogger(ComputedDischargeCurveGenerator.class); + + public static final String I18N_CHART_TITLE = + "chart.computed.discharge.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.computed.discharge.curve.subtitle"; + + public static final String I18N_YAXIS_LABEL = + "chart.computed.discharge.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurve"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; + public static final String I18N_MAINVALUES_Q_LABEL = "Q (Haupt- und Extremwerte)"; + public static final String I18N_MAINVALUES_W_LABEL = "W (Haupt- und Extremwerte)"; + + + /** Trivial Constructor. */ + public ComputedDischargeCurveGenerator () { + super(); + } + + + @Override + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + dist[0] + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + @Override + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = (facet != null) ? facet.getName() : null; + + logger.debug("ComputedDischargeCurveGenerator.doOut: " + name); + + if (name == null) { + logger.warn("Broken facet in computed discharge out generation."); + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (name.equals(COMPUTED_DISCHARGE_Q)) { + doQOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) + || name.equals(MAINVALUES_Q)) { + doMainValueQAnnotations( + f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_W) + || name.equals(MAINVALUES_W)) { + doMainValueWAnnotations( + f.getData(artifact, context), facet, attr, visible); + } + else { + logger.warn("Unknown facet type for computed discharge: " + name); + return; + } + } + + + /** + * Add Q-Series to plot. + * @param wqkms actual data + * @param theme theme to use. + */ + protected void doQOut( + WQKms wqkms, + Facet facet, + Document theme, + boolean visible + ) { + int size = wqkms.size(); + + double[] res = new double[3]; + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + for (int i = 0; i < size; i++) { + res = wqkms.get(i, res); + series.add(res[1], res[0]); + } + + addFirstAxisSeries(series, visible); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +package de.intevation.flys.exports; + + +/** + * A ChartInfoGenerator that generates meta information for specific computed + * discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ComputedDischargeCurveInfoGenerator extends ChartInfoGenerator { + + public ComputedDischargeCurveInfoGenerator() { + super(new ComputedDischargeCurveGenerator()); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,222 @@ +package de.intevation.flys.exports; + +import java.awt.Font; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.xy.XYSeries; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.FacetTypes; + + +/** + * An OutGenerator that generates cross section graphs. + */ +public class CrossSectionGenerator +extends XYChartGenerator +implements FacetTypes +{ + /** The logger that is used in this generator. */ + private static Logger logger = + Logger.getLogger(CrossSectionGenerator.class); + + public static final String I18N_CHART_TITLE = + "chart.cross_section.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.cross_section.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.cross_section.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.cross_section.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "Querprofildiagramm"; + public static final String I18N_XAXIS_LABEL_DEFAULT = "Abstand [m]"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; + + + /** Trivial Constructor. */ + public CrossSectionGenerator() { + super(); + } + + + /** + * Get localized chart title. + */ + protected String getChartTitle() { + // TODO get river etc for localized heading + Object[] i18n_msg_args = new Object[] { + getRiverName() + }; + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, i18n_msg_args); + } + + + /** + * Add localized subtitle to chart. + */ + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + getKm() + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + /** + * Get localized X Axis label. + */ + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + /** + * Get cross_section.km data from artifact. + */ + protected Double getKm() { + try { + WINFOArtifact winfo = (WINFOArtifact) master; + return winfo.getCrossSectionSnapKm(); + } + catch (Exception e) { + logger.error("Cannot convert cross_section.km to double"); + return 0.0d; + } + } + + + /** + * Get localized Y Axis label. + */ + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + protected void adjustAxes(XYPlot plot) { + super.adjustAxes(plot); + + NumberAxis qAxis = new NumberAxis( + msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT)); + + plot.setRangeAxis(1, qAxis); + + Font font = plot.getRangeAxis(0).getLabelFont(); + qAxis.setLabelFont(font); + } + + + /** + * Let one facet do its job. + */ + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = facet.getName(); + + logger.debug("CrossSectionGenerator.doOut: " + name); + + if (name == null) { + logger.error("No facet name for doOut(). No output generated!"); + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (f == null) { + return; + } + + if (name.equals(CROSS_SECTION)) { + doCrossSectionOut( + f.getData(artifact, context), + f.getDescription(), + attr, + visible); + } + else if (name.equals(CROSS_SECTION_WATER_LINE)) { + doCrossSectionWaterLineOut( + f.getData(artifact, context), + f.getDescription(), + attr, + visible); + } + else { + logger.warn("CrossSection.doOut: Unknown facet name: " + name); + return; + } + } + + + /** + * Do cross sections waterline out. + * + * @param seriesName name of the data (line) to display in legend. + * @param theme Theme for the data series. + */ + protected void doCrossSectionWaterLineOut( + Object o, + String seriesName, + Document theme, + boolean visible + ) { + logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut"); + + // DO NOT SORT DATA! This destroys the gaps indicated by NaNs + XYSeries series = new StyledXYSeries(seriesName, false, theme); + + StyledSeriesBuilder.addPoints(series, (double [][]) o); + + addFirstAxisSeries(series, visible); + } + + + /** + * Do cross sections out. + * + * @param seriesName name of the data (line) to display in legend. + * @param theme Theme for the data series. + */ + protected void doCrossSectionOut( + Object o, + String seriesName, + Document theme, + boolean visible + ) { + logger.debug("CrossSectionGenerator.doCrossSectionOut"); + + XYSeries series = new StyledXYSeries(seriesName, theme); + + StyledSeriesBuilder.addPoints(series, (double [][]) o); + + addFirstAxisSeries(series, visible); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/CrossSectionInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,14 @@ +package de.intevation.flys.exports; + + +/** + * A ChartInfoGenerator that generates meta information for specific cross + * sections. + */ +public class CrossSectionInfoGenerator extends ChartInfoGenerator { + + public CrossSectionInfoGenerator() { + super(new CrossSectionGenerator()); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,222 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; + +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.jfree.FLYSAnnotation; + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeCurveGenerator +extends XYChartGenerator +implements FacetTypes { + + /** The logger used in this generator. */ + private static Logger logger = + Logger.getLogger(DischargeCurveGenerator.class); + + public static final String I18N_CHART_TITLE = + "chart.discharge.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.discharge.curve.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.discharge.curve.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.discharge.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurven"; + public static final String I18N_XAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [cm]"; + + + + public DischargeCurveGenerator() { + super(); + } + + + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + /** + * Empty (suppress subtitle). + */ + @Override + protected void addSubtitles(JFreeChart chart) { + ; + } + + + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + @Override + protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) { + boolean zoomin = super.zoom(plot, axis, range, x); + + if (!zoomin) { + axis.setLowerBound(0d); + } + + return zoomin; + } + + + public void doOut( + Artifact artifact, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("DischargeCurveGenerator.doOut: " + facet.getName()); + + String name = facet.getName(); + + FLYSArtifact flysArtifact = (FLYSArtifact) artifact; + + Facet nativeFacet = flysArtifact.getNativeFacet(facet); + + if (nativeFacet == null) { + logger.debug("no facet found"); + return; + } + + if (name.equals(DISCHARGE_CURVE)) { + doDischargeOut( + (WINFOArtifact) flysArtifact, + nativeFacet.getData(flysArtifact, context), + nativeFacet.getDescription(), + theme, + visible); + } + else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) + || name.equals(MAINVALUES_Q)) + { + doMainValueQAnnotations( + nativeFacet.getData(artifact, context), facet, theme, visible); + } + else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_W) + || name.equals(MAINVALUES_W)) + { + doMainValueWAnnotations( + nativeFacet.getData(artifact, context), facet, theme, visible); + } + else { + logger.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name); + return; + } + } + + + /** + * Add series with discharge curve to diagram. + */ + protected void doDischargeOut( + WINFOArtifact artifact, + Object o, + String description, + Document theme, + boolean visible) + { + WQKms wqkms = (WQKms) o; + + String gaugeName = wqkms.getName(); + + River river = FLYSUtils.getRiver(artifact); + + if (river == null) { + logger.debug("no river found"); + return; + } + + Gauge gauge = river.determineGaugeByName(gaugeName); + + if (gauge == null) { + logger.debug("no gauge found"); + return; + } + + XYSeries series = new StyledXYSeries(description, theme); + + StyledSeriesBuilder.addPointsQW(series, wqkms); + + addFirstAxisSeries(series, visible); + } + + + + /** + * Store W MainValues as annotations for later plotting. + */ + protected void doMainValueWAnnotations( + Object o, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("ComputedDischargeCurveGenerator set W MainValues."); + + FLYSAnnotation fa = (FLYSAnnotation) o; + fa.setTheme(theme); + fa.setLabel(facet.getDescription()); + addAnnotations(fa, visible); + } + + + /** + * Store Q MainValues as annotations for later plotting. + */ + protected void doMainValueQAnnotations( + Object o, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("ComputedDischargeCurveGenerator set Q MainValues."); + + FLYSAnnotation fa = (FLYSAnnotation) o; + fa.setTheme(theme); + fa.setLabel(facet.getDescription()); + addAnnotations(fa, visible); + } + + + +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,16 @@ +package de.intevation.flys.exports; + + +/** + * A ChartInfoGenerator that generates meta information for specific discharge + * curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeCurveInfoGenerator extends ChartInfoGenerator { + + public DischargeCurveInfoGenerator() { + super(new DischargeCurveGenerator()); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,121 @@ +package de.intevation.flys.exports; + +import java.text.NumberFormat; + +import org.apache.log4j.Logger; + +import au.com.bytecode.opencsv.CSVWriter; + +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.CalculationResult; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeLongitudinalSectionExporter extends WaterlevelExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSectionExporter.class); + + + public static final String CSV_KM_HEADER = + "export.discharge.longitudinal.section.csv.header.km"; + + public static final String CSV_W_HEADER = + "export.discharge.longitudinal.section.csv.header.w"; + + public static final String CSV_CW_HEADER = + "export.discharge.longitudinal.section.csv.header.cw"; + + public static final String CSV_Q_HEADER = + "export.discharge.longitudinal.section.csv.header.q"; + + public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_CW_HEADER = "W korr."; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]"; + + + @Override + protected void addData(Object d) { + if (d instanceof CalculationResult) { + d = ((CalculationResult)d).getData(); + if (d instanceof WQKms []) { + data.add((WQKms [])d); + } + } + } + + + @Override + protected void writeCSVHeader(CSVWriter writer) { + logger.info("WaterlevelExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_CW_HEADER, DEFAULT_CSV_CW_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + @Override + protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) { + logger.debug("WaterlevelExporter.wQKms2CSV"); + + int size = wqkms.size(); + double[] result = new double[4]; + + NumberFormat kmf = getKmFormatter(); + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + for (int i = 0; i < size; i ++) { + result = wqkms.get(i, result); + + String wc = ""; + if (wqkms instanceof WQCKms) { + wc = wf.format(result[3]); + } + + writer.writeNext(new String[] { + kmf.format(result[2]), + wf.format(result[0]), + wc, + qf.format(result[1]) + }); + } + } + + + @Override + protected void addWSTColumn(WstWriter writer, WQKms wqkms) { + String name = wqkms.getName(); + + // is it a W or a Q mode? + int wIdx = name.indexOf("W"); + int qIdx = name.indexOf("Q"); + + String wq = null; + if (wIdx >= 0) { + wq = "W"; + } + else if (qIdx >= 0) { + wq = "Q"; + } + + // we just want to display the first W or Q value in the WST + int start = name.indexOf("("); + int end = name.indexOf(")"); + + String tmp = name.substring(start+1, end); + String[] values = tmp.split(";"); + + String column = wq + "=" + values[0]; + + writer.addColumn(column); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,110 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.jfree.data.xy.XYSeries; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; + + +/** + * An OutGenerator that generates discharge longitudinal section curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DischargeLongitudinalSectionGenerator +extends LongitudinalSectionGenerator +{ + private static Logger logger = + Logger.getLogger(DischargeLongitudinalSectionGenerator.class); + + + + public DischargeLongitudinalSectionGenerator() { + super(); + } + + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + logger.debug("DischargeLongitudinalSectionGenerator.doOut"); + + if (facet == null) { + return; + } + + String name = facet.getName(); + + if (name == null) { + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (name.equals(DISCHARGE_LONGITUDINAL_W)) { + doWOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(DISCHARGE_LONGITUDINAL_Q)) { + doQOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(DISCHARGE_LONGITUDINAL_C)) { + doCorrectedWOut( + (WQCKms) f.getData(artifact, context), + facet, + attr, + visible); + } + 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, + Document 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)); + } + + addFirstAxisSeries(series, visible); + } + + if (wqckms.guessWaterIncreasing()) { + setInverted(true); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,17 @@ +package de.intevation.flys.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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,149 @@ +package de.intevation.flys.exports; + +import java.io.OutputStream; +import java.text.NumberFormat; +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 de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.WQDay; +import de.intevation.flys.artifacts.model.CalculationResult; + +import de.intevation.flys.utils.Formatter; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveExporter extends AbstractExporter { + + /** The logger used in this exporter. */ + private static Logger logger = Logger.getLogger(WaterlevelExporter.class); + + + public static final String CSV_DURATION_HEADER = + "export.duration.curve.csv.header.duration"; + + public static final String CSV_W_HEADER = + "export.duration.curve.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.duration.curve.csv.header.q"; + + public static final String DEFAULT_CSV_DURATION_HEADER = "D [Tagen]"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]"; + + + /** 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>(); + } + + + @Override + protected void addData(Object d) { + if (d instanceof CalculationResult) { + d = ((CalculationResult)d).getData(); + if (d instanceof WQDay) { + data.add((WQDay)d); + } + } + } + + + protected void writeCSVData(CSVWriter writer) { + logger.info("DurationCurveExporter.writeData"); + + writeCSVHeader(writer); + + for (WQDay wqday: data) { + wQDay2CSV(writer, wqday); + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.info("DurationCurveExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), + msg(CSV_DURATION_HEADER, DEFAULT_CSV_DURATION_HEADER) + }); + } + + + protected void wQDay2CSV(CSVWriter writer, WQDay wqday) { + logger.debug("DurationCurveExporter.wQDay2CSV"); + + int size = wqday.size(); + + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + NumberFormat df = getDFormatter(); + + if (wqday.isIncreasing()) { + for (int i = size-1; i >= 0; i --) { + writer.writeNext(new String[] { + wf.format(wqday.getW(i)), + qf.format(wqday.getQ(i)), + df.format(wqday.getDay(i)) + }); + } + } + else { + for (int i = 0; i < size; i ++) { + writer.writeNext(new String[] { + wf.format(wqday.getW(i)), + qf.format(wqday.getQ(i)), + df.format(wqday.getDay(i)) + }); + } + } + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return Formatter.getDurationW(context); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return Formatter.getDurationQ(context); + } + + + /** + * Returns the number formatter for duration values. + * + * @return the number formatter for duration values. + */ + protected NumberFormat getDFormatter() { + return Formatter.getDurationD(context); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,231 @@ +package de.intevation.flys.exports; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQDay; +import de.intevation.flys.artifacts.resources.Resources; + + +/** + * An OutGenerator that generates duration curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveGenerator +extends XYChartGenerator +implements FacetTypes +{ + private static Logger logger = + Logger.getLogger(DurationCurveGenerator.class); + + + public static final String I18N_DURATION_W = + "chart.duration.curve.curve.w"; + + public static final String I18N_DURATION_Q = + "chart.duration.curve.curve.q"; + + public static final String I18N_CHART_TITLE = + "chart.duration.curve.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.duration.curve.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.duration.curve.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.duration.curve.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = + "Dauerlinie"; + + public static final String I18N_XAXIS_LABEL_DEFAULT = + "Unterschreitungsdauer [Tage]"; + + public static final String I18N_YAXIS_LABEL_DEFAULT = + "W [NN + m]"; + + + public DurationCurveGenerator() { + super(); + } + + + protected String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + dist[0] + }; + + String subtitle = msg(I18N_CHART_SUBTITLE, "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + protected String getXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + + protected String getYAxisLabel() { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + + @Override + protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) { + boolean zoomin = super.zoom(plot, axis, range, x); + + if (!zoomin) { + axis.setLowerBound(0d); + } + + axis.setUpperBound(364); + + return zoomin; + } + + + protected void adjustAxes(XYPlot plot) { + super.adjustAxes(plot); + + NumberAxis qAxis = new NumberAxis("Q [m\u00b3/s]"); + + plot.setRangeAxis(1, qAxis); + } + + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = facet != null ? facet.getName() : null; + + logger.debug("DurationCurveGenerator.doOut: " + name); + + if (name == null || name.length() == 0) { + logger.error("No facet given. Cannot create dataset."); + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (name.equals(DURATION_W)) { + doWOut((WQDay) f.getData(artifact, context), attr, visible); + } + else if (name.equals(DURATION_Q)) { + doQOut((WQDay) f.getData(artifact, context), attr, visible); + } + else { + logger.warn("Unknown facet name: " + name); + return; + } + } + + + /** + * Creates the series for a duration curve's W facet. + * + * @param wqdays The WQDay store that contains the Ws. + * @param theme + */ + protected void doWOut(WQDay wqdays, Document theme, boolean visible) { + logger.debug("DurationCurveGenerator.doWOut"); + + // TODO find the correct series name + XYSeries series = new StyledXYSeries( + getSeriesName(getRiverName(), DURATION_W), theme); + + int size = wqdays.size(); + for (int i = 0; i < size; i++) { + int day = wqdays.getDay(i); + double w = wqdays.getW(i); + + series.add((double) day, w); + } + + addFirstAxisSeries(series, visible); + } + + + /** + * Creates the series for a duration curve's Q facet. + * + * @param wqdays The WQDay store that contains the Qs. + * @param theme + */ + protected void doQOut(WQDay wqdays, Document theme, boolean visible) { + logger.debug("DurationCurveGenerator.doQOut"); + + // TODO find the correct series name + XYSeries series = new StyledXYSeries( + getSeriesName(getRiverName(), DURATION_Q), theme); + + int size = wqdays.size(); + for (int i = 0; i < size; i++) { + int day = wqdays.getDay(i); + double q = wqdays.getQ(i); + + series.add((double) day, q); + } + + addSecondAxisSeries(series, visible); + } + + + protected String getSeriesName(String river, String type) { + Object[] args = new Object[] { river }; + + if (type == null || type.length() == 0) { + logger.warn("No duration curve type given."); + return "n/a"; + } + else if (type.equals(DURATION_W)) { + return Resources.getMsg( + context.getMeta(), + I18N_DURATION_W, + "W", + args); + } + else if (type.equals(DURATION_Q)) { + return Resources.getMsg( + context.getMeta(), + I18N_DURATION_Q, + "W", + args); + } + + logger.warn("Could not determine chart curve type: " + type); + return type; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,17 @@ +package de.intevation.flys.exports; + + +/** + * A ChartInfoGenerator that generates meta information for specific duration + * curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DurationCurveInfoGenerator +extends ChartInfoGenerator +{ + public DurationCurveInfoGenerator() { + super(new DurationCurveGenerator()); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,316 @@ +package de.intevation.flys.exports; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; + +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.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.Range; +import org.jfree.data.xy.XYDataset; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * This class helps generating chart info documents. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class InfoGeneratorHelper { + + private static final Logger logger = + Logger.getLogger(InfoGeneratorHelper.class); + + + protected XYChartGenerator generator; + + + public InfoGeneratorHelper(XYChartGenerator 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) + { + Range range = axis.getRange(); + + Element e = cr.create(type); + cr.addAttr(e, "pos", String.valueOf(pos), true); + cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true); + cr.addAttr(e, "to", String.valueOf(range.getUpperBound()), true); + + //Range[] rs = generator.getRangesForDataset(dataset); + Range[] rs = generator.getRangesForDataset(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; + } + + + /** + * 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); + + 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 xRange The x axis range. + * @param yRange The y axis range. + * + * @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 = xAxis.getRange(); + Range yRange = yAxis.getRange(); + + 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 }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,467 @@ +package de.intevation.flys.exports; + +import java.awt.Font; + +import org.apache.log4j.Logger; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.title.TextTitle; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.jfree.FLYSAnnotation; + +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.DataUtil; + + +/** + * An OutGenerator that generates discharge curves. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class LongitudinalSectionGenerator +extends XYChartGenerator +implements FacetTypes +{ + /** The logger that is used in this generator. */ + private static Logger logger = + Logger.getLogger(LongitudinalSectionGenerator.class); + + /** Key to look up internationalized String for annotations label. */ + public static final String I18N_ANNOTATIONS_LABEL = + "chart.longitudinal.annotations.label"; + + /** + * Key to look up internationalized String for LongitudinalSection diagrams + * titles. + */ + public static final String I18N_CHART_TITLE = + "chart.longitudinal.section.title"; + + /** + * Key to look up internationalized String for LongitudinalSection diagrams + * subtitles. + */ + public static final String I18N_CHART_SUBTITLE = + "chart.longitudinal.section.subtitle"; + + public static final String I18N_XAXIS_LABEL = + "chart.longitudinal.section.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.longitudinal.section.yaxis.label"; + + public static final String I18N_2YAXIS_LABEL = + "chart.longitudinal.section.yaxis.second.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = "W-L\u00e4ngsschnitt"; + public static final String I18N_XAXIS_LABEL_DEFAULT = "km"; + public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; + public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; + + /** Whether or not the plot is inverted (left-right). */ + protected boolean inverted; + + + public LongitudinalSectionGenerator() { + super(); + } + + + public boolean isInverted() { + return inverted; + } + + + public void setInverted(boolean inverted) { + this.inverted = inverted; + } + + + /** + * Get internationalized title for chart. + */ + public String getChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + + /** + * Gets key to look up internationalized String for the charts subtitle. + * @return key to look up translated subtitle. + */ + protected String getChartSubtitleKey() { + return I18N_CHART_SUBTITLE; + } + + + /** + * Add (internationalized) subtitle to chart. + * @see getChartSubtitleKey + */ + @Override + protected void addSubtitles(JFreeChart chart) { + double[] dist = getRange(); + + Object[] args = new Object[] { + getRiverName(), + + dist[0], + + dist[1] + }; + + String subtitle = msg(getChartSubtitleKey(), "", args); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + /** + * Get internationalized label for the x axis. + */ + protected String getXAxisLabel() { + FLYSArtifact flys = (FLYSArtifact) master; + + return msg( + I18N_XAXIS_LABEL, + I18N_XAXIS_LABEL_DEFAULT, + new Object[] { FLYSUtils.getRiver(flys).getName() }); + } + + + /** + * Get internationalized label for the y axis. + */ + protected String getYAxisLabel() { + FLYSArtifact flys = (FLYSArtifact) master; + + String unit = FLYSUtils.getRiver(flys).getWstUnit().getName(); + + return msg( + I18N_YAXIS_LABEL, + I18N_YAXIS_LABEL_DEFAULT, + new Object[] { unit }); + } + + + /** + * Get default value for the second Y-Axis' label (if no translation was + * found). + */ + protected String get2YAxisDefaultLabel() { + return I18N_2YAXIS_LABEL_DEFAULT; + } + + + /** + * Get key for internationalization of the second Y-Axis' label. + */ + protected String get2YAxisLabelKey() { + return I18N_2YAXIS_LABEL; + } + + + /** + * Adjust the axis to meet LongitudinalSection diagram demands. + * (e.g. add second Y-axis with internationalized label, trigger + * inversion). + * @param see get2YAxisLabelKey, get2YAxisDefaultLabel + */ + @Override + protected void adjustAxes(XYPlot plot) { + super.adjustAxes(plot); + + NumberAxis qAxis = new NumberAxis( + msg(get2YAxisLabelKey(), get2YAxisDefaultLabel())); + + plot.setRangeAxis(1, qAxis); + + Font font = plot.getRangeAxis(0).getLabelFont(); + qAxis.setLabelFont(font); + + invertXAxis(plot.getDomainAxis()); + } + + + /** + * Create a range that includes 0 (for the Q axis). + * @param range range with which to look up upper bound. + * @return range with 0 included. + */ + protected Range createSecondAxisRange(Range range) { + if (range == null) { + return null; + } + return new Range(0d, range.getUpperBound()); + } + + + /** + * This method overrides the XYChartGenerators zoomY method to be able to + * modify the range of the Q axis (here, it shall include 0). + */ + @Override + protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) { + if (plot.getRangeAxisIndex(axis) == 1) { + // We want the Q axis to start at 0 if no zooming has been done. + range = createSecondAxisRange(range); + } + + return super.zoomY(plot, axis, range, x); + } + + + /** + * This method inverts the x-axis based on the kilometer information of the + * selected river. If the head of the river is at kilometer 0, the axis is + * not inverted, otherwise it is. + * + * @param xaxis The domain axis. + */ + protected void invertXAxis(ValueAxis xaxis) { + + if (inverted) { + logger.debug("X-Axis.setInverted(true)"); + xaxis.setInverted(true); + } + } + + + /** + * Produce output. + * @param facet current facet. + * @param attr theme for facet + */ + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = facet.getName(); + + logger.debug("LongitudinalSectionGenerator.doOut: " + name); + + if (name == null) { + logger.error("No facet name for doOut(). No output generated!"); + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (f == null) { + return; + } + + if (name.equals(LONGITUDINAL_W)) { + doWOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(LONGITUDINAL_Q)) { + doQOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(LONGITUDINAL_ANNOTATION)) { + doAnnotationsOut(f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(STATIC_WKMS) || name.equals(HEIGHTMARKS_POINTS)) { + doWOut((WKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(W_DIFFERENCES)) { + doWDifferencesOut( + (WKms) f.getData(artifact, context), + facet, + attr, + visible); + } + else { + logger.warn("Unknown facet name: " + name); + return; + } + } + + + /** + * Register annotations available for the diagram. + * + * @param o list of annotations (data of facet). + * @param facet The facet. This facet does NOT support any data objects. Use + * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports + * data. + * @param theme yet ignored. + * @param visible The visibility of the annotations. + */ + protected void doAnnotationsOut( + Object o, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("LongitudinalSectionGenerator.doAnnotationsOut"); + + // Add all annotations in list o to our annotation pool. + // TODO Duplicate in ComputedDischartgeCurveGenerator, merge + FLYSAnnotation fa = (FLYSAnnotation) o; + fa.setTheme(theme); + fa.setLabel(facet.getDescription()); + addAnnotations(fa, visible); + } + + + /** + * Process the output for W facets in a longitudinal section curve. + * + * @param wqkms An array of WQKms values. + * @param facet The facet. This facet does NOT support any data objects. Use + * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports + * data. + * @param theme The theme that contains styling information. + * @param visible The visibility of the curve. + */ + protected void doWOut( + WKms wkms, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("LongitudinalSectionGenerator.doWOut"); + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + + StyledSeriesBuilder.addPoints(series, wkms); + + addFirstAxisSeries(series, visible); + + if (wkms instanceof WQKms) { + if (needInvertAxis((WQKms) wkms)) { + setInverted(true); + } + } + } + + + /** + * Add items to dataseries which describes the differences. + */ + protected void doWDifferencesOut( + WKms wkms, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("WDifferencesCurveGenerator.doWDifferencesOut"); + if (wkms == null) { + logger.warn("No data to add to WDifferencesChart."); + return; + } + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + + if (logger.isDebugEnabled()) { + if (wkms.size() > 0) { + logger.debug("Generate series: " + series.getKey()); + logger.debug("Start km: " + wkms.getKm(0)); + logger.debug("End km: " + wkms.getKm(wkms.size() - 1)); + logger.debug("Values : " + wkms.size()); + } + } + + StyledSeriesBuilder.addPoints(series, wkms); + + addSecondAxisSeries(series, visible); + if (DataUtil.guessWaterIncreasing(wkms.allWs())) { + setInverted(true); + } + } + + + + /** + * Process the output for Q facets in a longitudinal section curve. + * + * @param wqkms An array of WQKms values. + * @param facet The facet. This facet does NOT support any data objects. Use + * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports + * data. + * @param theme The theme that contains styling information. + * @param visible The visibility of the curve. + */ + protected void doQOut( + WQKms wqkms, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("LongitudinalSectionGenerator.doQOut"); + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + + StyledSeriesBuilder.addPoints(series, wqkms); + + addSecondAxisSeries(series, 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 wqkms The data object that stores the x and y values used for this + * chart. + */ + public boolean needInvertAxis(WQKms wqkms) { + boolean wsUp = wqkms.guessWaterIncreasing(); + boolean kmUp = DataUtil.guessWaterIncreasing(wqkms.allKms()); + boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp); + + int size = wqkms.size(); + + if (logger.isDebugEnabled()) { + logger.debug("Values : " + size); + if (size > 0) { + logger.debug("Start km: " + wqkms.getKm(0)); + logger.debug("End km: " + wqkms.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. + */ + protected String getSeriesName(WQKms 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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,17 @@ +package de.intevation.flys.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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/MapGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,287 @@ +package de.intevation.flys.exports; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.log4j.Logger; + +import com.vividsolutions.jts.geom.Envelope; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WMSDBLayerFacet; +import de.intevation.flys.artifacts.model.WMSLayerFacet; +import de.intevation.flys.utils.GeometryUtils; +import de.intevation.flys.utils.MapfileGenerator; +import de.intevation.flys.utils.ThemeUtil; + + +public class MapGenerator implements OutGenerator, FacetTypes { + + private static Logger logger = Logger.getLogger(MapGenerator.class); + + + protected Artifact master; + + protected Document request; + + protected OutputStream out; + + protected CallContext context; + + protected List<WMSLayerFacet> layers; + + protected Envelope maxExtent; + protected Envelope initialExtent; + + protected String srid; + + + + @Override + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("MapGenerator.init"); + + this.request = request; + this.out = out; + this.context = context; + + this.layers = new ArrayList<WMSLayerFacet>(); + + this.maxExtent = null; + this.initialExtent = null; + } + + + @Override + public void setMasterArtifact(Artifact master) { + logger.debug("MapGenerator.setMasterArtifact"); + this.master = master; + } + + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible) + { + String name = facet.getName(); + + logger.debug("MapGenerator.doOut: " +artifact.identifier()+" | "+name); + FLYSArtifact flys = (FLYSArtifact) artifact; + + Facet nativeFacet = flys.getNativeFacet(facet); + + if (nativeFacet instanceof WMSLayerFacet) { + WMSLayerFacet wms = (WMSLayerFacet) nativeFacet; + Envelope extent = wms.getExtent(); + + layers.add(wms); + + setMaxExtent(extent); + setSrid(wms.getSrid()); + + if (FLOODMAP_WSPLGEN.equals(name)) { + if (initialExtent == null) { + setInitialExtent(extent); + } + + createWSPLGENLayer(flys, wms); + } + else if (FLOODMAP_BARRIERS.equals(name)) { + createBarriersLayer(flys, wms); + } + else { + createDatabaseLayer(flys, wms, attr); + } + } + else { + logger.warn("Facet not supported: " + nativeFacet.getClass()); + } + } + + + protected void createWSPLGENLayer(FLYSArtifact flys, WMSLayerFacet wms) { + try { + MapfileGenerator mfg = MapfileGenerator.getInstance(); + mfg.createUeskLayer(flys, wms); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + } + + + protected void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms) { + MapfileGenerator mfg = MapfileGenerator.getInstance(); + + try { + mfg.createBarriersLayer(flys, wms); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + } + + + protected void createDatabaseLayer( + FLYSArtifact flys, + WMSLayerFacet wms, + Document attr + ) { + logger.debug("createDatabaseLayer for facet: " + wms.getName()); + + MapfileGenerator mfg = MapfileGenerator.getInstance(); + + try { + File baseDir = mfg.getShapefileBaseDir(); + File artDir = new File(baseDir, flys.identifier()); + + if (artDir != null && !artDir.exists()) { + logger.debug("Create new directory: " + artDir.getPath()); + artDir.mkdir(); + } + + if (wms instanceof WMSDBLayerFacet) { + mfg.createDatabaseLayer( + flys, + (WMSDBLayerFacet) wms, + ThemeUtil.createMapserverStyle(attr)); + } + else { + logger.warn("Cannot create DB layer from: " + wms.getClass()); + } + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + } + + + @Override + public void generate() + throws IOException + { + logger.debug("MapGenerator.generate"); + + MapfileGenerator.getInstance().update(); + + Document response = XMLUtils.newDocument(); + ElementCreator c = new ElementCreator( + response, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = c.create("floodmap"); + Element layers = c.create("layers"); + + response.appendChild(root); + root.appendChild(layers); + + appendLayers(layers); + appendMapInformation(root, c); + + XMLUtils.toStream(response, out); + } + + + protected void appendLayers(Element parent) { + for (WMSLayerFacet facet: layers) { + parent.appendChild(facet.toXML(parent.getOwnerDocument())); + } + } + + + protected void setMaxExtent(Envelope maxExtent) { + if (maxExtent == null) { + return; + } + + if (this.maxExtent == null) { + logger.debug("Set max extent to: " + maxExtent); + this.maxExtent = maxExtent; + return; + } + + logger.debug("Expand max extent by: " + maxExtent); + logger.debug("Max extent before expanding: " + this.maxExtent); + this.maxExtent.expandToInclude(maxExtent); + logger.debug("Max extent after expanding: " + this.maxExtent); + } + + + protected void setInitialExtent(Envelope initialExtent) { + if (initialExtent == null) { + return; + } + + if (this.initialExtent == null) { + logger.debug("Set initial extent to: " + initialExtent); + this.initialExtent = initialExtent; + return; + } + + logger.debug("Set initial extent to: " + initialExtent); + this.initialExtent = initialExtent; + } + + + protected void setSrid(String srid) { + if (srid == null || srid.length() == 0) { + return; + } + + this.srid = srid; + } + + + protected void appendMapInformation(Element parent, ElementCreator c) { + String mE = GeometryUtils.jtsBoundsToOLBounds(this.maxExtent); + logger.debug("BUILD MAX EXTENT OF:" + this.maxExtent); + logger.debug("BUILD MAX EXTENT:" + mE); + + Element maxExtent = c.create("maxExtent"); + maxExtent.setTextContent(mE); + + String iE = GeometryUtils.jtsBoundsToOLBounds(this.initialExtent); + logger.debug("BUILD INITIAL EXTENT OF: " + this.initialExtent); + logger.debug("BUILD INITIAL EXTENT: " + iE); + Element initExtent = c.create("initialExtent"); + initExtent.setTextContent(iE); + + Element srid = c.create("srid"); + srid.setTextContent(this.srid); + + // TODO zoom levels + // TODO resolutation + + parent.appendChild(maxExtent); + parent.appendChild(initExtent); + parent.appendChild(srid); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/OutGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,59 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + + +/** + * An OutGenerator is used to create a collected outputs of a list of Artifacts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface OutGenerator { + + /** + * Initializes the OutGenerator with meta information which are necessary + * for the output generation. + * + * @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); + + /** + * This method is used to tell the OutGenerator which artifact is the master + * artifact which is used for special operations. + * + * @param master The master artifact. + */ + void setMasterArtifact(Artifact master); + + /** + * Creates the output of an Artifact and appends that single output to the + * total output. + * + * @param artifact The artifact that provides information and data for the + * single output. + * @param attr A document that might contain some attributes used while + * producing the output. + * @param visible Specifies, if this output should be visible or not. + */ + void doOut(Artifact artifact, Facet facet, Document attr, boolean visible); + + /** + * Writes the collected output of all artifacts specified in the + * <i>request</i> (see init()) document to the OutputStream <i>out</i> (see + * init()). + */ + void generate() throws IOException; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ReportGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,67 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; + +import de.intevation.flys.artifacts.model.Calculation; + +import org.w3c.dom.Document; + +public class ReportGenerator +implements OutGenerator +{ + private static Logger logger = Logger.getLogger(ReportGenerator.class); + + protected Document result; + protected OutputStream out; + protected CallContext context; + + public ReportGenerator() { + } + + @Override + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("init"); + this.out = out; + this.context = context; + result = XMLUtils.newDocument(); + } + + @Override + public void setMasterArtifact(Artifact master) { + // not needed + } + + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + logger.debug("doOut"); + facet = ((FLYSArtifact)artifact).getNativeFacet(facet); + if (facet != null) { + Calculation report = (Calculation)facet.getData(artifact, context); + report.toXML(result, context.getMeta()); + } + } + + @Override + public void generate() throws IOException { + logger.debug("generate"); + XMLUtils.toStream(result, out); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/StyledSeriesBuilder.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,84 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.jfree.data.xy.XYSeries; + +import de.intevation.flys.artifacts.model.WKms; +import de.intevation.flys.artifacts.model.WQKms; + +/** + * Helper to create and modify StyledXYSeries. + */ +public class StyledSeriesBuilder { + + private static final Logger logger = Logger.getLogger(StyledSeriesBuilder.class); + + + /** + * Trivial, hidden constructor. + */ + private StyledSeriesBuilder() {;} + + + /** + * Add points to series. + * + * @param series Series to add points to. + * @param points Points to add to series, points[0] to 1st dim, points[1] + * to 2nd dim. + */ + public static void addPoints(XYSeries series, double[][] points) { + double [] xPoints = points[0]; + double [] yPoints = points[1]; + for (int i = 0; i < xPoints.length; i++) { + series.add(xPoints[i], yPoints[i], false); + } + } + + + /** + * Add points to series (km to 1st dim, w to 2nd dim). + * + * @param series Series to add points to. + * @param points Points to add to series. + */ + public static void addPoints(XYSeries series, WKms wkms) { + int size = wkms.size(); + + for (int i = 0; i < size; i++) { + series.add(wkms.getKm(i), wkms.getW(i), false); + } + } + + + /** + * Add points to series (w to 1st dim, km to 2nd dim). + * + * @param series Series to add points to. + * @param points Points to add to series. + */ + public static void addPointsKmQ(XYSeries series, WQKms wqkms) { + int size = wqkms.size(); + + for (int i = 0; i < size; i++) { + series.add(wqkms.getKm(i), wqkms.getQ(i), false); + } + } + + + /** + * Add points to series (q to 1st dim, w to 2nd dim). + * + * @param series Series to add points to. + * @param points Points to add to series. + */ + public static void addPointsQW(XYSeries series, WQKms wqkms) { + int size = wqkms.size(); + + for (int i = 0; i < size; i++) { + series.add(wqkms.getQ(i), wqkms.getW(i)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/StyledXYSeries.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,101 @@ +package de.intevation.flys.exports; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.Ellipse2D; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; + +import de.intevation.flys.utils.ThemeUtil; + +/** + * Dataset in two dimensions with additional theme-document. + * The theme-document will later "style" the graphical representation. + */ +public class StyledXYSeries extends XYSeries { + + protected Document theme; + + private static final Logger logger = Logger.getLogger(StyledXYSeries.class); + + + public StyledXYSeries(String key, Document theme) { + this(key, true, theme); + } + + + /** + * @param sorted whether or not to sort the points. Sorting will move NANs + * to one extrema which can cause problems in certain + * algorithms. + */ + public StyledXYSeries(String key, boolean sorted, Document theme) { + super(key, sorted); + this.theme = theme; + } + + + public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx){ + applyLineColor(r, idx); + applyLineSize(r, idx); + applyLineType(r, idx); + applyShowLine(r, idx); + applyShowPoints(r, idx); + + return r; + } + + + protected void applyLineColor(XYLineAndShapeRenderer r, int idx) { + Color c = ThemeUtil.parseLineColorField(theme); + r.setSeriesPaint(idx, c); + } + + + protected void applyLineSize(XYLineAndShapeRenderer r, int idx) { + int size = ThemeUtil.parseLineWidth(theme); + r.setSeriesStroke( + idx, + new BasicStroke(Integer.valueOf(size))); + } + + + protected void applyLineType(XYLineAndShapeRenderer r, int idx) { + int size = ThemeUtil.parseLineWidth(theme); + float[] dashes = ThemeUtil.parseLineStyle(theme); + + // Do not apply the dashed style. + if (dashes.length <= 1) { + return; + } + + r.setSeriesStroke( + idx, + new BasicStroke(Integer.valueOf(size), + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, + 1.0f, + dashes, + 0.0f)); + } + + + protected void applyShowPoints(XYLineAndShapeRenderer r, int idx) { + boolean show = ThemeUtil.parseShowPoints(theme); + r.setSeriesShape(idx, new Ellipse2D.Double(-2.0, -2.0, 4.0, 4.0)); + r.setSeriesShapesVisible(idx, show); + r.setDrawOutlines(true); + } + + + protected void applyShowLine(XYLineAndShapeRenderer r, int idx) { + boolean show = ThemeUtil.parseShowLine(theme); + r.setSeriesLinesVisible(idx, show); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,295 @@ +package de.intevation.flys.exports; + +import org.apache.log4j.Logger; + +import org.jfree.chart.title.TextTitle; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.Range; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WKms; + +import de.intevation.flys.utils.DataUtil; + +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.utils.FLYSUtils; + + +/** + * 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); + + /** 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"; + + /** Key for internationalized title of WDiff charts. */ + public final static String I18N_WDIFF_2YAXIS_LABEL = + "chart.w_differences.yaxis.second.label"; + + /** Default for label for second Y-Axis when no translation found. */ + public final static String I18N_WDIFF_2YAXIS_LABEL_DEFAULT = "W [NN + m]"; + + public final static String I18N_WDIFF_SUBTITLE = + "chart.w_differences.subtitle"; + + public final static String I18N_WDIFF_YAXIS_LABEL = + "chart.w_differences.yaxis.label"; + + public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m"; + + + /** + * Get internationalized title for chart. + * @return internationalized Chart title. + */ + @Override + public String getChartTitle() { + return msg(I18N_WDIFF_TITLE, I18N_WDIFF_TITLE_DEFAULT); + } + + + /** + * Get default value for the second Y-Axis' label (if no translation was + * found). + * @return default value for second y-axis label. + */ + @Override + protected String get2YAxisDefaultLabel() { + return I18N_WDIFF_2YAXIS_LABEL_DEFAULT; + } + + + /** + * 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; + } + + + /** + * Get key for internationalization of the second Y-Axis' label. + * @return internationalized second y-axis. + */ + @Override + protected String get2YAxisLabelKey() { + return I18N_WDIFF_2YAXIS_LABEL; + } + + + /** + * Get internationalized label for the y axis. + * @return internationalized label for y axos. + */ + @Override + protected String getYAxisLabel() { + FLYSArtifact flys = (FLYSArtifact) master; + + String unit = FLYSUtils.getRiver(flys).getWstUnit().getName(); + + return msg( + I18N_WDIFF_YAXIS_LABEL, + I18N_WDIFF_YAXIS_LABEL_DEFAULT, + new Object[] { unit }); + } + + + /** + * Add (themed) data for chart generation. + */ + @Override + public void doOut( + Artifact artifact, + Facet facet, + Document attr, + boolean visible + ) { + String name = facet.getName(); + + logger.debug("WDifferencesCurveGenerator.doOut: " + name); + + if (name == null) { + logger.error("No facet name for doOut(). No output generated!"); + return; + } + + FLYSArtifact flys = (FLYSArtifact) artifact; + Facet f = flys.getNativeFacet(facet); + + if (f == null) { + return; + } + + if (name.equals(W_DIFFERENCES)) { + doWDifferencesOut( + (WKms) f.getData(artifact, context), + facet, + attr, + visible); + } + else if (name.equals(LONGITUDINAL_W)) { + doWOut((WQKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(STATIC_WKMS)) { + doWOut((WKms) f.getData(artifact, context), facet, attr, visible); + } + else if (name.equals(LONGITUDINAL_ANNOTATION)) { + doAnnotationsOut(f.getData(artifact, context), facet, attr, visible); + } + else { + logger.warn("Unknown facet name: " + name); + return; + } + } + + + /** + * Add the waterlevel-curves (the "absolutes" from which + * differences were calculated). + * + * @param wqkms The wqkms to add to the diagram. + * @param theme The theme that contains styling information. + */ + protected void doWOut( + WKms wkms, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("WDifferencesCurveGenerator.doWOut"); + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + + StyledSeriesBuilder.addPoints(series, wkms); + + // Note: the only difference in the super-implementation + // (in LongitudinalSectionGenerator) is here (adds with + // addFirstAxisSeries() . + addSecondAxisSeries(series, visible); + + if (wkms instanceof WQKms) { + if (needInvertAxis((WQKms) wkms)) { + setInverted(true); + } + } + } + + /** + * Add (internationalized) subtitle to chart. + * Overridden to avoid trying to access the range of masterartifact. + * @see getChartSubtitleKey + */ + @Override + protected void addSubtitles(JFreeChart chart) { + + // TODO i18n + /* + Object[] args = new Object[] { + getRiverName() + }; + */ + String subtitle = getRiverName(); + chart.addSubtitle(new TextTitle(subtitle)); + } + + + /** + * Add items to dataseries which describes the differences. + */ + protected void doWDifferencesOut( + WKms wkms, + Facet facet, + Document theme, + boolean visible + ) { + logger.debug("WDifferencesCurveGenerator.doWDifferencesOut"); + if (wkms == null) { + logger.warn("No data to add to WDifferencesChart."); + return; + } + + XYSeries series = new StyledXYSeries(facet.getDescription(), theme); + + if (logger.isDebugEnabled()) { + if (wkms.size() > 0) { + logger.debug("Generate series: " + series.getKey()); + logger.debug("Start km: " + wkms.getKm(0)); + logger.debug("End km: " + wkms.getKm(wkms.size()-1)); + logger.debug("Values : " + wkms.size()); + } + } + + StyledSeriesBuilder.addPoints(series, wkms); + + addFirstAxisSeries(series, visible); + if (DataUtil.guessWaterIncreasing(wkms.allWs())) { + setInverted(true); + } + } + + /** + * Disable Longitudinals behaviour to include "0" in the Q axis. + * + * @param range range with which to look up upper bound. + * @return range to be used for "auto-scaling" axis. + */ + @Override + protected Range createSecondAxisRange(Range range) { + if (range == null) { + return range; + } + return new Range(range.getLowerBound(), range.getUpperBound()); + } + + + /** + * + */ + @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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesCurveInfoGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,15 @@ +package de.intevation.flys.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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WDifferencesExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,161 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.NumberFormat; +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 de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.WKms; + +import de.intevation.flys.utils.Formatter; + +/** + * (CSV)Exporter for WDifferences. + */ +public class WDifferencesExporter extends AbstractExporter { + + /** The logger used in this exporter. */ + private static Logger logger = Logger.getLogger(WDifferencesExporter.class); + + + public static final String WDIFF_CSV_KM_HEADER = + "export.w_differences.csv.header.km"; + + public static final String WDIFF_CSV_W_HEADER = + "export.w_differences.csv.header.w"; + + public static final String WDIFF_DEFAULT_CSV_KM_HEADER = "Fluss-Km"; + public static final String WDIFF_DEFAULT_CSV_W_HEADER = "m"; + + + /** 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[]>(); + } + + + /** + * Genereate data in csv format. + */ + @Override + public void generate() + throws IOException + { + logger.debug("WDifferencesExporter.generate"); + + if (facet == null) { + throw new IOException("invalid (null) facet for exporter"); + } + else if (facet.equals(AbstractExporter.FACET_CSV)) { + generateCSV(); + } + else { + throw new IOException("invalid facet (" + facet + ") for exporter"); + } + } + + + /** + * Adds given data. + * @param d either a WKms or a CalculationResult to add to data. + */ + @Override + protected void addData(Object d) { + if (d instanceof CalculationResult) { + d = ((CalculationResult)d).getData(); + if (d instanceof WKms []) { + data.add((WKms [])d); + } + } + else if (d instanceof WKms) { + data.add(new WKms[] { (WKms) d }); + } + } + + + /** + * Lets writer write all data (including header). + * @param writer Writer to write data with. + */ + @Override + protected void writeCSVData(CSVWriter writer) { + logger.info("WDifferencesExporter.writeData"); + + writeCSVHeader(writer); + + for (WKms[] tmp: data) { + for (WKms wkms: tmp) { + wKms2CSV(writer, wkms); + } + } + } + + + /** + * Lets csvwriter write the header (first line in file). + * @param write Writer to write header with. + */ + protected void writeCSVHeader(CSVWriter writer) { + logger.info("WDifferencesExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(WDIFF_CSV_KM_HEADER, WDIFF_DEFAULT_CSV_KM_HEADER), + msg(WDIFF_CSV_W_HEADER, WDIFF_DEFAULT_CSV_W_HEADER) + }); + } + + + protected void wKms2CSV(CSVWriter writer, WKms wkms) { + logger.debug("WDifferencesExporter.wQKms2CSV"); + + NumberFormat kmf = getKmFormatter(); + NumberFormat wf = getWFormatter(); + int size = wkms.size(); + + for (int i = 0; i < size; i ++) { + + writer.writeNext(new String[] { + kmf.format(wkms.getKm(i)), + wf.format(wkms.getW(i)) + }); + } + } + + + /** + * Returns the number formatter for kilometer values. + * + * @return the number formatter for kilometer values. + */ + protected NumberFormat getKmFormatter() { + return Formatter.getWaterlevelKM(context); + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return Formatter.getWaterlevelW(context); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WaterlevelExporter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,221 @@ +package de.intevation.flys.exports; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.NumberFormat; +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 de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; + +import de.intevation.flys.utils.Formatter; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WaterlevelExporter extends AbstractExporter { + + /** The logger used in this exporter.*/ + private static Logger logger = Logger.getLogger(WaterlevelExporter.class); + + + public static final String FACET_WST = "wst"; + + + public static final String CSV_KM_HEADER = + "export.waterlevel.csv.header.km"; + + public static final String CSV_W_HEADER = + "export.waterlevel.csv.header.w"; + + public static final String CSV_Q_HEADER = + "export.waterlevel.csv.header.q"; + + public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km"; + public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; + public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]"; + + + /** The storage that contains all WQKms objects for the different facets.*/ + protected List<WQKms[]> data; + + + public void init(Document request, OutputStream out, CallContext context) { + logger.debug("WaterlevelExporter.init"); + + super.init(request, out, context); + + this.data = new ArrayList<WQKms[]>(); + } + + + @Override + public void generate() + throws IOException + { + logger.debug("WaterlevelExporter.generate"); + + if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) { + generateCSV(); + } + else if (facet != null && facet.equals(FACET_WST)) { + generateWST(); + } + else { + throw new IOException("invalid facet for exporter"); + } + } + + + @Override + protected void addData(Object d) { + if (d instanceof CalculationResult) { + d = ((CalculationResult)d).getData(); + if (d instanceof WQKms []) { + data.add((WQKms [])d); + } + } + } + + + @Override + protected void writeCSVData(CSVWriter writer) { + logger.info("WaterlevelExporter.writeData"); + + writeCSVHeader(writer); + + for (WQKms[] tmp: data) { + for (WQKms wqkms: tmp) { + wQKms2CSV(writer, wqkms); + } + } + } + + + protected void writeCSVHeader(CSVWriter writer) { + logger.info("WaterlevelExporter.writeCSVHeader"); + + writer.writeNext(new String[] { + msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), + msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), + msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER) + }); + } + + + protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) { + logger.debug("WaterlevelExporter.wQKms2CSV"); + + NumberFormat kmf = getKmFormatter(); + NumberFormat wf = getWFormatter(); + NumberFormat qf = getQFormatter(); + + int size = wqkms.size(); + double[] result = new double[3]; + + for (int i = 0; i < size; i ++) { + result = wqkms.get(i, result); + + writer.writeNext(new String[] { + kmf.format(result[2]), + wf.format(result[0]), + qf.format(result[1]) + }); + } + } + + + /** + * Generates the output in WST format. + */ + protected void generateWST() + throws IOException + { + logger.info("WaterlevelExporter.generateWST"); + + int cols = data.get(0).length; + WstWriter writer = new WstWriter(cols); + + writeWSTData(writer); + + writer.write(out); + } + + + protected void writeWSTData(WstWriter writer) { + logger.debug("WaterlevelExporter.writeWSTData"); + + double[] result = new double[4]; + + for (WQKms[] tmp: data) { + for (WQKms wqkms: tmp) { + int size = wqkms != null ? wqkms.size() : 0; + + addWSTColumn(writer, wqkms); + + for (int i = 0; i < size; i++) { + result = wqkms.get(i, result); + + writer.add(result); + } + + if (wqkms instanceof WQCKms) { + addWSTColumn(writer, wqkms); + + for (int c = 0; c < size; c++) { + result = wqkms.get(c, result); + + writer.addCorrected(result); + } + } + } + } + } + + + protected void addWSTColumn(WstWriter writer, WQKms wqkms) { + writer.addColumn(wqkms.getName()); + } + + + /** + * Returns the number formatter for kilometer values. + * + * @return the number formatter for kilometer values. + */ + protected NumberFormat getKmFormatter() { + return Formatter.getWaterlevelKM(context); + } + + + /** + * Returns the number formatter for W values. + * + * @return the number formatter for W values. + */ + protected NumberFormat getWFormatter() { + return Formatter.getWaterlevelW(context); + } + + + /** + * Returns the number formatter for Q values. + * + * @return the number formatter for Q values. + */ + protected NumberFormat getQFormatter() { + return Formatter.getWaterlevelQ(context); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/WstWriter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,237 @@ +package de.intevation.flys.exports; + +import java.io.BufferedWriter; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.WstLine; + + +/** + * A writer that creates WSTs. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WstWriter { + + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(WstWriter.class); + + + /** The default unit that is written into the header of the WST.*/ + public static final String DEFAULT_UNIT = "Wassserstand [NN + m]"; + + + /** The lines that need to be included for the export.*/ + protected Map<Double, WstLine> lines; + + /** The column names.*/ + protected List<String> columnNames; + + /** The locale used to format the values.*/ + protected Locale locale; + + /** The number of discharge columns.*/ + protected int cols; + + /** The last Q values.*/ + protected double[] qs; + + + + /** + * This constructor creates a new WstWriter with an OutputStream and a + * number of Q columns. + * + * @param out The output stream where the WST is written to. + * @param cols The number of columns of the resulting WST. + */ + public WstWriter(int cols) { + this.columnNames = new ArrayList<String>(cols); + this.lines = new HashMap<Double, WstLine>(); + this.qs = new double[cols]; + this.locale = Locale.US; + } + + + /** + * This method is used to create the WST from the data that has been + * inserted using add(double[]) before. + */ + public void write(OutputStream out) { + logger.info("WstWriter.write"); + + PrintWriter writer = new PrintWriter( + new BufferedWriter( + new OutputStreamWriter(out))); + + this.qs = new double[cols]; + + writeHeader(writer); + + Collection<WstLine> collection = new TreeMap(lines).values(); + + for (WstLine line: collection) { + writeWLine(writer, line); + } + + writer.flush(); + writer.close(); + } + + + /** + * This method is used to add a new line to the WST. + * + * @param wqkms A 3dim double array with [W,Q, KM]. + */ + public void add(double[] wqkms) { + Double km = wqkms[2]; + + WstLine line = lines.get(km); + + if (line == null) { + line = new WstLine(km.doubleValue()); + lines.put(km, line); + } + + line.add(wqkms[0], wqkms[1]); + } + + + public void addCorrected(double[] wqckms) { + Double km = wqckms[2]; + + WstLine line = lines.get(km); + + if (line == null) { + line = new WstLine(km.doubleValue()); + lines.put(km, line); + } + + line.add(wqckms[3], wqckms[1]); + } + + + /** + * Adds a further column name. + * + * @param name The name of the new column. + */ + public void addColumn(String name) { + if (name != null) { + cols++; + + String basename = name; + + int i = 0; + while (columnNames.contains(name)) { + name = basename + "_" + i++; + + if (name.length() > 9) { + name = name.substring(name.length() - 9); + } + } + + columnNames.add(name); + } + } + + + /** + * This method writes the header of the WST. + * + * @param writer The PrintWriter that creates the output. + */ + protected void writeHeader(PrintWriter writer) { + logger.debug("WstWriter.writeHeader"); + + writer.println(cols); + writer.print(" "); + + for (String columnName: columnNames) { + writer.printf(locale, "%9s", columnName); + } + + writer.println(); + + writer.write("* KM "); + writer.write(DEFAULT_UNIT); + writer.println(); + } + + + /** + * This method writes a line with W values and a certain kilometer. + * + * @param writer The PrintWriter that is used to create the output. + * @param line The WstLine that should be written to the output. + */ + protected void writeWLine(PrintWriter writer, WstLine line) { + double km = line.getKm(); + double[] qs = line.getQs(); + int num = line.getSize(); + + if (dischargesChanged(qs)) { + writeQLine(writer, qs); + } + + writer.printf(locale, "%8.3f", km); + + for (int i = 0; i < num; i++) { + writer.printf(locale, "%9.2f", line.getW(i)); + } + + writer.println(); + } + + + /** + * Writes a discharge line (Q values) into a WST. + * + * @param qs the Q values for the next range. + */ + protected void writeQLine(PrintWriter writer, double[] qs) { + writer.write("*\u001f "); + + for (int i = 0; i < qs.length; i++) { + this.qs[i] = qs[i]; + + writer.printf(locale, "%9.2f", qs[i]); + } + + writer.println(); + } + + + /** + * This method determines if a Q has changed from the last line to the + * current one. + * + * @param newQs The Q values of the next line. + * + * @return true, if a Q value have changed, otherwise false. + */ + protected boolean dischargesChanged(double[] newQs) { + // XXX maybe there is a way to do this faster + for (int i = 0; i < cols; i++) { + if (Math.abs(newQs[i] - qs[i]) >= 0.001) { + return true; + } + } + + return false; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,646 @@ +package de.intevation.flys.exports; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Stroke; + +import java.io.IOException; + +import java.text.NumberFormat; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import org.jfree.ui.RectangleInsets; + +import de.intevation.flys.exports.ChartExportHelper; +import de.intevation.flys.jfree.FLYSAnnotation; +import de.intevation.flys.jfree.StickyAxisAnnotation; + +import de.intevation.flys.utils.ThemeAccess; + +/** + * An abstract base class for creating XY charts. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class XYChartGenerator extends ChartGenerator { + + /** The logger that is used in this generator. */ + private static Logger logger = Logger.getLogger(XYChartGenerator.class); + + /** SeriesCollection used for the first axis. */ + protected XYSeriesCollection first; + + /** SeriesCollection used for the second axis. */ + protected XYSeriesCollection second; + + /** List of annotations to insert in plot. */ + protected List<FLYSAnnotation> annotations; + + /** The max X range to include all X values of all series for each axis. */ + protected Map<Integer, Range> xRanges; + + /** The max Y range to include all Y values of all series for each axis. */ + protected Map<Integer, Range> yRanges; + + public static final Color DEFAULT_GRID_COLOR = Color.GRAY; + public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; + + + /** + * Returns the title of a chart. + * + * @return the title of a chart. + */ + protected abstract String getChartTitle(); + + /** + * Returns the X-Axis label of a chart. + * + * @return the X-Axis label of a chart. + */ + protected abstract String getXAxisLabel(); + + /** + * Returns the Y-Axis label of a chart. + * + * @return the Y-Axis label of a chart. + */ + protected abstract String getYAxisLabel(); + + + public void generate() + throws IOException + { + logger.debug("XYChartGenerator.generate"); + + JFreeChart chart = generateChart(); + + String format = getFormat(); + int[] size = getSize(); + + 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)) { + 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); + + ChartExportHelper.exportPDF( + out, + chart, + context); + } + else if (format.equals(ChartExportHelper.FORMAT_SVG)) { + context.putContextValue( + "chart.encoding", + ChartExportHelper.DEFAULT_ENCODING); + + ChartExportHelper.exportSVG( + out, + chart, + context); + } + } + + + public JFreeChart generateChart() { + logger.debug("XYChartGenerator.generateChart"); + + JFreeChart chart = ChartFactory.createXYLineChart( + getChartTitle(), + getXAxisLabel(), + getYAxisLabel(), + null, + PlotOrientation.VERTICAL, + true, + false, + false); + + chart.setBackgroundPaint(Color.WHITE); + chart.getPlot().setBackgroundPaint(Color.WHITE); + + XYPlot plot = (XYPlot) chart.getPlot(); + + addDatasets(plot); + addAnnotations(plot); + addSubtitles(chart); + adjustPlot(plot); + localizeAxes(plot); + + removeEmptyRangeAxes(plot); + adjustAxes(plot); + + preparePointRanges(plot); + autoZoom(plot); + + applyThemes(plot); + + return chart; + } + + + /** + * Add first and second dataset to plot. + * @param plot plot to add datasets to. + */ + protected void addDatasets(XYPlot plot) { + if (first != null) { + logger.debug("Set the first axis dataset."); + plot.setDataset(0, first); + } + if (second != null) { + logger.debug("Set the second axis dataset."); + plot.setDataset(1, second); + } + } + + + public void addFirstAxisSeries(XYSeries series, boolean visible) { + if (first == null) { + first = new XYSeriesCollection(); + } + + if (series != null) { + if (visible) { + first.addSeries(series); + } + + combineYRanges(new Range(series.getMinY(), series.getMaxY()), 0); + combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); + } + } + + + public void addSecondAxisSeries(XYSeries series, boolean visible) { + if (second == null) { + second = new XYSeriesCollection(); + } + + if (series != null) { + if (visible) { + second.addSeries(series); + } + + combineYRanges(new Range(series.getMinY(), series.getMaxY()), 1); + combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); + } + } + + + private void combineXRanges(Range range, int index) { + Integer key = Integer.valueOf(index); + + if (xRanges == null) { + xRanges = new HashMap<Integer, Range>(); + xRanges.put(key, range); + return; + } + + Range newX = null; + Range oldX = xRanges.get(key); + + if (oldX != null) { + newX = Range.combine(oldX, range); + } + else { + newX = range; + } + + xRanges.put(key, newX); + } + + + private void combineYRanges(Range range, int index) { + Integer key = Integer.valueOf(index); + + if (yRanges == null) { + yRanges = new HashMap<Integer, Range>(); + yRanges.put(key, range); + return; + } + + Range newY = null; + Range oldY = yRanges.get(key); + + if (oldY != null) { + newY = Range.combine(oldY, range); + } + else { + newY = range; + } + + yRanges.put(key, newY); + } + + + /** + * Adds annotations to list (if visible is true). + */ + public void addAnnotations(FLYSAnnotation annotation, boolean visible) { + if (!visible) { + return; + } + + if (annotations == null) { + annotations = new ArrayList<FLYSAnnotation>(); + } + + annotations.add(annotation); + } + + + private void removeEmptyRangeAxes(XYPlot plot) { + if (first == null) { + plot.setRangeAxis(0, null); + } + + if (second == null) { + plot.setRangeAxis(1, null); + } + } + + + private void preparePointRanges(XYPlot plot) { + for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { + Integer key = Integer.valueOf(i); + + Range r = xRanges.get(key); + if (r != null && r.getLowerBound() == r.getUpperBound()) { + xRanges.put(key, expandRange(r, 5)); + } + } + + for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { + Integer key = Integer.valueOf(i); + + Range r = yRanges.get(key); + if (r != null && r.getLowerBound() == r.getUpperBound()) { + yRanges.put(key, expandRange(r, 5)); + } + } + } + + + public static Range expandRange(Range range, double percent) { + if (range == null) { + return null; + } + + double value = range.getLowerBound(); + double expand = value / 100 * percent; + + return expand != 0 + ? new Range(value-expand, value+expand) + : new Range(-0.01 * percent, 0.01 * percent); + } + + + /** + * 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(); + + zoomX(plot, plot.getDomainAxis(), xRanges.get(0), xrange); + + for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { + ValueAxis yaxis = plot.getRangeAxis(i); + + 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, yRanges.get(Integer.valueOf(i)), yrange); + } + } + + + protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) { + return zoom(plot, axis, range, x); + } + + + protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) { + return zoom(plot, axis, range, 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 range 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, Range range, Range x) { + + if (range == null) { + return false; + } + + if (x != null) { + double min = range.getLowerBound(); + double max = range.getUpperBound(); + double diff = max > min ? max - min : min - max; + + Range computed = new Range( + min + x.getLowerBound() * diff, + min + x.getUpperBound() * diff); + + axis.setRangeWithMargins(computed); + + logger.debug("Zoom axis to: " + computed); + + return true; + } + + axis.setRangeWithMargins(range); + return false; + } + + + /** + * This method extracts 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]. + */ + public Range[] getRangesForDataset(int index) { + return new Range[] { + xRanges.get(Integer.valueOf(0)), + yRanges.get(Integer.valueOf(index)) + }; + } + + + protected void addAnnotations(XYPlot plot) { + plot.clearAnnotations(); + + if (annotations == null) { + logger.debug("No Annotations given."); + return; + } + + LegendItemCollection lic = new LegendItemCollection(); + + int idx = 0; + if (plot.getRangeAxis(idx) == null && plot.getRangeAxisCount() >= 2) { + idx = 1; + } + + XYItemRenderer renderer = plot.getRenderer(idx); + + for (FLYSAnnotation fa: annotations) { + Document theme = fa.getTheme(); + + ThemeAccess themeAccess = new ThemeAccess(theme); + + Color color = themeAccess.parseLineColorField(); + int lineWidth = themeAccess.parseLineWidth(); + + lic.add(new LegendItem(fa.getLabel(), color)); + + for (XYTextAnnotation ta: fa.getAnnotations()) { + if(ta instanceof StickyAxisAnnotation) { + StickyAxisAnnotation sta = (StickyAxisAnnotation)ta; + sta.applyTheme(themeAccess); + renderer.addAnnotation(sta); + } + else { + ta.setPaint(color); + ta.setOutlineStroke(new BasicStroke((float) lineWidth)); + renderer.addAnnotation(ta); + } + } + + plot.setFixedLegendItems(lic); + } + } + + + /** + * Adjusts the axes of a plot. + * + * @param plot The XYPlot of the chart. + */ + protected void adjustAxes(XYPlot plot) { + NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); + + yAxis.setAutoRangeIncludesZero(false); + } + + + 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); + + plot.setDomainGridlineStroke(gridStroke); + plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); + plot.setDomainGridlinesVisible(true); + + plot.setRangeGridlineStroke(gridStroke); + plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); + plot.setRangeGridlinesVisible(true); + + plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); + + if (plot.getDataset(0) != null) { + plot.mapDatasetToRangeAxis(0, 0); + } + + if (plot.getDataset(1) != null) { + plot.mapDatasetToRangeAxis(1, 1); + } + } + + + protected void addSubtitles(JFreeChart chart) { + // override this method in subclasses that need subtitles + } + + + /** + * 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 domainAxis The domain axis that needs localization. + */ + protected void localizeRangeAxis(ValueAxis rangeAxis) { + NumberFormat nf = NumberFormat.getInstance(getLocale()); + ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); + } + + + protected void applyThemes(XYPlot plot) { + if (first != null) { + applyThemes(plot, first, 0); + } + + if (second != null) { + applyThemes(plot, second, 1); + } + } + + + protected void applyThemes(XYPlot plot, XYSeriesCollection dataset, int i) { + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection anno = plot.getFixedLegendItems(); + + XYLineAndShapeRenderer r = getRenderer(plot, i); + + for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { + XYSeries series = dataset.getSeries(s); + + if (series instanceof StyledXYSeries) { + ((StyledXYSeries) series).applyTheme(r, 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) { + r.setSeriesShapesVisible(s, true); + } + + lic.add(r.getLegendItem(i, s)); + } + + if (anno != null) { + lic.addAll(anno); + } + + plot.setFixedLegendItems(lic); + + plot.setRenderer(i, r); + } + + + protected XYLineAndShapeRenderer getRenderer(XYPlot plot, int idx) { + XYLineAndShapeRenderer r = + (XYLineAndShapeRenderer) plot.getRenderer(idx); + + if (r != null) { + return r; + } + + if (idx == 0) { + logger.warn("No default renderer set!"); + return new XYLineAndShapeRenderer(); + } + + r = (XYLineAndShapeRenderer) plot.getRenderer(0); + + try { + return (XYLineAndShapeRenderer) r.clone(); + } + catch (CloneNotSupportedException cnse) { + logger.warn(cnse, cnse); + } + + logger.warn("No applicalable renderer found!"); + + return new XYLineAndShapeRenderer(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/java2d/NOPGraphics2D.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,528 @@ +package de.intevation.flys.java2d; + +import java.util.Map; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Image; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Paint; +import java.awt.GraphicsConfiguration; +import java.awt.Stroke; +import java.awt.Rectangle; +import java.awt.Font; +import java.awt.FontMetrics; + +import java.awt.image.RenderedImage; + +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; + +import java.awt.image.renderable.RenderableImage; + +import java.awt.geom.AffineTransform; + +import java.text.AttributedCharacterIterator; + +import java.awt.font.GlyphVector; +import java.awt.font.FontRenderContext; + +import java.awt.RenderingHints; + +public final class NOPGraphics2D +extends Graphics2D +{ + private Graphics2D parent; + + public NOPGraphics2D(Graphics2D parent) { + this.parent = parent; + } + + @Override + public final void addRenderingHints(Map<?,?> hints) { + parent.addRenderingHints(hints); + } + + @Override + public final void clip(Shape s) { + } + + @Override + public final void draw(Shape s) { + } + + @Override + public final void drawGlyphVector(GlyphVector g, float x, float y) { + } + + @Override + public final void drawImage( + BufferedImage img, + BufferedImageOp op, + int x, + int y + ) { + } + + @Override + public final boolean drawImage( + Image img, + AffineTransform xform, + ImageObserver obs + ) { + return true; + } + + @Override + public final void drawRenderableImage( + RenderableImage img, + AffineTransform xform + ) { + } + + @Override + public final void drawRenderedImage( + RenderedImage img, + AffineTransform xform + ) { + } + + @Override + public final void drawString( + AttributedCharacterIterator iterator, + float x, + float y + ) { + } + + @Override + public final void drawString( + AttributedCharacterIterator iterator, + int x, + int y + ) { + } + + @Override + public final void drawString(String str, float x, float y) { + } + + @Override + public final void drawString(String str, int x, int y) { + } + + @Override + public final void fill(Shape s) { + } + + @Override + public final Color getBackground() { + return parent.getBackground(); + } + + @Override + public final Composite getComposite() { + return parent.getComposite(); + } + + @Override + public final GraphicsConfiguration getDeviceConfiguration() { + return parent.getDeviceConfiguration(); + } + + @Override + public final FontRenderContext getFontRenderContext() { + return parent.getFontRenderContext(); + } + + @Override + public final Paint getPaint() { + return parent.getPaint(); + } + + @Override + public final Object getRenderingHint(RenderingHints.Key hintKey) { + return parent.getRenderingHint(hintKey); + } + + @Override + public final RenderingHints getRenderingHints() { + return parent.getRenderingHints(); + } + + @Override + public final Stroke getStroke() { + return parent.getStroke(); + } + + @Override + public final AffineTransform getTransform() { + return parent.getTransform(); + } + + @Override + public final boolean hit(Rectangle rect, Shape s, boolean onStroke) { + return parent.hit(rect, s, onStroke); + } + + @Override + public final void rotate(double theta) { + parent.rotate(theta); + } + + @Override + public final void rotate(double theta, double x, double y) { + parent.rotate(theta); + } + + @Override + public final void scale(double sx, double sy) { + parent.scale(sx, sy); + } + + @Override + public final void setBackground(Color color) { + parent.setBackground(color); + } + + @Override + public final void setComposite(Composite comp) { + parent.setComposite(comp); + } + + @Override + public final void setPaint(Paint paint) { + parent.setPaint(paint); + } + + @Override + public final void setRenderingHint( + RenderingHints.Key hintKey, + Object hintValue + ) { + parent.setRenderingHint(hintKey, hintValue); + } + + @Override + public final void setRenderingHints(Map<?,?> hints) { + parent.setRenderingHints(hints); + } + + @Override + public final void setStroke(Stroke s) { + parent.setStroke(s); + } + + @Override + public final void setTransform(AffineTransform Tx) { + parent.setTransform(Tx); + } + + + @Override + public final void shear(double shx, double shy) { + parent.shear(shx, shy); + } + + @Override + public final void transform(AffineTransform Tx) { + parent.transform(Tx); + } + + @Override + public final void translate(double tx, double ty) { + parent.translate(tx, ty); + } + + @Override + public final void translate(int tx, int ty) { + parent.translate(tx, ty); + } + + @Override + public final void dispose() { + parent.dispose(); + } + + @Override + public final boolean drawImage( + Image img, + int x, + int y, + int width, + int height, + Color bgcolor, + ImageObserver observer + ) { + return true; + } + + @Override + public final boolean drawImage( + Image img, + int dx1, + int dy1, + int dx2, + int dy2, + int sx1, + int sy1, + int sx2, + int sy2, + Color bgcolor, + ImageObserver observer + ) { + return true; + } + + @Override + public final boolean drawImage( + Image img, + int dx1, + int dy1, + int dx2, + int dy2, + int sx1, + int sy1, + int sx2, + int sy2, + ImageObserver observer + ) { + return true; + } + + @Override + public final boolean drawImage( + Image img, + int x, + int y, + Color bgcolor, + ImageObserver observer + ) { + return true; + } + + @Override + public final boolean drawImage( + Image img, + int x, + int y, + int width, + int height, + ImageObserver observer + ) { + return true; + } + + @Override + public final boolean drawImage( + Image img, + int x, + int y, + ImageObserver observer + ) { + return true; + } + + @Override + public final void fillPolygon( + int [] xPoints, + int [] yPoints, + int nPoints + ) { + } + + @Override + public final void drawPolygon( + int [] xPoints, + int [] yPoints, + int nPoints + ) { + } + + @Override + public final void drawPolyline( + int [] xPoints, + int [] yPoints, + int nPoints + ) { + } + + @Override + public final void fillArc( + int x, + int y, + int width, + int height, + int startAngle, + int arcAngle + ) { + } + + @Override + public final void drawArc( + int x, + int y, + int width, + int height, + int startAngle, + int arcAngle + ) { + } + + @Override + public final void fillOval( + int x, + int y, + int width, + int height + ) { + } + + @Override + public final void drawOval( + int x, + int y, + int width, + int height + ) { + } + + @Override + public final void fillRoundRect( + int x, + int y, + int width, + int height, + int arcWidth, + int arcHeight + ) { + } + + @Override + public final void drawRoundRect( + int x, + int y, + int width, + int height, + int arcWidth, + int arcHeight + ) { + } + + @Override + public final void clearRect( + int x, + int y, + int width, + int height + ) { + } + + @Override + public final void fillRect( + int x, + int y, + int width, + int height + ) { + } + + @Override + public final void drawLine( + int x1, + int y1, + int x2, + int y2 + ) { + } + + @Override + public final void copyArea( + int x, + int y, + int width, + int height, + int dx, + int dy + ) { + } + + @Override + public final void setClip( + int x, + int y, + int width, + int height + ) { + parent.setClip(x, y, width, height); + } + + @Override + public final void setClip(Shape shape) { + parent.setClip(shape); + } + + @Override + public final Shape getClip() { + return parent.getClip(); + } + + @Override + public final void clipRect( + int x, + int y, + int width, + int height + ) { + parent.clipRect(x, y, width, height); + } + + @Override + public final Rectangle getClipBounds() { + return parent.getClipBounds(); + } + + @Override + public final FontMetrics getFontMetrics(Font f) { + return parent.getFontMetrics(f); + } + + @Override + public final void setFont(Font font) { + parent.setFont(font); + } + + @Override + public final Font getFont() { + return parent.getFont(); + } + + @Override + public final void setXORMode(Color c1) { + parent.setXORMode(c1); + } + + @Override + public final void setPaintMode() { + parent.setPaintMode(); + } + + @Override + public final void setColor(Color c) { + parent.setColor(c); + } + + @Override + public final Color getColor() { + return parent.getColor(); + } + + @Override + public final Graphics create() { + return new NOPGraphics2D((Graphics2D)parent.create()); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/FLYSAnnotation.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,46 @@ +package de.intevation.flys.jfree; + +import java.util.List; + +import org.w3c.dom.Document; + +import org.jfree.chart.annotations.XYTextAnnotation; + +/** + * List of Annotations with name and theme. + */ +public class FLYSAnnotation { + + protected List<XYTextAnnotation> annotations; + + protected Document theme; + + protected String label; + + + public FLYSAnnotation(String label, List<XYTextAnnotation> annotations) { + this.label = label; + this.annotations = annotations; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + + public List<XYTextAnnotation> getAnnotations() { + return annotations; + } + + public void setTheme(Document theme) { + this.theme = theme; + } + + public Document getTheme() { + return theme; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,1271 @@ +/* =========================================================== + * JFreeChart : a free chart library for the Java(tm) platform + * =========================================================== + * + * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. + * + * Project Info: http://www.jfree.org/jfreechart/index.html + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * [Java is a trademark or registered trademark of Sun Microsystems, Inc. + * in the United States and other countries.] + * + * ------------------------- + * StableXYDifferenceRenderer.java + * ------------------------- + * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors. + * + * Original Author: David Gilbert (for Object Refinery Limited); + * Contributor(s): Richard West, Advanced Micro Devices, Inc. (major rewrite + * of difference drawing algorithm); + * + * Changes: + * -------- + * 30-Apr-2003 : Version 1 (DG); + * 30-Jul-2003 : Modified entity constructor (CZ); + * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); + * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); + * 09-Feb-2004 : Updated to support horizontal plot orientation (DG); + * 10-Feb-2004 : Added default constructor, setter methods and updated + * Javadocs (DG); + * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); + * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG); + * 15-Jul-2004 : Switched getX() with getXValue() and getY() with + * getYValue() (DG); + * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG); + * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); + * 19-Jan-2005 : Now accesses only primitive values from dataset (DG); + * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG); + * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG); + * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); + * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() --> + * get/setShapesVisible (DG); + * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG); + * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG); + * ------------- JFREECHART 1.0.x --------------------------------------------- + * 24-Jan-2007 : Added flag to allow rounding of x-coordinates, and fixed + * bug in clone() (DG); + * 05-Feb-2007 : Added an extra call to updateCrosshairValues() in + * drawItemPass1(), to fix bug 1564967 (DG); + * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); + * 08-Mar-2007 : Fixed entity generation (DG); + * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); + * 23-Apr-2007 : Rewrite of difference drawing algorithm to allow use of + * series with disjoint x-values (RW); + * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG); + * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); + * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); + * 05-Nov-2007 : Draw item labels if visible (RW); + * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG); + * + */ +package de.intevation.flys.jfree; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Iterator; + +import org.jfree.chart.LegendItem; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.entity.EntityCollection; +import org.jfree.chart.entity.XYItemEntity; +import org.jfree.chart.event.RendererChangeEvent; +import org.jfree.chart.labels.XYToolTipGenerator; +import org.jfree.chart.plot.CrosshairState; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.urls.XYURLGenerator; +import org.jfree.data.xy.XYDataset; +import org.jfree.io.SerialUtilities; +import org.jfree.ui.RectangleEdge; +import org.jfree.util.PaintUtilities; +import org.jfree.util.PublicCloneable; +import org.jfree.util.ShapeUtilities; + +import org.jfree.chart.renderer.xy.AbstractXYItemRenderer; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.renderer.xy.XYItemRendererState; + +/** + * A renderer for an {@link XYPlot} that highlights the differences between two + * series. The example shown here is generated by the + * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart + * demo collection: + * <br><br> + * <img src="../../../../../images/StableXYDifferenceRendererSample.png" + * alt="StableXYDifferenceRendererSample.png" /> + */ +public class StableXYDifferenceRenderer extends AbstractXYItemRenderer + implements XYItemRenderer, PublicCloneable { + + /** For serialization. */ + private static final long serialVersionUID = -8447915602375584857L; + + /** The paint used to highlight positive differences (y(0) > y(1)). */ + private transient Paint positivePaint; + + /** The paint used to highlight negative differences (y(0) < y(1)). */ + private transient Paint negativePaint; + + /** Display shapes at each point? */ + private boolean shapesVisible; + + /** The shape to display in the legend item. */ + private transient Shape legendLine; + + private XYDatasetToZeroMapper mapper; + + /** + * This flag controls whether or not the x-coordinates (in Java2D space) + * are rounded to integers. When set to true, this can avoid the vertical + * striping that anti-aliasing can generate. However, the rounding may not + * be appropriate for output in high resolution formats (for example, + * vector graphics formats such as SVG and PDF). + * + * @since 1.0.4 + */ + private boolean roundXCoordinates; + + /** + * Creates a new renderer with default attributes. + */ + public StableXYDifferenceRenderer() { + this(Color.green, Color.red, false, null); + } + + /** + * Creates a new renderer. + * + * @param positivePaint the highlight color for positive differences + * (<code>null</code> not permitted). + * @param negativePaint the highlight color for negative differences + * (<code>null</code> not permitted). + * @param shapes draw shapes? + */ + public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint, + boolean shapes, + XYDatasetToZeroMapper mapper) { + if (positivePaint == null) { + throw new IllegalArgumentException( + "Null 'positivePaint' argument."); + } + if (negativePaint == null) { + throw new IllegalArgumentException( + "Null 'negativePaint' argument."); + } + this.positivePaint = positivePaint; + this.negativePaint = negativePaint; + this.shapesVisible = shapes; + this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); + this.roundXCoordinates = false; + this.mapper = mapper; + } + + /** + * Returns the paint used to highlight positive differences. + * + * @return The paint (never <code>null</code>). + * + * @see #setPositivePaint(Paint) + */ + public Paint getPositivePaint() { + return this.positivePaint; + } + + /** + * Sets the paint used to highlight positive differences and sends a + * {@link RendererChangeEvent} to all registered listeners. + * + * @param paint the paint (<code>null</code> not permitted). + * + * @see #getPositivePaint() + */ + public void setPositivePaint(Paint paint) { + if (paint == null) { + throw new IllegalArgumentException("Null 'paint' argument."); + } + this.positivePaint = paint; + fireChangeEvent(); + } + + /** + * Returns the paint used to highlight negative differences. + * + * @return The paint (never <code>null</code>). + * + * @see #setNegativePaint(Paint) + */ + public Paint getNegativePaint() { + return this.negativePaint; + } + + /** + * Sets the paint used to highlight negative differences. + * + * @param paint the paint (<code>null</code> not permitted). + * + * @see #getNegativePaint() + */ + public void setNegativePaint(Paint paint) { + if (paint == null) { + throw new IllegalArgumentException("Null 'paint' argument."); + } + this.negativePaint = paint; + notifyListeners(new RendererChangeEvent(this)); + } + + /** + * Returns a flag that controls whether or not shapes are drawn for each + * data value. + * + * @return A boolean. + * + * @see #setShapesVisible(boolean) + */ + public boolean getShapesVisible() { + return this.shapesVisible; + } + + /** + * Sets a flag that controls whether or not shapes are drawn for each + * data value, and sends a {@link RendererChangeEvent} to all registered + * listeners. + * + * @param flag the flag. + * + * @see #getShapesVisible() + */ + public void setShapesVisible(boolean flag) { + this.shapesVisible = flag; + fireChangeEvent(); + } + + /** + * Returns the shape used to represent a line in the legend. + * + * @return The legend line (never <code>null</code>). + * + * @see #setLegendLine(Shape) + */ + public Shape getLegendLine() { + return this.legendLine; + } + + /** + * Sets the shape used as a line in each legend item and sends a + * {@link RendererChangeEvent} to all registered listeners. + * + * @param line the line (<code>null</code> not permitted). + * + * @see #getLegendLine() + */ + public void setLegendLine(Shape line) { + if (line == null) { + throw new IllegalArgumentException("Null 'line' argument."); + } + this.legendLine = line; + fireChangeEvent(); + } + + /** + * Returns the flag that controls whether or not the x-coordinates (in + * Java2D space) are rounded to integer values. + * + * @return The flag. + * + * @since 1.0.4 + * + * @see #setRoundXCoordinates(boolean) + */ + public boolean getRoundXCoordinates() { + return this.roundXCoordinates; + } + + /** + * Sets the flag that controls whether or not the x-coordinates (in + * Java2D space) are rounded to integer values, and sends a + * {@link RendererChangeEvent} to all registered listeners. + * + * @param round the new flag value. + * + * @since 1.0.4 + * + * @see #getRoundXCoordinates() + */ + public void setRoundXCoordinates(boolean round) { + this.roundXCoordinates = round; + fireChangeEvent(); + } + + /** + * Initialises the renderer and returns a state object that should be + * passed to subsequent calls to the drawItem() method. This method will + * be called before the first item is rendered, giving the renderer an + * opportunity to initialise any state information it wants to maintain. + * The renderer can do nothing if it chooses. + * + * @param g2 the graphics device. + * @param dataArea the area inside the axes. + * @param plot the plot. + * @param data the data. + * @param info an optional info collection object to return data back to + * the caller. + * + * @return A state object. + */ + public XYItemRendererState initialise(Graphics2D g2, + Rectangle2D dataArea, + XYPlot plot, + XYDataset data, + PlotRenderingInfo info) { + + XYItemRendererState state = super.initialise(g2, dataArea, plot, data, + info); + state.setProcessVisibleItemsOnly(false); + return state; + + } + + /** + * Returns <code>2</code>, the number of passes required by the renderer. + * The {@link XYPlot} will run through the dataset this number of times. + * + * @return The number of passes required by the renderer. + */ + public int getPassCount() { + return 2; + } + + /** + * Draws the visual representation of a single data item. + * + * @param g2 the graphics device. + * @param state the renderer state. + * @param dataArea the area within which the data is being drawn. + * @param info collects information about the drawing. + * @param plot the plot (can be used to obtain standard color + * information etc). + * @param domainAxis the domain (horizontal) axis. + * @param rangeAxis the range (vertical) axis. + * @param dataset the dataset. + * @param series the series index (zero-based). + * @param item the item index (zero-based). + * @param crosshairState crosshair information for the plot + * (<code>null</code> permitted). + * @param pass the pass index. + */ + public void drawItem(Graphics2D g2, + XYItemRendererState state, + Rectangle2D dataArea, + PlotRenderingInfo info, + XYPlot plot, + ValueAxis domainAxis, + ValueAxis rangeAxis, + XYDataset dataset, + int series, + int item, + CrosshairState crosshairState, + int pass) { + + if (mapper == null) { + switch (pass) { + case 0: + drawItemPass0(g2, dataArea, info, + plot, domainAxis, rangeAxis, + dataset, series, item, crosshairState); + break; + case 1: + drawItemPass1(g2, dataArea, info, + plot, domainAxis, rangeAxis, + dataset, series, item, crosshairState); + } + } + else { + for (Iterator<XYDataset> iter = mapper.iterator(dataset); + iter.hasNext(); + ) { + switch (pass) { + case 0: + drawItemPass0(g2, dataArea, info, + plot, domainAxis, rangeAxis, + dataset, series, item, crosshairState); + break; + case 1: + drawItemPass1(g2, dataArea, info, + plot, domainAxis, rangeAxis, + dataset, series, item, crosshairState); + } + } // for all segments + } + + } + + /** + * Draws the visual representation of a single data item, first pass. + * + * @param x_graphics the graphics device. + * @param x_dataArea the area within which the data is being drawn. + * @param x_info collects information about the drawing. + * @param x_plot the plot (can be used to obtain standard color + * information etc). + * @param x_domainAxis the domain (horizontal) axis. + * @param x_rangeAxis the range (vertical) axis. + * @param x_dataset the dataset. + * @param x_series the series index (zero-based). + * @param x_item the item index (zero-based). + * @param x_crosshairState crosshair information for the plot + * (<code>null</code> permitted). + */ + protected void drawItemPass0(Graphics2D x_graphics, + Rectangle2D x_dataArea, + PlotRenderingInfo x_info, + XYPlot x_plot, + ValueAxis x_domainAxis, + ValueAxis x_rangeAxis, + XYDataset x_dataset, + int x_series, + int x_item, + CrosshairState x_crosshairState) { + + if (!((0 == x_series) && (0 == x_item))) { + return; + } + + boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount()); + + // check if either series is a degenerate case (i.e. less than 2 points) + if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) { + return; + } + + // check if series are disjoint (i.e. domain-spans do not overlap) + if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) { + return; + } + + // polygon definitions + LinkedList l_minuendXs = new LinkedList(); + LinkedList l_minuendYs = new LinkedList(); + LinkedList l_subtrahendXs = new LinkedList(); + LinkedList l_subtrahendYs = new LinkedList(); + LinkedList l_polygonXs = new LinkedList(); + LinkedList l_polygonYs = new LinkedList(); + + // state + int l_minuendItem = 0; + int l_minuendItemCount = x_dataset.getItemCount(0); + Double l_minuendCurX = null; + Double l_minuendNextX = null; + Double l_minuendCurY = null; + Double l_minuendNextY = null; + double l_minuendMaxY = Double.NEGATIVE_INFINITY; + double l_minuendMinY = Double.POSITIVE_INFINITY; + + int l_subtrahendItem = 0; + int l_subtrahendItemCount = 0; // actual value set below + Double l_subtrahendCurX = null; + Double l_subtrahendNextX = null; + Double l_subtrahendCurY = null; + Double l_subtrahendNextY = null; + double l_subtrahendMaxY = Double.NEGATIVE_INFINITY; + double l_subtrahendMinY = Double.POSITIVE_INFINITY; + + // if a subtrahend is not specified, assume it is zero + if (b_impliedZeroSubtrahend) { + l_subtrahendItem = 0; + l_subtrahendItemCount = 2; + l_subtrahendCurX = new Double(x_dataset.getXValue(0, 0)); + l_subtrahendNextX = new Double(x_dataset.getXValue(0, + (l_minuendItemCount - 1))); + l_subtrahendCurY = new Double(0.0); + l_subtrahendNextY = new Double(0.0); + l_subtrahendMaxY = 0.0; + l_subtrahendMinY = 0.0; + + l_subtrahendXs.add(l_subtrahendCurX); + l_subtrahendYs.add(l_subtrahendCurY); + } + else { + l_subtrahendItemCount = x_dataset.getItemCount(1); + } + + boolean b_minuendDone = false; + boolean b_minuendAdvanced = true; + boolean b_minuendAtIntersect = false; + boolean b_minuendFastForward = false; + boolean b_subtrahendDone = false; + boolean b_subtrahendAdvanced = true; + boolean b_subtrahendAtIntersect = false; + boolean b_subtrahendFastForward = false; + boolean b_colinear = false; + + boolean b_positive; + + // coordinate pairs + double l_x1 = 0.0, l_y1 = 0.0; // current minuend point + double l_x2 = 0.0, l_y2 = 0.0; // next minuend point + double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point + double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point + + // fast-forward through leading tails + boolean b_fastForwardDone = false; + while (!b_fastForwardDone) { + // get the x and y coordinates + l_x1 = x_dataset.getXValue(0, l_minuendItem); + l_y1 = x_dataset.getYValue(0, l_minuendItem); + l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); + l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); + + l_minuendCurX = new Double(l_x1); + l_minuendCurY = new Double(l_y1); + l_minuendNextX = new Double(l_x2); + l_minuendNextY = new Double(l_y2); + + if (b_impliedZeroSubtrahend) { + l_x3 = l_subtrahendCurX.doubleValue(); + l_y3 = l_subtrahendCurY.doubleValue(); + l_x4 = l_subtrahendNextX.doubleValue(); + l_y4 = l_subtrahendNextY.doubleValue(); + } + else { + l_x3 = x_dataset.getXValue(1, l_subtrahendItem); + l_y3 = x_dataset.getYValue(1, l_subtrahendItem); + l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); + l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); + + l_subtrahendCurX = new Double(l_x3); + l_subtrahendCurY = new Double(l_y3); + l_subtrahendNextX = new Double(l_x4); + l_subtrahendNextY = new Double(l_y4); + } + + if (l_x2 <= l_x3) { + // minuend needs to be fast forwarded + l_minuendItem++; + b_minuendFastForward = true; + continue; + } + + if (l_x4 <= l_x1) { + // subtrahend needs to be fast forwarded + l_subtrahendItem++; + b_subtrahendFastForward = true; + continue; + } + + // check if initial polygon needs to be clipped + if ((l_x3 < l_x1) && (l_x1 < l_x4)) { + // project onto subtrahend + double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); + l_subtrahendCurX = l_minuendCurX; + l_subtrahendCurY = new Double((l_slope * l_x1) + + (l_y3 - (l_slope * l_x3))); + + l_subtrahendXs.add(l_subtrahendCurX); + l_subtrahendYs.add(l_subtrahendCurY); + } + + if ((l_x1 < l_x3) && (l_x3 < l_x2)) { + // project onto minuend + double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); + l_minuendCurX = l_subtrahendCurX; + l_minuendCurY = new Double((l_slope * l_x3) + + (l_y1 - (l_slope * l_x1))); + + l_minuendXs.add(l_minuendCurX); + l_minuendYs.add(l_minuendCurY); + } + + l_minuendMaxY = l_minuendCurY.doubleValue(); + l_minuendMinY = l_minuendCurY.doubleValue(); + l_subtrahendMaxY = l_subtrahendCurY.doubleValue(); + l_subtrahendMinY = l_subtrahendCurY.doubleValue(); + + b_fastForwardDone = true; + } + + // start of algorithm + while (!b_minuendDone && !b_subtrahendDone) { + if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) { + l_x1 = x_dataset.getXValue(0, l_minuendItem); + l_y1 = x_dataset.getYValue(0, l_minuendItem); + l_minuendCurX = new Double(l_x1); + l_minuendCurY = new Double(l_y1); + + if (!b_minuendAtIntersect) { + l_minuendXs.add(l_minuendCurX); + l_minuendYs.add(l_minuendCurY); + } + + l_minuendMaxY = Math.max(l_minuendMaxY, l_y1); + l_minuendMinY = Math.min(l_minuendMinY, l_y1); + + l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); + l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); + l_minuendNextX = new Double(l_x2); + l_minuendNextY = new Double(l_y2); + } + + // never updated the subtrahend if it is implied to be zero + if (!b_impliedZeroSubtrahend && !b_subtrahendDone + && !b_subtrahendFastForward && b_subtrahendAdvanced) { + l_x3 = x_dataset.getXValue(1, l_subtrahendItem); + l_y3 = x_dataset.getYValue(1, l_subtrahendItem); + l_subtrahendCurX = new Double(l_x3); + l_subtrahendCurY = new Double(l_y3); + + if (!b_subtrahendAtIntersect) { + l_subtrahendXs.add(l_subtrahendCurX); + l_subtrahendYs.add(l_subtrahendCurY); + } + + l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3); + l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3); + + l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); + l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); + l_subtrahendNextX = new Double(l_x4); + l_subtrahendNextY = new Double(l_y4); + } + + // deassert b_*FastForward (only matters for 1st time through loop) + b_minuendFastForward = false; + b_subtrahendFastForward = false; + + Double l_intersectX = null; + Double l_intersectY = null; + boolean b_intersect = false; + + b_minuendAtIntersect = false; + b_subtrahendAtIntersect = false; + + // check for intersect + if ((l_x2 == l_x4) && (l_y2 == l_y4)) { + // check if line segments are colinear + if ((l_x1 == l_x3) && (l_y1 == l_y3)) { + b_colinear = true; + } + else { + // the intersect is at the next point for both the minuend + // and subtrahend + l_intersectX = new Double(l_x2); + l_intersectY = new Double(l_y2); + + b_intersect = true; + b_minuendAtIntersect = true; + b_subtrahendAtIntersect = true; + } + } + else { + // compute common denominator + double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1)) + - ((l_x4 - l_x3) * (l_y2 - l_y1)); + + // compute common deltas + double l_deltaY = l_y1 - l_y3; + double l_deltaX = l_x1 - l_x3; + + // compute numerators + double l_numeratorA = ((l_x4 - l_x3) * l_deltaY) + - ((l_y4 - l_y3) * l_deltaX); + double l_numeratorB = ((l_x2 - l_x1) * l_deltaY) + - ((l_y2 - l_y1) * l_deltaX); + + // check if line segments are colinear + if ((0 == l_numeratorA) && (0 == l_numeratorB) + && (0 == l_denominator)) { + b_colinear = true; + } + else { + // check if previously colinear + if (b_colinear) { + // clear colinear points and flag + l_minuendXs.clear(); + l_minuendYs.clear(); + l_subtrahendXs.clear(); + l_subtrahendYs.clear(); + l_polygonXs.clear(); + l_polygonYs.clear(); + + b_colinear = false; + + // set new starting point for the polygon + boolean b_useMinuend = ((l_x3 <= l_x1) + && (l_x1 <= l_x4)); + l_polygonXs.add(b_useMinuend ? l_minuendCurX + : l_subtrahendCurX); + l_polygonYs.add(b_useMinuend ? l_minuendCurY + : l_subtrahendCurY); + } + + // compute slope components + double l_slopeA = l_numeratorA / l_denominator; + double l_slopeB = l_numeratorB / l_denominator; + + // check if the line segments intersect + if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB) + && (l_slopeB <= 1)) { + // compute the point of intersection + double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1)); + double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1)); + + l_intersectX = new Double(l_xi); + l_intersectY = new Double(l_yi); + b_intersect = true; + b_minuendAtIntersect = ((l_xi == l_x2) + && (l_yi == l_y2)); + b_subtrahendAtIntersect = ((l_xi == l_x4) + && (l_yi == l_y4)); + + // advance minuend and subtrahend to intesect + l_minuendCurX = l_intersectX; + l_minuendCurY = l_intersectY; + l_subtrahendCurX = l_intersectX; + l_subtrahendCurY = l_intersectY; + } + } + } + + if (b_intersect) { + // create the polygon + // add the minuend's points to polygon + l_polygonXs.addAll(l_minuendXs); + l_polygonYs.addAll(l_minuendYs); + + // add intersection point to the polygon + l_polygonXs.add(l_intersectX); + l_polygonYs.add(l_intersectY); + + // add the subtrahend's points to the polygon in reverse + Collections.reverse(l_subtrahendXs); + Collections.reverse(l_subtrahendYs); + l_polygonXs.addAll(l_subtrahendXs); + l_polygonYs.addAll(l_subtrahendYs); + + // create an actual polygon + b_positive = (l_subtrahendMaxY <= l_minuendMaxY) + && (l_subtrahendMinY <= l_minuendMinY); + createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, + x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); + + // clear the point vectors + l_minuendXs.clear(); + l_minuendYs.clear(); + l_subtrahendXs.clear(); + l_subtrahendYs.clear(); + l_polygonXs.clear(); + l_polygonYs.clear(); + + // set the maxY and minY values to intersect y-value + double l_y = l_intersectY.doubleValue(); + l_minuendMaxY = l_y; + l_subtrahendMaxY = l_y; + l_minuendMinY = l_y; + l_subtrahendMinY = l_y; + + // add interection point to new polygon + l_polygonXs.add(l_intersectX); + l_polygonYs.add(l_intersectY); + } + + // advance the minuend if needed + if (l_x2 <= l_x4) { + l_minuendItem++; + b_minuendAdvanced = true; + } + else { + b_minuendAdvanced = false; + } + + // advance the subtrahend if needed + if (l_x4 <= l_x2) { + l_subtrahendItem++; + b_subtrahendAdvanced = true; + } + else { + b_subtrahendAdvanced = false; + } + + b_minuendDone = (l_minuendItem == (l_minuendItemCount - 1)); + b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount + - 1)); + } + + // check if the final polygon needs to be clipped + if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) { + // project onto subtrahend + double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); + l_subtrahendNextX = l_minuendNextX; + l_subtrahendNextY = new Double((l_slope * l_x2) + + (l_y3 - (l_slope * l_x3))); + } + + if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) { + // project onto minuend + double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); + l_minuendNextX = l_subtrahendNextX; + l_minuendNextY = new Double((l_slope * l_x4) + + (l_y1 - (l_slope * l_x1))); + } + + // consider last point of minuend and subtrahend for determining + // positivity + l_minuendMaxY = Math.max(l_minuendMaxY, + l_minuendNextY.doubleValue()); + l_subtrahendMaxY = Math.max(l_subtrahendMaxY, + l_subtrahendNextY.doubleValue()); + l_minuendMinY = Math.min(l_minuendMinY, + l_minuendNextY.doubleValue()); + l_subtrahendMinY = Math.min(l_subtrahendMinY, + l_subtrahendNextY.doubleValue()); + + // add the last point of the minuned and subtrahend + l_minuendXs.add(l_minuendNextX); + l_minuendYs.add(l_minuendNextY); + l_subtrahendXs.add(l_subtrahendNextX); + l_subtrahendYs.add(l_subtrahendNextY); + + // create the polygon + // add the minuend's points to polygon + l_polygonXs.addAll(l_minuendXs); + l_polygonYs.addAll(l_minuendYs); + + // add the subtrahend's points to the polygon in reverse + Collections.reverse(l_subtrahendXs); + Collections.reverse(l_subtrahendYs); + l_polygonXs.addAll(l_subtrahendXs); + l_polygonYs.addAll(l_subtrahendYs); + + // create an actual polygon + b_positive = (l_subtrahendMaxY <= l_minuendMaxY) + && (l_subtrahendMinY <= l_minuendMinY); + createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, + x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); + } + + /** + * Draws the visual representation of a single data item, second pass. In + * the second pass, the renderer draws the lines and shapes for the + * individual points in the two series. + * + * @param x_graphics the graphics device. + * @param x_dataArea the area within which the data is being drawn. + * @param x_info collects information about the drawing. + * @param x_plot the plot (can be used to obtain standard color + * information etc). + * @param x_domainAxis the domain (horizontal) axis. + * @param x_rangeAxis the range (vertical) axis. + * @param x_dataset the dataset. + * @param x_series the series index (zero-based). + * @param x_item the item index (zero-based). + * @param x_crosshairState crosshair information for the plot + * (<code>null</code> permitted). + */ + protected void drawItemPass1(Graphics2D x_graphics, + Rectangle2D x_dataArea, + PlotRenderingInfo x_info, + XYPlot x_plot, + ValueAxis x_domainAxis, + ValueAxis x_rangeAxis, + XYDataset x_dataset, + int x_series, + int x_item, + CrosshairState x_crosshairState) { + + Shape l_entityArea = null; + EntityCollection l_entities = null; + if (null != x_info) { + l_entities = x_info.getOwner().getEntityCollection(); + } + + Paint l_seriesPaint = getItemPaint(x_series, x_item); + Stroke l_seriesStroke = getItemStroke(x_series, x_item); + x_graphics.setPaint(l_seriesPaint); + x_graphics.setStroke(l_seriesStroke); + + PlotOrientation l_orientation = x_plot.getOrientation(); + RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); + RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); + + double l_x0 = x_dataset.getXValue(x_series, x_item); + double l_y0 = x_dataset.getYValue(x_series, x_item); + double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, + l_domainAxisLocation); + double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea, + l_rangeAxisLocation); + + if (getShapesVisible()) { + Shape l_shape = getItemShape(x_series, x_item); + if (l_orientation == PlotOrientation.HORIZONTAL) { + l_shape = ShapeUtilities.createTranslatedShape(l_shape, + l_y1, l_x1); + } + else { + l_shape = ShapeUtilities.createTranslatedShape(l_shape, + l_x1, l_y1); + } + if (l_shape.intersects(x_dataArea)) { + x_graphics.setPaint(getItemPaint(x_series, x_item)); + x_graphics.fill(l_shape); + } + l_entityArea = l_shape; + } + + // add an entity for the item... + if (null != l_entities) { + if (null == l_entityArea) { + l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), + 4, 4); + } + String l_tip = null; + XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, + x_item); + if (null != l_tipGenerator) { + l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, + x_item); + } + String l_url = null; + XYURLGenerator l_urlGenerator = getURLGenerator(); + if (null != l_urlGenerator) { + l_url = l_urlGenerator.generateURL(x_dataset, x_series, + x_item); + } + XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset, + x_series, x_item, l_tip, l_url); + l_entities.add(l_entity); + } + + // draw the item label if there is one... + if (isItemLabelVisible(x_series, x_item)) { + drawItemLabel(x_graphics, l_orientation, x_dataset, x_series, + x_item, l_x1, l_y1, (l_y1 < 0.0)); + } + + int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis); + int l_rangeAxisIndex = x_plot.getRangeAxisIndex(x_rangeAxis); + updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex, + l_rangeAxisIndex, l_x1, l_y1, l_orientation); + + if (0 == x_item) { + return; + } + + double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series, + (x_item - 1)), x_dataArea, l_domainAxisLocation); + double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series, + (x_item - 1)), x_dataArea, l_rangeAxisLocation); + + Line2D l_line = null; + if (PlotOrientation.HORIZONTAL == l_orientation) { + l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2); + } + else if (PlotOrientation.VERTICAL == l_orientation) { + l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2); + } + + if ((null != l_line) && l_line.intersects(x_dataArea)) { + x_graphics.setPaint(getItemPaint(x_series, x_item)); + x_graphics.setStroke(getItemStroke(x_series, x_item)); + x_graphics.draw(l_line); + } + } + + /** + * Determines if a dataset is degenerate. A degenerate dataset is a + * dataset where either series has less than two (2) points. + * + * @param x_dataset the dataset. + * @param x_impliedZeroSubtrahend if false, do not check the subtrahend + * + * @return true if the dataset is degenerate. + */ + private boolean isEitherSeriesDegenerate(XYDataset x_dataset, + boolean x_impliedZeroSubtrahend) { + + if (x_impliedZeroSubtrahend) { + return (x_dataset.getItemCount(0) < 2); + } + + return ((x_dataset.getItemCount(0) < 2) + || (x_dataset.getItemCount(1) < 2)); + } + + /** + * Determines if the two (2) series are disjoint. + * Disjoint series do not overlap in the domain space. + * + * @param x_dataset the dataset. + * + * @return true if the dataset is degenerate. + */ + private boolean areSeriesDisjoint(XYDataset x_dataset) { + + int l_minuendItemCount = x_dataset.getItemCount(0); + double l_minuendFirst = x_dataset.getXValue(0, 0); + double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1); + + int l_subtrahendItemCount = x_dataset.getItemCount(1); + double l_subtrahendFirst = x_dataset.getXValue(1, 0); + double l_subtrahendLast = x_dataset.getXValue(1, + l_subtrahendItemCount - 1); + + return ((l_minuendLast < l_subtrahendFirst) + || (l_subtrahendLast < l_minuendFirst)); + } + + /** + * Draws the visual representation of a polygon + * + * @param x_graphics the graphics device. + * @param x_dataArea the area within which the data is being drawn. + * @param x_plot the plot (can be used to obtain standard color + * information etc). + * @param x_domainAxis the domain (horizontal) axis. + * @param x_rangeAxis the range (vertical) axis. + * @param x_positive indicates if the polygon is positive (true) or + * negative (false). + * @param x_xValues a linked list of the x values (expects values to be + * of type Double). + * @param x_yValues a linked list of the y values (expects values to be + * of type Double). + */ + private void createPolygon (Graphics2D x_graphics, + Rectangle2D x_dataArea, + XYPlot x_plot, + ValueAxis x_domainAxis, + ValueAxis x_rangeAxis, + boolean x_positive, + LinkedList x_xValues, + LinkedList x_yValues) { + + PlotOrientation l_orientation = x_plot.getOrientation(); + RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); + RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); + + Object[] l_xValues = x_xValues.toArray(); + Object[] l_yValues = x_yValues.toArray(); + + GeneralPath l_path = new GeneralPath(); + + if (PlotOrientation.VERTICAL == l_orientation) { + double l_x = x_domainAxis.valueToJava2D(( + (Double) l_xValues[0]).doubleValue(), x_dataArea, + l_domainAxisLocation); + if (this.roundXCoordinates) { + l_x = Math.rint(l_x); + } + + double l_y = x_rangeAxis.valueToJava2D(( + (Double) l_yValues[0]).doubleValue(), x_dataArea, + l_rangeAxisLocation); + + l_path.moveTo((float) l_x, (float) l_y); + for (int i = 1; i < l_xValues.length; i++) { + l_x = x_domainAxis.valueToJava2D(( + (Double) l_xValues[i]).doubleValue(), x_dataArea, + l_domainAxisLocation); + if (this.roundXCoordinates) { + l_x = Math.rint(l_x); + } + + l_y = x_rangeAxis.valueToJava2D(( + (Double) l_yValues[i]).doubleValue(), x_dataArea, + l_rangeAxisLocation); + l_path.lineTo((float) l_x, (float) l_y); + } + l_path.closePath(); + } + else { + double l_x = x_domainAxis.valueToJava2D(( + (Double) l_xValues[0]).doubleValue(), x_dataArea, + l_domainAxisLocation); + if (this.roundXCoordinates) { + l_x = Math.rint(l_x); + } + + double l_y = x_rangeAxis.valueToJava2D(( + (Double) l_yValues[0]).doubleValue(), x_dataArea, + l_rangeAxisLocation); + + l_path.moveTo((float) l_y, (float) l_x); + for (int i = 1; i < l_xValues.length; i++) { + l_x = x_domainAxis.valueToJava2D(( + (Double) l_xValues[i]).doubleValue(), x_dataArea, + l_domainAxisLocation); + if (this.roundXCoordinates) { + l_x = Math.rint(l_x); + } + + l_y = x_rangeAxis.valueToJava2D(( + (Double) l_yValues[i]).doubleValue(), x_dataArea, + l_rangeAxisLocation); + l_path.lineTo((float) l_y, (float) l_x); + } + l_path.closePath(); + } + + if (l_path.intersects(x_dataArea)) { + x_graphics.setPaint(x_positive ? getPositivePaint() + : getNegativePaint()); + x_graphics.fill(l_path); + } + } + + /** + * Returns a default legend item for the specified series. Subclasses + * should override this method to generate customised items. + * + * @param datasetIndex the dataset index (zero-based). + * @param series the series index (zero-based). + * + * @return A legend item for the series. + */ + public LegendItem getLegendItem(int datasetIndex, int series) { + LegendItem result = null; + XYPlot p = getPlot(); + if (p != null) { + XYDataset dataset = p.getDataset(datasetIndex); + if (dataset != null) { + if (getItemVisible(series, 0)) { + String label = getLegendItemLabelGenerator().generateLabel( + dataset, series); + String description = label; + String toolTipText = null; + if (getLegendItemToolTipGenerator() != null) { + toolTipText + = getLegendItemToolTipGenerator().generateLabel( + dataset, series); + } + String urlText = null; + if (getLegendItemURLGenerator() != null) { + urlText = getLegendItemURLGenerator().generateLabel( + dataset, series); + } + Paint paint = lookupSeriesPaint(series); + Stroke stroke = lookupSeriesStroke(series); + Shape line = getLegendLine(); + result = new LegendItem(label, description, + toolTipText, urlText, line, stroke, paint); + result.setLabelFont(lookupLegendTextFont(series)); + Paint labelPaint = lookupLegendTextPaint(series); + if (labelPaint != null) { + result.setLabelPaint(labelPaint); + } + result.setDataset(dataset); + result.setDatasetIndex(datasetIndex); + result.setSeriesKey(dataset.getSeriesKey(series)); + result.setSeriesIndex(series); + } + } + + } + + return result; + + } + + /** + * Tests this renderer for equality with an arbitrary object. + * + * @param obj the object (<code>null</code> permitted). + * + * @return A boolean. + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof StableXYDifferenceRenderer)) { + return false; + } + if (!super.equals(obj)) { + return false; + } + StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj; + if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) { + return false; + } + if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) { + return false; + } + if (this.shapesVisible != that.shapesVisible) { + return false; + } + if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) { + return false; + } + if (this.roundXCoordinates != that.roundXCoordinates) { + return false; + } + return true; + } + + /** + * Returns a clone of the renderer. + * + * @return A clone. + * + * @throws CloneNotSupportedException if the renderer cannot be cloned. + */ + public Object clone() throws CloneNotSupportedException { + StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone(); + clone.legendLine = ShapeUtilities.clone(this.legendLine); + return clone; + } + + /** + * Provides serialization support. + * + * @param stream the output stream. + * + * @throws IOException if there is an I/O error. + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + SerialUtilities.writePaint(this.positivePaint, stream); + SerialUtilities.writePaint(this.negativePaint, stream); + SerialUtilities.writeShape(this.legendLine, stream); + } + + /** + * Provides serialization support. + * + * @param stream the input stream. + * + * @throws IOException if there is an I/O error. + * @throws ClassNotFoundException if there is a classpath problem. + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.positivePaint = SerialUtilities.readPaint(stream); + this.negativePaint = SerialUtilities.readPaint(stream); + this.legendLine = SerialUtilities.readShape(stream); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,358 @@ +package de.intevation.flys.jfree; + +import org.apache.log4j.Logger; + +import java.util.Iterator; + +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Line2D; +import java.awt.Color; +import java.awt.Font; +import java.awt.BasicStroke; + +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.util.LineUtilities; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.entity.XYAnnotationEntity; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.plot.Plot; + +import org.jfree.data.Range; + +import org.jfree.text.TextUtilities; + +import org.jfree.ui.RectangleEdge; +import org.jfree.ui.TextAnchor; + +import de.intevation.flys.utils.ThemeAccess; + +/** + * Custom Annotations class that is drawn only if no collisions with other + * already drawn CustomAnnotations in current plot are found. A mark on + * the axis is shown in all cases, though. + * Draws a given text and a line to it from either axis. + */ +public class StickyAxisAnnotation extends XYTextAnnotation { + + /** Logger for this class. */ + private static Logger logger = + Logger.getLogger(StickyAxisAnnotation.class); + + /** Simplified view on axes. */ + public static enum SimpleAxis { + X_AXIS, /** Usually "horizontal". */ + Y_AXIS /** Usually "vertical". */ + } + + /** Which axis to stick to. */ + protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS; + + /** The 1-dimensional position of this annotation. */ + protected float pos; + + /** Theme attributes. */ + protected Color textColor; + protected Color lineColor; + protected Font font; + protected int lineWidth; + protected String textOrientation = "vertical"; + protected Color textBackground; + protected boolean showBackground; + + + /** + * Trivial constructor. + * + * @param text Text to display. + * @param x X-position in dataspace (typical horizontal, in km). + * @param y Y-position in dataspace (typical vertical, in m). + * @deprecated + */ + public StickyAxisAnnotation(String text, float x, float y) { + super(text, x, y); + setStickyAxis(SimpleAxis.X_AXIS); + } + + + /** + * Constructor with implicit sticky x-axis. + * @param text the text to display. + * @param pos the position at which to draw the text and mark. + */ + public StickyAxisAnnotation(String text, float pos) { + this(text, pos, pos, SimpleAxis.X_AXIS); + } + + + /** + * Constructor with given explicit axis. + * @param text the text to display. + * @param pos the position at which to draw the text and mark. + * @param stickyAxis the axis at which to stick (and to which 'pos' is + * relative). + */ + public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis) { + super(text, pos, pos); + setStickyAxis(stickAxis); + this.pos = pos; + } + + /** + * Legacy-Constructor. + * @deprecated + */ + public StickyAxisAnnotation(String text, float x, float y, + SimpleAxis stickAxis) { + super(text, x, y); + setStickyAxis(stickAxis); + this.pos = x; + } + + + /** + * Sets the "sticky axis" (whether to draw annotations at the + * X- or the Y-Axis. + * + * @param stickyAxis axis to stick to. + */ + public void setStickyAxis(SimpleAxis stickyAxis) { + this.stickyAxis = stickyAxis; + if (stickyAxis == SimpleAxis.X_AXIS){ + float o = 270f; + if(textOrientation.equals("horizontal")) { + o = 0f; + } + this.setRotationAngle(o * (Math.PI / 180f)); + this.setRotationAnchor(TextAnchor.CENTER_LEFT); + this.setTextAnchor(TextAnchor.CENTER_LEFT); + } else { + this.setRotationAngle(0f * (Math.PI / 180f)); + this.setRotationAnchor(TextAnchor.CENTER_LEFT); + this.setTextAnchor(TextAnchor.CENTER_LEFT); + } + } + + + /** + * Draws a small line at axis where this annotation resides. + * + * @param g2 the graphics device. + * @param dataArea the data area. + * @param domainAxis the domain axis. + * @param rangeAxis the range axis. + * @param domainEdge the domain edge. + * @param rangeEdge the range edge. + * @param orientation the plot orientation. + */ + protected void drawAxisMark( + java.awt.Graphics2D g2, + java.awt.geom.Rectangle2D dataArea, + ValueAxis domainAxis, + ValueAxis rangeAxis, + RectangleEdge domainEdge, + RectangleEdge rangeEdge, + PlotOrientation orientation) { + float j2DX1 = 0.0f; + float j2DX2 = 0.0f; + float j2DY1 = 0.0f; + float j2DY2 = 0.0f; + float x = (float) getX(); + float y = (float) getY(); + /* When dependent on X/Y-Axis and orientation, following + can be used as a base: + if (orientation == PlotOrientation.VERTICAL) { + j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DY1 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + j2DX2 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DY2 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + } + else if (orientation == PlotOrientation.HORIZONTAL) { + j2DY1 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DX1 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + j2DY2 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DX2 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + } + + g2.setPaint(this.paint); + g2.setStroke(this.stroke); + */ + if (this.stickyAxis == SimpleAxis.X_AXIS) { + j2DY1 = (float) RectangleEdge.coordinate(dataArea, domainEdge); + double rangeLow = rangeAxis.getRange().getLowerBound(); + // Line ends at 1.5% of full distance. + j2DY2 = (float) rangeAxis.valueToJava2D( + (1f - 0.015f) * rangeLow + 0.015f * + rangeAxis.getRange().getUpperBound(), + dataArea, rangeEdge); + j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, domainEdge); + j2DX2 = j2DX1; + } else { + j2DX1 = (float) RectangleEdge.coordinate(dataArea, rangeEdge); + Range domainRange = domainAxis.getRange(); + double rangeLow = domainRange.getLowerBound(); + // Line ends at 1.5% of full distance. + j2DX2 = (float) domainAxis.valueToJava2D( + (1f - 0.015f) * rangeLow + 0.015f * + domainRange.getUpperBound(), + dataArea, domainEdge); + j2DY1 = (float) rangeAxis.valueToJava2D(pos, dataArea, rangeEdge); + j2DY2 = j2DY1; + } + + Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2); + + // line is clipped to avoid JRE bug 6574155, for more info + // see JFreeChart bug 2221495 + boolean visible = LineUtilities.clipLine(line, dataArea); + if (visible) { + setOutlineStroke(new BasicStroke((float) lineWidth)); + g2.setStroke(getOutlineStroke()); + g2.setPaint(lineColor); + g2.draw(line); + } + } + + + /** + * Draw the Annotiation if it does not collide with other already drawn + * Annotations. + * + * @param g2 the graphics device. + * @param plot the plot. + * @param dataArea the data area. + * @param domainAxis the domain axis. + * @param rangeAxis the range axis. + * @param rendererIndex the render index. + * @param info state information, escpecially collects info about + * already drawn shapes (and thus annotations), used + * for collision detection. + */ + @Override + public void draw( + java.awt.Graphics2D g2, + XYPlot plot, + java.awt.geom.Rectangle2D dataArea, + ValueAxis domainAxis, + ValueAxis rangeAxis, + int rendererIndex, + PlotRenderingInfo info) { + + if (info == null) + return; + + if (domainAxis == null || rangeAxis == null + || domainAxis.getRange() == null + || rangeAxis.getRange() == null + ) { + logger.error("Annotation cannot be drawn (missing axis)."); + return; + } + + // Calculate the bounding box. + ChartRenderingInfo chartInfo = info.getOwner(); + + PlotOrientation orientation = plot.getOrientation(); + RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( + plot.getDomainAxisLocation(), orientation); + RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( + plot.getRangeAxisLocation(), orientation); + float anchorX = 0f; + float anchorY = 0.0f; + if (this.stickyAxis == SimpleAxis.X_AXIS) { + // Text starts at 1.5% of full distance. + float rangeLow = (float) rangeAxis.getRange().getLowerBound(); + float y = rangeLow + 0.02f * ((float) + rangeAxis.getRange().getUpperBound() - rangeLow); + setY(y); + + anchorX = (float) domainAxis.valueToJava2D( + getX(), dataArea, domainEdge); + anchorY = (float) rangeAxis.valueToJava2D( + getY(), dataArea, rangeEdge); + } else { + float rangeLow = (float) domainAxis.getRange().getLowerBound(); + float x = rangeLow + 0.02f * ((float) + domainAxis.getRange().getUpperBound() - rangeLow); + setX(x); + anchorX = (float) domainAxis.valueToJava2D( + getX(), dataArea, domainEdge); + anchorY = (float) rangeAxis.valueToJava2D( + getY(), dataArea, rangeEdge); + } + if (orientation == PlotOrientation.HORIZONTAL) { + float tempAnchor = anchorX; + anchorX = anchorY; + anchorY = tempAnchor; + } + + //Call to apply orientation. + setStickyAxis(stickyAxis); + + // Always draw the small line at axis. + drawAxisMark(g2, dataArea, domainAxis, rangeAxis, domainEdge, + rangeEdge, orientation); + + Shape hotspot = TextUtilities.calculateRotatedStringBounds( + getText(), g2, anchorX, anchorY, getTextAnchor(), + getRotationAngle(), getRotationAnchor()); + Rectangle2D hotspotBox = hotspot.getBounds2D(); + // Check for collisions with other XYAnnotations. + for (Iterator i = chartInfo.getEntityCollection().iterator(); + i.hasNext(); ) { + Object next = i.next(); + // Collision with other stuff than XYAnnotations are okay. + if (next instanceof XYAnnotationEntity) { + XYAnnotationEntity drawnShape = (XYAnnotationEntity) next; + if (drawnShape.getArea().intersects(hotspotBox)) { + // Found collision, early stop. + return; + } + } + } + + // Draw the background. + if (showBackground) { + g2.setStroke(new BasicStroke ((float) 1)); + g2.setBackground(textBackground); + g2.setPaint(textBackground); + hotspot = TextUtilities.calculateRotatedStringBounds( + getText(), g2, anchorX, anchorY, getTextAnchor(), + getRotationAngle(), getRotationAnchor()); + g2.fill(hotspot); + g2.draw(hotspot); + } + + // Draw the text. + g2.setPaint(textColor); + g2.setFont(font); + TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY, + getTextAnchor(), getRotationAngle(), getRotationAnchor()); + + // Add info that we have drawn this Annotation. + addEntity(info, hotspot, rendererIndex, getToolTipText(), getURL()); + } + + + public void applyTheme(ThemeAccess ta) { + lineWidth = ta.parseLineWidth(); + lineColor = ta.parseLineColorField(); + textColor = ta.parseTextColor(); + font = ta.parseTextFont(); + textOrientation = ta.parseTextOrientation(); + textBackground = ta.parseTextBackground(); + showBackground = ta.parseShowTextBackground(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/XYDatasetToZeroMapper.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,135 @@ +package de.intevation.flys.jfree; + +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; + +import org.jfree.data.xy.XYDataset; + +import org.jfree.data.general.DatasetChangeListener; +import org.jfree.data.general.DatasetGroup; + +import org.jfree.data.DomainOrder; + +import java.io.Serializable; + +public class XYDatasetToZeroMapper +implements Serializable +{ + protected int seriesA; + protected int seriesB; + + public class MappedXYDataset implements XYDataset, Serializable { + + protected XYDataset parent; + + public MappedXYDataset() { + } + + public MappedXYDataset(XYDataset parent) { + this.parent = parent; + } + + protected int remap(int series) { + switch (series) { + case 0: return seriesA; + case 1: return seriesB; + } + return series; + } + + @Override + public int getSeriesCount() { + return seriesA != seriesB ? 2 : 1; + } + + @Override + public Comparable getSeriesKey(int series) { + return parent.getSeriesKey(remap(series)); + } + + @Override + public int indexOf(Comparable seriesKey) { + Comparable cmpA = parent.getSeriesKey(seriesA); + if (cmpA == seriesKey + || (cmpA != null && cmpA.equals(seriesKey))) { + return 0; + } + Comparable cmpB = parent.getSeriesKey(seriesB); + if (cmpB == seriesKey + || (cmpB != null && cmpB.equals(seriesKey))) { + return 1; + } + return -1; + } + + @Override + public void addChangeListener(DatasetChangeListener listener) { + parent.addChangeListener(listener); + } + + @Override + public void removeChangeListener(DatasetChangeListener listener) { + parent.removeChangeListener(listener); + } + + @Override + public DatasetGroup getGroup() { + return parent.getGroup(); + } + + @Override + public void setGroup(DatasetGroup group) { + parent.setGroup(group); + } + + @Override + public DomainOrder getDomainOrder() { + return parent.getDomainOrder(); + } + + @Override + public int getItemCount(int series) { + return parent.getItemCount(remap(series)); + } + + @Override + public Number getX(int series, int item) { + return parent.getX(remap(series), item); + } + + @Override + public double getXValue(int series, int item) { + return parent.getXValue(remap(series), item); + } + + @Override + public Number getY(int series, int item) { + return parent.getY(remap(series), item); + } + + @Override + public double getYValue(int series, int item) { + return parent.getYValue(remap(series), item); + } + } // class MappedXYDataset + + public XYDatasetToZeroMapper() { + } + + public XYDatasetToZeroMapper(int seriesA) { + this(seriesA, seriesA); + } + + public XYDatasetToZeroMapper(int seriesA, int seriesB) { + this.seriesA = seriesA; + this.seriesB = seriesB; + } + + public Iterator<XYDataset> iterator(XYDataset dataset) { + List<XYDataset> list = new ArrayList<XYDataset>(1); + list.add(new MappedXYDataset(dataset)); + return list.iterator(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultTheme.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,193 @@ +package de.intevation.flys.themes; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultTheme implements Theme { + + /** The name of the theme.*/ + protected String name; + + /** The description of the theme.*/ + protected String description; + + protected String facet; + + protected int index; + + + /** The map storing the fields of this theme.*/ + protected Map<String, ThemeField> fields; + + /** The map storing the attributes of this theme.*/ + protected Map<String, String> attr; + + + /** + * Initializes the components of this Theme. + */ + public DefaultTheme(String name, String description) { + this.name = name; + this.description = description; + this.fields = new HashMap<String, ThemeField>(); + this.attr = new HashMap<String, String>(); + } + + + public void init(Node config) { + } + + + public String getName() { + return name; + } + + + public String getDescription() { + return description; + } + + + public String getFacet() { + return facet; + } + + + public void setFacet(String facet) { + this.facet = facet; + } + + + public int getIndex() { + return index; + } + + + public void setIndex(int index) { + this.index = index; + } + + + public void addAttribute(String name, String value) { + if (name != null && value != null) { + attr.put(name, value); + } + } + + + public String getAttribute(String name) { + return attr.get(name); + } + + + public void addField(String name, ThemeField field) { + if (name != null && field != null) { + fields.put(name, field); + } + } + + + public void setFieldValue(String name, Object value) { + if (name != null && value != null) { + ThemeField field = fields.get(name); + + if (field != null) { + field.setValue(value); + } + } + } + + + public ThemeField getField(String name) { + return fields.get(name); + } + + + public String getFieldType(String name) { + ThemeField field = fields.get(name); + + return field != null ? field.getType() : null; + } + + + public Object getFieldValue(String name) { + ThemeField field = fields.get(name); + + return field != null ? field.getValue() : null; + } + + + public Document toXML() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator(doc, null, null); + + Element theme = cr.create("theme"); + theme.setAttribute("facet", facet); + theme.setAttribute("index", String.valueOf(index)); + + appendAttributes(cr, theme); + appendFields(cr, theme); + + doc.appendChild(theme); + + return doc; + } + + + /** + * Appends the attributes configured for this theme. + * + * @param cr The ElementCreator. + * @param theme The document root element. + */ + protected void appendAttributes(ElementCreator cr, Element theme) { + Iterator<String> iter = attr.keySet().iterator(); + + while (iter.hasNext()) { + String key = iter.next(); + String val = getAttribute(key); + + if (key == null || val == null) { + continue; + } + + cr.addAttr(theme, key, val); + } + } + + + /** + * Appends the fields configured for this theme. + * + * @param cr The ElementCreator. + * @param theme The document root element. + */ + protected void appendFields(ElementCreator cr, Element theme) { + Iterator<String> iter = fields.keySet().iterator(); + + while (iter.hasNext()) { + String name = iter.next(); + + ThemeField field = getField(name); + + Document doc = field.toXML(); + Node root = doc.getFirstChild(); + + theme.appendChild(theme.getOwnerDocument().importNode(root, true)); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/DefaultThemeField.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,85 @@ +package de.intevation.flys.themes; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultThemeField implements ThemeField { + + protected String name; + + protected Map<String, Object> attr; + + + public DefaultThemeField(String name) { + this.name = name; + this.attr = new HashMap<String, Object>(); + } + + + public String getName() { + return name; + } + + + public String getType() { + return (String) getAttribute("type"); + } + + + public Object getValue() { + return getAttribute("value"); + } + + + public void setValue(Object value) { + setAttribute("value", value); + } + + + public Object getAttribute(String name) { + return attr.get(name); + } + + + public void setAttribute(String name, Object value) { + if (name == null || value == null) { + return; + } + + attr.put(name, value); + } + + + public Document toXML() { + Document doc = XMLUtils.newDocument(); + + ElementCreator cr = new ElementCreator(doc, null, null); + + Element field = cr.create("field"); + + Iterator<String> iter = attr.keySet().iterator(); + + while (iter.hasNext()) { + String name = iter.next(); + String value = (String) getAttribute(name); + + cr.addAttr(field, name, value); + } + + doc.appendChild(field); + + return doc; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/Theme.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,119 @@ +package de.intevation.flys.themes; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Theme { + + /** + * Method to initialize the theme. + * + * @param config The configuration node. + */ + void init(Node config); + + + /** + * Returns the name of the theme. + * + * @return the name of the theme. + */ + String getName(); + + + /** + * Returns the description of the theme. + * + * @return the description of the theme. + */ + String getDescription(); + + + String getFacet(); + + void setFacet(String facet); + + int getIndex(); + + void setIndex(int index); + + + /** + * Adds a new attribute. + * + * @param name The name of the attribute. + * @param value The value of the attribute. + */ + void addAttribute(String name, String value); + + + /** + * Returns the value of a specific attribute. + * + * @param name the name of the attribute. + * + * @return the value of the attribute <i>name</i>. + */ + String getAttribute(String name); + + + /** + * Adds a new field to the theme. + * + * @param name The name of the field. + * @param field The field. + */ + void addField(String name, ThemeField field); + + + /** + * Sets the value of an field. + * + * @param name The name of the field. + * @param value The new value of the field. + */ + void setFieldValue(String name, Object value); + + + /** + * Returns the field specified by name. + * + * @param name The name of the desired field. + * + * @return an field. + */ + ThemeField getField(String name); + + + /** + * Returns the typename of a field. + * + * @param name the name of the field. + * + * @return the typename of a field. + */ + String getFieldType(String name); + + + /** + * Returns the value of a field. + * + * @param name The name of the field. + * + * @return the value of a field. + */ + Object getFieldValue(String name); + + + /** + * Dumps the theme to XML. + * + * @return a document. + */ + Document toXML(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeAccess.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,96 @@ +package de.intevation.flys.utils; + +import java.awt.Color; +import java.awt.Font; + +import org.w3c.dom.Document; + + +public class ThemeAccess +{ + protected Document theme; + + protected Integer lineWidth; + + protected Color lineColor; + protected Color textColor; + protected Font font; + protected String textOrientation; + protected Color textBackground; + protected Boolean showTextBackground; + + public ThemeAccess(Document theme) { + this.theme = theme; + } + + public int parseLineWidth() { + if (lineWidth == null) { + lineWidth = ThemeUtil.parseLineWidth(theme); + } + return lineWidth; + } + + public Color parseLineColorField() { + if (lineColor == null) { + lineColor = ThemeUtil.parseLineColorField(theme); + if (lineColor == null) { + lineColor = Color.BLACK; + } + } + return lineColor; + } + + public Color parseTextColor() { + if (textColor == null) { + textColor = ThemeUtil.parseTextColor(theme); + if (textColor == null) { + textColor = Color.BLACK; + } + } + return textColor; + } + + public Font parseTextFont() { + if (font == null) { + font = ThemeUtil.parseTextFont(theme); + if (font == null) { + font = new Font("Arial", Font.BOLD, 10); + } + } + return font; + } + + public String parseTextOrientation() { + if (textOrientation == null) { + textOrientation = ThemeUtil.parseTextOrientation(theme); + if (textOrientation == null) { + textOrientation = "horizontal"; + } + } + return textOrientation; + } + + + public Color parseTextBackground() { + if (textBackground == null) { + textBackground = ThemeUtil.parseTextBackground(theme); + if (textBackground == null) { + textBackground = Color.WHITE; + } + } + return textBackground; + } + + public boolean parseShowTextBackground() { + if (showTextBackground == null) { + showTextBackground = ThemeUtil.parseShowTextBackground(theme); + } + return showTextBackground; + } + + /** + more of this + */ + +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeFactory.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,253 @@ +package de.intevation.flys.themes; + +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.FLYSArtifact; + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + * + * Mapping-matching rules: + * + */ +public class ThemeFactory { + + private static Logger logger = Logger.getLogger(ThemeFactory.class); + + /** Trivial, hidden constructor. */ + private ThemeFactory() { + ; + } + + + /** + * Creates a new theme from <i>config</i>. + * + * @param themeCfg The theme config document that is used to fetch parent + * themes. + * @param config The theme config node. + * + * @return a new theme. + */ + public static Theme createTheme(Document themeCfg, Node config) { + String name = getName(config); + String desc = getDescription(config); + + logger.debug("Create new theme: " + name); + + Theme theme = new DefaultTheme(name, desc); + + parseInherits(themeCfg, config, theme); + parseFields(config, theme); + parseAttrs(config, theme); + + return theme; + } + + + /** + * Get first matching theme for facet. + * + * @param name Name to match "from" of theme mapping. + * @param pattern String to 'compare' to pattern in mapping. + * @param output Name of the current output + * + * @return First matching theme. + */ + public static Theme getTheme( + FLYSContext c, + String name, + String pattern, + String output) + { + logger.debug("Search theme for: " + name + " - pattern: " + pattern); + + if (c == null || name == null) { + logger.warn("Cannot search for theme."); + return null; + } + + // Fetch mapping and themes. + Map<String, List<ThemeMapping>> map = (Map<String, List<ThemeMapping>>) + c.get(FLYSContext.THEME_MAPPING); + + Map<String, Theme> t = (Map<String, Theme>) + c.get(FLYSContext.THEMES); + + FLYSArtifact artifact = (FLYSArtifact) c.get(FLYSContext.ARTIFACT_KEY); + + if (map == null || map.size() == 0 || t == null || t.size() == 0) { + logger.warn("No mappings or themes found. Cannot retrieve theme!"); + return null; + } + + List<ThemeMapping> mapping = map.get(name); + + if (mapping == null) { + logger.warn("No theme found for mapping: " + name); + return null; + } + + // Take first mapping of which all conditions are satisfied. + for (ThemeMapping tm: mapping) { + if (name.equals(tm.getFrom()) + && tm.applyPattern(pattern) + && tm.masterAttrMatches(artifact) + && tm.outputMatches(output)) + { + return t.get(tm.getTo()); + } + } + + String msg = + "No theme found for '" + name + + "' with pattern '" + pattern + "' and output " + output + "."; + + logger.warn(msg); + + return null; + } + + + protected static String getName(Node config) { + return (String) XMLUtils.xpath(config, "@name", XPathConstants.STRING); + } + + + protected static String getDescription(Node config) { + return (String) XMLUtils.xpath(config, "@desc", XPathConstants.STRING); + } + + + protected static void parseInherits(Document themeCfg, Node cfg, Theme t) { + logger.debug("ThemeFactory.parseInherits"); + + NodeList inherits = (NodeList) XMLUtils.xpath( + cfg, "inherits/inherit", XPathConstants.NODESET); + + int num = inherits != null ? inherits.getLength() : 0; + + if (num == 0) { + logger.debug("Theme does not inherit from other themes."); + return; + } + + logger.debug("Theme inherits from " + num + " other themes."); + + for (int i = 0; i < num; i++) { + Node inherit = inherits.item(i); + String from = (String) XMLUtils.xpath( + inherit, "@from", XPathConstants.STRING); + + Node tmp = getThemeNode(themeCfg, from); + + parseInherits(themeCfg, tmp, t); + parseFields(tmp, t); + } + } + + + protected static Node getThemeNode(Document themeCfg, String name) { + if (name == null) { + logger.warn("Cannot search theme config without name!"); + return null; + } + + logger.debug("Search for theme: " + name); + + String xpath = "/themes/theme[@name='" + name + "']"; + + return (Node) XMLUtils.xpath(themeCfg, xpath, XPathConstants.NODE); + } + + + protected static void parseFields(Node config, Theme theme) { + if (config == null || theme == null) { + logger.warn("Parsing fields without node or theme is senseless!"); + return; + } + + NodeList fields = (NodeList) XMLUtils.xpath( + config, "fields/field", XPathConstants.NODESET); + + int num = fields != null ? fields.getLength() : 0; + + logger.debug("Found " + num + " own fields in this theme."); + + if (num == 0) { + logger.debug("Theme has no own fields."); + return; + } + + for (int i = 0; i < num; i++) { + Node field = fields.item(i); + + addField(theme, field); + } + } + + + protected static void addField(Theme theme, Node field) { + String name = (String) XMLUtils.xpath( + field, "@name", XPathConstants.STRING); + + logger.debug("Add field: " + name); + + NamedNodeMap attrs = field.getAttributes(); + + int num = attrs != null ? attrs.getLength() : 0; + + if (num == 0) { + logger.warn("This field has no attributes."); + return; + } + + ThemeField theField = new DefaultThemeField(name); + + for (int i = 0; i < num; i++) { + Node attr = attrs.item(i); + + String key = attr.getNodeName(); + String value = attr.getNodeValue(); + + theField.setAttribute(key, value); + } + + theme.addField(name, theField); + } + + + protected static void parseAttrs(Node config, Theme theme) { + NamedNodeMap attrs = config.getAttributes(); + + int num = attrs != null ? attrs.getLength() : 0; + + if (num == 0) { + logger.debug("Theme has no attributes set."); + return; + } + + for (int i = 0; i < num; i++) { + Node attr = attrs.item(i); + + String name = attr.getNodeName(); + String value = attr.getNodeValue(); + + theme.addAttribute(name, value); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeField.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,58 @@ +package de.intevation.flys.themes; + +import org.w3c.dom.Document; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface ThemeField { + + /** + * Returns the name of this field. + * + * @return the name of this field. + */ + String getName(); + + /** + * Returns the type of this field. + * + * @return the type of this field. + */ + String getType(); + + + /** + * Returns the value of this field. + * + * @return the value of this field. + */ + Object getValue(); + + + /** + * Changes the value of this field. + * + * @param value The new value. + */ + void setValue(Object value); + + + /** + * Sets the value of an attribute. + * + * @param name The name of an attribute. + * @param value The value of an attribute. + */ + void setAttribute(String name, Object value); + + + /** + * Dumps the field to XML. + * + * @return a document. + */ + Document toXML(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/themes/ThemeMapping.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,135 @@ +package de.intevation.flys.themes; + +import org.apache.log4j.Logger; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.intevation.flys.artifacts.FLYSArtifact; + +/** + * Represents mapping to a theme (including conditions). + */ +public class ThemeMapping implements Serializable { + + /** The logger that is used in this class */ + private static Logger logger = Logger.getLogger(ThemeMapping.class); + + /** Name from which to map. */ + protected String from; + + /** Name to which to map. */ + protected String to; + + /** Given pattern (held against facet description). */ + protected String patternStr; + + /** Given masterAttr pattern (held against masterartifacts attributes). */ + protected String masterAttr; + + /** Given output for which mapping is valid. */ + protected String output; + + protected Pattern pattern; + + + public ThemeMapping(String from, String to) { + this(from, to, null, null, null); + } + + + public ThemeMapping( + String from, + String to, + String patternStr, + String masterAttr, + String output) + { + this.from = from; + this.to = to; + this.patternStr = patternStr; + this.masterAttr = masterAttr; + this.output = output; + + this.pattern = Pattern.compile(patternStr); + } + + + public String getFrom() { + return from; + } + + + /** + * Get name of theme that is mapped to. + */ + public String getTo() { + return to; + } + + + /** + * Get pattern. + */ + public String getPatternStr() { + return patternStr; + } + + + /** + * Match regular expression against text. + * + * @param text string to be matched against. + * @return true if pattern matches text or pattern is empty. + */ + public boolean applyPattern(String text) { + if (patternStr == null || patternStr.equals("")) { + return true; + } + Matcher m = pattern.matcher(text); + return m.matches(); + } + + + /** + * Inspects Artifacts data given the masterAttr-condition. + * + * The only condition implemented so far is 'key==value', for which + * the Artifacts data with name "key" has to be of value "value" in order + * for true to be returned. + * + * @param artifact Artifact of which to inspect data. + * @return true if no condition is specified or condition is met. + */ + public boolean masterAttrMatches(FLYSArtifact artifact) { + if (masterAttr == null || masterAttr.equals("")) { + return true; + } + + // Operator split. + String[] parts = masterAttr.split("=="); + if (parts.length != 2) { + logger.error("ThemeMapping could not parse masterAttr.-condition:_" + + masterAttr + "_"); + return false; + } + + // Test. + return artifact.getDataAsString(parts[0]).equals(parts[1]); + } + + + /** + * Returns true if no output condition exists, or the condition is met + * in parameter output. + */ + public boolean outputMatches(String output) { + if (this.output == null || this.output.equals("")) { + return true; + } + + return this.output.equals(output); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/DataUtil.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,33 @@ +package de.intevation.flys.utils; + +import java.util.Random; + +import gnu.trove.TDoubleArrayList; + +public class DataUtil +{ + public static boolean guessWaterIncreasing(TDoubleArrayList data) { + return guessWaterIncreasing(data, 0.05f); + } + + public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) { + int N = data.size(); + if (N < 2) return false; + + int samples = (int)(factor*N) + 1; + + int up = 0; + + Random rand = new Random(); + + for (int i = 0; i < samples; ++i) { + int pos2 = rand.nextInt(N-1) + 1; + int pos1 = rand.nextInt(pos2); + double w1 = data.getQuick(pos1); + double w2 = data.getQuick(pos2); + if (w2 > w1) ++up; + } + + return up > samples/2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/DoubleUtil.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,136 @@ +package de.intevation.flys.utils; + +import java.util.Arrays; + +import de.intevation.flys.artifacts.math.Linear; + + +public class DoubleUtil +{ + public static final double DEFAULT_STEP_PRECISION = 1e6; + + private DoubleUtil() { + } + + public static final double [] explode(double from, double to, double step) { + return explode(from, to, step, DEFAULT_STEP_PRECISION); + } + + public static final double round(double x, double precision) { + return Math.round(x * precision)/precision; + } + + public static final double round(double x) { + return Math.round(x * DEFAULT_STEP_PRECISION)/DEFAULT_STEP_PRECISION; + } + + public static final double [] explode( + double from, + double to, + double step, + double precision + ) { + double lower = from; + + double diff = to - from; + double tmp = diff / step; + int num = (int)Math.abs(Math.ceil(tmp)) + 1; + + double [] values = new double[num]; + + if (from > to) { + step = -step; + } + + double max = Math.max(from, to); + + for (int idx = 0; idx < num; idx++) { + if (lower > max) { + return Arrays.copyOfRange(values, 0, idx); + } + + values[idx] = round(lower, precision); + lower += step; + } + + return values; + } + + public static final double interpolateSorted( + double [] xs, + double [] ys, + double x + ) { + int lo = 0, hi = xs.length-1; + + int mid = -1; + + while (lo <= hi) { + mid = (lo + hi) >> 1; + double mx = xs[mid]; + if (x < mx) hi = mid - 1; + else if (x > mx) lo = mid + 1; + else return ys[mid]; + } + if (mid < lo) { + return lo >= xs.length + ? Double.NaN + : Linear.linear(x, xs[mid], xs[mid+1], ys[mid], ys[mid+1]); + } + return hi < 0 + ? Double.NaN + : Linear.linear(x, xs[mid-1], xs[mid], ys[mid-1], ys[mid]); + } + + public static final boolean isIncreasing(double [] array) { + int inc = 0; + int dec = 0; + int sweet = (array.length-1)/2; + for (int i = 1; i < array.length; ++i) { + if (array[i] > array[i-1]) { + if (++inc > sweet) { + return true; + } + } + else if (++dec > sweet) { + return false; + } + } + return inc > sweet; + } + + public static final double [] swap(double [] array) { + int lo = 0; + int hi = array.length-1; + while (hi > lo) { + double t = array[lo]; + array[lo] = array[hi]; + array[hi] = t; + ++lo; + --hi; + } + + return array; + } + + public static final double [] swapClone(double [] in) { + double [] out = new double[in.length]; + + for (int j = out.length-1, i = 0; j >= 0;) { + out[j--] = in[i++]; + } + + return out; + } + + public static final double [] sumDiffs(double [] in) { + double [] out = new double[in.length]; + + for (int i = 1; i < out.length; ++i) { + out[i] = out[i-1] + Math.abs(in[i-1] - in[i]); + } + + return out; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,368 @@ +package de.intevation.flys.utils; + +import org.apache.log4j.Logger; + +import java.text.NumberFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.w3c.dom.Document; + +import gnu.trove.TDoubleArrayList; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.WINFOArtifact; +import de.intevation.flys.artifacts.model.RiverFactory; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.MainValue; +import de.intevation.flys.model.River; + +public class FLYSUtils { + + /** The logger that is used in this utility. */ + private static Logger logger = Logger.getLogger(FLYSUtils.class); + + public static enum KM_MODE { RANGE, LOCATIONS, NONE }; + + public static final String XPATH_RIVER_PROJECTION = + "/artifact-database/floodmap/river[@name=$name]/srid/@value"; + + public static final String XPATH_SHAPEFILE_DIR = + "/artifact-database/floodmap/shapefile-path/@value"; + + public static final String XPATH_VELOCITY_LOGFILE = + "/artifact-database/floodmap/velocity/logfile/@path"; + + public static final String XPATH_MAPSERVER_URL = + "/artifact-database/floodmap/mapserver/server/@path"; + + public static final String XPATH_MAPFILE_PATH = + "/artifact-database/floodmap/mapserver/mapfile/@path"; + + public static final String XPATH_MAPFILE_TEMPLATE = + "/artifact-database/floodmap/mapserver/map-template/@path"; + + public static final String XPATH_MAPSERVER_TEMPLATE_PATH = + "/artifact-database/floodmap/mapserver/templates/@path"; + + + private FLYSUtils() { + } + + + public static FLYSArtifact getArtifact(String uuid, CallContext context) { + try { + Artifact artifact = context.getDatabase().getRawArtifact(uuid); + + if (artifact == null) { + logger.error("Artifact '" + uuid + "' does not exist."); + return null; + } + + if (!(artifact instanceof FLYSArtifact)) { + logger.error("Artifact '" +uuid+ "' is no valid FLYSArtifact."); + return null; + } + + return (FLYSArtifact) artifact; + } + // TODO: catch more selective + catch (Exception e) { + logger.error("Cannot get FLYSArtifact " + uuid + " from database (" + e.getMessage() + ")."); + return null; + } + } + + + /** + * Returns the FLYSContext from context object. + * + * @param context The CallContext or the FLYSContext. + * + * @return the FLYSContext. + */ + public static FLYSContext getFlysContext(Object context) { + return context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) ((CallContext) context).globalContext(); + } + + + /** + * Convinience function to retrieve an XPath as string with replaced config + * directory. + * + * @param xpath The XPath expression. + * + * @return a string with replaced config directory. + */ + public static String getXPathString(String xpath) { + String tmp = Config.getStringXPath(xpath); + tmp = Config.replaceConfigDir(tmp); + + return tmp; + } + + + public static KM_MODE getKmRangeMode(FLYSArtifact flys) { + String mode = flys.getDataAsString("ld_mode"); + + if (mode == null || mode.length() == 0) { + return KM_MODE.NONE; + } + else if (mode.equals("distance")) { + return KM_MODE.RANGE; + } + else if (mode.equals("locations")) { + return KM_MODE.LOCATIONS; + } + else { + return KM_MODE.NONE; + } + } + + + public static double[] getKmRange(FLYSArtifact flys) { + switch (getKmRangeMode(flys)) { + case RANGE: { + return getKmFromTo(flys); + } + + case LOCATIONS: { + double[] locs = getLocations(flys); + return new double[] { locs[0], locs[locs.length-1] }; + } + + case NONE: { + double[] locs = getLocations(flys); + if (locs != null) { + return new double[] { locs[0], locs[locs.length-1] }; + } + else { + return getKmFromTo(flys); + } + } + } + + return new double[] { Double.NaN, Double.NaN }; + } + + + public static double[] getKmFromTo(FLYSArtifact flys) { + String strFrom = flys.getDataAsString("ld_from"); + String strTo = flys.getDataAsString("ld_to"); + + if (strFrom == null || strTo == null) { + return null; + } + + try { + return new double[] { + Double.parseDouble(strFrom), + Double.parseDouble(strTo) }; + } + catch (NumberFormatException nfe) { + return null; + } + } + + + public static double[] getLocations(FLYSArtifact flys) { + String locationStr = flys.getDataAsString("ld_locations"); + + if (locationStr == null || locationStr.length() == 0) { + return null; + } + + String[] tmp = locationStr.split(" "); + TDoubleArrayList locations = new TDoubleArrayList(); + + for (String l: tmp) { + try { + locations.add(Double.parseDouble(l)); + } + catch (NumberFormatException nfe) { + } + } + + locations.sort(); + + return locations.toNativeArray(); + } + + + /** + * Returns the selected River object based on the 'river' data that might + * have been inserted by the user. + * + * @return the selected River or null if no river has been chosen yet. + */ + public static River getRiver(FLYSArtifact flys) { + String sRiver = flys.getDataAsString("river"); + + return (sRiver != null) + ? RiverFactory.getRiver(sRiver) + : null; + } + + + /** + * Extracts the SRID defined in the global configuration for the river + * specified in <i>artifact</i>. + * + * @param artifact The FLYSArtifact that stores the name of the river. + * + * @return the SRID as string (e.g. "31466"). + */ + public static String getRiverSrid(FLYSArtifact artifact) { + String river = artifact.getDataAsString("river"); + + if (river == null || river.length() == 0) { + return null; + } + + return getRiverSrid(river); + } + + + public static String getRiverSrid(String rivername) { + Map<String, String> variables = new HashMap<String, String>(1); + variables.put("name", rivername); + + Document cfg = Config.getConfig(); + + return (String) XMLUtils.xpath( + cfg, + XPATH_RIVER_PROJECTION, + XPathConstants.STRING, + null, + variables); + } + + + public static String createWspWTitle( + WINFOArtifact winfo, + CallContext cc, + String name + ) { + String[] parts = name.split("="); + + NumberFormat nf = Formatter.getWaterlevelW(cc); + + String namedMainValue = null; + + boolean isQ = winfo.isQ(); + boolean isFree = winfo.isFreeQ(); + + double v; + + try { + v = Double.valueOf(parts[1]); + + namedMainValue = getNamedMainValue(winfo.getGauge(), v); + } + catch (NumberFormatException nfe) { + logger.warn("Cannot parse Double of: '" + parts[1] + "'"); + return name; + } + + String prefix = null; + + if (isQ && !isFree && namedMainValue != null) { + return "W (" + namedMainValue + ")"; + } + + if (isQ) { + prefix = "Q="; + } + + return prefix == null + ? "W(" + nf.format(v) + ")" + : "W(" + prefix + nf.format(v) + ")"; + } + + + public static String createWspQTitle( + WINFOArtifact winfo, + CallContext cc, + String name + ) { + String[] parts = name.split("="); + + NumberFormat nf = Formatter.getWaterlevelQ(cc); + + String namedMainValue = null; + + boolean isQ = winfo.isQ(); + boolean isFree = winfo.isFreeQ(); + + double v; + + try { + v = Double.valueOf(parts[1]); + + namedMainValue = getNamedMainValue(winfo.getGauge(), v); + } + catch (NumberFormatException nfe) { + logger.warn("Cannot parse Double of: '" + parts[1] + "'"); + return name; + } + + String prefix = null; + + if (isQ && !isFree && namedMainValue != null) { + return namedMainValue; + } + + if (!isQ) { + prefix = "W="; + } + + return prefix == null + ? "Q(" + nf.format(v) + ")" + : "Q(" + prefix + nf.format(v) + ")"; + } + + + public static String getNamedMainValue(Gauge gauge, double value) { + List<MainValue> mainValues = gauge.getMainValues(); + logger.debug("Search named main value for: " + value); + + for (MainValue mv: mainValues) { + if (mv.getValue().doubleValue() == value) { + logger.debug("Found named main value: " + mv.getMainValue().getName()); + return mv.getMainValue().getName(); + } + } + + logger.debug("Did not find a named main value for: " + value); + return null; + } + + + /** + * Returns the URL of user mapfile for the owner of Artifact + * <i>artifactId</i>. + * + * @param artifactId The UUID of an artifact. + * + * @return the URL of the user wms. + */ + public static String getUserWMSUrl(String artifactId) { + String url = getXPathString(XPATH_MAPSERVER_URL); + url = url + "user-wms"; + + return url; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/Formatter.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,154 @@ +package de.intevation.flys.utils; + +import java.text.NumberFormat; +import java.util.Locale; + +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.resources.Resources; + + +public final class Formatter { + + // WATERLEVEL FORMATTER CONSTANTS + public static final int WATERLEVEL_KM_MIN_DIGITS = 3; + public static final int WATERLEVEL_KM_MAX_DIGITS = 3; + public static final int WATERLEVEL_W_MIN_DIGITS = 0; + public static final int WATERLEVEL_W_MAX_DIGITS = 2; + public static final int WATERLEVEL_Q_MIN_DIGITS = 0; + public static final int WATERLEVEL_Q_MAX_DIGITS = 2; + + + // COMPUTED DISCHARGE CURVE FORMATTER CONSTANTS + public static final int COMPUTED_DISCHARGE_W_MIN_DIGITS = 2; + public static final int COMPUTED_DISCHARGE_W_MAX_DIGITS = 2; + public static final int COMPUTED_DISCHARGE_Q_MIN_DIGITS = 0; + public static final int COMPUTED_DISCHARGE_Q_MAX_DIGITS = 2; + + + // DURATION CURVE FORMATTER CONSTANTS + public static final int DURATION_W_MIN_DIGITS = 0; + public static final int DURATION_W_MAX_DIGITS = 2; + public static final int DURATION_Q_MIN_DIGITS = 0; + public static final int DURATION_Q_MAX_DIGITS = 1; + public static final int DURATION_D_MIN_DIGITS = 0; + public static final int DURATION_D_MAX_DIGITS = 0; + + + public static NumberFormat getFormatter(CallContext c, int min, int max){ + Locale locale = Resources.getLocale(c.getMeta()); + NumberFormat nf = NumberFormat.getInstance(locale); + + nf.setMaximumFractionDigits(max); + nf.setMinimumFractionDigits(min); + + return nf; + } + + + /** + * Returns the number formatter for kilometer values in waterlevel exports. + * + * @return the number formatter for kilometer values. + */ + public static NumberFormat getWaterlevelKM(CallContext context) { + return getFormatter( + context, + WATERLEVEL_KM_MIN_DIGITS, + WATERLEVEL_KM_MAX_DIGITS); + } + + + /** + * Returns the number formatter for W values in waterlevel exports. + * + * @return the number formatter for W values. + */ + public static NumberFormat getWaterlevelW(CallContext context) { + return getFormatter( + context, + WATERLEVEL_W_MIN_DIGITS, + WATERLEVEL_W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values in waterlevel exports. + * + * @return the number formatter for Q values. + */ + public static NumberFormat getWaterlevelQ(CallContext context) { + return getFormatter( + context, + WATERLEVEL_Q_MIN_DIGITS, + WATERLEVEL_Q_MAX_DIGITS); + } + + + /** + * Returns the number formatter for W values in exports of computed + * discharge curves. + * + * @return the number formatter for W values. + */ + public static NumberFormat getComputedDischargeW(CallContext context) { + return getFormatter( + context, + COMPUTED_DISCHARGE_W_MIN_DIGITS, + COMPUTED_DISCHARGE_W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values in exports of computed + * discharge curves. + * + * @return the number formatter for Q values. + */ + public static NumberFormat getComputedDischargeQ(CallContext context) { + return getFormatter( + context, + COMPUTED_DISCHARGE_Q_MIN_DIGITS, + COMPUTED_DISCHARGE_Q_MAX_DIGITS); + } + + + /** + * Returns the number formatter for W values in duration curve exports. + * + * @return the number formatter for W values. + */ + public static NumberFormat getDurationW(CallContext context) { + return getFormatter( + context, + DURATION_W_MIN_DIGITS, + DURATION_W_MAX_DIGITS); + } + + + /** + * Returns the number formatter for Q values in duration curve exports. + * + * @return the number formatter for W values. + */ + public static NumberFormat getDurationQ(CallContext context) { + return getFormatter( + context, + DURATION_Q_MIN_DIGITS, + DURATION_Q_MAX_DIGITS); + } + + + /** + * Returns the number formatter for D values in duration curve exports. + * + * @return the number formatter for W values. + */ + public static NumberFormat getDurationD(CallContext context) { + return getFormatter( + context, + DURATION_D_MIN_DIGITS, + DURATION_D_MAX_DIGITS); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/GeometryUtils.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,251 @@ +package de.intevation.flys.utils; + +import java.io.IOException; +import java.io.File; +import java.io.Serializable; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; + +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import org.geotools.data.DataStoreFactorySpi; +import org.geotools.data.FeatureStore; +import org.geotools.data.DefaultTransaction; +import org.geotools.data.Transaction; +import org.geotools.data.shapefile.ShapefileDataStore; +import org.geotools.data.shapefile.ShapefileDataStoreFactory; +import org.geotools.feature.FeatureIterator; +import org.geotools.feature.FeatureCollection; +import org.geotools.feature.simple.SimpleFeatureTypeBuilder; +import org.geotools.geojson.feature.FeatureJSON; +import org.geotools.referencing.CRS; + +import de.intevation.flys.model.RiverAxis; + + +public class GeometryUtils { + + private static final Logger logger = Logger.getLogger(GeometryUtils.class); + + + private GeometryUtils() { + } + + + public static Envelope getRiverBoundary(String rivername) { + RiverAxis axis = RiverAxis.getRiverAxis(rivername); + if (axis != null) { + // TODO Take the correct EPSG into account. Maybe, we need to + // reproject the geometry. + return axis.getGeom().getEnvelopeInternal(); + } + + return null; + } + + + public static String getRiverBounds(String rivername) { + Envelope env = getRiverBoundary(rivername); + + if (env == null) { + return jtsBoundsToOLBounds(env); + } + + return null; + } + + + /** + * Returns the boundary of Envelope <i>env</i> in OpenLayers + * representation. + * + * @param env The envelope of a geometry. + * + * @return the OpenLayers boundary of <i>env</i>. + */ + public static String jtsBoundsToOLBounds(Envelope env) { + return "" + + env.getMinX() + " " + + env.getMinY() + " " + + env.getMaxX() + " " + + env.getMaxY(); + } + + + public static String createOLBounds(Geometry a, Geometry b) { + Coordinate[] ca = a.getCoordinates(); + Coordinate[] cb = b.getCoordinates(); + + double lowerX = Double.MAX_VALUE; + double lowerY = Double.MAX_VALUE; + double upperX = -Double.MAX_VALUE; + double upperY = -Double.MAX_VALUE; + + for (Coordinate c: ca) { + lowerX = lowerX < c.x ? lowerX : c.x; + lowerY = lowerY < c.y ? lowerY : c.y; + + upperX = upperX > c.x ? upperX : c.x; + upperY = upperY > c.y ? upperY : c.y; + } + + for (Coordinate c: cb) { + lowerX = lowerX < c.x ? lowerX : c.x; + lowerY = lowerY < c.y ? lowerY : c.y; + + upperX = upperX > c.x ? upperX : c.x; + upperY = upperY > c.y ? upperY : c.y; + } + + return "" + lowerX + " " + lowerY + " " + upperX + " " + upperY; + } + + + public static SimpleFeatureType buildFeatureType( + String name, String srs, Class geometryType) + { + return buildFeatureType(name, srs, geometryType, null); + } + + + /** + * Creates a new SimpleFeatureType using a SimpleFeatureTypeBuilder. + * + * @param name The name of the FeatureType. + * @param srs The SRS (e.g. "EPSG:31466"). + * @param geometryType The geometry type's class (e.g. Polygon.class). + * @param attrs Optional. An object with attribute-name/attribute-class + * pairs where index 0 specifies the name as string and index 1 the type + * as class. + * + * @return a new SimpleFeatureType. + */ + public static SimpleFeatureType buildFeatureType( + String name, String srs, Class geometryType, Object[][] attrs) + { + try { + SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); + CoordinateReferenceSystem crs = CRS.decode(srs); + + builder.setName("flys"); + builder.setNamespaceURI("http://www.intevation.de/"); + builder.setCRS(crs); + builder.setSRS(srs); + + builder.add("geometry", geometryType, crs); + + if (attrs != null) { + for (Object[] attr: attrs) { + builder.add((String) attr[0], (Class) attr[1]); + } + } + + return builder.buildFeatureType(); + } + catch (NoSuchAuthorityCodeException nsae) { + } + catch (FactoryException fe) { + } + + return null; + } + + + public static List<SimpleFeature> parseGeoJSON( + String geojson, SimpleFeatureType ft + ) { + List<SimpleFeature> collection = new ArrayList<SimpleFeature>(); + + try { + FeatureJSON fjson = new FeatureJSON(); + fjson.setFeatureType(ft); + + FeatureIterator<SimpleFeature> iterator = + fjson.streamFeatureCollection(geojson); + + while (iterator.hasNext()) { + collection.add(iterator.next()); + } + } + catch (IOException ioe) { + // TODO handle exception + } + + return collection; + } + + + public static boolean writeShapefile( + File shape, + SimpleFeatureType featureType, + FeatureCollection collection + ) { + if (collection.isEmpty()) { + logger.warn("Shapefile is not written - no features given!"); + return false; + } + + Transaction transaction = null; + + try { + Map<String, Serializable> params = + new HashMap<String, Serializable>(); + + params.put("url", shape.toURI().toURL()); + params.put("create spatial index", Boolean.TRUE); + + DataStoreFactorySpi dataStoreFactory = + new ShapefileDataStoreFactory(); + + ShapefileDataStore newDataStore = + (ShapefileDataStore)dataStoreFactory.createNewDataStore(params); + newDataStore.createSchema(featureType); + + transaction = new DefaultTransaction("create"); + + String typeName = newDataStore.getTypeNames()[0]; + + FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = + (FeatureStore<SimpleFeatureType, SimpleFeature>) + newDataStore.getFeatureSource(typeName); + + featureStore.setTransaction(transaction); + + featureStore.addFeatures(collection); + transaction.commit(); + + return true; + } + catch (MalformedURLException mue) { + logger.error("Unable to prepare shapefile: " + mue.getMessage()); + } + catch (IOException ioe) { + logger.error("Unable to write shapefile: " + ioe.getMessage()); + } + finally { + if (transaction != null) { + try { + logger.debug("XXX CLOSE TRANSACTION!"); + transaction.close(); + } + catch (IOException ioe) { /* do nothing */ } + } + } + + return false; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,670 @@ +package de.intevation.flys.utils; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.LayerInfo; +import de.intevation.flys.artifacts.model.WMSLayerFacet; +import de.intevation.flys.artifacts.model.WMSDBLayerFacet; + +/** + * This class iterates over a bunch of directories, searches for meta + * information coresponding to shapefiles and creates a mapfile which is used by + * a <i>MapServer</i>. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class MapfileGenerator +extends Thread +{ + public static final String WSPLGEN_RESULT_SHAPE = "wsplgen.shp"; + public static final String WSPLGEN_LINES_SHAPE = "barrier_lines.shp"; + public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp"; + + public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm"; + public static final String DB_LAYER_TEMPLATE = "db_layer.vm"; + + public static final String MS_WSPLGEN_POSTFIX = "-wsplgen"; + public static final String MS_BARRIERS_POSTFIX = "-barriers"; + public static final String MS_LINE_POSTFIX = "-lines"; + public static final String MS_POLYGONS_POSTFIX = "-polygons"; + public static final String MS_LAYER_PREFIX = "ms_layer-"; + + protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds + + private static Logger logger = Logger.getLogger(MapfileGenerator.class); + + private static MapfileGenerator instance; + + private File mapfile; + private File shapefileDirectory; + + private String mapServerUrl; + private String templatePath; + private String velocityLogfile; + + private VelocityEngine velocityEngine; + private boolean lock[]; + + + + private MapfileGenerator() { + lock = new boolean[1]; + } + + + /** + * A main method which can be used to create a mapfile without a running + * artifact server.<br> + * <b>NOTE:</b> This method is not implemented yet! + * + * @param args Arguments. + */ + public static void main(String[] args) { + throw new Error("MapfileGenerator.main() is CURRENTLY NOT IMPLEMENTED!"); + } + + + /** + * Returns the instance of this generator. + * + * @return the instance. + */ + public static synchronized MapfileGenerator getInstance() { + if (instance == null) { + instance = new MapfileGenerator(); + instance.setDaemon(true); + instance.start(); + } + + return instance; + } + + + /** + * Triggers the mapfile generation process. + */ + public void update() { + synchronized (lock) { + logger.debug("update"); + lock[0] = true; + lock.notify(); + } + } + + + /** + * Thread to generate a mapfile. + */ + @Override + public void run() { + logger.debug("Start MapfileGenerator thread..."); + try { + for (;;) { + synchronized (lock) { + while (!lock[0]) { + lock.wait(SLEEPTIME); + } + lock[0] = false; + } + + logger.debug("Start sync process now..."); + generate(); + } + } + catch (InterruptedException ie) { + logger.debug("MapfileGenerator thread got an interrupt."); + } + catch (FileNotFoundException fnfe) { + logger.debug("Error while mapfile creation: " + fnfe.getMessage()); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + finally { + logger.debug("THREAD END"); + } + } + + /** + * Method to check the existance of a template file. + * + * @param templateID The name of a template. + * @return true, of the template exists - otherwise false. + */ + public boolean templateExists(String templateID){ + Template template = getTemplateByName(templateID); + return template != null; + } + + + /** + * Method which starts searching for meta information file and mapfile + * generation. + */ + protected void generate() + throws FileNotFoundException, IOException + { + File[] userDirs = getUserDirs(); + + List<String> layers = parseLayers(userDirs); + + logger.info("Found " + layers.size() + " layers for user mapfile."); + + writeMapfile(layers); + } + + + /** + * Returns the VelocityEngine used for the template mechanism. + * + * @return the velocity engine. + */ + protected VelocityEngine getVelocityEngine() { + if (velocityEngine == null) { + velocityEngine = new VelocityEngine(); + try { + setupVelocity(velocityEngine); + } + catch (Exception e) { + logger.error(e, e); + return null; + } + } + return velocityEngine; + } + + + /** + * Initialize velocity. + * + * @param engine Velocity engine. + * @throws Exception if an error occured while initializing velocity. + */ + protected void setupVelocity(VelocityEngine engine) + throws Exception + { + engine.setProperty( + "input.encoding", + "UTF-8"); + + engine.setProperty( + VelocityEngine.RUNTIME_LOG, + FLYSUtils.getXPathString(FLYSUtils.XPATH_VELOCITY_LOGFILE)); + + engine.setProperty( + "resource.loader", + "file"); + + engine.setProperty( + "file.resource.loader.path", + FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_TEMPLATE_PATH)); + + engine.init(); + } + + + protected VelocityContext getVelocityContext() { + VelocityContext context = new VelocityContext(); + + try { + context.put("MAPSERVERURL", + FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_URL)); + context.put("SHAPEFILEPATH", + getShapefileBaseDir().getCanonicalPath()); + context.put("CONFIGDIR", + Config.getConfigDirectory().getCanonicalPath()); + } + catch (FileNotFoundException fnfe) { + // this is bad + } + catch (IOException ioe) { + // this is also bad + } + + return context; + } + + + /** + * Returns a template specified by <i>model</i>. + * + * @param model The name of the template. + * @return a template. + */ + protected Template getTemplateByName(String model) { + if (model.indexOf(".vm") < 0) { + model = model.concat(".vm"); + } + + try { + VelocityEngine engine = getVelocityEngine(); + if (engine == null) { + logger.error("Error while fetching VelocityEngine."); + return null; + } + + return engine.getTemplate(model); + } + catch (Exception e) { + logger.warn(e, e); + } + + return null; + } + + + /** + * Returns the mapfile template. + * + * @return the mapfile template. + * @throws Exception if an error occured while reading the configuration. + */ + protected Template getMapfileTemplate() + throws Exception + { + String mapfileName = FLYSUtils.getXPathString( + FLYSUtils.XPATH_MAPFILE_TEMPLATE); + + return getTemplateByName(mapfileName); + } + + + /** + * Returns the base directory storing the shapefiles. + * + * @return the shapefile base directory. + * + * @throws FileNotFoundException if no shapefile path is found or + * configured. + */ + public File getShapefileBaseDir() + throws FileNotFoundException, IOException + { + if (shapefileDirectory == null) { + String path = FLYSUtils.getXPathString( + FLYSUtils.XPATH_SHAPEFILE_DIR); + + if (path != null) { + shapefileDirectory = new File(path); + } + + if (shapefileDirectory == null) { + throw new FileNotFoundException("No shapefile directory given"); + } + + if (!shapefileDirectory.exists()) { + shapefileDirectory.createNewFile(); + } + } + + return shapefileDirectory; + } + + + protected File[] getUserDirs() + throws FileNotFoundException, IOException + { + File baseDir = getShapefileBaseDir(); + File[] artifactDirs = baseDir.listFiles(); + + // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER + + return artifactDirs; + } + + + + protected List<String> parseLayers(File[] dirs) { + List<String> layers = new ArrayList<String>(); + + for (File dir: dirs) { + File[] layerFiles = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File directory, String name) { + return name.startsWith(MS_LAYER_PREFIX); + } + }); + + for (File layer: layerFiles) { + try { + layers.add(layer.getCanonicalPath()); + } + catch (IOException ioe) { + logger.warn(ioe, ioe); + } + } + } + + return layers; + } + + + /** + * Creates a layer file used for Mapserver's mapfile which represents the + * floodmap. + * + * @param flys The FLYSArtifact that owns <i>wms</i>. + * @param wms The WMSLayerFacet that contains information for the layer. + */ + public void createUeskLayer(FLYSArtifact flys, WMSLayerFacet wms) + throws FileNotFoundException, IOException + { + logger.debug("createUeskLayer"); + + LayerInfo layerinfo = new LayerInfo(); + layerinfo.setName(flys.identifier() + MS_WSPLGEN_POSTFIX); + layerinfo.setType("POLYGON"); + layerinfo.setDirectory(flys.identifier()); + layerinfo.setData(WSPLGEN_RESULT_SHAPE); + layerinfo.setTitle("I18N_WSPLGEN_RESULT"); + + String name = MS_LAYER_PREFIX + wms.getName(); + + Template template = getTemplateByName(SHP_LAYER_TEMPLATE); + if (template == null) { + logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found."); + return; + } + + try { + File dir = new File(getShapefileBaseDir(), flys.identifier()); + writeLayer(layerinfo, dir, name, template); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + logger.warn("Unable to write layer: " + name); + } + } + + + /** + * Creates a layer file used for Mapserver's mapfile which represents the + * user defined barriers. + * + * @param flys The FLYSArtifact that owns <i>wms</i>. + * @param wms The WMSLayerFacet that contains information for the layer. + */ + public void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms) + throws FileNotFoundException, IOException + { + logger.debug("createBarriersLayer"); + + String uuid = flys.identifier(); + File dir = new File(getShapefileBaseDir(), uuid); + + createBarriersLineLayer(flys, wms); + createBarriersPolygonLayer(flys, wms); + } + + + protected void createBarriersLineLayer( + FLYSArtifact flys, + WMSLayerFacet wms + ) + throws FileNotFoundException, IOException + { + String uuid = flys.identifier(); + String group = uuid + MS_BARRIERS_POSTFIX; + String groupTitle = "I18N_BARRIERS_TITLE"; + + File dir = new File(getShapefileBaseDir(), uuid); + File test = new File(dir, WSPLGEN_LINES_SHAPE); + + if (!test.exists() || !test.canRead()) { + logger.debug("No barrier line layer existing."); + return; + } + + LayerInfo lineInfo = new LayerInfo(); + lineInfo.setName(uuid + MS_LINE_POSTFIX); + lineInfo.setType("LINE"); + lineInfo.setDirectory(uuid); + lineInfo.setData(WSPLGEN_LINES_SHAPE); + lineInfo.setTitle("I18N_LINE_SHAPE"); + lineInfo.setGroup(group); + lineInfo.setGroupTitle(groupTitle); + + String nameLines = MS_LAYER_PREFIX + wms.getName() + "-lines"; + + Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE); + if (tpl == null) { + logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found."); + return; + } + + try { + writeLayer(lineInfo, dir, nameLines, tpl); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + logger.warn("Unable to write layer: " + nameLines); + } + } + + + protected void createBarriersPolygonLayer( + FLYSArtifact flys, + WMSLayerFacet wms + ) + throws FileNotFoundException, IOException + { + String uuid = flys.identifier(); + String group = uuid + MS_BARRIERS_POSTFIX; + String groupTitle = "I18N_BARRIERS_TITLE"; + + File dir = new File(getShapefileBaseDir(), uuid); + File test = new File(dir, WSPLGEN_POLYGONS_SHAPE); + + if (!test.exists() || !test.canRead()) { + logger.debug("No barrier line layer existing."); + return; + } + + LayerInfo polygonInfo = new LayerInfo(); + polygonInfo.setName(uuid + MS_POLYGONS_POSTFIX); + polygonInfo.setType("POLYGON"); + polygonInfo.setDirectory(uuid); + polygonInfo.setData(WSPLGEN_POLYGONS_SHAPE); + polygonInfo.setTitle("I18N_POLYGON_SHAPE"); + polygonInfo.setGroup(group); + polygonInfo.setGroupTitle(groupTitle); + + String namePolygons = MS_LAYER_PREFIX + wms.getName() + "-polygons"; + + Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE); + if (tpl == null) { + logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found."); + return; + } + + try { + writeLayer(polygonInfo, dir, namePolygons, tpl); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + logger.warn("Unable to write layer: " + namePolygons); + } + } + + + /** + * Creates a layer file used for Mapserver's mapfile which represents + * geometries from database. + * + * @param flys The FLYSArtifact that owns <i>wms</i>. + * @param wms The WMSLayerFacet that contains information for the layer. + */ + public void createDatabaseLayer( + FLYSArtifact flys, + WMSDBLayerFacet wms, + String style + ) + throws FileNotFoundException, IOException + { + logger.debug("createDatabaseLayer"); + + LayerInfo layerinfo = new LayerInfo(); + layerinfo.setName(flys.identifier() + "-" + wms.getName()); + layerinfo.setType(wms.getGeometryType()); + layerinfo.setFilter(wms.getFilter()); + layerinfo.setData(wms.getData()); + layerinfo.setTitle(wms.getDescription()); + layerinfo.setStyle(style); + layerinfo.setExtent(GeometryUtils.jtsBoundsToOLBounds(wms.getExtent())); + + String name = MS_LAYER_PREFIX + wms.getName(); + + Template template = getTemplateByName(DB_LAYER_TEMPLATE); + if (template == null) { + logger.warn("Template '" + DB_LAYER_TEMPLATE + "' found."); + return; + } + + try { + File dir = new File(getShapefileBaseDir(), flys.identifier()); + writeLayer(layerinfo, dir, name, template); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + logger.warn("Unable to write layer: " + name); + } + } + + + /** + * Creates a layer snippet which might be included in the mapfile. + * + * @param layerinfo A LayerInfo object that contains all necessary + * information to build a Mapserver LAYER section. + * @param dir The base dir for the LAYER snippet. + * @param filename The name of the file that is written. + * @param tpl The Velocity template which is used to create the LAYER + * section. + */ + protected void writeLayer( + LayerInfo layerinfo, + File dir, + String filename, + Template tpl + ) + throws FileNotFoundException + { + if (logger.isDebugEnabled()) { + logger.debug("Write layer for:"); + logger.debug(" directory: " + dir.getName()); + logger.debug(" name: " + filename); + } + + File layer = new File(dir, filename); + Writer writer = null; + + try { + writer = new FileWriter(layer); + + VelocityContext context = getVelocityContext(); + context.put("LAYER", layerinfo); + + tpl.merge(context, writer); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + catch (Exception e) { + logger.error(e, e); + } + finally { + try { + if (writer != null) { + writer.close(); + } + } + catch (IOException ioe) { + logger.debug(ioe, ioe); + } + } + } + + + /** + * Creates a mapfile with the layer information stored in <i>layers</i>. + * + * @param layers Layer information. + */ + protected void writeMapfile(List<String> layers) { + String tmpMapName = "mapfile" + new Date().getTime(); + + File mapfile = new File( + FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPFILE_PATH)); + + File tmp = null; + Writer writer = null; + + try { + tmp = new File(mapfile.getParent(), tmpMapName); + tmp.createNewFile(); + + writer = new FileWriter(tmp); + + VelocityContext context = getVelocityContext(); + context.put("LAYERS", layers); + + Template mapTemplate = getMapfileTemplate(); + if (mapTemplate == null) { + logger.warn("No mapfile template found."); + return; + } + + mapTemplate.merge(context, writer); + + // we need to create a temporary mapfile first und rename it into + // real mapfile because we don't run into race conditions on this + // way. (iw) + tmp.renameTo(mapfile); + } + catch (FileNotFoundException fnfe) { + logger.error(fnfe, fnfe); + } + catch (IOException ioe) { + logger.error(ioe, ioe); + } + catch (Exception e) { + logger.error(e, e); + } + finally { + try { + if (writer != null) { + writer.close(); + } + + if (tmp.exists()) { + tmp.delete(); + } + } + catch (IOException ioe) { + logger.debug(ioe, ioe); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/Pair.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.flys.utils; + +import java.io.Serializable; + +/** + * @param <A> + * @param <B> + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public final class Pair<A, B> +implements Serializable +{ + private A a; + private B b; + + private Pair() { + } + + public Pair(A a, B b) { + this.a = a; + this.b = b; + } + + public A getA() { + return a; + } + + public B getB() { + return b; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,331 @@ +package de.intevation.flys.utils; + +import org.apache.log4j.Logger; + +import java.awt.Color; +import java.awt.Font; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.model.MapserverStyle; +import de.intevation.flys.artifacts.model.MapserverStyle.Clazz; + + +/** + * Utility to deal with themes and their representations. + */ +public class ThemeUtil { + + private static Logger logger = + Logger.getLogger(ThemeUtil.class); + + public final static String XPATH_LINE_COLOR = + "/theme/field[@name='linecolor']/@default"; + + public static final String XPATH_LINE_SIZE = + "/theme/field[@name='linesize']/@default"; + + public static final String XPATH_LINE_STYLE = + "/theme/field[@name='linetype']/@default"; + + public final static String XPATH_SHOW_POINTS = + "/theme/field[@name='showpoints']/@default"; + + public final static String XPATH_SHOW_LINE = + "/theme/field[@name='showlines']/@default"; + + public final static String XPATH_TEXT_COLOR = + "/theme/field[@name='textcolor']/@default"; + + public final static String XPATH_TEXT_SIZE = + "/theme/field[@name='textsize']/@default"; + + public final static String XPATH_TEXT_FONT = + "/theme/field[@name='font']/@default"; + + public final static String XPATH_TEXT_STYLE = + "/theme/field[@name='textstyle']/@default"; + + public final static String XPATH_TEXT_ORIENTATION = + "/theme/field[@name='textorientation']/@default"; + + public final static String XPATH_TEXT_BACKGROUND = + "/theme/field[@name='backgroundcolor']/@default"; + + public final static String XPATH_SHOW_BACKGROUND = + "/theme/field[@name='showbackground']/@default"; + + public final static String XPATH_SYMBOL = + "/theme/field[@name='symbol']/@default"; + + /** + * Parses line width, defaulting to 0. + * @param theme the theme + */ + public static int parseLineWidth(Document theme) { + String size = XMLUtils.xpathString(theme, XPATH_LINE_SIZE, null); + if (size == null || size.length() == 0) { + return 0; + } + + try { + return Integer.valueOf(size); + } + catch (NumberFormatException nfe) { + logger.warn("Unable to set line size from string: '" + size + "'"); + } + return 0; + } + + + /** + * Parses the line style, defaulting to '10'. + * @param theme The theme. + */ + public static float[] parseLineStyle(Document theme) { + String dash = XMLUtils.xpathString(theme, XPATH_LINE_STYLE, null); + + float[] def = {10}; + if (dash == null || dash.length() == 0) { + return def; + } + + String[] pattern = dash.split(","); + if(pattern.length == 1) { + return def; + } + + try { + float[] dashes = new float[pattern.length]; + for (int i = 0; i < pattern.length; i++) { + dashes[i] = Float.parseFloat(pattern[i]); + } + return dashes; + } + catch(NumberFormatException nfe) { + logger.warn("Unable to set dash from string: '" + dash + "'"); + return def; + } + } + + + /** + * Parses text size, defaulting to 10. + * @param theme The theme. + */ + public static int parseTextSize(Document theme) { + String size = XMLUtils.xpathString(theme, XPATH_TEXT_SIZE, null); + if (size == null || size.length() == 0) { + return 10; + } + + try { + return Integer.valueOf(size); + } + catch (NumberFormatException nfe) { + } + return 10; + } + + + /** + * Parses the attribute 'showpoints', defaults to false. + * @param theme The theme. + */ + public static boolean parseShowPoints(Document theme) { + String show = XMLUtils.xpathString(theme, XPATH_SHOW_POINTS, null); + if (show == null || show.length() == 0) { + return false; + } + if (show.equals("true")) { + return true; + } + else { + return false; + } + } + + + /** + * Parses the attribute 'showlines', defaults to true. + * @param theme The theme. + */ + public static boolean parseShowLine(Document theme) { + String show = XMLUtils.xpathString(theme, XPATH_SHOW_LINE, null); + if (show == null || show.length() == 0) { + return true; + } + if (show.equals("false")) { + return false; + } + else { + return true; + } + } + + + /** + * Parses text color. + * @param theme The theme. + */ + public static Color parseTextColor(Document theme) { + String color = XMLUtils.xpathString(theme, XPATH_TEXT_COLOR, null); + return parseRGB(color); + } + + + /** + * Parses the font. + * @param theme The theme. + */ + public static Font parseTextFont(Document theme) { + String font = XMLUtils.xpathString(theme, XPATH_TEXT_FONT, null); + if (font == null || font.length() == 0) { + return null; + } + + int size = parseTextSize(theme); + int style = parseTextStyle(theme); + Font f = new Font (font, style, size); + return f; + } + + + /** + * Parses the text style, defaults to 'Font.PLAIN'. + * @param theme The theme. + */ + public static int parseTextStyle(Document theme) { + String style = XMLUtils.xpathString(theme, XPATH_TEXT_STYLE, null); + if (style == null || style.length() == 0) { + return Font.PLAIN; + } + + if (style.equals("italic")) { + return Font.ITALIC; + } + else if (style.equals("bold")) { + return Font.BOLD; + } + else { + return Font.PLAIN; + } + } + + + /** + * Parses the textorientation, defaults to 'vertical'. + * @param theme The theme. + */ + public static String parseTextOrientation(Document theme) { + String o = XMLUtils.xpathString(theme, XPATH_TEXT_ORIENTATION, null); + if (o == null || o.length() == 0) { + return "vertical"; + } + if(o.equals("true")) { + return "horizontal"; + } + else { + return "vertical"; + } + } + + + /** + * Parses the text background color, defaults to white. + * @param theme The theme. + */ + public static Color parseTextBackground(Document theme) { + String color = XMLUtils.xpathString(theme, XPATH_TEXT_BACKGROUND, null); + if (color == null || color.length() == 0) { + return Color.WHITE; + } + return parseRGB(color); + } + + + /** + * Parses the attribute whether to show background or not, defaults to + * false. + * @param theme The theme. + */ + public static boolean parseShowTextBackground(Document theme) { + String show = XMLUtils.xpathString(theme, XPATH_SHOW_BACKGROUND, null); + if(show == null || show.length() == 0) { + return false; + } + + if(show.equals("true")) { + return true; + } + else { + return false; + } + } + + + /** + * Parse a string like "103, 100, 0" and return a corresping color. + * @param rgbtext Color as string representation, e.g. "255,0,20". + * @return Color, null in case of issues. + */ + public static Color parseRGB(String rgbtext) { + if (rgbtext == null) { + return null; + } + String rgb[] = rgbtext.split(","); + Color c = null; + try { + c = new Color( + Integer.valueOf(rgb[0].trim()), + Integer.valueOf(rgb[1].trim()), + Integer.valueOf(rgb[2].trim())); + } + catch (NumberFormatException nfe) { + c = null; + } + return c; + } + + + public static String getLineColorString(Document theme) { + return XMLUtils.xpathString(theme, XPATH_LINE_COLOR, null); + } + + + public static String getSymbol(Document theme) { + return XMLUtils.xpathString(theme, XPATH_SYMBOL, null); + } + + + /** + * Gets color from color field. + * @param theme the theme document. + * @return color. + */ + public static Color parseLineColorField(Document theme) { + return parseRGB(getLineColorString(theme)); + } + + + public static String createMapserverStyle(Document theme) { + String symbol = getSymbol(theme); + String linecolor = getLineColorString(theme); + linecolor = linecolor.replace(",", ""); + + int linewidth = parseLineWidth(theme); + + MapserverStyle ms = new MapserverStyle(); + + Clazz c = new Clazz(" "); + c.setOutlineColor(linecolor); + c.setSize(linewidth); + c.setSymbol(symbol); + + ms.addClazz(c); + + return ms.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/FacetCreator.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,151 @@ +package de.intevation.flys.wsplgen; + +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Envelope; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.CrossSectionTrack; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WMSLayerFacet; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.GeometryUtils; +import de.intevation.flys.utils.MapfileGenerator; + + +public class FacetCreator implements FacetTypes { + + public static final String I18N_WSPLGEN_RESULT = "floodmap.uesk"; + public static final String I18N_WSPLGEN_DEFAULT = "floodmap.uesk"; + public static final String I18N_BARRIERS = "floodmap.barriers"; + public static final String I18N_BARRIERS_DEFAULT = "floodmap.barriers"; + + protected FLYSArtifact artifact; + + protected CallContext cc; + + protected List<Facet> facets; + protected List<Facet> tmpFacets; + + protected String url; + protected String hash; + protected String stateId; + + public FacetCreator( + FLYSArtifact artifact, + CallContext cc, + String hash, + String sId, + List<Facet> facets + ) { + this.tmpFacets = new ArrayList<Facet>(2); + this.facets = facets; + this.artifact = artifact; + this.cc = cc; + this.hash = hash; + this.stateId = sId; + } + + protected String getRiver() { + return artifact.getDataAsString("river"); + } + + protected String getUrl() { + return FLYSUtils.getUserWMSUrl(artifact.identifier()); + } + + protected String getSrid() { + return FLYSUtils.getRiverSrid(artifact); + } + + protected Envelope getWSPLGENBounds() { + String river = getRiver(); + double kms[] = FLYSUtils.getKmRange(artifact); + + CrossSectionTrack a = + CrossSectionTrack.getCrossSectionTrack(river, kms[0]); + + CrossSectionTrack b = + CrossSectionTrack.getCrossSectionTrack(river, kms[1]); + + if (a == null || b == null) { + return null; + } + + Envelope envA = a.getGeom().getEnvelopeInternal(); + Envelope envB = b.getGeom().getEnvelopeInternal(); + + envA.expandToInclude(envB); + + return envA; + } + + protected Envelope getBounds() { + return GeometryUtils.getRiverBoundary(getRiver()); + } + + public List<Facet> getFacets() { + return tmpFacets; + } + + public void createWSPLGENFacet() { + WMSLayerFacet wsplgen = new WMSLayerFacet( + 0, + FLOODMAP_WSPLGEN, + Resources.getMsg( + cc.getMeta(), + I18N_WSPLGEN_RESULT, + I18N_WSPLGEN_DEFAULT), + ComputeType.ADVANCE, + stateId, + hash, + getUrl()); + + Envelope bounds = getWSPLGENBounds(); + + if (bounds == null) { + bounds = getBounds(); + } + + wsplgen.addLayer( + artifact.identifier() + MapfileGenerator.MS_WSPLGEN_POSTFIX); + wsplgen.setSrid(getSrid()); + wsplgen.setExtent(bounds); + + tmpFacets.add(wsplgen); + } + + public void createBarrierFacet() { + WMSLayerFacet barriers = new WMSLayerFacet( + 1, + FLOODMAP_BARRIERS, + Resources.getMsg( + cc.getMeta(), + I18N_BARRIERS, + I18N_BARRIERS_DEFAULT), + ComputeType.ADVANCE, + stateId, + hash, + getUrl()); + + barriers.addLayer( + artifact.identifier() + MapfileGenerator.MS_BARRIERS_POSTFIX); + barriers.setSrid(getSrid()); + barriers.setExtent(getBounds()); + + tmpFacets.add(barriers); + } + + + public void finish() { + facets.addAll(getFacets()); + } +} // end of FacetCreator
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/JobExecutor.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,113 @@ +package de.intevation.flys.wsplgen; + +import java.io.IOException; +import java.io.File; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallContext; + +import de.intevation.flys.artifacts.model.WSPLGENJob; + + +public class JobExecutor { + + public static final String WSPLGEN_PARAMETER_FILE = + "wsplgen.par"; + + public static final String WSPLGEN_BIN_PATH = + System.getProperty("wsplgen.bin.path"); + + + private Logger logger = Logger.getLogger(JobExecutor.class); + + private Process process; + + protected WSPLGENJob job; + + protected JobObserver logObserver; + protected ProblemObserver errorObserver; + + + public JobExecutor(WSPLGENJob job) { + this.job = job; + this.logObserver = new JobObserver(job); + this.errorObserver = new ProblemObserver(job); + } + + + public void execute() { + File dir = job.getWorkingDir(); + File parameter = new File(dir, WSPLGEN_PARAMETER_FILE); + + String[] args = new String[] { + WSPLGEN_BIN_PATH, + "-PAR=\"" + parameter.getAbsolutePath() + "\"" + }; + + execute(args, dir); + } + + + protected void execute(String[] args, File dir) { + logger.info("Start JobExecutor for artifact: " + dir.getName()); + + String errorMsg = null; + + try { + synchronized (this) { + process = Runtime.getRuntime().exec(args, null, dir); + + logObserver.setInputStream(process.getInputStream()); + errorObserver.setInputStream(process.getErrorStream()); + + logObserver.start(); + errorObserver.start(); + + try { + process.waitFor(); + } + catch (InterruptedException ie) { + logger.error("WSPLGEN job interrupted: " + ie.getMessage()); + } + + try { + logObserver.join(); + errorObserver.join(); + } + catch (InterruptedException iee) { /* do nothing */ } + + logger.info("WSPLGEN exit value: " + process.exitValue()); + logger.info( + "WSPLGEN throw " + + errorObserver.numErrors() + " errors."); + logger.info( + "WSPLGEN throw " + + errorObserver.numWarnings() + " warnings."); + + if (process.exitValue() < 2 && errorObserver.numErrors() == 0) { + FacetCreator fc = job.getFacetCreator(); + fc.createWSPLGENFacet(); + fc.finish(); + } + + job.getCallContext().afterBackground(CallContext.STORE); + + return; + } + } + catch (SecurityException se) { + logger.error(se); + } + catch (IOException ioe) { + logger.error(ioe); + } + catch (NullPointerException npe) { + logger.error(npe, npe); + } + catch (IndexOutOfBoundsException ioobe) { + logger.error(ioobe, ioobe); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/JobObserver.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,106 @@ +package de.intevation.flys.wsplgen; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.CalculationMessage; +import de.intevation.flys.artifacts.model.WSPLGENJob; + + +public class JobObserver extends Thread { + + private static Logger logger = Logger.getLogger(JobObserver.class); + + + public static final String WSPLGEN_ENCODING = + "ISO-8859-1"; + + public static final String WSPLGEN_LOG_OUTPUT = + System.getProperty("wsplgen.log.output", "false"); + + public static final String[] STEPS = { + ".*<-Auswertung der Kommandozeilen-Parameter beendet.*", + ".*->Laden des DGM in Datei '.*' gestartet.*", + ".*->Triangulierung der Knoten gestartet.*", + ".*->Anpassung der Elemente an Dämme und Gräben gestartet.*", + ".*<-WSPLGEN Version .* beendet.*" + }; + + + protected WSPLGENJob job; + + protected InputStream in; + + protected Pattern[] patterns; + + protected int len; + + protected boolean copy; + + + public JobObserver(WSPLGENJob job) { + this.job = job; + this.len = 0; + this.copy = Boolean.parseBoolean(WSPLGEN_LOG_OUTPUT); + + patterns = new Pattern[STEPS.length]; + } + + + protected void prepareRegexes() { + for (int num = STEPS.length, i = 0; i < num; i++) { + patterns[i] = Pattern.compile(STEPS[i], Pattern.DOTALL); + } + } + + + public void setInputStream(InputStream in) { + this.in = in; + } + + + public void run() { + logger.debug("Start observation..."); + prepareRegexes(); + + try { + BufferedReader reader = + new BufferedReader( + new InputStreamReader(in, WSPLGEN_ENCODING)); + + String line = null; + + while ((line = reader.readLine()) != null) { + if (copy) { + logger.debug(line); + } + + update(line); + } + } + catch (IOException ioe) { + logger.warn("Observation canceled: " + ioe.getMessage()); + } + } + + + protected void update(String log) { + for (int num = patterns.length, i = 0; i < num; i++) { + Matcher m = patterns[i].matcher(log); + + if (m.matches()) { + job.getCallContext().addBackgroundMessage( + new CalculationMessage(num, i+1, log)); + + logger.info("Finished step " + (i+1) + " / " + num); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/ProblemObserver.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,115 @@ +package de.intevation.flys.wsplgen; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.WSPLGENCalculation; +import de.intevation.flys.artifacts.model.WSPLGENJob; + + +public class ProblemObserver extends JobObserver { + + private static Logger logger = Logger.getLogger(ProblemObserver.class); + + + public static final Pattern WSPLGEN_ERROR_START = Pattern.compile( + ".*->(.*Fehler)(\\s*\\((\\d+)\\).*)*", + Pattern.DOTALL); + + public static final Pattern WSPLGEN_ERROR_END = Pattern.compile( + ".*<-(.*Fehler).*", + Pattern.DOTALL); + + public static final Pattern WSPLGEN_WARNING_START = Pattern.compile( + ".*->Warnung\\s*\\((\\d+)\\).*", + Pattern.DOTALL); + + public static final Pattern WSPLGEN_WARNING_END = Pattern.compile( + ".*<-Warnung .*", + Pattern.DOTALL); + + + protected int error; + protected int warning; + + protected WSPLGENCalculation calculation; + + + public ProblemObserver(WSPLGENJob job) { + super(job); + error = -1; + warning = -1; + calculation = job.getCalculation(); + } + + + public void run() { + logger.debug("Start observation..."); + + super.run(); + } + + + @Override + protected void prepareRegexes() { + // do nothing + } + + + @Override + protected void update(String log) { + Matcher startError = WSPLGEN_ERROR_START.matcher(log); + if (startError.matches()) { + if (startError.groupCount() >= 2) { + String tmp = startError.group(3); + + if (tmp != null && tmp.length() > 0) { + error = Integer.parseInt(tmp); + } + else error = 1; + } + else { + error = 1; + } + + return; + } + + Matcher endError = WSPLGEN_ERROR_END.matcher(log); + if (endError.matches()) { + error = -1; + } + + if (error > 0) { + calculation.addError(new Integer(error), log); + } + + Matcher startWarning = WSPLGEN_WARNING_START.matcher(log); + if (startWarning.matches()) { + warning = Integer.parseInt(startWarning.group(1)); + return; + } + + Matcher endWarning = WSPLGEN_WARNING_END.matcher(log); + if (endWarning.matches()) { + warning = -1; + } + + if (warning > 0) { + calculation.addWarning(new Integer(warning), log); + } + } + + + public int numErrors() { + return calculation.numErrors(); + } + + + public int numWarnings() { + return calculation.numWarnings(); + } +} +// vim:set ts=4 sw=4 si et sta sts=5 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/wsplgen/Scheduler.java Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,109 @@ +package de.intevation.flys.wsplgen; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.apache.log4j.Logger; + +import de.intevation.flys.artifacts.model.WSPLGENJob; + + +public class Scheduler implements Runnable { + + public static final int MAX_WSPLGEN_PROCESSES = 1; + + + protected List<WSPLGENJob> jobs; + + + private static Scheduler INSTANCE; + + private static final Logger logger = Logger.getLogger(Scheduler.class); + + + + private Scheduler() { + jobs = Collections.synchronizedList(new LinkedList<WSPLGENJob>()); + } + + + public static Scheduler getInstance() { + if (INSTANCE == null) { + logger.info("Create new WSPLGEN Scheduler..."); + + INSTANCE = new Scheduler(); + new Thread(INSTANCE).start(); + } + + return INSTANCE; + } + + + public void addJob(WSPLGENJob job) { + synchronized(jobs) { + jobs.add(job); + + logger.info("New WSPLGEN job added."); + + jobs.notifyAll(); + } + } + + + public WSPLGENJob getJob() { + synchronized(jobs) { + if (!jobs.isEmpty()) { + return jobs.remove(0); + } + + return null; + } + } + + + public void run() { + logger.info("WSPLGEN Scheduler started."); + + for (;;) { + try { + doRun(); + } + catch (InterruptedException ie) { + logger.warn("Interrupt in WSPLGEN Scheduler -> restart it!"); + } + } + } + + + public void doRun() + throws InterruptedException + { + for (;;) { + final WSPLGENJob job = getJob(); + + if (job != null) { + logger.debug("Got new job to execute..."); + + Thread t = new Thread() { + public void run() { + JobExecutor executor = new JobExecutor(job); + executor.execute(); + } + }; + + t.start(); + t.join(); + } + else { + logger.info("No more jobs in Scheduler -> go sleep!"); + synchronized (jobs) { + jobs.wait(); + } + + logger.info("New jobs in Scheduler -> wake up!"); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/datacage-sql/org-h2-driver.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,29 @@ +delete.all.users = DELETE FROM users +delete.all.artifacts = DELETE FROM artifacts +user.id.nextval = SELECT NEXTVAL('USERS_ID_SEQ') +user.by.gid = SELECT id FROM users WHERE gid = ? +insert.user = INSERT INTO users (id, gid) VALUES (?, ?) +collection.by.gid = SELECT id FROM collections WHERE gid = ? +collection.id.nextval = SELECT NEXTVAL('COLLECTIONS_ID_SEQ') +insert.collection = INSERT INTO collections (id, gid, user_id, name, creation) VALUES (?, ?, ?, ?, ?) +artifact.by.gid = SELECT id FROM artifacts WHERE gid = ? +collection.item.id.nextval = SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ') +insert.collection.item = INSERT INTO collection_items (id, collection_id, artifact_id) VALUES (?, ?, ?) +artifact.id.nextval = SELECT NEXTVAL('ARTIFACTS_ID_SEQ') +insert.artifact = INSERT INTO artifacts (id, gid, state, creation) VALUES (?, ?, ?, ?) +artifact.data.id.nextval = SELECT NEXTVAL('ARTIFACT_DATA_ID_SEQ') +insert.artifact.data = INSERT INTO artifact_data (id, artifact_id, kind, k, v) VALUES (?, ?, ?, ?, ?) +out.id.nextval = SELECT NEXTVAL('OUTS_ID_SEQ') +insert.out = INSERT INTO outs (id, artifact_id, name, description, out_type) VALUES (?, ?, ?, ?, ?) +facet.id.nextval = SELECT NEXTVAL('FACETS_ID_SEQ') +insert.facet = INSERT INTO facets (id, out_id, name, num, state, description) VALUES (?, ?, ?, ?, ?, ?) + +update.collection.name = UPDATE collections SET name = ? WHERE gid = ? +delete.artifact.from.collection = DELETE FROM collection_items WHERE collection_id = ? AND artifact_id = ? +delete.collection.by.gid = DELETE FROM collections WHERE gid = ? +delete.user.by.gid = DELETE FROM user WHERE gid = ? +delete.artifact.data.by.artifact.id = DELETE FROM artifact_data WHERE artifact_id = ? +delete.outs.by.artifact.id = DELETE FROM outs WHERE artifact_id = ? +delete.facets.by.artifact.id = DELETE FROM facets WHERE out_id IN (SELECT id FROM outs WHERE artifact_id = ?) + +delete.artifact.by.gid = DELETE FROM artifacts WHERE gid = ?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/datacage-sql/org-postgresql-driver.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,29 @@ +delete.all.users = DELETE FROM users +delete.all.artifacts = DELETE FROM artifacts +user.id.nextval = SELECT NEXTVAL('USERS_ID_SEQ') +user.by.gid = SELECT id FROM users WHERE gid = ?::uuid +insert.user = INSERT INTO users (id, gid) VALUES (?, ?::uuid) +collection.by.gid = SELECT id FROM collections WHERE gid = ?::uuid +collection.id.nextval = SELECT NEXTVAL('COLLECTIONS_ID_SEQ') +insert.collection = INSERT INTO collections (id, gid, user_id, name, creation) VALUES (?, ?::uuid, ?, ?, ?) +artifact.by.gid = SELECT id FROM artifacts WHERE gid = ?::uuid +collection.item.id.nextval = SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ') +insert.collection.item = INSERT INTO collection_items (id, collection_id, artifact_id) VALUES (?, ?, ?) +artifact.id.nextval = SELECT NEXTVAL('ARTIFACTS_ID_SEQ') +insert.artifact = INSERT INTO artifacts (id, gid, state, creation) VALUES (?, ?::uuid, ?, ?) +artifact.data.id.nextval = SELECT NEXTVAL('ARTIFACT_DATA_ID_SEQ') +insert.artifact.data = INSERT INTO artifact_data (id, artifact_id, kind, k, v) VALUES (?, ?, ?, ?, ?) +out.id.nextval = SELECT NEXTVAL('OUTS_ID_SEQ') +insert.out = INSERT INTO outs (id, artifact_id, name, description, out_type) VALUES (?, ?, ?, ?, ?) +facet.id.nextval = SELECT NEXTVAL('FACETS_ID_SEQ') +insert.facet = INSERT INTO facets (id, out_id, name, num, state, description) VALUES (?, ?, ?, ?, ?, ?) + +update.collection.name = UPDATE collections SET name = ? WHERE gid = ?::uuid +delete.artifact.from.collection = DELETE FROM collection_items WHERE collection_id = ? AND artifact_id = ? +delete.collection.by.gid = DELETE FROM collections WHERE gid = ?::uuid +delete.user.by.gid = DELETE FROM user WHERE gid = ?::uuid +delete.artifact.data.by.artifact.id = DELETE FROM artifact_data WHERE artifact_id = ? +delete.outs.by.artifact.id = DELETE FROM outs WHERE artifact_id = ? +delete.facets.by.artifact.id = DELETE FROM facets WHERE out_id IN (SELECT id FROM outs WHERE artifact_id = ?) + +delete.artifact.by.gid = DELETE FROM artifacts WHERE gid = ?::uuid
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,94 @@ +state.winfo.river = River +state.winfo.calculation_mode = Calculation Mode +state.winfo.location_distance = Location or distance selection +state.winfo.wq = Input for W/Q data +state.winfo.wq_adapted = Input for W/Q data +state.winfo.location = Choose the location +state.winfo.distance = Choose the range +state.winfo.uesk.wsp = Choose the waterlevel +state.winfo.uesk.dgm = Digital Terrain Model +state.winfo.uesk.profiles = Interpolated Profiles +state.winfo.uesk.floodplain = Lateral Boundary +state.winfo.uesk.differences = Differenzen between waterlevel and terrain +state.winfo.uesk.scenario = Flood Plain / Scenario +state.winfo.waterlevel_pair_select = Chosen differences + +calc.surface.curve = Water Level/Surface Curve +calc.flood.map = Flood Plain +calc.discharge.curve = State Discharge Curve/Stage Discharge Relation +calc.duration.curve = Duration Curve +calc.discharge.longitudinal.section = TODO (W bei...) +calc.w.differences = Differenzen + +cross_section = Cross Section + +scenario.current = Current +scenario.potentiel = Potentiel +scenario.scenario = Scenario +floodplain.option = Use Floodplain? + +river = River +calculation_mode = Calculation Mode +ld_locations = Location(s) + +chart.longitudinal.section.title = W-Longitudinal Section +chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,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] +chart.longitudinal.annotations.label = {0}.km + +chart.cross_section.title = Cross Section for river {0} +chart.cross_section.subtitle = {0}-km: {1,number,#.###} +chart.cross_section.xaxis.label = Distance [m] +chart.cross_section.yaxis.label = W [NN + m] + +chart.discharge.curve.title = Discharge Curve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.discharge.curve.curve.valid.from = {0} (valid from {1,date,short}) +chart.discharge.curve.curve.valid.range = {0} (valid from {1,date,short} - {2,date,short}) +chart.computed.discharge.curve.title = Discharge Curve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1} +chart.duration.curve.title = Duration Curve +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days] +chart.duration.curve.yaxis.label = W [NN + m] +chart.duration.curve.curve.w = Waterlevel duration curve for {0} +chart.duration.curve.curve.q = Discharge duration curve for {0} + +chart.w_differences.title = Differences +chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.yaxis.label = m +chart.w_differences.yaxis.second.label = W [NN + m] + +facet.longitudinal_section.annotations = POIs +facet.discharge_curves.mainvalues.q = Q (main values) +facet.discharge_curves.mainvalues.w = W (main values) + +export.waterlevel.csv.header.km = River-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Days] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = River-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W corr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s] +export.discharge.curve.at.header = Computed Discharge Curve for {0} {0}-km: {1} + +floodmap.wmsbackground = Background Map +floodmap.riveraxis = River Axis +floodmap.uesk = Floodmap +floodmap.barriers = Digitized Objects +floodmap.kms = Kilometrage + +wsplgen.job.queued = WSPLGEN job in queue. +wsplgen.job.error = An unexpected error while starting WSPLGEN occured. + +wsp.selected.string = {0}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_de.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,94 @@ +state.winfo.river = Gew\u00e4sser +state.winfo.calculation_mode = Berechnungsart +state.winfo.location_distance = Wahl des Berechnungsortes/strecke +state.winfo.wq = Eingabe f\u00fcr W/Q Daten +state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten +state.winfo.location = Wahl des Berechnungsortes +state.winfo.distance = Wahl der Berechnungsstrecke +state.winfo.uesk.wsp = Wahl der Wasserspiegellage +state.winfo.uesk.dgm = Digitales Gel\u00e4ndemodell +state.winfo.uesk.profiles = Interpolierte Profile +state.winfo.uesk.floodplain = Laterale Begrenzung +state.winfo.uesk.differences = Differenzen zwischen Wasserspiegellage und Gel\u00e4nde +state.winfo.uesk.scenario = \u00dcberschwemmungsfl\u00e4che / Szenario +state.winfo.waterlevel_pair_select = Ausgew\u00e4hlte Differenzen + +calc.surface.curve = Wasserstand/Wasserspiegellage +calc.flood.map = \u00dcberschwemmungsfl\u00e4che +calc.discharge.curve = Abflusskurve/abflusstafel +calc.duration.curve = Dauerlinie +calc.discharge.longitudinal.section = W bei ungleichwertigem Abflussl\u00e4ngsschnitt +calc.w.differences = Differenzen + +cross_section = Querprofil + +scenario.current = Aktuell +scenario.potentiel = Potentiell +scenario.scenario = Szenario + +floodplain.option = Talaue verwenden? + +river = Fluss +calculation_mode = Berechnungsart +ld_locations = Ort(e) + +chart.cross_section.title = Querprofildiagram für Gew\u00e4sser {0} +chart.cross_section.subtitle = {0}-km: {1,number,#.###} +chart.cross_section.xaxis.label = Abstand [m] +chart.cross_section.yaxis.label = W [NN + m] + +chart.longitudinal.section.title = W-L\u00e4ngsschnitt +chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,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] +chart.longitudinal.annotations.label = {0}.km +chart.discharge.curve.title = Abflusskurve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.discharge.curve.curve.valid.from = {0} (g\u00fcltig ab {1,date,medium}) +chart.discharge.curve.curve.valid.range = {0} (g\u00fcltig ab {1,date,medium} - {2,date,medium}) +chart.computed.discharge.curve.title = Abflusskurve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1} +chart.duration.curve.title = Dauerlinie +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage] +chart.duration.curve.yaxis.label = W [NN + m] +chart.duration.curve.curve.w = Wasserstandsdauerline f\u00fcr {0} +chart.duration.curve.curve.q = Abflussdauerline f\u00fcr {0} + +chart.w_differences.title = Differenzen +chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.yaxis.label = m +chart.w_differences.yaxis.second.label = W [NN + m] + +facet.longitudinal_section.annotations = Streckenfavoriten +facet.discharge_curves.mainvalues.q = Q (Haupt- und Extremwerte) +facet.discharge_curves.mainvalues.w = W (Haupt- und Extremwerte) + +export.waterlevel.csv.header.km = Fluss-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Tagen] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = Fluss-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W korr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s] +export.discharge.curve.at.header = Berechnete Abflusskurve f\u00fcr {0} {0}-km: {1} + +floodmap.wmsbackground = Hintergrundkarte +floodmap.riveraxis = Flussachse +floodmap.uesk = \u00dcberschwemmungsfl\u00e4che +floodmap.barriers = Digitalisierte Objekte +floodmap.kms = Kilometrierung + +wsplgen.job.queued = WSPLGEN Berechnung befindet sich in Warteschlange. +wsplgen.job.error = Ein unerwarteter Fehler beim Starten von WSPLGEN ist aufgetreten. + +wsp.selected.string = {0}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_de_DE.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,94 @@ +state.winfo.river = Gew\u00e4sser +state.winfo.calculation_mode = Berechnungsart +state.winfo.location_distance = Wahl des Berechnungsortes/strecke +state.winfo.wq = Eingabe f\u00fcr W/Q Daten +state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten +state.winfo.location = Wahl des Berechnungsortes +state.winfo.distance = Wahl der Berechnungsstrecke +state.winfo.uesk.wsp = Wahl der Wasserspiegellage +state.winfo.uesk.dgm = Digitales Gel\u00e4ndemodell +state.winfo.uesk.profiles = Interpolierte Profile +state.winfo.uesk.floodplain = Laterale Begrenzung +state.winfo.uesk.differences = Differenzen zwischen Wasserspiegellage und Gel\u00e4nde +state.winfo.uesk.scenario = \u00dcberschwemmungsfl\u00e4che / Szenario +state.winfo.waterlevel_pair_select = Ausgew\u00e4hlte Differenzen + +calc.surface.curve = Wasserstand/Wasserspiegellage +calc.flood.map = \u00dcberschwemmungsfl\u00e4che +calc.discharge.curve = Abflusskurve/Abflusstafel +calc.duration.curve = Dauerlinie +calc.discharge.longitudinal.section = W bei ungleichwertigem Abflussl\u00e4ngsschnitt +calc.w.differences = Differenzen + +cross_section = Querprofil + +scenario.current = Aktuell +scenario.potentiel = Potentiell +scenario.scenario = Szenario + +floodplain.option = Talaue verwenden? + +river = Fluss +calculation_mode = Berechnungsart +ld_locations = Ort(e) + +chart.cross_section.title = Querprofildiagramm für Gew\u00e4sser {0} +chart.cross_section.subtitle = {0}-km: {1,number,#.###} +chart.cross_section.xaxis.label = Abstand [m] +chart.cross_section.yaxis.label = W [NN + m] + +chart.longitudinal.section.title = W-L\u00e4ngsschnitt +chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,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] +chart.longitudinal.annotations.label = {0}.km +chart.discharge.curve.title = Abflusskurve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.discharge.curve.curve.valid.from = {0} (g\u00fcltig ab {1,date,medium}) +chart.discharge.curve.curve.valid.range = {0} (g\u00fcltig ab {1,date,medium} - {2,date,medium}) +chart.computed.discharge.curve.title = Abflusskurve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1} +chart.duration.curve.title = Dauerlinie +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage] +chart.duration.curve.yaxis.label = W [NN + m] +chart.duration.curve.curve.w = Wasserstandsdauerline f\u00fcr {0} +chart.duration.curve.curve.q = Abflussdauerline f\u00fcr {0} + +chart.w_differences.title = Differenzen +chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.yaxis.label = m +chart.w_differences.yaxis.second.label = W [NN + m] + +facet.longitudinal_section.annotations = Streckenfavoriten +facet.discharge_curves.mainvalues.q = Q (Haupt- und Extremwerte) +facet.discharge_curves.mainvalues.w = W (Haupt- und Extremwerte) + +export.waterlevel.csv.header.km = Fluss-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Tagen] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = Fluss-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W korr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s] +export.discharge.curve.at.header = Berechnete Abflusskurve f\u00fcr {0} {0}-km: {1} + +floodmap.wmsbackground = Hintergrundkarte +floodmap.riveraxis = Flussachse +floodmap.uesk = \u00dcberschwemmungsfl\u00e4che +floodmap.barriers = Digitalisierte Objekte +floodmap.kms = Kilometrierung + +wsplgen.job.queued = WSPLGEN Berechnung befindet sich in Warteschlange. +wsplgen.job.error = Ein unerwarteter Fehler beim Starten von WSPLGEN ist aufgetreten. + +wsp.selected.string = {0}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/resources/messages_en.properties Fri Sep 28 12:14:50 2012 +0200 @@ -0,0 +1,92 @@ +state.winfo.river = River +state.winfo.calculation_mode = Calculation Mode +state.winfo.location_distance = Location or distance selection +state.winfo.wq = Input for W/Q data +state.winfo.wq_adapted = Input for W/Q data +state.winfo.location = Choose the location +state.winfo.distance = Choose the range +state.winfo.uesk.wsp = Choose the waterlevel +state.winfo.uesk.dgm = Digital Terrain Model +state.winfo.uesk.profiles = Interpolated Profiles +state.winfo.uesk.floodplain = Lateral Boundary +state.winfo.uesk.differences = Differences between waterlevel and terrain +state.winfo.uesk.scenario = Flood Plain / Scenario +state.winfo.waterlevel_pair_select = Chosen Differences + +calc.surface.curve = Water Level/Surface Curve +calc.flood.map = Flood Plain +calc.discharge.curve = State Discharge Curve/Stage Discharge Relation +calc.duration.curve = Duration Curve +calc.discharge.longitudinal.section = TODO (W bei...) +calc.w.differences = Differences + +cross_section = Cross Section + +scenario.current = Current +scenario.potentiel = Potentiel +scenario.scenario = Scenario + +floodplain.option = Use Floodplain? + +river = River +calculation_mode = Calculation Mode +ld_locations = Location(s) + +chart.cross_section.title = Cross Section for river {0} +chart.cross_section.subtitle = {0}-km: {1,number,#.###} +chart.cross_section.xaxis.label = Distance [m] +chart.cross_section.yaxis.label = W [NN + m] + +chart.longitudinal.section.title = W-Longitudinal Section +chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,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] +chart.longitudinal.annotations.label = {0}.km +chart.discharge.curve.title = Discharge Curve +chart.discharge.curve.xaxis.label = Q [m\u00b3/s] +chart.discharge.curve.yaxis.label = W [cm] +chart.discharge.curve.curve.valid.from = {0} (valid from {1,date,short}) +chart.discharge.curve.curve.valid.range = {0} (valid from {1,date,short} - {2,date,short}) +chart.computed.discharge.curve.title = Discharge Curve +chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###} +chart.computed.discharge.curve.yaxis.label = W [NN + m] +chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1} +chart.duration.curve.title = Duration Curve +chart.duration.curve.subtitle = {0}-km: {1,number,#.###} +chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days] +chart.duration.curve.yaxis.label = W [NN + m] +chart.duration.curve.curve.w = Waterlevel duration curve for {0} +chart.duration.curve.curve.q = Discharge duration curve for {0} + +chart.w_differences.title = Differences +chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###} +chart.w_differences.yaxis.label = m +chart.w_differences.yaxis.second.label = W [NN + m] + +facet.longitudinal_section.annotations = POIs + +export.waterlevel.csv.header.km = River-Km +export.waterlevel.csv.header.w = W [NN + m] +export.waterlevel.csv.header.q = Q [m\u00b3/s] +export.computed.discharge.curve.csv.header.w = W [NN + m] +export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s] +export.duration.curve.csv.header.duration = D [Days] +export.duration.curve.csv.header.w = W [NN + m] +export.duration.curve.csv.header.q = Q [m\u00b3/s] +export.discharge.longitudinal.section.csv.header.km = River-Km +export.discharge.longitudinal.section.csv.header.w = W [NN + m] +export.discharge.longitudinal.section.csv.header.cw = W corr. +export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s] +export.discharge.curve.at.header = Computed Discharge Curve for {0} {0}-km: {1} + +floodmap.wmsbackground = Background Map +floodmap.riveraxis = River Axis +floodmap.uesk = Floodmap +floodmap.barriers = Digitized Objects +floodmap.kms = Kilometrage + +wsplgen.job.queued = WSPLGEN job in queue. +wsplgen.job.error = An unexpected error while starting WSPLGEN occured. + +wsp.selected.string = {0}