gernotbelger@9263: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@9263: * Software engineering by gernotbelger@9263: * Björnsen Beratende Ingenieure GmbH gernotbelger@9263: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@9263: * gernotbelger@9263: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@9263: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@9263: * documentation coming with Dive4Elements River for details. gernotbelger@9263: */ gernotbelger@9263: package org.dive4elements.river.client.client.ui.chart; gernotbelger@9263: gernotbelger@9263: import java.util.Set; gernotbelger@9263: import java.util.SortedSet; gernotbelger@9263: import java.util.TreeSet; gernotbelger@9263: gernotbelger@9263: /** gernotbelger@9263: * Allows stepping through a list of distinct (known) values. gernotbelger@9263: * gernotbelger@9263: * @author Gernot Belger gernotbelger@9263: */ gernotbelger@9263: public class DistinctValuesNaviChartStepper implements INaviChartStepper { gernotbelger@9263: gernotbelger@9263: private final TreeSet validSteps = new TreeSet(); gernotbelger@9263: gernotbelger@9263: private double currentKm; gernotbelger@9263: gernotbelger@9263: public DistinctValuesNaviChartStepper(final Set validKms) { gernotbelger@9263: this.validSteps.addAll(validKms); gernotbelger@9263: gernotbelger@9263: if (this.validSteps.isEmpty()) gernotbelger@9263: this.validSteps.add(-1d); gernotbelger@9263: gernotbelger@9263: this.currentKm = this.validSteps.first(); gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: @Override gernotbelger@9263: public double getCurrentKm() { gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: @Override gernotbelger@9263: public double stepForward() { gernotbelger@9263: this.currentKm = calculateStepFormward(); gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: private double calculateStepFormward() { gernotbelger@9263: // REMARK: can't use higher due to gwt bug gernotbelger@9263: // final Double next = this.validSteps.higher(this.currentKm); gernotbelger@9263: gernotbelger@9263: // REMAREK: add a bit, because tailSet is inclusive gernotbelger@9263: final SortedSet tailSet = this.validSteps.tailSet(this.currentKm + 1e-6); gernotbelger@9263: final Double next = tailSet.isEmpty() ? null : tailSet.first(); gernotbelger@9263: gernotbelger@9263: if (next != null) gernotbelger@9263: return next; gernotbelger@9263: gernotbelger@9263: return this.validSteps.last(); gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: @Override gernotbelger@9263: public double stepBackward() { gernotbelger@9263: this.currentKm = calculateStepBackward(); gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: private double calculateStepBackward() { gernotbelger@9263: // REMARK: can't use lower due to gwt bug gernotbelger@9263: // final Double prev = this.validSteps.lower(this.currentKm); gernotbelger@9263: gernotbelger@9263: final SortedSet headSet = this.validSteps.headSet(this.currentKm); gernotbelger@9263: final Double prev = headSet.isEmpty() ? null : headSet.last(); gernotbelger@9263: gernotbelger@9263: if (prev != null) gernotbelger@9263: return prev; gernotbelger@9263: gernotbelger@9263: return this.validSteps.first(); gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: @Override gernotbelger@9263: public double setValidCurrentKm(final double currentKm) { gernotbelger@9263: this.currentKm = calculateValidCurrentKm(currentKm); gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: private double calculateValidCurrentKm(final double newKm) { gernotbelger@9263: gernotbelger@9263: if (this.validSteps.contains(newKm)) { gernotbelger@9263: /* special case, and because headSet() is not inclusive */ gernotbelger@9263: return newKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: final SortedSet headSet = this.validSteps.headSet(newKm); gernotbelger@9263: final SortedSet tailSet = this.validSteps.tailSet(newKm); gernotbelger@9263: gernotbelger@9263: // REMARK: can't use floor/ceiling because of gwt bug gernotbelger@9263: // final Double floor = this.validSteps.floor(currentKm); gernotbelger@9263: // final Double ceiling = this.validSteps.ceiling(currentKm); gernotbelger@9263: final Double floor = headSet.isEmpty() ? null : headSet.last(); gernotbelger@9263: final Double ceiling = tailSet.isEmpty() ? null : tailSet.first(); gernotbelger@9263: gernotbelger@9263: if (floor != null && ceiling == null) gernotbelger@9263: return floor; gernotbelger@9263: gernotbelger@9263: if (floor == null && ceiling != null) gernotbelger@9263: return ceiling; gernotbelger@9263: gernotbelger@9263: if (floor == null && ceiling == null) { gernotbelger@9263: /* should never happen as validKms is never empty */ gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: if (floor == null || ceiling == null) { gernotbelger@9263: /* will never happen, but makes the NullPointer access checker happy, else we get warnings in the folowing code */ gernotbelger@9263: return this.currentKm; gernotbelger@9263: } gernotbelger@9263: gernotbelger@9263: /* both not null; find nearest */ gernotbelger@9263: final double floorDiff = Math.abs(newKm - floor); gernotbelger@9263: final double ceilingDiff = Math.abs(newKm - ceiling); gernotbelger@9263: if (floorDiff < ceilingDiff) gernotbelger@9263: return floor; gernotbelger@9263: gernotbelger@9263: return ceiling; gernotbelger@9263: } gernotbelger@9263: }