仅作自己学习适用
0 引言
搞完这个小项目后,看着自己的成果感觉非常有成就感,但是如果是仅跟一次是没有什么效果的,还需要继续努力。
1 程序运行效果图
页面布局:
运行效果:
2 代码结构
不同的绘图区域,需要有不同的数据结构。如全国油气产量前十名,就对应着油气田名称和油气当量;又如石油价格走势,对应着交易日期,开盘价、收市价、最高价和最低价,因次需要单独定义一个参数类,用于对这些存储结构进行定义;在地图部分,为了显示油田在地图中的位置,需要记录油田的坐标信息,因此定义了油田类型:
/// <summary>
/// 油田信息
/// </summary>
public List<OilField> OilFields { get; set; }
在右边绘制曲线的时候,是在参数类里是定义的一个曲线字典如下:
/// <summary>
/// 曲线信息字典
/// </summary>
public Dictionary<string,LogCurve> LogCurveDic { get; set; }
那么曲线这个类型也是自定义的类型,因次再创建一个类型用于定义曲线类。整个项目的文件结构如下:
3 完整代码
完整的项目下载链接:数据可视化大屏课程设计
也可以直接复制这里的代码,只有图标和背景图片的文件没有了,其他都可以完全复现。
3.1 UI部分(XAML)
这里需要添加外部包,右击项目,选择"管理NuGet程序包",下载一些程序包:
需要下载如下几个程序包,用于绘图和地图部分的显示:
其Xaml代码如下
<Window x:Class="LoggingScreen.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LoggingScreen" xmlns:oxy="http://oxyplot.org/wpf" xmlns:windowspresentation="clr-namespace:GMap.NET.WindowsPresentation;assembly=GMap.NET.WindowsPresentation"
mc:Ignorable="d"
Title="LoggingScreen V1.0" Height="500" Width="900">
<Window.Background>
<ImageBrush ImageSource="D:\GiteeRepository\csharpe-sutdy\LoggingScreen\bin\Debug\Data\background.png" Stretch="UniformToFill"/>
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="45"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--<Image Grid.RowSpan="6" Grid.ColumnSpan="7" Source="C:\Users\JTL_1\Desktop\background.jpeg" Opacity="0.6"></Image>-->
<Label Grid.ColumnSpan="3" FontSize="30" HorizontalAlignment="Center" Foreground="Green" >
<Label.Background>
<ImageBrush/>
</Label.Background>
全国油气田实时监测大屏
</Label>
<DockPanel Grid.Row="1">
<!--pv1-->
<!--第一个图形-->
<oxy:PlotView Name="pv_production" MouseDoubleClick="pv_production_MouseDoubleClick">
<oxy:PlotView.Background>
<SolidColorBrush Opacity="0.5"/>
</oxy:PlotView.Background>
</oxy:PlotView>
</DockPanel>
<DockPanel Grid.Row="2">
<!--<Label>第二个图像</Label>-->
<!--pv2-->
<oxy:PlotView Name="pv_HChart">
<oxy:PlotView.Background>
<SolidColorBrush Opacity="0.5"/>
</oxy:PlotView.Background>
</oxy:PlotView>
</DockPanel>
<DockPanel Grid.Row="3">
<!--<Label>第三个图像</Label>-->
<oxy:PlotView Name="pv_KLine">
<oxy:PlotView.Background>
<SolidColorBrush Opacity="0.5"/>
</oxy:PlotView.Background>
</oxy:PlotView>
</DockPanel>
<DockPanel Grid.Row="4">
<!--<Label>第四个图像</Label>-->
<oxy:PlotView Name="pv_isoLine">
<oxy:PlotView.Background>
<SolidColorBrush Opacity="0.5"/>
</oxy:PlotView.Background>
</oxy:PlotView>
</DockPanel>
<Label Grid.Row="5" Grid.ColumnSpan="3" FontSize="15" HorizontalAlignment="Left" Foreground="PaleVioletRed">
第29分队 || 队长:袁朗 队员:许三多 吴哲 成才 || 装备:班用95轻机枪 88狙击枪
</Label>
<DockPanel Grid.Row="1" Grid.Column="1">
<Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第①个监控</Label>
<MediaElement Name="media1" MediaEnded="MediaEnd_Replay" ></MediaElement>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="2">
<Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第②个监控</Label>
<MediaElement Name="media2" MediaEnded="MediaEnd_Replay" ></MediaElement>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="1">
<Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第③个监控</Label>
<MediaElement Name="media3" MediaEnded="MediaEnd_Replay"></MediaElement>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="2">
<Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第④个监控</Label>
<MediaElement Name="media4" MediaEnded="MediaEnd_Replay"></MediaElement>
</DockPanel>
<DockPanel Grid.Row="3" Grid.RowSpan="2" Grid.Column="1" Grid.ColumnSpan="2">
<Label Foreground="Orange" DockPanel.Dock="Top" HorizontalAlignment="Left" >中国油气田分布</Label>
<windowspresentation:GMapControl Name="gmap">
</windowspresentation:GMapControl>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="4" Grid.RowSpan="4">
<Label Name="Label_oilInfo" Foreground="Orange" DockPanel.Dock="Top" HorizontalAlignment="Center" FontSize="12">
随钻测井曲线监控
</Label>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<oxy:PlotView Name="pv_depth" Grid.Column="0">
<oxy:PlotView.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</oxy:PlotView.Background>
</oxy:PlotView>
<oxy:PlotView Name="pv_log1" Grid.Column="1">
<oxy:PlotView.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</oxy:PlotView.Background>
</oxy:PlotView>
<oxy:PlotView Name="pv_log2" Grid.Column="2">
<oxy:PlotView.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</oxy:PlotView.Background>
</oxy:PlotView>
<oxy:PlotView Name="pv_log3" Grid.Column="3">
<oxy:PlotView.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</oxy:PlotView.Background>
</oxy:PlotView>
</Grid>
</DockPanel>
<Button x:Name="button_recover" Grid.Column="3" Click="button_recover_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
<Button.Background>
<ImageBrush ImageSource="/恢复.png" Stretch="Uniform" Opacity="0.6"/>
</Button.Background>
<Button.Foreground>
<SolidColorBrush Color="Black" Opacity="0.5"/>
</Button.Foreground>
</Button>
<Button x:Name="button_miniSize" Grid.Column="4" Click="button_miniSize_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
<Button.Background>
<ImageBrush ImageSource="/最小化.png" Stretch="Uniform" Opacity="0.6"/>
</Button.Background>
</Button>
<Button x:Name="button_fullScreen" Grid.Column="5" Click="button_fullScreen_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
<Button.Background>
<ImageBrush ImageSource="/最大化.png" Stretch="Uniform" Opacity="0.6"/>
</Button.Background>
</Button>
<Button x:Name="button_close" Grid.Column="6" Click="button_close_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
<Button.Background>
<ImageBrush ImageSource="/关闭.png" Stretch="Uniform" Opacity="0.6"/>
</Button.Background>
</Button>
<Button x:Name="button_connect" Grid.Row="5" Grid.Column="3" Click="button_connect_Click" Content="" BorderThickness="0,0,0,0">
<Button.Background>
<ImageBrush ImageSource="/连接.png" Stretch="Uniform" Opacity="0.59"/>
</Button.Background>
</Button>
<Button x:Name="button7" Grid.Row="5" Grid.Column="4" Content="" BorderThickness="0">
<Button.Background>
<ImageBrush ImageSource="/未完成.png" Stretch="Uniform" Opacity="0.6"/>
</Button.Background>
</Button>
<Button x:Name="button8" Grid.Row="5" Grid.Column="5" Content="" BorderThickness="0">
<Button.Background>
<ImageBrush ImageSource="/未完成.png" Opacity="0.6" Stretch="Uniform"/>
</Button.Background>
</Button>
<Button x:Name="button9" Grid.Row="5" Grid.Column="6" Content="" BorderThickness="0">
<Button.Background>
<ImageBrush Stretch="Uniform" ImageSource="/未完成.png" Opacity="0.6"/>
</Button.Background>
</Button>
</Grid>
</Window>
3.2 参数模型类(DataPars.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LoggingScreen
{
/// <summary>
/// 专门用于存储数据的类
/// </summary>
public class DataPars
{
/// <summary>
/// 油田名称列表
/// </summary>
public string[] FieldNames { get; set; }
/// <summary>
/// 油气当量列表
/// </summary>
public double[] Productions { get; set; }
/// <summary>
/// 油气类型
/// </summary>
public string[] TypeNames { get; set; }
/// <summary>
/// 类型占比
/// </summary>
public double[] TypePersents { get; set; }
/// <summary>
/// 交易日期
/// </summary>
public DateTime[] ShareData { get; set; }
/// <summary>
/// 开盘价
/// </summary>
public double[] OpenPrice { get; set; }
/// <summary>
/// 收盘价
/// </summary>
public double[] ClosePrice { get; set; }
/// <summary>
/// 最高价
/// </summary>
public double[] HighPrice { get; set; }
/// <summary>
/// 最低价
/// </summary>
public double[] LowPrice { get; set;}
/// <summary>
/// 等值线 X坐标位置
/// </summary>
public double[] XScale { get; set; }
/// <summary>
/// 等值线y坐标位置
/// </summary>
public double[] YScale { get; set;}
/// <summary>
/// 饱和度值 等值线坐标(x,y)上的值
/// </summary>
public double[,] Values { get; set; }
/// <summary>
/// 油田信息
/// </summary>
public List<OilField> OilFields { get; set; }
/// <summary>
/// 曲线信息字典
/// </summary>
public Dictionary<string,LogCurve> LogCurveDic { get; set; }
}
}
3.3 曲线信息类(LogCurve.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LoggingScreen
{
/// <summary>
/// 描述测井曲线信息类
/// </summary>
public class LogCurve
{
/// <summary>
/// 曲线名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 曲线单位
/// </summary>
public string Unit { get; set; }
/// <summary>
/// 起始深度
/// </summary>
public double TopDep { get; set; }
/// <summary>
/// 终止深度
/// </summary>
public double BotDep { get; set; }
/// <summary>
/// 采样间隔
/// </summary>
public double Spacing { get; set; }
/// <summary>
/// 采样点数
/// </summary>
public int PointsNum { get; set; }
/// <summary>
/// 曲线值
/// </summary>
public double[] Values { get; set; }
}
}
3.4 油田信息类(OilField)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LoggingScreen
{
/// <summary>
/// 油田信息类
/// </summary>
public class OilField
{
/// <summary>
/// 油田名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 油田所在中心城市名字
/// </summary>
public string CityName { get; set; }
/// <summary>
/// 油田位置经度坐标
/// </summary>
public double Longitude { get; set; }
/// <summary>
/// 油田位置纬度坐标
/// </summary>
public double Latitude { get; set; }
/// <summary>
/// 油田的当期产量
/// </summary>
public double Production { get; set; }
}
}
3.5 UI后台代码(MainWindow.xaml.cs)
using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsPresentation;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using OxyPlot.Wpf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LoggingScreen
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
// 全局参数对象
DataPars dp = new DataPars();
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// 连接数据按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_connect_Click(object sender, RoutedEventArgs e)
{
// 绘制第一个图形 横向柱状图, 全国油气产量前10名
RankingPlot();
// 绘制第二个图形,油气类型占比环形图
DoughnutChartPlot();
// 绘制第三个图形,原油价格走势k线图 (蜡烛图)
KLineChartPlot();
// 绘制第四个图形,等值线图
IsoLinePlot();
// 显示监控设备
ShowMedia();
// 显示地图
MapPlot();
}
/// <summary>
/// 绘制地图
/// </summary>
private void MapPlot()
{
// 指定地图的缓存地址
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
string cachePath = basePath + "MapData";
gmap.CacheLocation = cachePath;
// 指定地图的提供着(用的内置的微软的卫星地图) BingSatelLiteMap
gmap.MapProvider = GMapProviders.CzechGeographicMap;
// 设置地图初始化信息
gmap.MinZoom = 2; // 最小缩放级别
gmap.MaxZoom = 16; // 最大缩放级别
gmap.Zoom = 4; // 当前缩放级别
gmap.ShowCenter = true; // 设置显示中心十字
gmap.DragButton = MouseButton.Right; // 指定拖拽的键(鼠标右键拖拽)
gmap.Position = new PointLatLng(34.8, 104.7); // 指定当前位置
// 加载表格数据
string filePath = AppDomain.CurrentDomain.BaseDirectory + @"Data\\油田位置及当期产量.csv";
string[] strArray = File.ReadAllLines(filePath, Encoding.Default);
dp.OilFields = new List<OilField>();
for (int i = 0; i < strArray.Length - 1; i++)
{
string[] strings = strArray[i + 1].Split(',');
OilField of = new OilField();
of.Name = strings[0];
of.Longitude = double.Parse(strings[1]);
of.Latitude = double.Parse(strings[2]);
of.Production = double.Parse(strings[3]);
of.CityName = strings[4];
dp.OilFields.Add(of);
}
// 标注数据到地图上
// 首先清空原来的数据
gmap.Markers.Clear();
// 定义显示的点的直径的最大值和最小值
double showMin = 5.0;
double showMax = 30.0;
double proMin = 999999; // 定义产量的最小值
double proMax = -999999; // 定义产量的最大值
for (int i = 0; i < dp.OilFields.Count; i++)
{
if (dp.OilFields[i].Production < proMin)
{
// 更新最小值
proMin = dp.OilFields[i].Production;
}
if (dp.OilFields[i].Production > proMax)
{
// 更新最大值
proMax = dp.OilFields[i].Production;
}
}
double step = (showMax - showMin) / (proMax - proMin); //步长
// 2. 通过循环添加每个油田的信息
for (int i = 0; i < dp.OilFields.Count; i++)
{
// 计算显示的大小
double showSize = showMin + (dp.OilFields[i].Production - proMin) * step;
// 注意参数的顺序,这里是先纬度再经度
PointLatLng pll = new PointLatLng(dp.OilFields[i].Latitude, dp.OilFields[i].Longitude);
GMapMarker gmm = new GMapMarker(pll);
// 设置显示的形状 以⚪的方式显示
Ellipse e = new Ellipse();
e.Width = showSize;
e.Height = showSize;
e.Fill = new RadialGradientBrush(Colors.Blue, Colors.Red); // 设置填充色
// 设置当鼠标停留在点上时候,显示的信心
e.ToolTip = dp.OilFields[i].Name + ": " + dp.OilFields[i].Production.ToString() + "万吨";
// 添加鼠标左键单击点时的事件,绑定
e.MouseDown += E_MouseDown;
// 设置鼠标移动到点上时鼠标的形状改变
e.Cursor = Cursors.Hand;
// 设置选中一个点后,其他数据区实时显示这个油田的信息(Binding)
e.Tag = dp.OilFields[i];
gmm.Shape = e;
gmap.Markers.Add(gmm);
}
}
/// <summary>
/// 点击信息点时的操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void E_MouseDown(object sender, MouseButtonEventArgs e)
{
// 当点击另外一个点时,恢复上一个点的状态
for (int i = 0; i < gmap.Markers.Count; i++)
{
(gmap.Markers[i].Shape as Ellipse).StrokeThickness = 0;
}
Ellipse eNow = sender as Ellipse;
eNow.Stroke = Brushes.GreenYellow;
eNow.StrokeThickness = 3;
OilField ofNow = (OilField)eNow.Tag;
Label_oilInfo.Content = ofNow.Name + ":随钻测井曲线监控";
// 画出当前选中油田的测井曲线
PlotLWDCurves(ofNow);
}
/// <summary>
/// 绘制ofNow的随钻测井曲线
/// </summary>
/// <param name="ofNow">OilField ofNow</param>
private void PlotLWDCurves(OilField ofNow)
{
// step1 初始化测井曲线字典
dp.LogCurveDic = new Dictionary<string, LogCurve>();
// step2 获取文件所在路径
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string filePath = "";
// step3 判断选中的油田,
switch (ofNow.Name)
{
case "塔里木油田":
{
filePath = basePath + @"Data\\测井曲线_塔里木油田.csv";
};
break;
case "长庆油田":
{
filePath = basePath + @"Data\\测井曲线_长庆油田.csv";
};
break;
case "南海西部油田":
{
filePath = basePath + @"Data\\测井曲线_南海西部油田.csv";
};
break;
case "南海东部油田":
{
filePath = basePath + @"Data\\测井曲线_南海东部油田.csv";
};
break;
case "胜利油田":
{
filePath = basePath + @"Data\\测井曲线_胜利油田.csv";
};
break;
case "延长石油":
{
filePath = basePath + @"Data\\测井曲线_延长石油.csv";
};
break;
case "渤海油田":
{
filePath = basePath + @"Data\\测井曲线_渤海油田.csv";
};
break;
case "新疆油田":
{
filePath = basePath + @"Data\\测井曲线_新疆油田.csv";
};
break;
case "西南油气田":
{
filePath = basePath + @"Data\\测井曲线_西南油气田.csv";
};
break;
case "大庆油田":
{
filePath = basePath + @"Data\\测井曲线_大庆油田.csv";
};
break;
default:
filePath = basePath + @"Data\\测井曲线_默认.csv";
break;
}
// step4 取出文件中数据
string[] strArray = File.ReadAllLines(filePath, Encoding.UTF8);
string[] nameStr = strArray[0].Split(','); // 拆分第一行,第一行是曲线的名字
for (int i = 0; i < nameStr.Length-1; i++)
{
LogCurve lc = new LogCurve();
lc.Name = nameStr[i+1];
lc.Values = new double[strArray.Length - 1]; // 已经知道了值的个数,就先分配内存了
lc.TopDep = double.Parse(strArray[1].Split(',').First()); // 第二行第一个是起始深度
lc.BotDep = double.Parse(strArray.Last().Split(',').First()); // 最后一行第一个是起始深度
lc.PointsNum = strArray.Length - 1; // 采样点数 = 文件总行数 - 1
lc.Spacing = (lc.BotDep - lc.TopDep) / (lc.PointsNum - 1); // 采样间隔
// 添加到曲线字典中
dp.LogCurveDic.Add(lc.Name, lc);
}
// step5 读出数据
for (int i = 0; i < strArray.Length-1; i++)
{
string[] valueStr = strArray[i + 1].Split(',');
double depNow = double.Parse(valueStr[0]); // 每一行的第一个数就是当前深度
// 开始读取每一行的值(重点检查)
for (int j = 0; j < valueStr.Length-1; j++)
{
LogCurve lcNow = dp.LogCurveDic.ElementAt(j).Value;
lcNow.Values[i] = double.Parse(valueStr[j + 1]);
}
}
// 深度绘制
pv_depth.Model = new PlotModel(); // 1 初始化一个model
//pv_depth.Model.PlotAreaBorderThickness = new OxyThickness(1); // 去除边框
pv_depth.Model.PlotAreaBorderColor = OxyColors.GreenYellow;
LinearAxis la = new LinearAxis(); // 2 只用添加一个线性的y轴
double depMin = dp.LogCurveDic.First().Value.TopDep; // 获取起始深度
double depMax = dp.LogCurveDic.First().Value.BotDep; // 获取终止深度
la.Position = AxisPosition.Right; // 设置放置的位置在右边
la.Minimum = depMin; // 设置轴的最小深度
la.Maximum = depMax; // 设置轴的最大深度
la.StartPosition = 1; // 因为深度是往下变大的,0是正常向上,1是往下变大
la.EndPosition = 0;
la.TicklineColor = OxyColors.GreenYellow; // 设置轴的颜色
la.TextColor = OxyColors.GreenYellow; // 设置标识的颜色
pv_depth.Model.Axes.Add(la); // 把轴放到model里
pv_depth.Model.InvalidatePlot(true); // 刷新一下
// 岩性组曲线绘制(GR SP CAL)
pv_log1.Model = new PlotModel();
pv_log1.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
LinearAxis la1 = new LinearAxis(); // 添加岩性组的y轴
la1.Position = AxisPosition.Right;
la1.StartPosition = 1;
la1.EndPosition = 0;
la1.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
pv_log1.Model.Axes.Add(la1);
PlotLogCurve(pv_log1, dp.LogCurveDic["GR"], true, OxyColors.AliceBlue);
PlotLogCurve(pv_log1, dp.LogCurveDic["SP"], true, OxyColors.Red);
PlotLogCurve(pv_log1, dp.LogCurveDic["CAL"], true, OxyColors.Yellow);
// 添加图例
Legend l1 = new Legend() { LegendPosition = LegendPosition.TopCenter,LegendTextColor= OxyColors.GreenYellow };
l1.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
pv_log1.Model.Legends.Add(l1);
pv_log1.Model.InvalidatePlot(true);
// 孔隙度组曲线绘制(GR SP CAL)
pv_log2.Model = new PlotModel();
pv_log2.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
LinearAxis la2 = new LinearAxis(); // 添加岩性组的y轴
la2.Position = AxisPosition.Right;
la2.StartPosition = 1;
la2.EndPosition = 0;
la2.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
pv_log2.Model.Axes.Add(la2);
PlotLogCurve(pv_log2, dp.LogCurveDic["DEN"], true,OxyColors.Blue);
PlotLogCurve(pv_log2, dp.LogCurveDic["AC"], true,OxyColors.Red);
PlotLogCurve(pv_log2, dp.LogCurveDic["CNL"], true,OxyColors.Orange);
Legend l2 = new Legend() { LegendPosition = LegendPosition.TopCenter, LegendTextColor = OxyColors.GreenYellow };
l2.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
pv_log2.Model.Legends.Add(l2);
pv_log2.Model.InvalidatePlot(true);
// 电阻率组曲线绘制(GR SP CAL)
pv_log3.Model = new PlotModel();
pv_log3.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
LinearAxis la3 = new LinearAxis(); // 添加岩性组的y轴
la3.Position = AxisPosition.Right;
la3.StartPosition = 1;
la3.EndPosition = 0;
la3.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
pv_log3.Model.Axes.Add(la3);
PlotLogCurve(pv_log3, dp.LogCurveDic["RILD"], false,0.1,100,OxyColors.Brown);
PlotLogCurve(pv_log3, dp.LogCurveDic["RILM"], false, 0.1, 100, OxyColors.MistyRose);
PlotLogCurve(pv_log3, dp.LogCurveDic["RFOC"], false, 0.1, 100, OxyColors.Chartreuse);
Legend l3 = new Legend() { LegendPosition = LegendPosition.TopCenter, LegendTextColor = OxyColors.GreenYellow };
l3.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
pv_log3.Model.Legends.Add(l3);
pv_log3.Model.InvalidatePlot(true);
}
/// <summary>
/// 绘制测井曲线的方法
/// </summary>
/// <param name="pv_log">绘图位置</param>
/// <param name="logCurve">曲线</param>
/// <param name="isLinear">是否为线性轴</param>
/// <param name="color">绘图颜色</param>
private void PlotLogCurve(PlotView pv_log, LogCurve logCurve, bool isLinear,OxyColor color)
{
// 添加x轴
if (isLinear)
{
LinearAxis la = new LinearAxis(); // 线性轴
la.Position = AxisPosition.Bottom;
la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
la.IsAxisVisible = false; // 隐藏
pv_log.Model.Axes.Add(la);
}
else
{
LogarithmicAxis la = new LogarithmicAxis(); // 对数轴
la.Position = AxisPosition.Bottom;
la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
la.IsAxisVisible = false; // 隐藏
pv_log.Model.Axes.Add(la);
}
// 添加曲线
LineSeries ls = new LineSeries();
ls.Title = logCurve.Name;
ls.Color = color;
for (int i = 0; i < logCurve.PointsNum; i++)
{
double x = logCurve.Values[i]; // x坐标上是值
double y = logCurve.TopDep + logCurve.Spacing * i; // y坐标上是深度 = 起始深度+采样间隔×点数
DataPoint dp = new DataPoint(x, y);
ls.Points.Add(dp);
}
ls.XAxisKey = logCurve.Name;
pv_log.Model.Series.Add(ls);
}
/// <summary>
/// 绘制测井曲线的方法重载(适用于电阻率,含指定的x轴的最大最小刻度)
/// </summary>
/// <param name="pv_log">画板</param>
/// <param name="logCurve">曲线</param>
/// <param name="isLinear">是否为线性</param>
/// <param name="xMin">x轴最小刻度</param>
/// <param name="xMax">x轴最大刻度</param>
/// <param name="color">绘图颜色</param>
private void PlotLogCurve(PlotView pv_log, LogCurve logCurve, bool isLinear,double xMin,double xMax,OxyColor color)
{
// 添加x轴
if (isLinear)
{
LinearAxis la = new LinearAxis(); // 线性轴
la.Position = AxisPosition.Bottom;
la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
la.IsAxisVisible = false; // 隐藏
la.Minimum = xMin;
la.Maximum = xMax;
pv_log.Model.Axes.Add(la);
}
else
{
LogarithmicAxis la = new LogarithmicAxis(); // 对数轴
la.Position = AxisPosition.Bottom;
la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
la.IsAxisVisible = false; // 隐藏
la.Minimum = xMin;
la.Maximum = xMax;
pv_log.Model.Axes.Add(la);
}
// 添加曲线
LineSeries ls = new LineSeries();
ls.Title = logCurve.Name;
ls.Color = color;
for (int i = 0; i < logCurve.PointsNum; i++)
{
double x = logCurve.Values[i]; // x坐标上是值
double y = logCurve.TopDep + logCurve.Spacing * i; // y坐标上是深度 = 起始深度+采样间隔×点数
DataPoint dp = new DataPoint(x, y);
ls.Points.Add(dp);
}
ls.XAxisKey = logCurve.Name;
pv_log.Model.Series.Add(ls);
}
/// <summary>
/// 横向柱状图的绘制(棒状图)
/// </summary>
private void RankingPlot()
{
// step1 获取程序的启动路径,就是debug里边
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
// 由程序启动路径获得csv文件的路径
string csvfile = basePath + @"Data\\油气产量前10名.csv";
// step2 读取数据
string[] strArray = File.ReadAllLines(csvfile, Encoding.Default); // 把csv文件中的数据一行一行的读取出来
dp.FieldNames = new string[strArray.Length - 1]; // 存放油田名称的数组 第一行是油田名称 油田当量等等..所以总行数减1是油田个数
dp.Productions = new double[strArray.Length - 1]; // 存放油田产量的数组
// 开始取数据,循环的次数就是元素的个数
for (int n = 0; n < strArray.Length - 1; n++)
{
// 取出当前行
string tempStr = strArray[n + 1];
// 对当前行进行拆分
string[] strings = tempStr.Split(','); // 长庆油田 6565 万吨
// 油田名 是拆分后的第一个元素
dp.FieldNames[n] = strings[0];
// 油气当量 是拆分后的第二个元素
dp.Productions[n] = double.Parse(strings[1]);
}
// step3 绘制图形
// 首先给pv_production初始化一个model
pv_production.Model = new PlotModel();
// pv_production.Model.Title = "油气当量前10名";
pv_production.Model.PlotAreaBorderThickness = new OxyPlot.OxyThickness(0); //设置边框为0,就是不显示边框
// 设置背景颜色,可以在UI界面更改
// pv_production.Model.Background = OxyColors.Black;
// pv_production.Background = new SolidColorBrush(Colors.Blue); //设置背景颜色
pv_production.Background.Opacity = 0; // 设置背景颜色的透明度
// 分类轴
CategoryAxis ca = new CategoryAxis();
// 指定轴的位置
ca.Position = AxisPosition.Left;
// 设置分类轴的标签(Labels是可读的属性,只能通过这样塞进去,不能直接赋值) ca.Labels = dp.FieldNames.ToList(); 这样是错误的
for (int n = 0; n < dp.FieldNames.Length; n++)
{
ca.Labels.Add(dp.FieldNames[n]);
}
// 设置不显示轴的刻度,但是要显示名称
// ca.IsAxisVisible = false; // 这种方式会隐藏刻度,也会隐藏名称
ca.TickStyle = TickStyle.None;
// 设置y轴显示的颜色
ca.TextColor = OxyColors.GreenYellow;
// 把调整好的轴添加到pv_production里边
pv_production.Model.Axes.Add(ca);
// 线性轴,用于标注产量的值
LinearAxis la = new LinearAxis();
la.Position = AxisPosition.Bottom;
la.IsAxisVisible = false; // 设置不显示轴的刻度
pv_production.Model.Axes.Add(la);
// 放数据进去(横状的柱状图)
BarSeries bs = new BarSeries();
for (int n = 0; n < dp.FieldNames.Length; n++)
{
BarItem bt = new BarItem(dp.Productions[n]);
bs.Items.Add(bt);
}
bs.LabelFormatString = "{0} 吨"; // 设置横向柱状图信息
bs.LabelPlacement = LabelPlacement.Inside;
bs.FillColor = OxyColors.CadetBlue;
bs.TextColor = OxyColors.MediumVioletRed;
pv_production.Model.Series.Add(bs);
// step5 刷新一下视图
pv_production.Model.InvalidatePlot(true);
}
/// <summary>
/// 油气类型占比环形图
/// </summary>
private void DoughnutChartPlot()
{
// step1 获得文件路径
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
string csvfile = basePath + @"Data\\不同油气类型占比.csv";
// step2 读取数据
string[] strArray = File.ReadAllLines(csvfile,Encoding.Default);
dp.TypeNames = new string[strArray.Length - 1];
dp.TypePersents = new double[strArray.Length - 1];
for (int i = 0; i < strArray.Length - 1; i++)
{
string strTemp = strArray[i+1];
string[] strs = strTemp.Split(',');
dp.TypeNames[i] = strs[0];
dp.TypePersents[i] = double.Parse(strs[1]);
}
// step3 初始化一个新Model
pv_HChart.Model = new PlotModel();
pv_HChart.Model.PlotAreaBorderThickness = new OxyThickness(0);
// pv_HChart.Model.Background = OxyColor.FromArgb(0,0, 0, 0); // 第一个参数是透明度,255是100%,后面三个参数是rgb
pv_HChart.Model.PlotMargins = new OxyThickness(5); //设置绘图时的边距,向里收缩5个单位
pv_HChart.Background.Opacity = 0; // 设置背景颜色的透明度
// stpe4 添加饼图
PieSeries ps = new PieSeries();
for (int i = 0; i < dp.TypeNames.Length; i++)
{
PieSlice bt = new PieSlice(dp.TypeNames[i], dp.TypePersents[i]); // 饼图的组成是一个一个扇形
ps.Slices.Add(bt);
}
// 修改饼图为环形图
ps.InnerDiameter = 0.45; //默认是1,可以自己修改一下看一下效果
ps.InsideLabelFormat = "{0}"; // 设置内标签 0是数值,1是名称
ps.OutsideLabelFormat = "{1}"; // 设置外标签
ps.TextColor = OxyColors.GreenYellow; // 设置标签颜色
// step5 添加饼图到Model的数据序列里
pv_HChart.Model.Series.Add(ps);
// step6 刷新
pv_HChart.Model.InvalidatePlot(true);
}
/// <summary>
/// 原油价格走势k线图 (蜡烛图)
/// </summary>
private void KLineChartPlot()
{
// step1 获取文件所在路径
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
string csvPath = basePath + @"Data\\石油价格变化.csv";
// step2 读取数据
string[] strArray = File.ReadAllLines(csvPath,Encoding.Default);
int dataLines = strArray.Length - 1;
dp.ShareData = new DateTime[dataLines];
dp.OpenPrice = new double[dataLines];
dp.ClosePrice = new double[dataLines];
dp.HighPrice = new double[dataLines];
dp.LowPrice = new double[dataLines];
for (int i = 0; i < dataLines; i++)
{
string strTemp = strArray[i + 1];
string[] strs = strTemp.Split(new char[] { ',' }); // 这种拆分方式会严重影响性能
dp.ShareData[i] = DateTime.Parse(strs[0]);
dp.OpenPrice[i] = double.Parse(strs[1]);
dp.ClosePrice[i] = double.Parse(strs[2]);
dp.HighPrice[i] = double.Parse(strs[3]);
dp.LowPrice[i] = double.Parse(strs[4]);
}
// step3 添加新Model
pv_KLine.Model = new PlotModel();
pv_KLine.Model.PlotAreaBorderThickness = new OxyThickness(0);
// pv_KLine.Model.PlotAreaBackground = OxyColor.FromArgb(0, 0, 0, 0);
// pv_KLine.Model.PlotMargins = new OxyThickness(1);
pv_KLine.Background.Opacity = 0; // 设置背景颜色的透明度
// step4 设置x轴
DateTimeAxis ca = new DateTimeAxis();
ca.Position = AxisPosition.Bottom;
ca.TextColor = OxyColors.GreenYellow;
ca.TickStyle = TickStyle.None;
pv_KLine.Model.Axes.Add(ca); //添加到Model
// step5 设置y轴
LinearAxis la = new LinearAxis();
la.Position = AxisPosition.Left;
la.IsAxisVisible = false;
pv_KLine.Model.Axes.Add(la);
// stpe6 绘制蜡烛图
CandleStickSeries css = new CandleStickSeries();
for (int i = 0; i < dataLines; i++)
{
// 看css.Items是什么类型就往里塞什么类型(要把日期转换一下,ToOADate()后才是double类型)
HighLowItem hli = new HighLowItem(dp.ShareData[i].ToOADate(), dp.HighPrice[i], dp.LowPrice[i], dp.OpenPrice[i], dp.ClosePrice[i]);
css.Items.Add(hli);
}
// step7 添加到model并刷新
pv_KLine.Model.Series.Add(css);
pv_KLine.Model.InvalidatePlot(true);
}
/// <summary>
/// 饱和度等值线图
/// </summary>
private void IsoLinePlot()
{
// step1 获取文件路径
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
string filePath = basePath + @"Data//重油藏饱和度等值线.csv";
// step2 读取数据
string[] strArray = File.ReadAllLines(filePath);
// step3 初始化存储结构,赋予相应个数的存储单元(这里更麻烦一点,因为数据在csv中的存储结构发生了变化)
dp.YScale = new double[strArray.Length - 1]; //y轴坐标的个数就是数据的行数减一
string[] strX = strArray[0].Split(',');
dp.XScale = new double[strX.Length - 1]; // x轴坐标的个数是第一行拆出来的字符串的个数减一
dp.Values = new double[dp.YScale.Length, dp.XScale.Length];
// step4 存值进去(重点检查,可能会出错)
for (int i = 0; i < dp.XScale.Length; i++)
{
dp.XScale[i] = double.Parse(strX[i + 1]); // 给X轴存值
}
for (int i = 0; i < dp.YScale.Length; i++)
{
string[] strTemp = strArray[i + 1].Split(',');
dp.YScale[i] = double.Parse(strTemp[0]); // 给y轴存值
for (int j = 0; j < dp.XScale.Length; j++)
{
dp.Values[i, j] = double.Parse(strTemp[j + 1]);
}
}
// step5 初始化一个新model
pv_isoLine.Model = new PlotModel();
pv_isoLine.Model.PlotAreaBorderThickness = new OxyThickness(0);
// pv_isoLine.Model.Background = OxyColor.FromArgb(0, 0, 0, 0);
pv_isoLine.Background.Opacity = 0; // 设置背景颜色的透明度
// step6 添加笛卡尔坐标轴 x轴
LinearAxis lax = new LinearAxis();
lax.Position = AxisPosition.Bottom;
lax.TextColor = OxyColors.GreenYellow;
pv_isoLine.Model.Axes.Add(lax);
// step7 添加笛卡尔坐标轴 y轴
LinearAxis lay = new LinearAxis();
lay.Position = AxisPosition.Left;
lay.TextColor = OxyColors.GreenYellow;
pv_isoLine.Model.Axes.Add(lay);
// step9 添加等值线图
ContourSeries cs = new ContourSeries();
cs.Data = dp.Values;
cs.RowCoordinates = dp.XScale;
cs.ColumnCoordinates = dp.YScale;
//
int count = 30; //设置网格数,这里相当于把数值等分为count份
cs.ContourLevels = new double[count]; // 也就是画15根等值线
double step = (dp.Values.Max2D() - dp.Values.Min2D()) / (count - 1); //步长 = (最大值-最小值)/(等值线数-1)
for (int i = 0; i < count; i++)
{
cs.ContourLevels[i] = (i + 0.5) * step;
}
cs.ContourColors = OxyPalettes.Jet(count).Colors.ToArray();
// step8 添加颜色轴
LinearColorAxis lca = new LinearColorAxis();
lca.Palette = OxyPalettes.Jet(count);
lca.Position = AxisPosition.Right;
pv_isoLine.Model.Axes.Add(lca);
// step9 加到model里并刷新
pv_isoLine.Model.Series.Add(cs);
pv_isoLine.Model.InvalidatePlot(true);
}
/// <summary>
/// 双击 放大图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pv_production_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// 获得临时的绘图面板
PlotView pvTemp = sender as PlotView;
// 找到pvTemp的上一级控件(因为拿到过后放大后要还原,就是还给他的父级控件)
DockPanel dp = (DockPanel)pvTemp.Parent;
dp.Children.Clear();
// 再把他弹出来
Window newWindow = new Window();
Grid newGrid = new Grid();
newGrid.Background = Brushes.Black;
newGrid.Children.Add(pvTemp);
newWindow.Content = newGrid;
newWindow.ShowDialog();
// 关闭后还给父级控件
newGrid.Children.Clear();
dp.Children.Add(pvTemp);
}
/// <summary>
/// 全屏按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_fullScreen_Click(object sender, RoutedEventArgs e)
{
// 去掉原来的标题头
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
}
/// <summary>
/// 恢复按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_recover_Click(object sender, RoutedEventArgs e)
{
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.WindowState = WindowState.Normal;
}
/// <summary>
/// 最小化按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_miniSize_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// 关闭按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_close_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
/// <summary>
/// 显示监控设备
/// </summary>
private void ShowMedia()
{
string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
string Monitor1 = basePath + @"Data\\监控设备1.mp4";
string Monitor2 = basePath + @"Data\\监控设备2.mp4";
string Monitor3 = basePath + @"Data\\监控设备3.mp4";
string Monitor4 = basePath + @"Data\\监控设备4.mp4";
VideoPlay(media1, Monitor1);
VideoPlay(media2, Monitor2);
VideoPlay(media3, Monitor3);
VideoPlay(media4, Monitor4);
}
/// <summary>
/// 提取出来的播放监控的方法
/// </summary>
/// <param name="me">监控设备名</param>
/// <param name="path">监控所在路径</param>
public void VideoPlay(MediaElement me,string path)
{
me.Source = new Uri(path);
me.Stretch = Stretch.Fill; // 设置一下画面填充
me.LoadedBehavior = MediaState.Manual; // 把播放模式设置为手动的,就可以循环播放了
me.Volume = 0; //设置播放的声音为0
me.Play();
}
// 重复播放事件
private void MediaEnd_Replay(object sender, RoutedEventArgs e)
{
MediaElement me = sender as MediaElement;
me.Position = TimeSpan.Zero; //回到最开始的时刻
me.Play();
}
}
}
4 功能介绍
// TODO 2023.0828
5 总结
本次实训还有很多地方值得改进,如在绘图部分很多地方的代码是相同的,还有几个按钮的功能没有实现,希望在以后闲下来的时候完成这个部分的优化。