All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zavtech.morpheus.viz.google.GXyDataset Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2014-2017 Xavier Witdouck
 *
 * 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.zavtech.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.zavtech.morpheus.array.Array;
import com.zavtech.morpheus.frame.DataFrame;
import com.zavtech.morpheus.range.Range;
import com.zavtech.morpheus.util.IO;
import com.zavtech.morpheus.viz.chart.xy.XyDataset;
import com.zavtech.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 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 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.data().getValue(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 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.data().getDouble(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 domainValueFunc = createDomainFunction(domainClass); for (int i = 0; i { series.append(stringValue, false); for (int j=0; j createDomainFunction(Class dataType) { if (Number.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : String.valueOf(value); } else if (Date.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : "new Date(" + ((Date)value).getTime() + ")"; } else if (LocalDate.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : "new Date(" + ((LocalDate)value).toEpochDay() * 86400 * 1000 + ")"; } else if (LocalDateTime.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : "new Date(" + ((LocalDateTime)value).toInstant(ZoneOffset.UTC).toEpochMilli() + ")"; } else if (ZonedDateTime.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : "new Date(" + ((ZonedDateTime)value).toInstant().toEpochMilli() + ")"; } else if (Calendar.class.isAssignableFrom(dataType)) { return value -> value == null ? "null" : "new Date(" + ((Calendar)value).getTimeInMillis() + ")"; } else { return value -> value == null ? "null" : "'" + value.toString() + "'"; } } public static void main(String[] args) { final Range rowAxis = Range.ofLocalDates("2017-01-01", "2017-06-01"); final Array colAxis = Array.of("A", "B", "C", "D"); final DataFrame frame = DataFrame.ofDoubles(rowAxis, colAxis, v -> Math.random()); final GXyDataset dataset = GXyDataset.of(() -> frame); final JsCode js = new JsCode(); dataset.accept(js); IO.println(js.toString()); } }