井斜水平投影图
前面发文介绍过在wpf中平滑曲线的绘制方法,今天介绍一个绘制曲线的实际应用。
井斜水平投影图就是把某斜井的各个斜井段投影到某一水平面上所得到的图件,反映实际井底偏离井口的水平位移和方位,及钻遇目的层的垂直井深,是油气勘探、开发的基础图件之一。
定义基本的绘图对象:
public abstract class GraphicsBase : DrawingVisual
从DrawingVisual对象继承定义基本绘制对象
public class GraphicsAxis : GraphicsBase
定义绘图对象
定义一个画布对象进行绘制控制DrawingCanvas
坐标轴及刻度设置及绘制
横纵坐标轴及刻度是图件的基础,实际使用中井东西/南北位移数值差异较大,位移的取值能从几米到几百上千米。范围必须能够根据实际的需要调节,同理坐标的原点可以是0点,也可以设置成任意数值。
因为打印的需要,图件本身需要设置成A4大小,然后根据横纵坐标轴设置的取值范围,获取原点的坐标。基于原点坐标计算曲线上的各个点的坐标,绘制曲线。
设置画布宽高为A4大小,
Width="21cm" Height="29.7cm"
定义坐标轴和刻度设置对象,设置坐标轴的起始值范围,主刻度间隔值及每个主刻度中子刻度的个数,以及原点属性。
StartValue :坐标轴起始数值,建议取 0,100,200,等正数数值。
MainTicksValue:主刻度数值,比如设置成200,每个主刻度间隔200
MainTicksCount:坐标轴上有多少个主刻度
DetailTicksCount:子刻度数量,每个主刻度有多少个子刻度,建议设置成4,每个主刻度的子刻度进行5等分
OriginValue:坐标原点的取值,默认0
public class GraphicsDataModel
public int StartValue { get; set; }
public int MainTicksValue { get; set; }
public int MainTicksCount { get; set; }
public int DetailTicksCount { get; set; }
public int OriginValue { get; set; }
///刻度对象
IsZero属性描述这个刻度是否是原点
public class Tick
{
public int Value { get; set; }
public string Label { get; set; }
public bool IsZero { get; set; }
public List DetailTicks { get; set; }
}
绘制代码:
获取纵坐标刻度
var yTicks = this.TicksY.GetTicks();
计算主刻度对应的像素值
var yTickPixleValue = (this.Height - TopMargin - BottomMargin) / (yTicks.Count - 1);
获取纵坐标原点对应的像素Y值
var yZero = this.Height - this.BottomMargin - GetXZero(yTicks, yTickPixleValue);
获取横坐标的刻度
var xTicks = this.TicksX.GetTicks();
计算横坐标主刻度对应的像素值
var xTickPixleValue = (this.Width - LeftMargin - RightMargin) / (xTicks.Count - 1);
绘制横坐标的刻度
foreach (var tick in xTicks)
{
DrawXTicks(drawingContext, tick, xTickPixleValue, yPosition, xTicks.IndexOf(tick));
}
绘制刻度方法定义如下:
private void DrawXTicks(DrawingContext drawingContext, GraphicsDataModel.Tick tick, double tickPixleValue, double yPosition, int index)
{
double xPosition;
if(index == 0)
{
xPosition = this.LeftMargin;
}
else
{
xPosition = this.LeftMargin + tickPixleValue * index;
}
Pen pen = new Pen
{
StartLineCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round,
Brush = new SolidColorBrush(Colors.DarkGray),
Thickness = 1,
DashCap = PenLineCap.Round,
DashStyle = dashstyle
};
drawingContext.DrawLine(
pen,
new Point(xPosition, this.TopMargin),
new Point(xPosition, this.Height - this.BottomMargin)
);
drawingContext.DrawLine(
new Pen(new SolidColorBrush(Colors.Black), 1),
new Point(xPosition, yPosition),
new Point(xPosition, yPosition + 10)
);
foreach(var detail in tick.DetailTicks)
{
double detailXPosition;
double detailTickValue = tickPixleValue / (tick.DetailTicks.Count + 1);
var detailIndex = tick.DetailTicks.IndexOf(detail);
if(detailIndex ==0)
{
detailXPosition = xPosition + detailTickValue;
}
else
{
detailXPosition = xPosition + detailTickValue + detailTickValue * detailIndex;
}
var detailPen = new Pen
{
StartLineCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round,
Brush = new SolidColorBrush(Colors.LightGray),
Thickness = 1,
DashCap = PenLineCap.Round,
DashStyle = dashstyle
};
drawingContext.DrawLine(
detailPen,
new Point(detailXPosition, this.TopMargin),
new Point(detailXPosition, this.Height - this.BottomMargin)
);
drawingContext.DrawLine(
new Pen(new SolidColorBrush(Colors.Black), 1),
new Point(detailXPosition, yPosition),
new Point(detailXPosition, yPosition +4)
);
}
其中一下代码用于绘制刻度延长的虚线
Pen pen = new Pen
{
StartLineCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round,
Brush = new SolidColorBrush(Colors.DarkGray),
Thickness = 1,
DashCap = PenLineCap.Round,
DashStyle = dashstyle
};
接下来绘制坐标轴上的标题文字
var formattedText = new FormattedText("西 —>东坐标轴", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("DefaultFont"), 15, Brushes.Black);
drawingContext.DrawText(formattedText, new Point(this.Width - this.RightMargin - (formattedText.Width) - 5, yPosition + 36));
然后计算X轴原点的位置,绘制纵坐标的上刻度值
var xZero = this.LeftMargin + GetXZero(xTicks, xTickPixleValue);
foreach (var tick in yTicks)
{
DrawYTicks(drawingContext, tick, yTickPixleValue, yPosition, yTicks.IndexOf(tick), xZero);
}
绘制横纵坐标轴线
DrawXAxisLine(drawingContext, yPosition);
DrawYAxisLine(drawingContext, xZero);
方法定义如下:
private void DrawXAxisLine(DrawingContext drawingContext, double yPosition)
{
var startPoint = new Point(this.LeftMargin, yPosition);
var endPoint = new Point(this.Width - RightMargin + 15, yPosition);
//横坐标轴
drawingContext.DrawLine(
new Pen(new SolidColorBrush(Colors.Black), 1),
startPoint,
endPoint
);
DrawXLineArrow(drawingContext, endPoint);
}
方法DrawXLineArrow绘制坐标轴上的箭头
private static void DrawXLineArrow(DrawingContext drawingContext, Point endPoint)
{
var angle = 20;
var arrowLength = 10;
var x = arrowLength * Math.Cos(angle / 180f * Math.PI);
var y = arrowLength * Math.Sin(angle / 180f * Math.PI);
var topArrowPoint = new Point(endPoint.X - x, endPoint.Y - y);
var bottomArrowPoint = new Point(endPoint.X - x, endPoint.Y + y);
drawingContext.DrawLine(
new Pen(new SolidColorBrush(Colors.Black), 1),
topArrowPoint,
endPoint
);
drawingContext.DrawLine(
new Pen(new SolidColorBrush(Colors.Black), 1),
bottomArrowPoint,
endPoint
);
}
绘制标题文本
DrawTitle(drawingContext);
井轨迹投影绘制
private void DrawCurve(DrawingContext drawingContext, double xZero, double yZero, double xTickPixleValue, double yTickPixleValue)
{
if(ActualData != null)
{
DrawCurveData(drawingContext, xZero, yZero, xTickPixleValue, yTickPixleValue, ActualData, Colors.Green);
}
if(DesignData != null)
{
DrawCurveData(drawingContext, xZero, yZero, xTickPixleValue, yTickPixleValue, DesignData, Colors.Blue);
}
}
绘制轨迹曲线
private void DrawCurveData(DrawingContext drawingContext, double xZero, double yZero, double xTickPixleValue, double yTickPixleValue, List data,Color color)
{
List list = new List();
foreach (var point in data)
{
var newPoint = new Point();
newPoint.Y = yZero - (yTickPixleValue / this.TicksY.MainTicksValue) * (point.Y - this.TicksY.OriginValue);
newPoint.X = xZero + (xTickPixleValue / this.TicksX.MainTicksValue) * (point.X - this.TicksX.OriginValue);
list.Add(newPoint);
}
var path = MakeCurve(list.ToArray(), 0.4);
PathGeometry geometry = path.Data as PathGeometry;
drawingContext.DrawGeometry(null,
new Pen(new SolidColorBrush(color), 2),
path.Data);
}
实际使用代码坐标参数设置功能
根据指定的宽高,声明一个绘图对象
var graphic = new GraphicsAxis(model, new Point(0, 0),this.Width,this.Height);
设置x,y坐标轴
var model = new GraphicsDataModel();
model.StartValue = -200;
model.EndValue = 730;
model.OriginValue = 0;
model.MainTicksValue = 200;
model.DetailTicksCount = 4;
graphic.TicksX = model;
model = new GraphicsDataModel();
model.StartValue = -200;
model.OriginValue = 0;
model.EndValue = 730;
model.MainTicksValue = 200;
model.DetailTicksCount = 4;
graphic.TicksY = model;
加载曲线数据
graphic.ActualData = LoadData();
graphic.DesignData = LoadDesignData();
效果如下:
我们可以通过设置OriginValue数值,改变原点位置
或者改变刻度范围