com.d3x.morpheus.viz.google.GXyDataset Maven / Gradle / Ivy
/*
* Copyright (C) 2014-2018 D3X Systems - All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.d3x.morpheus.viz.google;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import com.d3x.morpheus.array.Array;
import com.d3x.morpheus.frame.DataFrame;
import com.d3x.morpheus.range.Range;
import com.d3x.morpheus.util.IO;
import com.d3x.morpheus.viz.chart.xy.XyDataset;
import com.d3x.morpheus.viz.js.JsCode;
/**
* An implementation of the XyDataset interface to be used with Google charts
*
* @author Xavier Witdouck
*
* This is open source software released under the Apache 2.0 License
*/
class GXyDataset implements XyDataset {
private DataFrame,S> frame;
private Array colOrdinals;
private Supplier> domainType;
private IntFunction domainValueFunction;
private Consumer> refreshHandler;
/**
* Constructor
* @param refreshHandler the refresh handler
*/
private GXyDataset(Consumer> refreshHandler) {
this.refreshHandler = refreshHandler;
this.refresh();
}
/**
* Returns a newly created model using a frame supplier where the domain is presented by the DataFrame row keys
* @param frameSupplier the DataFrame supplier for this model
* @param the domain key type
* @param the series key type
* @return the newly created model
*/
static GXyDataset of(Supplier> frameSupplier) {
return new GXyDataset<>(dataset -> {
try {
final DataFrame frame = frameSupplier.get();
if (frame != null) {
final Array colOrdinals = Array.of(IntStream.range(0, frame.colCount()).toArray());
final Supplier> domainType = () -> frame.rows().keyType();
dataset.update(frame, colOrdinals, domainType, rowIndex -> frame.rows().key(rowIndex));
} else {
dataset.clear(true);
}
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
/**
* Returns a newly created model using a frame supplier where the domain is presented by a column in the DataFrame
* @param domainAxisKey the DataFrame column key for the domain
* @param frameSupplier the DataFrame supplier for this model
* @param the domain key type
* @param the series key type
* @return the newly created model
*/
@SuppressWarnings("unchecked")
static GXyDataset of(S domainAxisKey, Supplier> frameSupplier) {
return new GXyDataset<>(dataset -> {
try {
final DataFrame,S> frame = frameSupplier.get();
if (frame != null) {
final int domainAxisColOrdinal = frame.cols().ordinalOf(domainAxisKey);
final Array colOrdinals = Array.of(IntStream.range(0, frame.colCount()).filter(i -> i != domainAxisColOrdinal).toArray());
final Supplier> domainType = () -> (Class)frame.cols().type(domainAxisKey);
dataset.update(frame, colOrdinals, domainType, rowIndex -> frame.getValueAt(rowIndex, domainAxisColOrdinal));
} else {
dataset.clear(true);
}
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
/**
* Updates this model with the DataFrame, series column ordinals and domain value function
* @param frame the DataFrame to accept
* @param colOrdinals the series column ordinals
* @param domainValueFunction the domain value function
*/
private void update(DataFrame,S> frame, Array colOrdinals, Supplier> domainType, IntFunction domainValueFunction) {
this.frame = frame;
this.colOrdinals = colOrdinals;
this.domainType = domainType;
this.domainValueFunction = domainValueFunction;
}
@Override
public void refresh() {
this.refreshHandler.accept(this);
}
@Override
public boolean isEmpty() {
return frame == null || frame.rowCount() == 0;
}
@Override
public void clear(boolean notify) {
this.frame = null;
this.colOrdinals = null;
this.domainValueFunction = null;
}
@Override
@SuppressWarnings("unchecked")
public DataFrame frame() {
return (DataFrame)frame;
}
@Override
public Class domainType() {
return isEmpty() ? null : domainType.get();
}
@Override
public boolean contains(S seriesKey) {
return !isEmpty() && frame.cols().contains(seriesKey);
}
@Override
public IntFunction domainFunction() {
return domainValueFunction;
}
@Override
public XyDataset withLowerDomainInterval(Function lowerIntervalFunction) {
return null;
}
@Override
public XyDataset withUpperDomainInterval(Function upperIntervalFunction) {
return null;
}
/**
* Returns the class of the domain values in this dataset
* @return the class of the domain values in this dataset
*/
@SuppressWarnings("unchecked")
Class getDomainKeyType() {
if (isEmpty()) {
return (Class)Double.class;
} else {
for (int i=0; i)value.getClass();
}
}
return (Class)Double.class;
}
}
/**
* Returns the iterable of domain values for this dataset
* @return the iterable domain values
*/
public Iterable getDomainValues() {
if (isEmpty()) {
return Collections.emptyList();
} else {
return Range.of(0, frame.rowCount()).map(domainValueFunction::apply);
}
}
/**
* Returns the series keys for this dataset
* @return the series keys for dataset
*/
public Iterable getSeriesKeys() {
if (isEmpty()) {
return Collections.emptyList();
} else {
return colOrdinals.map(v -> frame.cols().key(v.getInt()));
}
}
/**
* Returns the number of series in this dataset
* @return the number of series
*/
public int getSeriesCount() {
return isEmpty() ? 0 : colOrdinals.length();
}
/**
* Returns the series for the index specified
* @param series the series index
* @return the series key
*/
public S getSeriesKey(int series) {
return isEmpty() ? null : frame.cols().key(colOrdinals.getInt(series));
}
/**
* Returns the number of values per series
* @return the number of values per series
*/
public int getDomainSize() {
return isEmpty() ? 0 : frame.rowCount();
}
/**
* Returns the domain value for the item index
* @param item the item index
* @return the corresponding domain value
*/
public X getDomainValue(int item) {
return isEmpty() ? null : domainValueFunction.apply(item);
}
/**
* Returns the range value for the item and series index
* @param item the item index
* @param series the series index
* @return the range value
*/
public double getRangeValue(int item, int series) {
if (isEmpty()) {
return Double.NaN;
} else {
final int colOrdinal = colOrdinals.getInt(series);
return frame.getDoubleAt(item, colOrdinal);
}
}
public void accept(JsCode script) {
final Class> domainClass = domainType();
final GDataType domainType = GDataType.getDataType(domainClass, GDataType.STRING);
script.newArray(array -> {
array.appendArray(false, header -> {
header.appendObject(true, domain -> {
domain.newAttribute("id", "domain");
domain.newAttribute("label", "Domain");
domain.newAttribute("type", domainType.getLabel());
});
for (int i=0; i {
series.newAttribute("id", seriesKey.toString());
series.newAttribute("label", seriesKey.toString());
series.newAttribute("type", "number");
});
}
});
final Function