changeset 3274:43e42315ce22

Added service for an overview chart of measuring points in sq relation. flys-artifacts/trunk@4918 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Raimund Renkert <raimund.renkert@intevation.de>
date Tue, 10 Jul 2012 16:37:48 +0000
parents 739aa90eb79e
children 3570abdf7daa
files flys-artifacts/ChangeLog flys-artifacts/doc/conf/conf.xml flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverview.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverviewFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SQKMChartService.java flys-artifacts/src/main/resources/messages.properties flys-artifacts/src/main/resources/messages_de.properties flys-artifacts/src/main/resources/messages_de_DE.properties flys-artifacts/src/main/resources/messages_en.properties
diffstat 9 files changed, 662 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/ChangeLog	Tue Jul 10 16:37:48 2012 +0000
@@ -1,3 +1,23 @@
+2012-07-10  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/SQKMChartService.java:
+	  New. Service that generates an overview chart for measuring points.
+
+	* src/main/java/de/intevation/flys/artifacts/model/SQOverview.java:
+	  New. Instance of measuring points overview data.
+
+	* src/main/java/de/intevation/flys/artifacts/model/SQOverviewFactory.java:
+	  New. Foctory for overview data objects.
+
+	* doc/conf/conf.xml:
+	  Added new service to config.
+
+	* 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.
+
 2012-07-10	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
 
 	Fix for issue694.
--- a/flys-artifacts/doc/conf/conf.xml	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/doc/conf/conf.xml	Tue Jul 10 16:37:48 2012 +0000
@@ -172,6 +172,10 @@
                 name="themelisting"
                 service="de.intevation.flys.artifacts.services.ThemeListingService"
                 description="Returns a list of Themes filtered by theme name.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+            <service-factory
+                name="sq-km-chart"
+                service="de.intevation.flys.artifacts.services.SQKMChartService"
+                description="Returns a chart of km and date of meassuring points of a given river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
         </service-factories>
 
     </factories>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverview.java	Tue Jul 10 16:37:48 2012 +0000
@@ -0,0 +1,206 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+import org.hibernate.type.StandardBasicTypes;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.flys.utils.KMIndex;
+
+public class SQOverview
+implements   Serializable
+{
+
+    /**
+     * Serial version UId. 
+     */
+    private static final long serialVersionUID = -8934372438968398508L;
+
+    public interface Filter {
+
+        boolean accept(KMIndex<List<Date>> entry);
+
+    } // interface Filter
+
+
+    public static final Filter ACCEPT = new Filter() {
+        @Override
+        public boolean accept(KMIndex<List<Date>> entry) {
+            return true;
+        }
+    };
+
+    public static class KmFilter implements Filter {
+
+        protected double km;
+
+        public KmFilter (double km) {
+            this.km = km;
+        }
+        @Override
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getKm() == km) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    public static class DateFilter implements Filter {
+
+        protected Date date;
+
+        public DateFilter (Date date) {
+            this.date = date;
+        }
+        @Override
+        public boolean accept(KMIndex<List<Date>> list) {
+            for (KMIndex.Entry<List<Date>> e: list){
+                if (e.getValue().equals(this.date)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    private static Logger log = Logger.getLogger(FixingsOverview.class);
+
+    public static final double EPSILON = 1e-1;
+
+    public static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    public static final String SQL_SQ =
+        "SELECT" +
+        "    s.km," +
+        "    m.datum " +
+        "FROM messung m " +
+        "    JOIN station s" +
+        "       on m.stationid = s.stationid " +
+        "    JOIN gewaesser g " +
+        "       on s.gewaesserid = g.gewaesserid " +
+        "WHERE" +
+        "    g.name = :name " +
+        "        order by" +
+        "            s.km, m.datum";
+
+    protected String       riverName;
+
+    protected KMIndex<List<Date>> entries;
+
+    public SQOverview() {
+        entries = new KMIndex<List<Date>>();
+    }
+
+    public SQOverview(String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    protected void loadData(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_SQ)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("datum",    StandardBasicTypes.DATE);
+
+        query.setString("name", riverName);
+
+        List<Object []> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + riverName + "' found.");
+        }
+
+        Double prevKm = -1d;
+        List<Date> dates = new ArrayList<Date>();
+        for (int i = 0; i < list.size(); i++) {
+            Object[] row = list.get(i);
+            Double km = (Double) row[0];
+            Date date = (Date) row[1];
+            if (km.equals(prevKm)) {
+                dates.add(date);
+            }
+            else {
+                entries.add(prevKm, dates);
+                dates = new ArrayList<Date>();
+                dates.add(date);
+            }
+            prevKm = km;
+        }
+    }
+
+    public boolean load(Session session) {
+
+        loadData(session);
+
+        return true;
+    }
+
+
+    public void generateOverview(Document document) {
+        generateOverview(document, ACCEPT);
+    }
+
+    public KMIndex<List<Date>> filter(Filter f) {
+        return entries;
+    }
+
+    public void generateOverview(
+        Document document,
+        Filter   filter
+    ) {
+        KMIndex<List<Date>> filtered = filter(ACCEPT);
+
+        Element sqElement = document.createElement("sq");
+
+        Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("name", riverName);
+
+        sqElement.appendChild(riverElement);
+
+        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        Element kmE = document.createElement("km");
+
+        for (KMIndex.Entry<List<Date>> e: filtered) {
+
+            List<Date> dates = e.getValue();
+
+            if (!dates.isEmpty()) {
+                Element dEs = document.createElement("dates");
+
+                for (Date d: dates) {
+                    Element dE = document.createElement("date");
+
+                    dE.setAttribute("value", df.format(d));
+
+                    dEs.appendChild(dE);
+                }
+
+                kmE.appendChild(dEs);
+            }
+        }
+
+        sqElement.appendChild(kmE);
+
+        document.appendChild(sqElement);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/SQOverviewFactory.java	Tue Jul 10 16:37:48 2012 +0000
@@ -0,0 +1,71 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SedDBSessionHolder;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+public class SQOverviewFactory {
+   private static Logger log = Logger.getLogger(SQOverviewFactory.class);
+
+    public static final String CACHE_NAME = "sq-overviews";
+
+    private SQOverviewFactory() {
+    }
+
+
+    public static SQOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for sq overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "sq-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (SQOverview)element.getValue();
+        }
+
+        SQOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static SQOverview getUncached(String river) {
+        SQOverview overview = new SQOverview(river);
+
+        Session session = SedDBSessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/SQKMChartService.java	Tue Jul 10 16:37:48 2012 +0000
@@ -0,0 +1,344 @@
+package de.intevation.flys.artifacts.services;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.GlobalContext;
+import de.intevation.artifacts.Service;
+
+import de.intevation.flys.artifacts.model.FixingsColumn;
+import de.intevation.flys.artifacts.model.FixingsColumnFactory;
+import de.intevation.flys.artifacts.model.FixingsFilterBuilder;
+
+import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
+
+import de.intevation.flys.artifacts.model.SQOverview;
+import de.intevation.flys.artifacts.model.SQOverviewFactory;
+import de.intevation.flys.artifacts.model.GaugeFinder;
+import de.intevation.flys.artifacts.model.GaugeFinderFactory;
+import de.intevation.flys.artifacts.model.GaugeRange;
+
+import de.intevation.flys.artifacts.model.fixings.QW;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.flys.backend.SedDBSessionHolder;
+
+import de.intevation.flys.jfree.ShapeRenderer;
+
+import de.intevation.flys.utils.Formatter;
+import de.intevation.flys.utils.Pair;
+import de.intevation.flys.utils.KMIndex;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Transparency;
+
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Date;
+
+import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItemCollection;
+
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.ValueMarker;
+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.DefaultXYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.jfree.ui.RectangleAnchor;
+import org.jfree.ui.TextAnchor;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class SQKMChartService
+extends DefaultService {
+
+    private static final Logger log =
+        Logger.getLogger(FixingsKMChartService.class);
+
+    public static final int DEFAULT_WIDTH  = 240;
+    public static final int DEFAULT_HEIGHT = 180;
+
+    public static final String I18N_CHART_LABEL =
+        "sq.km.chart.label";
+
+    public static final String DEFAULT_CHART_LABEL =
+        "Measuring Points";
+
+    public static final String I18N_CHART_TITLE =
+        "sq.km.chart.title";
+
+    public static final String DEFAULT_CHART_TITLE =
+        "Measuring points";
+
+    public static final String I18N_KM_AXIS =
+        "sq.km.chart.km.axis";
+
+    public static final String DEFAULT_KM_AXIS =
+        "km";
+
+    public static final String I18N_DATE_AXIS =
+        "sq.km.chart.date.axis";
+
+    public static final String DEFAULT_DATE_AXIS =
+        "Date";
+
+    public static final String DEFAULT_FORMAT = "png";
+
+    // TODO: Load fancy image from resources.
+    public static final byte [] EMPTY = {
+        (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47,
+        (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0d,
+        (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+        (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x3a, (byte)0x7e, (byte)0x9b,
+        (byte)0x55, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x01, (byte)0x73, (byte)0x52, (byte)0x47,
+        (byte)0x42, (byte)0x00, (byte)0xae, (byte)0xce,
+        (byte)0x1c, (byte)0xe9, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x09, (byte)0x70, (byte)0x48,
+        (byte)0x59, (byte)0x73, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x00, (byte)0x00,
+        (byte)0x0b, (byte)0x13, (byte)0x01, (byte)0x00,
+        (byte)0x9a, (byte)0x9c, (byte)0x18, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x74,
+        (byte)0x49, (byte)0x4d, (byte)0x45, (byte)0x07,
+        (byte)0xdc, (byte)0x04, (byte)0x04, (byte)0x10,
+        (byte)0x30, (byte)0x15, (byte)0x7d, (byte)0x77,
+        (byte)0x36, (byte)0x0b, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x08, (byte)0x74, (byte)0x45,
+        (byte)0x58, (byte)0x74, (byte)0x43, (byte)0x6f,
+        (byte)0x6d, (byte)0x6d, (byte)0x65, (byte)0x6e,
+        (byte)0x74, (byte)0x00, (byte)0xf6, (byte)0xcc,
+        (byte)0x96, (byte)0xbf, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x0a, (byte)0x49, (byte)0x44,
+        (byte)0x41, (byte)0x54, (byte)0x08, (byte)0xd7,
+        (byte)0x63, (byte)0xf8, (byte)0x0f, (byte)0x00,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00,
+        (byte)0x1b, (byte)0xb6, (byte)0xee, (byte)0x56,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x49, (byte)0x45, (byte)0x4e, (byte)0x44,
+        (byte)0xae, (byte)0x42, (byte)0x60, (byte)0x82
+    };
+
+    private static final Output empty() {
+        return new Output(EMPTY, "image/png");
+    }
+
+    @Override
+    public Service.Output process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("SQKMChartService.process");
+
+        SedDBSessionHolder.acquire();
+        try {
+            return doProcess(data, globalContext, callMeta);
+        }
+        finally {
+            SedDBSessionHolder.HOLDER.get().close();
+            SedDBSessionHolder.release();
+        }
+    }
+
+    protected Service.Output doProcess(
+        Document      input,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        String    river  = getRiverName(input);
+        Dimension extent = getExtent(input);
+        String    format = getFormat(input);
+
+        if (river == null) {
+            log.warn("River invalid.");
+            return empty();
+        }
+
+        SQOverview overview = SQOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            log.warn("No overview found for river '" + river + "'");
+            return empty();
+        }
+
+        KMIndex<List<Date>> entries = overview.filter(SQOverview.ACCEPT);
+
+        JFreeChart chart = createChart(entries, river, callMeta);
+
+        return encode(chart, extent, format);
+    }
+
+    protected static Output encode(
+        JFreeChart chart,
+        Dimension  extent,
+        String     format
+    ) {
+        BufferedImage image = chart.createBufferedImage(
+            extent.width, extent.height,
+            Transparency.BITMASK,
+            null);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            ImageIO.write(image, format, out);
+        }
+        catch (IOException ioe) {
+            log.warn("writing image failed", ioe);
+            return empty();
+        }
+
+        return new Output(out.toByteArray(), "image/" + format);
+    }
+
+    protected static JFreeChart createChart(
+        KMIndex<List<Date>> entries,
+        String      river,
+        CallMeta    callMeta
+    ) {
+
+        XYSeriesCollection dataset = new XYSeriesCollection();
+        String key = Resources.format(
+            callMeta, I18N_CHART_LABEL, DEFAULT_CHART_LABEL, river);
+
+        XYSeries series = new XYSeries(key);
+        for (KMIndex.Entry<List<Date>> e: entries) {
+            double km = e.getKm();
+            List<Date> ds = e.getValue();
+            for (Date d: ds) {
+                series.add(km, d.getTime());
+            }
+        }
+
+        dataset.addSeries(series);
+        String title = Resources.format(
+            callMeta, I18N_CHART_TITLE, DEFAULT_CHART_TITLE, river);
+
+        String kmAxis = Resources.getMsg(
+            callMeta, I18N_KM_AXIS, DEFAULT_KM_AXIS);
+
+        String dateAxis = Resources.getMsg(
+            callMeta, I18N_DATE_AXIS, DEFAULT_DATE_AXIS);
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            title,
+            kmAxis,
+            dateAxis,
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            true,
+            false);
+
+        XYPlot plot = (XYPlot)chart.getPlot();
+
+        DateAxis dA = new DateAxis();
+        plot.setRangeAxis(dA);
+        plot.setDataset(0, dataset);
+
+        chart.setBackgroundPaint(Color.white);
+        plot.setBackgroundPaint(Color.white);
+        plot.setDomainGridlinePaint(Color.gray);
+        plot.setRangeGridlinePaint(Color.gray);
+        plot.setDomainGridlinesVisible(true);
+        plot.setRangeGridlinesVisible(true);
+        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
+
+        renderer.setSeriesPaint(0, Color.gray);
+        renderer.setSeriesLinesVisible(0, false);
+        renderer.setSeriesShapesVisible(0, true);
+        renderer.setDrawOutlines(true);
+        return chart;
+    }
+
+
+    protected static String getRiverName(Document input) {
+        NodeList rivers = input.getElementsByTagName("river");
+
+        if (rivers.getLength() == 0) {
+            return null;
+        }
+
+        String river = ((Element)rivers.item(0)).getAttribute("name");
+
+        return river.length() > 0 ? river : null;
+    }
+
+    protected static Dimension getExtent(Document input) {
+
+        int width  = DEFAULT_WIDTH;
+        int height = DEFAULT_HEIGHT;
+
+        NodeList extents = input.getElementsByTagName("extent");
+
+        if (extents.getLength() > 0) {
+            Element element = (Element)extents.item(0);
+            String w = element.getAttribute("width");
+            String h = element.getAttribute("height");
+
+            try {
+                width = Math.max(1, Integer.parseInt(w));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("width '" + w + "' is not a valid.");
+            }
+
+            try {
+                height = Math.max(1, Integer.parseInt(h));
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("height '" + h + "' is not a valid");
+            }
+        }
+
+        return new Dimension(width, height);
+    }
+
+    protected static String getFormat(Document input) {
+        String format = DEFAULT_FORMAT;
+
+        NodeList formats = input.getElementsByTagName("format");
+
+        if (formats.getLength() > 0) {
+            String type = ((Element)formats.item(0)).getAttribute("type");
+            if (type.length() > 0) {
+                format = type;
+            }
+        }
+
+        return format;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/resources/messages.properties	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/src/main/resources/messages.properties	Tue Jul 10 16:37:48 2012 +0000
@@ -371,3 +371,7 @@
 fix.km.chart.interpolated=interpolated
 
 fix.export.at.header = Exported fixings discharge curve for {0} {0}-km: {1}
+sq.km.chart.label = Measuring Points
+sq.km.chart.title = Measuring Points
+sq.km.chart.km.axis = km
+sq.km.chart.date.axis = Date
--- a/flys-artifacts/src/main/resources/messages_de.properties	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/src/main/resources/messages_de.properties	Tue Jul 10 16:37:48 2012 +0000
@@ -372,3 +372,7 @@
 fix.km.chart.interpolated=interpoliert
 
 fix.export.at.header =  Abflusskurve aus der Fixierungsanalyse f\u00fcr {0} {0}-km: {1}
+sq.km.chart.label = Messstellen
+sq.km.chart.title = Messstellen
+sq.km.chart.km.axis = km
+sq.km.chart.date.axis = Datum
--- a/flys-artifacts/src/main/resources/messages_de_DE.properties	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/src/main/resources/messages_de_DE.properties	Tue Jul 10 16:37:48 2012 +0000
@@ -371,3 +371,7 @@
 fix.km.chart.interpolated=interpoliert
 
 fix.export.at.header =  Abflusskurve aus der Fixierungsanalyse f\u00fcr {0} {0}-km: {1}
+sq.km.chart.label = Messstellen
+sq.km.chart.title = Messstellen
+sq.km.chart.km.axis = km
+sq.km.chart.date.axis = Datum
--- a/flys-artifacts/src/main/resources/messages_en.properties	Tue Jul 10 16:04:44 2012 +0000
+++ b/flys-artifacts/src/main/resources/messages_en.properties	Tue Jul 10 16:37:48 2012 +0000
@@ -371,3 +371,8 @@
 fix.km.chart.interpolated=interpolated
 
 fix.export.at.header = Exported fixings discharge curve for {0} {0}-km: {1}
+sq.km.chart.label = Measuring Points
+sq.km.chart.title = Measuring Points
+sq.km.chart.km.axis = km
+sq.km.chart.date.axis = Date
+

http://dive4elements.wald.intevation.org