QuantizedXYZDatasetImpl.java
/*
* Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.spf4j.perf.impl.chart;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.concurrent.Immutable;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.TickUnits;
import org.jfree.data.DomainOrder;
import org.jfree.data.general.DatasetChangeListener;
import org.jfree.data.general.DatasetGroup;
import org.jfree.data.xy.XYZDataset;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.spf4j.base.Arrays;
import org.spf4j.base.ComparablePair;
import org.spf4j.perf.impl.Quanta;
@Immutable
public final class QuantizedXYZDatasetImpl implements XYZDataset, Serializable {
private final double[] x;
private final double[] y;
private final double[] z;
private final double minValue;
private final double maxValue;
private final ArrayList<ComparablePair<Quanta, Integer>> quantas;
private final double [][] data;
private final long startTimeMillis;
private final long stepMillis;
public QuantizedXYZDatasetImpl(final String[] dataSources, final double [][] pdata,
final long startTimeMillis, final long step) {
this.data = pdata.clone();
this.startTimeMillis = startTimeMillis;
this.stepMillis = step;
quantas = new ArrayList<ComparablePair<Quanta, Integer>>();
for (int i = 0; i < dataSources.length; i++) {
String ds = dataSources[i];
if (ds.startsWith("Q")) {
Quanta quanta = new Quanta(ds);
quantas.add(ComparablePair.of(quanta, i));
}
}
Collections.sort(quantas);
int seriesSize = quantas.size() * data.length;
x = new double[seriesSize];
y = new double[seriesSize];
z = new double[seriesSize];
double lMinValue = Double.POSITIVE_INFINITY;
double lMaxValue = Double.NEGATIVE_INFINITY;
int k = 0;
for (int j = 0; j < quantas.size(); j++) {
ComparablePair<Quanta, Integer> pair = quantas.get(j);
double[] values = Arrays.getColumn(data, pair.getSecond());
for (int i = 0; i < values.length; i++) {
x[k] = i; //timestamps[i]*1000;
y[k] = j; //(double) pair.getFirst().getClosestToZero();
double zval = values[i];
z[k] = zval;
if (zval > lMaxValue) {
lMaxValue = zval;
}
if (zval < lMinValue) {
lMinValue = zval;
}
k++;
}
}
this.minValue = lMinValue;
this.maxValue = lMaxValue;
}
@Override
public Number getZ(final int series, final int item) {
return z[item];
}
@Override
public double getZValue(final int series, final int item) {
return z[item];
}
@Override
public DomainOrder getDomainOrder() {
return DomainOrder.ASCENDING;
}
@Override
public int getItemCount(final int series) {
return x.length;
}
@Override
public Number getX(final int series, final int item) {
return x[item];
}
@Override
public double getXValue(final int series, final int item) {
return x[item];
}
@Override
public Number getY(final int series, final int item) {
return y[item];
}
@Override
public double getYValue(final int series, final int item) {
return y[item];
}
@Override
public int getSeriesCount() {
return 1;
}
@Override
public Comparable getSeriesKey(final int series) {
return "RrdXYZDataset";
}
@Override
public int indexOf(final Comparable seriesKey) {
return 0;
}
@Override
public void addChangeListener(final DatasetChangeListener listener) {
// nothing
}
@Override
public void removeChangeListener(final DatasetChangeListener listener) {
// nothing
}
@Override
public DatasetGroup getGroup() {
return null;
}
@Override
public void setGroup(final DatasetGroup group) {
// nothing
}
public double getMaxValue() {
return maxValue;
}
public double getMinValue() {
return minValue;
}
public List<ComparablePair<Quanta, Integer>> getQuantas() {
return quantas;
}
public TickUnits createXTickUnits() {
TickUnits tux = new TickUnits();
final DateTimeFormatter formatter = ISODateTimeFormat.dateHourMinuteSecond();
final DateTimeFormatter shortFormat = ISODateTimeFormat.dateHour();
final DateTimeFormatter mediumFormat = ISODateTimeFormat.dateHourMinute();
final long[] timestamps = new long[data[0].length];
long time = startTimeMillis;
for (int i = 0; i < timestamps.length; i++) {
timestamps[i] = time;
time += stepMillis;
}
tux.add(new NumberTickUnitImpl(1, timestamps, stepMillis, formatter)); // base
long nr = 5 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, formatter));
}
nr = 15 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, formatter));
}
// minute
nr = 60 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, mediumFormat));
}
// 15 minute
nr = 900 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, mediumFormat));
}
// hour
nr = 3600 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, shortFormat));
}
// 6 hour
nr = 21600 / stepMillis;
if (nr > 1) {
tux.add(new NumberTickUnitImpl(nr, timestamps, stepMillis, shortFormat));
}
return tux;
}
public TickUnits createYTickUnits() {
TickUnits tu = new TickUnits();
final List<ComparablePair<Quanta, Integer>> lquantas = this.getQuantas();
tu.add(new NumberTickUnit(1) {
@Override
public String valueToString(final double value) {
int idx = (int) Math.round(value);
if (idx < 0) {
return "NI";
} else if (idx >= lquantas.size()) {
return "PI";
}
long val = lquantas.get(idx).getFirst().getIntervalStart();
if (val == Long.MIN_VALUE) {
return "NI";
} else {
return Long.toString(val);
}
}
});
return tu;
}
private static class NumberTickUnitImpl extends NumberTickUnit {
private static final long serialVersionUID = 0L;
private final long[] timestamps;
private final long stepMillis;
private final transient DateTimeFormatter formatter;
public NumberTickUnitImpl(final double size, final long[] timestamps,
final long stepMillis, final DateTimeFormatter formatter) {
super(size);
this.timestamps = timestamps;
this.formatter = formatter;
this.stepMillis = stepMillis;
}
@Override
public String valueToString(final double value) {
int ival = (int) Math.round(value);
long val;
if (ival >= timestamps.length) {
val = timestamps[timestamps.length - 1] + stepMillis * (ival - timestamps.length + 1);
} else if (ival < 0) {
val = timestamps[0] + ival * stepMillis;
} else {
val = timestamps[ival];
}
return formatter.print(val);
}
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + java.util.Arrays.hashCode(this.timestamps);
hash = 89 * hash + (int) (this.stepMillis ^ (this.stepMillis >>> 32));
hash = 89 * hash + (this.formatter != null ? this.formatter.hashCode() : 0);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NumberTickUnitImpl other = (NumberTickUnitImpl) obj;
if (!java.util.Arrays.equals(this.timestamps, other.timestamps)) {
return false;
}
if (this.stepMillis != other.stepMillis) {
return false;
}
if (this.formatter != other.formatter
&& (this.formatter == null || !this.formatter.equals(other.formatter))) {
return false;
}
return true;
}
}
}