nplot/src/LabelPointPlot.cs

269 lines
9.6 KiB
C#

/*
* NPlot - A charting library for .NET
*
* LabelPointPlot.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.
*
* 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;
using System.Drawing;
namespace NPlot
{
/// <summary>
/// Encapsulates functionality
/// </summary>
public class LabelPointPlot : PointPlot, ISequencePlot
{
/// <summary>
/// Enumeration of all label positions relative to a point.
/// </summary>
public enum LabelPositions
{
/// <summary>
/// Above the point
/// </summary>
Above,
/// <summary>
/// Below the point
/// </summary>
Below,
/// <summary>
/// To the left of the point
/// </summary>
Left,
/// <summary>
/// To the right of the point
/// </summary>
Right
}
private Font font_ = new Font("Arial", 8.0f);
private LabelPositions labelTextPosition_ = LabelPositions.Above;
/// <summary>
/// Default Constructor
/// </summary>
public LabelPointPlot()
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="marker">The marker type to use for this plot.</param>
public LabelPointPlot(Marker marker)
: base(marker)
{
}
/// <summary>
/// The position of the text label in relation to the point.
/// </summary>
public LabelPositions LabelTextPosition
{
get { return labelTextPosition_; }
set { labelTextPosition_ = value; }
}
/// <summary>
/// The text datasource to attach to each point.
/// </summary>
public object TextData { get; set; }
/// <summary>
/// The Font used to write text.
/// </summary>
public Font Font
{
get { return font_; }
set { font_ = value; }
}
/// <summary>
/// Draws the plot on a GDI+ surface against the provided x and y axes.
/// </summary>
/// <param name="g">The GDI+ surface on which to draw.</param>
/// <param name="xAxis">The X-Axis to draw against.</param>
/// <param name="yAxis">The Y-Axis to draw against.</param>
public override void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis)
{
SequenceAdapter data =
new SequenceAdapter(DataSource, DataMember, OrdinateData, AbscissaData);
TextDataAdapter textData =
new TextDataAdapter(DataSource, DataMember, TextData);
// first plot the marker
// we can do this cast, since the constructor accepts only this type!
for (int i = 0; i < data.Count; ++i)
{
try
{
PointD pt = data[i];
if (!Double.IsNaN(pt.X) && !Double.IsNaN(pt.Y))
{
PointF xPos = xAxis.WorldToPhysical(pt.X, false);
PointF yPos = yAxis.WorldToPhysical(pt.Y, false);
Marker.Draw(g, (int) xPos.X, (int) yPos.Y);
if (textData[i] != "")
{
SizeF size = g.MeasureString(textData[i], Font);
switch (labelTextPosition_)
{
case LabelPositions.Above:
g.DrawString(textData[i], font_, Brushes.Black,
new PointF(xPos.X - size.Width/2, yPos.Y - size.Height - Marker.Size*2/3));
break;
case LabelPositions.Below:
g.DrawString(textData[i], font_, Brushes.Black, new PointF(xPos.X - size.Width/2, yPos.Y + Marker.Size*2/3));
break;
case LabelPositions.Left:
g.DrawString(textData[i], font_, Brushes.Black,
new PointF(xPos.X - size.Width - Marker.Size*2/3, yPos.Y - size.Height/2));
break;
case LabelPositions.Right:
g.DrawString(textData[i], font_, Brushes.Black, new PointF(xPos.X + Marker.Size*2/3, yPos.Y - size.Height/2));
break;
}
}
}
}
catch
{
throw new NPlotException("Error in TextPlot.Draw");
}
}
}
/// <summary>
/// This class us used in conjunction with SequenceAdapter to interpret data
/// specified to the TextPlot class.
/// </summary>
private class TextDataAdapter
{
private readonly string dataMember_;
private readonly object dataSource_;
private readonly object data_;
public TextDataAdapter(object dataSource, string dataMember, object data)
{
data_ = data;
dataSource_ = dataSource;
dataMember_ = dataMember;
}
public string this[int i]
{
get
{
// this is inefficient [could set up delegates in constructor].
if (data_ is string[])
{
return ((string[]) data_)[i];
}
if (data_ is string)
{
if (dataSource_ == null)
{
throw new NPlotException("Error: DataSource null");
}
DataRowCollection rows;
if (dataSource_ is DataSet)
{
if (dataMember_ != null)
{
// TODO error check
rows = (((DataSet) dataSource_).Tables[dataMember_]).Rows;
}
else
{
// TODO error check
rows = (((DataSet) dataSource_).Tables[0]).Rows;
}
}
else if (dataSource_ is DataTable)
{
rows = ((DataTable) dataSource_).Rows;
}
else
{
throw new NPlotException("not implemented yet");
}
return (string) ((rows[i]))[(string) data_];
}
if (data_ is ArrayList)
{
object dataPoint = ((ArrayList) data_)[i];
if (dataPoint is string)
return (string) dataPoint;
throw new NPlotException("TextDataAdapter: data not in recognised format");
}
if (data_ == null)
{
return "text";
}
throw new NPlotException("Text data not of recognised type");
}
}
public int Count
{
get
{
// this is inefficient [could set up delegates in constructor].
if (data_ == null)
{
return 0;
}
if (data_ is string[])
{
return ((string[]) data_).Length;
}
if (data_ is ArrayList)
{
return ((ArrayList) data_).Count;
}
throw new NPlotException("Text data not in correct format");
}
}
}
}
}