view backend/src/main/java/org/dive4elements/river/importer/parsers/W80Parser.java @ 9650:a2a42a6bac6b

Importer (s/u-info) extensions: outer try/catch for parse and log of line no, catching parsing exception if not enough value fields, parsing error and warning log messages with line number, detecting and rejecting duplicate data series, better differentiation between error and warning log messages
author mschaefer
date Mon, 23 Mar 2020 14:57:03 +0100
parents 5e38e2924c07
children 0a5239a1e46e
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.importer.parsers;

import org.dive4elements.artifacts.common.utils.FileTools;

import org.dive4elements.river.importer.XY;

import org.dive4elements.river.importer.parsers.tim.Coordinate;

import org.dive4elements.river.backend.utils.DateGuesser;
import org.dive4elements.river.backend.utils.EpsilonComparator;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;


/**
 * To create cross-sections, generate: Map<double,list<xy>> from files
 * in w80 format.
 */
public class W80Parser extends LineParser implements CrossSectionParser
{
    /** Private log. */
    private static Logger log = Logger.getLogger(W80Parser.class);


    /** The current line to which add points. */
    private List<XY> currentLine;


    /** Data collected so far, last element will be currentLine. */
    protected Map<Double, List<XY>> data;


    /** Anchor to project to. */
    private static class Anchor extends Coordinate {

        private static final double EPSILON = 1e-5;

        private double station;

        public Anchor(double x, double y, double z, double station) {
            super(x, y, z);
            this.station = station;
        }

        public boolean sameStation(double station) {
            return Math.abs(this.station - station) < EPSILON;
        }
    }


    /** Reference point for simple projection. */
    private Anchor anchor;


    /**
     * Reference point for distance calculations, introduced to
     * deal with bends in the lines.
     * Array has two entrys: first is GK-Right, second GK-High.
     */
    private double[] lastPointGK;


    /** Measurement date of anchor as listed in w80 file. */
    private Date anchorDate;


    private double distanceToLastPoint(double gkr, double gkh) {
        double dx = gkr - lastPointGK[0];
        double dy = gkh - lastPointGK[1];
        double d  = dx*dx + dy*dy;

        return Math.sqrt(d);
    }


    /** Trivial constructor. */
    public W80Parser() {
        data = new TreeMap<Double, List<XY>>(EpsilonComparator.CMP);
    }


    /** Get the description of the cross section parsed. */
    @Override
    public String getDescription() {
        return FileTools.removeExtension(getFileName());
    }


    /** Get the year of this cross sections measurement. */
    @Override
    public Integer getYear() {
        if (anchorDate == null) {
            return null;
        }
        Calendar dateCalendar = Calendar.getInstance();
        dateCalendar.setTime(anchorDate);
        return dateCalendar.get(Calendar.YEAR);
    }


    /**
     * Return the data parsed.
     * @return map of stations (km) to list of points.
     */
    @Override
    public Map<Double, List<XY>> getData() {
        return data;
    }


    public void parseW80s(File root, final Callback callback) {

        FileTools.walkTree(root, new FileTools.FileVisitor() {
            @Override
            public boolean visit(File file) {
                if (file.isFile() && file.canRead()
                && file.getName().toLowerCase().endsWith(".w80")
                && (callback == null || callback.accept(file))) {
                    reset();
                    try {
                        parse(file);
                        log.info("parsing done");
                        if (callback != null) {
                            callback.parsed(W80Parser.this);
                        }
                    }
                    catch (IOException ioe) {
                        log.error("IOException while parsing file");
                        return false;
                    }
                }
                return true;
            }
        });
    }


    /** Called before consuming first line of file. */
    public void reset() {
        data.clear();
        currentLine = new ArrayList<XY>();
        anchor = null;
        anchorDate = null;
        lastPointGK = new double[] {0d,0d};
    }


    /**
     * Get the Index of the last cross-section lines point.
     * @return last points index, -1 if not available.
     */
    private int getLastPointIdx() {
        if (currentLine == null || currentLine.isEmpty()) {
            return -1;
        }
        XY lastPoint = this.currentLine.get(currentLine.size()-1);
        return lastPoint.getIndex();
    }


    private double getLastPointX() {
        if (currentLine == null || currentLine.isEmpty()) {
            return 0d;
        }
        XY lastPoint = this.currentLine.get(currentLine.size()-1);
        return lastPoint.getX();
    }


    /**
     * Add a Point (YZ,Index) to the current cross section line.
     * @param y The y coordinate of new point.
     * @param z The z coordinate of new point.
     * @param idx Ignored, the parameter of new point.
     * @return true if point could been added, false otherwise (e.g. not
     *         parsable y or z values.
     */
    private boolean addPoint(
        double gkr,
        double gkh,
        double height,
        String idx
    ) {
        // Calculate distance between this and lst point (add distances).
        double d = distanceToLastPoint(gkr, gkh);
        double totalX = getLastPointX() + d;

        // We ignore idx, and increment instead.
        int index;
        int lastPointIdx = getLastPointIdx();
        if (lastPointIdx <= 0) {
            index = 1;
        } else {
            index = lastPointIdx + 1;
        }

        this.lastPointGK[0] = gkr;
        this.lastPointGK[1] = gkh;
        currentLine.add(new XY(totalX, height/1000d, index));
        return true;
    }


    /**
     * Called for each line. Try to extract info from a w80 line.
     */
    @Override
    protected void handleLine(int lineNum, String line) {
        // The 'shore' field shows which side of the river
        // the shore is measured.
        // Therefore, the points have to be added in the correct order (also
        // because later distances are calculated which cannot be
        // negative.
        String pointId = line.substring(0,20);
        String station = line.substring(9,15);
        String shore   = line.substring(15,16);
        // TODO: There is 'station' and a 'shore'-code behind.
        // 1 = left, 2 = right. none = middle
        String pointIndex = line.substring(16,21);
        // For GK, first seven digits are of interest.
        String gkRight = line.substring(20,30);
        String gkHigh  = line.substring(30,40);
        String date    = line.substring(40,46);
        /* Fields not (yet?) of interest for FLYS
        String locType = line.substring(46,47);
        */
        String height  = line.substring(47,54);
        String dateH   = line.substring(54,60);
        /* Fields not (yet?) of interest for FLYS
        String typeH   = line.substring(60,61);
        String kindH   = line.substring(61,64);
        */
        String dateDec = line.substring(64,70);
        /* Fields not (yet?) of interest for FLYS
        String note    = line.substring(70,78);
        String actual  = line.substring(78);
        */

        double stationKm = Double.parseDouble(station) / 1000d;
        double gkRightKm = Double.parseDouble(gkRight.substring(0,7));
        double gkHighKm  = Double.parseDouble(gkHigh.substring(0,7));
        double heightM   = Double.parseDouble(height);

        // New (or first) line.
        if (anchor == null || !anchor.sameStation(stationKm)) {
            anchor = new Anchor(gkRightKm, gkHighKm, heightM, stationKm);
            lastPointGK[0] = gkRightKm;
            lastPointGK[1] = gkHighKm;
            currentLine = new ArrayList<XY>();
            data.put(stationKm, currentLine);
            currentLine.add(new XY(0d, heightM, 0));
            try {
                anchorDate = DateGuesser.guessDate(date);
            }
            catch (IllegalArgumentException iae) {
                log.warn("W80: Invalid date '" + date + "'.");
            }
        }
        else {
            addPoint(gkRightKm, gkHighKm, heightM, pointIndex);
        }
    }


    /** Called when file is fully consumed. */
    @Override
    protected void finish() {
        log.info("Parsed " + data.size() + " lines");
    }


    /** Parses files given as arguments. */
    public static void main(String [] args) {

        W80Parser parser = new W80Parser();

        log.warn("Start parsing files.");
        for (String arg: args) {
            log.warn("Parsing a file.");
            parser.parseW80s(new File(arg), null);
        }
        log.error("Finished parsing files.");
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org