Mercurial > dive4elements > river
changeset 7733:228be10e6165
Merge slt-simplify-cross-sections branch into default.
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Wed, 22 Jan 2014 12:54:18 +0100 (2014-01-22) |
parents | 772c22b0ea12 (current diff) e1b831fe435a (diff) |
children | 46273d890da5 |
files | |
diffstat | 4 files changed, 204 insertions(+), 33 deletions(-) [+] |
line wrap: on
line diff
--- a/backend/src/main/java/org/dive4elements/river/importer/Config.java Mon Jan 20 14:54:35 2014 +0100 +++ b/backend/src/main/java/org/dive4elements/river/importer/Config.java Wed Jan 22 12:54:18 2014 +0100 @@ -100,6 +100,9 @@ public static final String SKIP_SQ_RELATION = "flys.backend.importer.skip.sq.relation"; + public static final Double CROSS_SECTION_SIMPLIFICATION_EPSILON = + getDouble("flys.backend.importer.cross.section.simplification.epsilon"); + public static final Config INSTANCE = new Config(); @@ -113,6 +116,21 @@ : Boolean.getBoolean(SKIP_DEFAULT); } + public static final Double getDouble(String key) { + try { + String value = System.getProperty(key); + return value != null + ? Double.valueOf(value) + : null; + } catch (NumberFormatException nfe) { + return null; + } + } + + public Double getCrossSectionSimplificationEpsilon() { + return CROSS_SECTION_SIMPLIFICATION_EPSILON; + } + public boolean dryRun() { return getFlag(DRY_RUN); }
--- a/backend/src/main/java/org/dive4elements/river/importer/ImportRiver.java Mon Jan 20 14:54:35 2014 +0100 +++ b/backend/src/main/java/org/dive4elements/river/importer/ImportRiver.java Wed Jan 22 12:54:18 2014 +0100 @@ -39,23 +39,29 @@ import org.dive4elements.river.model.River; import org.dive4elements.river.model.Unit; +import org.dive4elements.river.utils.DouglasPeuker; + import java.io.File; import java.io.IOException; +import java.sql.SQLException; + import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Iterator; import org.apache.log4j.Logger; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.exception.ConstraintViolationException; + /** Import all river-related data (files) that can be found. */ public class ImportRiver @@ -183,24 +189,24 @@ protected River peer; - /** Callback-implementation for CrossSectionParsers: - * Accept files with different md5(?)sums than what has already been parsed, - * on successfull parse, add data. */ - class ImportRiverCrossSectionParserCallback implements CrossSectionParser.Callback { - Set<HashedFile> files = new HashSet<HashedFile>(); - String type; + /** Callback-implementation for CrossSectionParsers. */ + private class ImportRiverCrossSectionParserCallback + implements CrossSectionParser.Callback { + private Set<HashedFile> files = new HashSet<HashedFile>(); + private String type; /** * Create new Callback, given type which is used for logging * purposes only. */ - public ImportRiverCrossSectionParserCallback (String type) { + public ImportRiverCrossSectionParserCallback(String type) { this.type = type; } /** Accept file if not duplicate. */ + @Override public boolean accept(File file) { HashedFile hf = new HashedFile(file); boolean success = files.add(hf); @@ -212,14 +218,60 @@ /** Add crosssection. */ + @Override public void parsed(CrossSectionParser parser) { log.debug("callback from " + type + " parser"); - addCrossSections(parser); + String description = parser.getDescription(); + Integer year = parser.getYear(); + ImportTimeInterval ti = year != null + ? new ImportTimeInterval(yearToDate(year)) + : null; + + Map<Double, List<XY>> data = parser.getData(); + + List<ImportCrossSectionLine> lines = + new ArrayList<ImportCrossSectionLine>(data.size()); + + Double simplificationEpsilon = + Config.INSTANCE.getCrossSectionSimplificationEpsilon(); + + long numReadPoints = 0L; + long numRemainingPoints = 0L; + + for (Map.Entry<Double, List<XY>> entry: data.entrySet()) { + Double km = entry.getKey(); + List<XY> points = entry.getValue(); + numReadPoints += points.size(); + if (simplificationEpsilon != null) { + points = DouglasPeuker.simplify(points, simplificationEpsilon); + } + numRemainingPoints += points.size(); + lines.add(new ImportCrossSectionLine(km, points)); + } + + ImportRiver.this.addCrossSections(description, ti, lines); + + double percent = numReadPoints > 0L + ? ((double)numRemainingPoints/numReadPoints)*100d + : 0d; + + log.info(String.format( + "Number of points in cross section: %d / %d (%.2f%%)", + numReadPoints, numRemainingPoints, percent)); } } // ImportRiverCrossSectionParserCallback + private void addCrossSections( + String description, + ImportTimeInterval ti, + List<ImportCrossSectionLine> lines + ) { + crossSections.add(new ImportCrossSection(this, description, ti, lines)); + } + + public ImportRiver() { hyks = new ArrayList<ImportHYK>(); crossSections = new ArrayList<ImportCrossSection>(); @@ -1052,30 +1104,6 @@ } - /** Add cross sections with description, years and lines to - * store. */ - private void addCrossSections(CrossSectionParser parser) { - String description = parser.getDescription(); - Integer year = parser.getYear(); - ImportTimeInterval ti = year != null - ? new ImportTimeInterval(yearToDate(year)) - : null; - - Map<Double, List<XY>> data = parser.getData(); - - List<ImportCrossSectionLine> lines = - new ArrayList<ImportCrossSectionLine>(data.size()); - - for (Map.Entry<Double, List<XY>> entry: data.entrySet()) { - Double km = entry.getKey(); - List<XY> points = entry.getValue(); - lines.add(new ImportCrossSectionLine(km, points)); - } - - crossSections.add(new ImportCrossSection( - ImportRiver.this, description, ti, lines)); - } - /** Create a W80 Parser and parse w80 files found. */ public void parseW80s() { if (Config.INSTANCE.skipW80s()) { @@ -1134,6 +1162,7 @@ ImportRiverCrossSectionParserCallback da50Callback = new ImportRiverCrossSectionParserCallback("da50"); + parser.parseDA50s(riverDir, da50Callback); } @@ -1155,6 +1184,7 @@ ImportRiverCrossSectionParserCallback da66Callback = new ImportRiverCrossSectionParserCallback("da66"); + parser.parseDA66s(riverDir, da66Callback); }
--- a/backend/src/main/java/org/dive4elements/river/importer/XY.java Mon Jan 20 14:54:35 2014 +0100 +++ b/backend/src/main/java/org/dive4elements/river/importer/XY.java Wed Jan 22 12:54:18 2014 +0100 @@ -22,6 +22,10 @@ public XY() { } + public XY(XY other) { + this(other.x, other.y, other.index); + } + public XY(double x, double y, int index) { this.x = x; this.y = y; @@ -60,5 +64,43 @@ public void setIndex(int index) { this.index = index; } + + public double dot(double ox, double oy) { + return x*ox + y*oy; + } + + public double dot(XY other) { + return dot(other.x, other.y); + } + + public XY sub(XY other) { + x -= other.x; + y -= other.y; + return this; + } + + public XY ortho() { + double z = x; + x = y; + y = -z; + return this; + } + + public XY normalize() { + double len = dot(this); + + if (len > 1e-6) { + len = 1d/Math.sqrt(len); + x *= len; + y *= len; + } + + return this; + } + + // x*nx + y*ny + d = 0 <=> d = -x*nx -y*ny + public double lineOffset(XY p) { + return -x*p.x -y*p.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/backend/src/main/java/org/dive4elements/river/utils/DouglasPeuker.java Wed Jan 22 12:54:18 2014 +0100 @@ -0,0 +1,81 @@ +package org.dive4elements.river.utils; + +import org.dive4elements.river.importer.XY; // TODO: Move to a more common package. + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class DouglasPeuker +{ + public static final double EPSILON = 1e-4; + + private DouglasPeuker() { + } + + public static List<XY> simplify(List<XY> input) { + return simplify(input, EPSILON); + } + + public static List<XY> simplify(List<XY> input, double epsilon) { + + int N = input.size(); + + if (N < 3) { + return new ArrayList<XY>(input); + } + + List<XY> simplified = recursiveSimplify(input, 0, N-1, epsilon); + + List<XY> output = new ArrayList<XY>(simplified.size()+2); + output.add(input.get(0)); + output.addAll(simplified); + output.add(input.get(N-1)); + + return output; + } + + private static List recursiveSimplify( + List<XY> input, + int start, + int end, + double epsilon + ) { + XY a = input.get(start); + XY b = input.get(end); + + // Normal of hesse normal form. + XY n = new XY(b).sub(a).ortho().normalize(); + + // distance offset of the hesse normal form. + double d = n.lineOffset(a); + + double maxDist = -Double.MAX_VALUE; + int maxIdx = -1; + + for (int i = start+1; i < end; ++i) { + double dist = Math.abs(n.dot(input.get(i)) + d); + if (dist > maxDist) { + maxDist = dist; + maxIdx = i; + } + } + + if (maxDist < epsilon) { + // All points between a and b can be ignored. + return Collections.<XY>emptyList(); + } + + // Split by input[maxIdx]. + List<XY> before = recursiveSimplify(input, start, maxIdx, epsilon); + List<XY> after = recursiveSimplify(input, maxIdx, end, epsilon); + + List<XY> output = new ArrayList<XY>(before.size()+1+after.size()); + output.addAll(before); + output.add(input.get(maxIdx)); + output.addAll(after); + + return output; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :