/* * NPlot - A charting library for .NET * * AdapterUtils.cs * Copyright (C) 2003-2006 Matt Howlett and others. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of NPlot nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Data; namespace NPlot { /// /// Encapsulates functionality relating to exposing data in various /// different data structures in a consistent way. /// /// /// It would be more efficient to have iterator style access /// to the data, rather than index based, and Count. /// public class AdapterUtils { #region AxisSuggesters /// /// Provides default axis if only data corresponding to orthogonal axis is provided. /// public class AxisSuggester_Auto : IAxisSuggester { private readonly IList ordinateData_; /// /// Constructor /// /// Data corresponding to orthogonal axis. public AxisSuggester_Auto(IList ordinateData) { ordinateData_ = ordinateData; } /// /// Calculates a suggested axis given the data specified in the constructor. /// /// the suggested axis public Axis Get() { if (ordinateData_ != null && ordinateData_.Count > 0) { return new LinearAxis(0, ordinateData_.Count - 1); } else { return new LinearAxis(0.0, 1.0); } } } /// /// Provides axis suggestion for data in a particular column of a DataView. /// public class AxisSuggester_DataView : IAxisSuggester { private readonly string columnName_; private readonly DataView data_; /// /// Constructor /// /// DataView that contains data to suggest axis for /// the column of interest in the DataView public AxisSuggester_DataView(DataView data, string columnName) { data_ = data; columnName_ = columnName; } /// /// Calculates a suggested axis given the data specified in the constructor. /// /// the suggested axis public Axis Get() { double min; double max; if (Utils.DataViewArrayMinMax(data_, out min, out max, columnName_)) { if ((data_[0])[columnName_] is DateTime) { return new DateTimeAxis(min, max); } else { return new LinearAxis(min, max); } } return new LinearAxis(0.0, 1.0); } } /// /// This class gets an axis suitable for plotting the data contained in an IList. /// public class AxisSuggester_IList : IAxisSuggester { private readonly IList data_; /// /// Constructor. /// /// the data we want to find a suitable axis for. public AxisSuggester_IList(IList data) { data_ = data; } /// /// Calculates a suggested axis for the IList data. /// /// the suggested axis public Axis Get() { double min; double max; if (Utils.ArrayMinMax(data_, out min, out max)) { if (data_[0] is DateTime) { return new DateTimeAxis(min, max); } else { return new LinearAxis(min, max); } // perhaps return LogAxis here if range large enough // + other constraints? } return new LinearAxis(0.0, 1.0); } } /// /// Implements functionality for suggesting an axis suitable for charting /// data in multiple columns of a DataRowCollection. /// /// This is currently not used. public class AxisSuggester_MultiColumns : IAxisSuggester { private readonly string abscissaName_; private readonly DataRowCollection rows_; /// /// Constructor /// /// The DataRowCollection containing the data. /// the column with this name is not considered public AxisSuggester_MultiColumns(DataRowCollection rows, string abscissaName) { rows_ = rows; abscissaName_ = abscissaName; } /// /// Calculates a suggested axis for the DataRowCollection data. /// /// the suggested axis public Axis Get() { double t_min = double.MaxValue; double t_max = double.MinValue; IEnumerator en = rows_[0].Table.Columns.GetEnumerator(); while (en.MoveNext()) { string colName = ((DataColumn) en.Current).Caption; if (colName == abscissaName_) { continue; } double min; double max; if (Utils.RowArrayMinMax(rows_, out min, out max, colName)) { if (min < t_min) { t_min = min; } if (max > t_max) { t_max = max; } } } return new LinearAxis(t_min, t_max); } } /// /// This class is responsible for supplying a default axis via the IAxisSuggester interface. /// public class AxisSuggester_Null : IAxisSuggester { /// /// Returns a default axis. /// /// the suggested axis public Axis Get() { return new LinearAxis(0.0, 1.0); } } /// /// Provides default axis if only data corresponding to orthogonal axis is provided. /// public class AxisSuggester_RowAuto : IAxisSuggester { private readonly DataRowCollection ordinateData_; /// /// Construbtor /// /// Data corresponding to orthogonal axis. public AxisSuggester_RowAuto(DataRowCollection ordinateData) { ordinateData_ = ordinateData; } /// /// Calculates a suggested axis given the data specified in the constructor. /// /// the suggested axis public Axis Get() { if (ordinateData_ != null && ordinateData_.Count > 0) { return new LinearAxis(0, ordinateData_.Count - 1); } else { return new LinearAxis(0.0, 1.0); } } } /// /// Provides axis for data in a given column of a DataRowCollection. /// public class AxisSuggester_Rows : IAxisSuggester { private readonly string columnName_; private readonly DataRowCollection rows_; /// /// Constructor /// /// DataRowCollection containing the data to suggest axis for. /// the column to get data. public AxisSuggester_Rows(DataRowCollection rows, string columnName) { rows_ = rows; columnName_ = columnName; } /// /// Calculates a suggested axis given the data specified in the constructor. /// /// the suggested axis public Axis Get() { double min; double max; if (Utils.RowArrayMinMax(rows_, out min, out max, columnName_)) { if ((rows_[0])[columnName_] is DateTime) { return new DateTimeAxis(min, max); } else { return new LinearAxis(min, max); } } return new LinearAxis(0.0, 1.0); } } /// /// This class gets an axis corresponding to a StartStep object. The data on /// the orthogonal axis is of course also needed to calculate this. /// public class AxisSuggester_StartStep : IAxisSuggester { private readonly StartStep abscissaData_; private readonly IList ordinateData_; /// /// Constructor /// /// StartStep object corresponding to axis of interest /// data of other axis (needed to get count value) public AxisSuggester_StartStep(StartStep axisOfInterest, IList otherAxisData) { ordinateData_ = otherAxisData; abscissaData_ = axisOfInterest; } /// /// Calculates a suggested axis given the data specified in the constructor. /// /// the suggested axis public Axis Get() { if (ordinateData_ != null && ordinateData_.Count > 0) { return new LinearAxis( abscissaData_.Start, abscissaData_.Start + (ordinateData_.Count - 1)*abscissaData_.Step); } else { return new LinearAxis(0.0, 1.0); } } } /// /// Interface for classes that can suggest an axis for data they contain. /// public interface IAxisSuggester { /// /// Calculates a suggested axis for the data contained by the implementing class. /// /// the suggested axis Axis Get(); } #endregion #region Counters /// /// Class that provides the number of items in a DataView via the ICounter interface. /// public class Counter_DataView : ICounter { private readonly DataView dataView_; /// /// Constructor /// /// the DataBiew data to provide count of number of rows of. public Counter_DataView(DataView dataView) { dataView_ = dataView; } /// /// Number of data items in container. /// /// Number of data items in container. public int Count { get { return dataView_.Count; } } } /// /// Class that provides the number of items in an IList via the ICounter interface. /// public class Counter_IList : ICounter { private readonly IList data_; /// /// Constructor /// /// the IList data to provide count of public Counter_IList(IList data) { data_ = data; } /// /// Number of data items in container. /// /// Number of data items in container. public int Count { get { return data_.Count; } } } /// /// Class that returns 0 via the ICounter interface. /// public class Counter_Null : ICounter { /// /// Number of data items in container. /// /// Number of data items in container. public int Count { get { return 0; } } } /// /// Class that provides the number of items in a DataRowCollection via the ICounter interface. /// public class Counter_Rows : ICounter { private readonly DataRowCollection rows_; /// /// Constructor /// /// the DataRowCollection data to provide count of number of rows of. public Counter_Rows(DataRowCollection rows) { rows_ = rows; } /// /// Number of data items in container. /// /// Number of data items in container. public int Count { get { return rows_.Count; } } } /// /// Interface that enables a dataholding class to report how many data items it holds. /// public interface ICounter { /// /// Number of data items in container. /// /// Number of data items in container. int Count { get; } } #endregion #region DataGetters /// /// Provides the natural numbers (and 0) via the IDataGetter interface. /// public class DataGetter_Count : IDataGetter { /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return i; } } /// /// Provides data in a DataView via the IDataGetter interface. /// public class DataGetter_DataView : IDataGetter { private readonly string columnName_; private readonly DataView data_; /// /// Constructor /// /// DataView to get data from. /// Get data in this column public DataGetter_DataView(DataView data, string columnName) { data_ = data; columnName_ = columnName; } /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return Utils.ToDouble((data_[i])[columnName_]); } } /// /// Provides data in an array of doubles via the IDataGetter interface. /// /// /// A speed-up version of DataDetter_IList; no boxing/unboxing overhead. /// public class DataGetter_DoublesArray : IDataGetter { private readonly double[] data_; /// /// Constructor /// /// array of doubles that contains the data public DataGetter_DoublesArray(double[] data) { data_ = data; } /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return data_[i]; } } /// /// Provides data in an IList via the IDataGetter interface. /// public class DataGetter_IList : IDataGetter { private readonly IList data_; /// /// Constructor /// /// IList that contains the data public DataGetter_IList(IList data) { data_ = data; } /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return Utils.ToDouble(data_[i]); } } /// /// Gets data /// /// Note: Does not implement IDataGetter... Currently this class is not used. public class DataGetter_MultiRows { private readonly int abscissaColumnNumber_; private readonly DataRowCollection rows_; private string abscissaName_; /// /// Constructor /// /// DataRowCollection to get data from. /// don't get data from this column public DataGetter_MultiRows(DataRowCollection rows, string omitThisColumn) { rows_ = rows; abscissaName_ = omitThisColumn; abscissaColumnNumber_ = rows_[0].Table.Columns.IndexOf(omitThisColumn); if (abscissaColumnNumber_ < 0) throw new NPlotException("invalid column name"); } /// /// Number of data points /// public int Count { get { return rows_[0].Table.Columns.Count - 1; } } /// /// Gets data at a given index, in the given series (column number). /// /// index in the series to get data for /// series number (column number) to get data for. /// the required data point. public double PointAt(int index, int seriesIndex) { if (seriesIndex < abscissaColumnNumber_) return Utils.ToDouble(rows_[index][seriesIndex]); else return Utils.ToDouble(rows_[index][seriesIndex + 1]); } } /// /// Provides no data. /// public class DataGetter_Null : IDataGetter { /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { throw new NPlotException("No Data!"); } } /// /// Provides data in a DataRowCollection via the IDataGetter interface. /// public class DataGetter_Rows : IDataGetter { private readonly string columnName_; private readonly DataRowCollection rows_; /// /// Constructor /// /// DataRowCollection to get data from /// Get data in this column public DataGetter_Rows(DataRowCollection rows, string columnName) { rows_ = rows; columnName_ = columnName; } /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return Utils.ToDouble((rows_[i])[columnName_]); } } /// /// Provides data points from a StartStep object via the IDataGetter interface. /// public class DataGetter_StartStep : IDataGetter { private readonly StartStep data_; /// /// Constructor /// /// StartStep to derive data from. public DataGetter_StartStep(StartStep data) { data_ = data; } /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. public double Get(int i) { return data_.Start + (i)*data_.Step; } } /// /// Interface for data holding classes that allows users to get the ith value. /// /// /// TODO: should change this to GetNext() and Reset() for more generality. /// public interface IDataGetter { /// /// Gets the ith data value. /// /// sequence number of data to get. /// ith data value. double Get(int i); } #endregion } }