# HG changeset patch # User Sascha L. Teichmann # Date 1321192955 0 # Node ID 4ae9c92feb8cbca40fefe18e2a90d4ba4696d352 # Parent eb671699fbc27629b682f392ca0cb38bce67e6bb StableXYDifferenceRenderer: Make rendering work with definition holes. flys-artifacts/trunk@3243 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r eb671699fbc2 -r 4ae9c92feb8c flys-artifacts/ChangeLog --- a/flys-artifacts/ChangeLog Sat Nov 12 22:26:27 2011 +0000 +++ b/flys-artifacts/ChangeLog Sun Nov 13 14:02:35 2011 +0000 @@ -1,3 +1,17 @@ +2011-11-13 Sascha L. Teichmann + + * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: + Added system properties 'waterlevel' and 'km'. Useful to + init the UI with a given waterlevel and drawing the cross-sections + at the given km. + + * src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java: + Spliting by NaNs definition holes _should_ work now. Needs + some more testing. + TODOs: + - Use log4j instead of println for logging. + - Subclass XYDifferenceRenderer instead of replacing it totally. + 2011-11-12 Sascha L. Teichmann * src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java: diff -r eb671699fbc2 -r 4ae9c92feb8c flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java --- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java Sat Nov 12 22:26:27 2011 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java Sun Nov 13 14:02:35 2011 +0000 @@ -83,7 +83,11 @@ { public static final String RIVER = System.getProperty("river", "Saar"); - public static final double EPSILON = 1e-4; + public static final String WATER_LEVEL = System.getProperty("waterlevel"); + + public static final String KM = System.getProperty("km"); + + public static final double EPSILON = 1e-4; protected Session session; @@ -171,9 +175,9 @@ 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; + case 1: + case 2: + case 3: case 4: return Boolean.class; } return null; @@ -238,16 +242,16 @@ return query.list(); } - protected Map>> loadAllLines( - List crossSections - ) { + protected Map>> + loadAllLines(List crossSections) { Map>> km2lines = new TreeMap>>(); for (CrossSection cs: crossSections) { List lines = cs.getLines(); for (CrossSectionLine csl: lines) { Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d; - List> ls = km2lines.get(km); + List> ls + = km2lines.get(km); if (ls == null) { ls = new ArrayList>(2); km2lines.put(km, ls); @@ -279,6 +283,30 @@ crossSectionLinesCB = new JComboBox(dcbm); + if (KM != null) { + try { + double km = Double.parseDouble(KM); + + CrossSectionLineItem found = null; + + for (Object o: clis) { + CrossSectionLineItem csli = (CrossSectionLineItem)o; + if (Math.abs(csli.km - km) < EPSILON) { + found = csli; + break; + } + } + + if (found != null) { + crossSectionLinesCB.setSelectedItem(found); + } + } + catch (NumberFormatException nfe) { + System.err.println("km is not a number: " + + nfe.getMessage()); + } + } + nav.add(crossSectionLinesCB); crossSectionLinesCB.addItemListener(new ItemListener() { @@ -292,6 +320,17 @@ waterlevelTF = new JTextField(5); + if (WATER_LEVEL != null) { + try { + waterlevelTF.setText( + (lastWaterLevel = Double.valueOf(WATER_LEVEL)).toString()); + } + catch (NumberFormatException nfe) { + System.err.println("Water level not a number: " + + nfe.getMessage()); + } + } + waterlevelTF.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { @@ -343,7 +382,7 @@ protected void waterLevelChanged() { String value = waterlevelTF.getText(); try { - lastWaterLevel = Double.parseDouble(value); + lastWaterLevel = Double.valueOf(value); } catch (NumberFormatException nfe) { waterlevelTF.setText( @@ -528,6 +567,21 @@ List points = null; CrossSection cs = crossSections.get(i); + if (drawGround[i]) { + for (Pair csl: csli.lines) { + if (csl.getA() == cs) { + if (points == null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + generateGround( + points, + cs.getDescription() + "/Boden", + datasets); + break; + } + } + } + if (drawFill[i]) { for (Pair csl: csli.lines) { if (csl.getA() == cs) { @@ -568,20 +622,6 @@ } } - if (drawGround[i]) { - for (Pair csl: csli.lines) { - if (csl.getA() == cs) { - if (points == null) { - points = csl.getB().fetchCrossSectionLinesPoints(); - } - generateGround( - points, - cs.getDescription() + "/Boden", - datasets); - break; - } - } - } } return datasets; diff -r eb671699fbc2 -r 4ae9c92feb8c flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java --- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java Sat Nov 12 22:26:27 2011 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java Sun Nov 13 14:02:35 2011 +0000 @@ -116,6 +116,8 @@ import gnu.trove.TDoubleArrayList; +import de.intevation.flys.artifacts.math.Linear; + /** * A renderer for an {@link XYPlot} that highlights the differences between two * series. The example shown here is generated by the @@ -364,56 +366,292 @@ return 2; } - public static List splitByNaNs(XYDataset dataset) { + private static final void addSeries( + DefaultXYDataset ds, + Comparable key, + TDoubleArrayList xs, + TDoubleArrayList ys + ) { + ds.addSeries( + key, + new double [][] { + xs.toNativeArray(), + ys.toNativeArray() + }); + } + + protected static List splitByNaNsOneSeries( + XYDataset dataset + ) { List datasets = new ArrayList(); - switch (dataset.getSeriesCount()) { - case 0: - return datasets; - case 1: - int N = dataset.getItemCount(0); - TDoubleArrayList xs = new TDoubleArrayList(N); - TDoubleArrayList ys = new TDoubleArrayList(N); - for (int i = 0; i < N; ++i) { - double x = dataset.getXValue(0, i); - double y = dataset.getYValue(0, i); - if (Double.isNaN(x) || Double.isNaN(y)) { - if (!xs.isEmpty()) { - DefaultXYDataset ds = new DefaultXYDataset(); - datasets.add(ds); - ds.addSeries( - dataset.getSeriesKey(0), - new double [][] { - xs.toNativeArray(), - ys.toNativeArray() }); - xs.reset(); - ys.reset(); - } - } - else { - xs.add(x); - ys.add(y); - } - } + int N = dataset.getItemCount(0); + TDoubleArrayList xs = new TDoubleArrayList(N); + TDoubleArrayList ys = new TDoubleArrayList(N); + for (int i = 0; i < N; ++i) { + double x = dataset.getXValue(0, i); + double y = dataset.getYValue(0, i); + if (Double.isNaN(x) || Double.isNaN(y)) { if (!xs.isEmpty()) { DefaultXYDataset ds = new DefaultXYDataset(); + addSeries(ds, dataset.getSeriesKey(0), xs, ys); datasets.add(ds); - ds.addSeries( - dataset.getSeriesKey(0), - new double [][] { - xs.toNativeArray(), - ys.toNativeArray() }); + xs.reset(); + ys.reset(); } - break; - default: // two or more - // TODO: split two parts - datasets.add(dataset); - break; + } + else { + xs.add(x); + ys.add(y); + } + } + if (!xs.isEmpty()) { + DefaultXYDataset ds = new DefaultXYDataset(); + addSeries(ds, dataset.getSeriesKey(0), xs, ys); + datasets.add(ds); } return datasets; } + private static final boolean add(TDoubleArrayList xs, double x) { + int N = xs.size(); + if (N == 0 || xs.getQuick(N-1) < x) { + xs.add(x); + return true; + } + System.err.println("pushed smaller"); + return false; + } + + protected static List splitByNaNsTwoSeries( + XYDataset dataset + ) { + List datasets = new ArrayList(); + + int N = dataset.getItemCount(0); + int M = dataset.getItemCount(1); + + int i = 0, j = 0; + // ignore leading NaNs + for (; i < N; ++i) { + double x = dataset.getXValue(0, i); + double y = dataset.getYValue(0, i); + if (!Double.isNaN(x) && !Double.isNaN(y)) { + break; + } + } + + for (; j < M; ++j) { + double x = dataset.getXValue(1, j); + double y = dataset.getYValue(1, j); + if (!Double.isNaN(x) && !Double.isNaN(y)) { + break; + } + } + + TDoubleArrayList six = new TDoubleArrayList(); + TDoubleArrayList siy = new TDoubleArrayList(); + TDoubleArrayList sjx = new TDoubleArrayList(); + TDoubleArrayList sjy = new TDoubleArrayList(); + + while (i < N && j < M) { + int ni = i+1; + for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni); + for (; ni < N && Double.isNaN(dataset.getXValue(0, ni)); ++ni); + + int nj = j+1; + for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj); + for (; nj < M && Double.isNaN(dataset.getXValue(1, nj)); ++nj); + + if (ni == N && nj == M) { // no more splits + System.err.println("no more splits ...."); + for (; i < ni; ++i) { + double x = dataset.getXValue(0, i); + double y = dataset.getYValue(0, i); + if (!Double.isNaN(x) + && !Double.isNaN(y) + && add(six, x)) { + siy.add(y); + } + } + for (; j < nj; ++j) { + double x = dataset.getXValue(1, j); + double y = dataset.getYValue(1, j); + if (!Double.isNaN(x) + && !Double.isNaN(y) + && add(sjx, x)) { + sjy.add(y); + } + } + if (!six.isEmpty() && !sjx.isEmpty()) { + DefaultXYDataset ds = new DefaultXYDataset(); + addSeries(ds, dataset.getSeriesKey(0), six, siy); + addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); + datasets.add(ds); + } + break; + } + + System.err.println("ni: " + ni + " " + N); + System.err.println("nj: " + nj + " " + M); + + double xni = ni < N + ? dataset.getXValue(0, ni) + : Double.MAX_VALUE; + + double xnj = nj < M + ? dataset.getXValue(1, nj) + : Double.MAX_VALUE; + + double xns = Math.min(xni, xnj); + + double pushxi = Double.NaN; + double pushyi = Double.NaN; + double pushxj = Double.NaN; + double pushyj = Double.NaN; + + for (; i < ni; ++i) { + double x = dataset.getXValue(0, i); + double y = dataset.getYValue(0, i); + if (Double.isNaN(x) || Double.isNaN(y)) { + continue; + } + if (x < xns) { + if (add(six, x)) { + siy.add(y); + } + continue; + } + if (x == xns) { // exact match + if (add(six, x)) { + siy.add(y); + } + pushxi = x; pushyi = y; + } + else { // x > xns: intersection + System.err.println("xns: " + xns); + System.err.println("x/y: " + x + " / " + y); + int SIX = six.size(); + if (SIX > 0) { // should always be true + double yns = Linear.linear( + xns, + six.getQuick(SIX-1), x, + siy.getQuick(SIX-1), y); + System.err.println("intersection at: " + yns); + if (add(six, xns)) { + siy.add(yns); + } + pushxi = xns; + pushyi = yns; + } + } + break; // Split point reached. + } + + for (; j < nj; ++j) { + double x = dataset.getXValue(1, j); + double y = dataset.getYValue(1, j); + if (Double.isNaN(x) || Double.isNaN(y)) { + continue; + } + if (x < xns) { + if (add(sjx, x)) { + sjy.add(y); + } + continue; + } + if (x == xns) { // exact match + if (add(sjx, x)) { + sjy.add(y); + } + pushxj = x; pushyj = y; + } + else { // x > xns: intersection + int SJX = sjx.size(); + if (SJX > 0) { // should always be true + double yns = Linear.linear( + xns, + sjx.getQuick(SJX-1), x, + sjy.getQuick(SJX-1), y); + System.err.println("intersection at: " + yns); + if (add(sjx, xns)) { + sjy.add(yns); + } + pushxj = xns; pushyj = yns; + } + } + break; // Split point reached. + } + + if (!six.isEmpty() && !sjx.isEmpty()) { + DefaultXYDataset ds = new DefaultXYDataset(); + addSeries(ds, dataset.getSeriesKey(0), six, siy); + addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); + datasets.add(ds); + } + + six.reset(); siy.reset(); + sjx.reset(); sjy.reset(); + + // Push split points. + if (!Double.isNaN(pushxi)) { + six.add(pushxi); + siy.add(pushyi); + } + + if (!Double.isNaN(pushxj)) { + sjx.add(pushxj); + sjy.add(pushyj); + } + } + + // Copy the rest. + for (; i < N; ++i) { + double x = dataset.getXValue(0, i); + double y = dataset.getXValue(0, i); + if (!Double.isNaN(x) + && !Double.isNaN(y) + && add(six, x)) { + siy.add(y); + } + } + + for (; j < M; ++j) { + double x = dataset.getXValue(1, j); + double y = dataset.getXValue(1, j); + if (!Double.isNaN(x) + && !Double.isNaN(y) + && add(sjx, x)) { + sjy.add(y); + } + } + + // Build final dataset. + if (!six.isEmpty() && !sjx.isEmpty()) { + DefaultXYDataset ds = new DefaultXYDataset(); + addSeries(ds, dataset.getSeriesKey(0), six, siy); + addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); + datasets.add(ds); + } + + System.err.println("datasets after split: " + datasets.size()); + + return datasets; + } + + public static List splitByNaNs(XYDataset dataset) { + + switch (dataset.getSeriesCount()) { + case 0: + return Collections.emptyList(); + case 1: + return splitByNaNsOneSeries(dataset); + default: // two or more + return splitByNaNsTwoSeries(dataset); + } + } + /** * Draws the visual representation of a single data item. * @@ -449,7 +687,7 @@ for (XYDataset ds: splitByNaNs(dataset)) { drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, - dataset, series, item, crosshairState); + ds, series, item, crosshairState); } break; case 1: