LineSeries--api--form

本文介绍LineSeries图表的构造函数、属性、方法等核心内容。详细解释了如何通过配置不同的属性来定制LineSeries图表的表现形式,如线条样式、填充颜色、数据点半径等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

公共方法
  方法 定义方
   
构造函数。
LineSeries
受保护的方法
  方法 定义方
   
自定义用于表示图表的项呈示器实例。
LineSeries
   
[override]
LineSeries
样式

Styles are either common or associated with a specific theme. If the style is common, it can be used with any theme. If a style is associated with a specific theme, it can only be used if your application uses that theme.

常见样式
  样式 说明 定义方
   
adjustedRadius
类型: Number 格式: Length CSS 继承: yes
指定在突显或选择图表项目时其半径要增加的像素数目。 默认值为 2.
LineSeries
   
fill
类型: mx.graphics.IFill CSS 继承: no
设置此数据系列的填充。您可以指定实现 IFill 接口的对象,也可以指定代表纯颜色值的数字。您还可以使用 CSS 指定实心填充。
LineSeries
   
fills
类型: Array CSS 继承: no
指定用于定义系列中每个项目填充内容的填充对象数组。它优先于 fill 样式属性。如果自定义方法是由 fillFunction 属性指定的,则它优先于此 Array。如果为每个项目提供的 Array 元素不足,则 Flex 将从 Array 的开始部分重复填充。

使用 CSS 设置此属性的值:

    LineSeries {
      fills:#CC66FF, #9966CC, #9999CC;
    }
   

使用 MXML 设置此属性的值:

    <mx:LineSeries ... >
     <mx:fills>
      <mx:SolidColor color="0xCC66FF"/>
      <mx:SolidColor color="0x9966CC"/>
      <mx:SolidColor color="0x9999CC"/>
     </mx:fills>
    </mx:LineSeries>
   

如果您指定了 fills 属性并希望包含 Legend 控件,则必须手动创建 Legend 控件,然后向其中添加 LegendItems。

LineSeries
   
form
类型: String CSS 继承: no
指定图表的线条类型。可能的值包括:
  • "curve":在数据点之间绘制曲线。
  • "horizontal":在第二个点的 y 坐标处仅绘制从第一个点的 x 坐标到第二个点的 x 坐标的垂直线。对每个数据点重复此操作。
  • "vertical":在第二个点的 x 坐标处仅绘制从第一个点的 y 坐标到第二个点的 y 坐标的垂直线。对每个数据点重复此操作。
  • "segment":将线条绘制为连接的线段,使它们呈一定角度以便连接系列的各个数据点。
  • "step":将线条绘制为水平线段。在第一个数据点处,先绘制一条水平线,然后绘制到第二个点的垂直线,并对每个数据点重复此操作。
  • "reverseStep":将线条绘制为水平线段。在第一个数据点处,先绘制一条垂直线,然后绘制到第二个点的水平线,并对每个数据点重复此操作。
默认值为 "segment"
LineSeries
   
itemRenderer
类型: mx.core.IFactory CSS 继承: no
表示系列将用来表示图表上单个项目的类工厂。针对图表中的每个元素实例化此类一次。用作 itemRenderer 的类将实现 IFlexDisplayObject、ISimpleStyleClient 和 IDataRenderer 接口。为 data 属性分配外观实例呈示的 chartItem。
LineSeries
   
legendMarkerRenderer
类型: mx.core.IFactory CSS 继承: no
系列用来在任何关联图例中呈示该系列标记的类。如果此样式为 null,则大多数系列将默认改为使用其 itemRenderer 作为图例标记外观。用作图例标记的类应实现 IFlexDisplayObject 接口、ISimpleStyleClient 和 IDataRenderer 接口(可选)。如果用作图例标记的类实现 IDataRenderer 接口,则为该 data 属性分配 LegendData 实例。
LineSeries
   
lineSegmentRenderer
类型: mx.core.IFactory CSS 继承: no
表示系列用于表示系列中各线段的类的类工厂。此类针对系列的每个不同线段都进行一次实例化。用作 lineSegmentRenderer 的类应实现 IFlexDisplayObject、ISimpleStyleClient 和 IDataRenderer 接口。为 data 属性分配描述要呈示的线段的 mx.charts.series.items.LineSeriesSegment 实例。
LineSeries
   
lineStroke
类型: mx.graphics.IStroke CSS 继承: no
设置实际线段的笔触。LineChart 控件的默认值为橙色 (0xE48701)。在 CartesianChart 控件中使用的 LineSeries 的默认颜色为黑色 (0x000000)。宽度的默认值为 3。
LineSeries
   
radius
类型: Number 格式: Length CSS 继承: no
指定数据点的图表元素的半径(以像素为单位)。仅当使用 itemRenderer 属性指定项呈示器时,才会应用此属性。您可以通过 MXML 或使用样式来指定 itemRenderer。 默认值为 4.
LineSeries
   
stroke
类型: mx.graphics.IStroke CSS 继承: no
设置此数据系列的笔触样式。必须指定某个 Stroke 对象来定义笔触。
LineSeries
Spark 主题样式
Halo 主题样式
属性详细信息

fillFunction

属性
fillFunction:Function

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

指定返回系列中当前图表项目的填充的方法。如果已设置此属性,则自定义填充函数返回的值将优先于 fill 和 fills 样式属性。但是,如果返回 null,则在该顺序中将优先选择fills 和 fill

自定义 fillFunction 包含以下签名:

     function_name (item:ChartItem, index:Number):IFill { ... }
     
item 表示对要呈示的图表项目的引用。 index 表示 renderData 的缓存中的图表项目的索引。这与图表的数据提供程序的索引不同,因为后者是基于 x、y 和 z 值进行排序的。此函数将返回实现  IFill 接口的对象。

自定义的 fillFunction 的使用示例是基于某些阈值返回填充。



实现 
    public function get fillFunction():Function
    public function set fillFunction(value:Function):void

示例 
如何使用本示例 
     public function myFillFunction(item:ChartItem, index:Number):IFill {
          var curItem:LineSeriesItem = LineSeriesItem(item);
          if (curItem.yNumber > 10)
              return(new SolidColor(0x123456, .75));
          else
              return(new SolidColor(0x563412, .75));
     }
     

如果您为图表系列指定了自定义填充函数,并希望包含 Legend 控件,则必须手动创建 Legend 控件,然后向其中添加 LegendItems。

horizontalAxis

属性 
horizontalAxis:IAxis

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

为 x 轴上的项定义标签、刻度线和数据位置。使用 LinearAxis 类或 CategoryAxis 类可以设置 horizontalAxis 的属性,将其作为 MXML 中的子标签,或者在 ActionScript 中创建一个 LinearAxis 或 CategoryAxis 对象。



实现 
    public function get horizontalAxis():IAxis
    public function set horizontalAxis(value:IAxis):void

interpolateValues

属性 
interpolateValues:Boolean

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

指定如何表示丢失的数据。

设置为 false 可在丢失值处换行。设置为 true 可通过插补丢失的值绘制一条连续线条。

默认值为 false。



实现 
    public function get interpolateValues():Boolean
    public function set interpolateValues(value:Boolean):void

items

属性 
items:Array  [只读] [override]

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1



实现 
    override public function get items():Array

itemType

属性 
itemType:Class  [只读]

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

此系列用于表示各项目的 ChartItem 的子类型。如果子类需要在项目中存储其他信息,则可以覆盖和返回更特定化的类。



实现 
    protected function get itemType():Class

lineSegmentType

属性 
lineSegmentType:Class  [只读]

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

此系列使用该类存储表示线段必需的所有数据。如果子类需要存储其他信息以供显示,则可以覆盖和返回更特定化的类。



实现 
    protected function get lineSegmentType():Class

moduleFactory

属性 
moduleFactory:IFlexModuleFactory[override]

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1



实现 
    override public function get moduleFactory():IFlexModuleFactory
    override public function set moduleFactory(value:IFlexModuleFactory):void

radius

属性 
radius:Number

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

指定数据点的图表元素的半径(以像素为单位)。仅当使用 itemRenderer 属性指定项呈示器时,才会应用此属性。您可以通过 MXML 或使用样式来指定 itemRenderer

默认值为 0。



实现 
    public function get radius():Number
    public function set radius(value:Number):void

renderDataType

属性 
renderDataType:Class  [只读]

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

此系列用于存储所有必须呈示的数据的 ChartRenderData 子类型。如果子类需要存储其他信息以供显示,则可以覆盖和返回更特定化的类。



实现 
    protected function get renderDataType():Class

sortOnXField

属性 
sortOnXField:Boolean

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

在呈示之前请求从左到右对线段数据点进行排序。

默认情况下,LineSeries 从左到右显示点。将此属性设置为 false 可使项目按照在数据提供程序中的显示顺序呈示。

默认值为 true。



实现 
    public function get sortOnXField():Boolean
    public function set sortOnXField(value:Boolean):void

verticalAxis

属性 
verticalAxis:IAxis

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

为 y 轴上的项目定义标签、刻度线和数据位置。使用 LinearAxis 类或 CategoryAxis 类可将 verticalAxis 的属性设置为 MXML 中的子标签,也可以在 ActionScript 中创建 LinearAxis 或 CategoryAxis 对象。



实现 
    public function get verticalAxis():IAxis
    public function set verticalAxis(value:IAxis):void

xField

属性 
xField:String

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

指定用于确定每个数据点的 x 轴位置的数据提供程序字段。如果为 null,则数据点将按照在数据提供程序中的显示顺序呈示。

默认值为 null。



实现 
    public function get xField():String
    public function set xField(value:String):void

yField

属性 
yField:String

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

指定用于确定每个数据点的 y 轴位置的数据提供程序字段。如果为 null,则 LineSeries 将假定数据提供程序为数字 Array,并使用这些数字作为值。

默认值为 null。



实现 
    public function get yField():String
    public function set yField(value:String):void
构造函数详细信息

LineSeries

() 构造函数
public function LineSeries()

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

构造函数。

方法详细信息

applyItemRendererProperties

() 方法
protected function applyItemRendererProperties(instance:DisplayObject, cache:InstanceCache):void

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

自定义用于表示图表的项呈示器实例。如果在呈示图表时需要新的项呈示器,则会自动调用此方法。您可以覆盖此方法以根据需要添加自己的自定义方法。

参数

 instance:DisplayObject — 正在创建的新项呈示器实例。
 
 cache:InstanceCache — 用于管理项呈示器实例的 InstanceCache。

commitProperties

() 方法 
override protected function commitProperties():void

语言版本: ActionScript 3.0
产品版本: Flex 3
运行时版本: Flash Player 9, AIR 1.1

示例  (  如何使用本示例  )
Line_AreaChartExample.mxml
<?xml version="1.0"?>
<!-- Simple example to demonstrate the LineChart and AreaChart controls. -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <fx:Script>
        <![CDATA[

        import mx.collections.ArrayCollection;

        [Bindable]
        private var expensesAC:ArrayCollection = new ArrayCollection( [
            { Month: "Jan", Profit: 2000, Expenses: 1500, Amount: 450 },
            { Month: "Feb", Profit: 1000, Expenses: 200, Amount: 600 },
            { Month: "Mar", Profit: 1500, Expenses: 500, Amount: 300 },
            { Month: "Apr", Profit: 1800, Expenses: 1200, Amount: 900 },
            { Month: "May", Profit: 2400, Expenses: 575, Amount: 500 } ]);
        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Define custom colors for use as fills in the AreaChart control. -->
        <mx:SolidColor id="sc1" color="blue" alpha=".3"/>
        <mx:SolidColor id="sc2" color="red" alpha=".3"/>
        <mx:SolidColor id="sc3" color="green" alpha=".3"/>

        <!-- Define custom Strokes. -->
        <mx:SolidColorStroke id = "s1" color="blue" weight="2"/>
        <mx:SolidColorStroke id = "s2" color="red" weight="2"/>
        <mx:SolidColorStroke id = "s3" color="green" weight="2"/>
    </fx:Declarations>

    <mx:Panel title="LineChart and AreaChart Controls Example" 
        height="100%" width="100%" layout="horizontal">

        <mx:LineChart id="linechart" height="100%" width="45%"
            paddingLeft="5" paddingRight="5" 
            showDataTips="true" dataProvider="{expensesAC}">
                
            <mx:horizontalAxis>
                <mx:CategoryAxis categoryField="Month"/>
            </mx:horizontalAxis>

            <mx:series>
                <mx:LineSeries yField="Profit" form="curve" displayName="Profit" lineStroke="{s1}"/>
                <mx:LineSeries yField="Expenses" form="curve" displayName="Expenses" lineStroke="{s2}"/>
                <mx:LineSeries yField="Amount" form="curve" displayName="Amount" lineStroke="{s3}"/>
            </mx:series>
        </mx:LineChart>

        <mx:Legend dataProvider="{linechart}"/>

        <mx:AreaChart id="Areachart" height="100%" width="45%"
             paddingLeft="5" paddingRight="5" 
             showDataTips="true" dataProvider="{expensesAC}">
                 
            <mx:horizontalAxis>
                <mx:CategoryAxis categoryField="Month"/>
            </mx:horizontalAxis>

            <mx:series>
                <mx:AreaSeries yField="Profit" form="curve" displayName="Profit" areaStroke="{s1}" areaFill="{sc1}"/>
                <mx:AreaSeries yField="Expenses" form="curve" displayName="Expenses" areaStroke="{s2}" areaFill="{sc2}"/>
                <mx:AreaSeries yField="Amount" form="curve" displayName="Amount" areaStroke="{s3}" areaFill="{sc3}"/>
            </mx:series>
        </mx:AreaChart>
            
        <mx:Legend dataProvider="{Areachart}"/>

    </mx:Panel>
</s:Application>
using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Windows.Forms; using OxyPlot; using OxyPlot.Series; using OxyPlot.WindowsForms; using OxyPlot.Axes; using MathNet.Numerics; using MathNet.Numerics.Interpolation; using Microsoft.VisualBasic.FileIO; using PdfSharp.Pdf; using PdfSharp.Drawing; namespace TestPro { public partial class Form1 : Form { private List<double> depthData = new List<double>(); private List<double> gammaData = new List<double>(); private PlotModel plotModel; private PlotView plotView; private Panel plotPanel; private ComboBox interpolationComboBox; private Button exportPdfButton; private Label statusLabel; private double scaleRatio = 200; // 默认比例尺1:200 private double minVisibleDepth = 0; private double maxVisibleDepth = 100; private const double VisibleDepthRange = 100; // 初始可见深度范围 private double pixelsPerMeter = 20; // 增加默认每米像素数 private double visibleDepthRange = 100; // 可见深度范围(米) private double minDepth = 0; private double maxDepth = 0; private VScrollBar depthScrollBar; // 垂直滚动条 private double totalDepthRange = 0; // 总深度范围 private bool isScrollBarDragging = false; // 标记是否正在拖动滚动条 private double dpi = 96; // 默认DPI值 public Form1() { InitializeComponent(); InitializeCustomComponents(); LoadData(); SetupPlotModel(); SetupScrollBar(); } private void InitializeCustomComponents() { this.Text = "自然伽马测井曲线可视化工具"; this.Size = new Size(1200, 800); this.StartPosition = FormStartPosition.CenterScreen; // 获取系统DPI using (Graphics g = this.CreateGraphics()) { dpi = g.DpiY; } // 创建主布局面板 var mainPanel = new Panel { Dock = DockStyle.Fill }; this.Controls.Add(mainPanel); // 创建顶部控制面板 var controlPanel = new Panel { Dock = DockStyle.Top, Height = 50, BackColor = Color.LightGray }; mainPanel.Controls.Add(controlPanel); // 创建插值方法选择下拉框 interpolationComboBox = new ComboBox { Location = new Point(20, 15), Width = 150, DropDownStyle = ComboBoxStyle.DropDownList }; interpolationComboBox.Items.AddRange(new object[] { "线性插值", "三次样条", "PCHIP", "Akima" }); interpolationComboBox.SelectedIndex = 2; // 默认选择PCHIP interpolationComboBox.SelectedIndexChanged += InterpolationComboBox_SelectedIndexChanged; controlPanel.Controls.Add(interpolationComboBox); // 添加比例尺选择 var scaleLabel = new Label { Text = "比例尺:", Location = new Point(190, 18), AutoSize = true }; controlPanel.Controls.Add(scaleLabel); var scaleComboBox = new ComboBox { Location = new Point(240, 15), Width = 80, DropDownStyle = ComboBoxStyle.DropDownList }; scaleComboBox.Items.AddRange(new object[] { "1:100", "1:200", "1:500" }); scaleComboBox.SelectedIndex = 1; // 默认1:200 // 初始计算像素/米 - 使用优化的公式 pixelsPerMeter = CalculatePixelsPerMeter(200, dpi); scaleComboBox.SelectedIndexChanged += (s, e) => { string selectedScale = scaleComboBox.SelectedItem.ToString(); switch (selectedScale) { case "1:100": scaleRatio = 100; pixelsPerMeter = CalculatePixelsPerMeter(100, dpi); break; case "1:200": scaleRatio = 200; pixelsPerMeter = CalculatePixelsPerMeter(200, dpi); break; case "1:500": scaleRatio = 500; pixelsPerMeter = CalculatePixelsPerMeter(500, dpi); break; } // 重新计算可见深度范围 CalculateVisibleDepthRange(); // 更新图表 UpdatePlot(); // 更新滚动条 UpdateScrollBar(); }; controlPanel.Controls.Add(scaleComboBox); // 比例因子调整滑块 - 修改为仅影响深度范围 var scaleFactorTrackBar = new TrackBar { Location = new Point(520, 15), Width = 150, Minimum = 50, Maximum = 200, Value = 100, TickFrequency = 10 }; scaleFactorTrackBar.ValueChanged += (s, e) => { double factor = scaleFactorTrackBar.Value / 100.0; double basePixels = CalculatePixelsPerMeter(scaleRatio, dpi); pixelsPerMeter = basePixels * factor; // 仅更新可见范围和图表 CalculateVisibleDepthRange(); UpdatePlot(); UpdateScrollBar(); }; controlPanel.Controls.Add(scaleFactorTrackBar); // 创建PDF导出按钮 exportPdfButton = new Button { Text = "导出PDF", Location = new Point(690, 15), Size = new Size(80, 25) }; exportPdfButton.Click += ExportPdfButton_Click; controlPanel.Controls.Add(exportPdfButton); // 创建状态标签 statusLabel = new Label { Dock = DockStyle.Bottom, Height = 20, Text = "就绪", TextAlign = ContentAlignment.MiddleLeft, BorderStyle = BorderStyle.FixedSingle }; mainPanel.Controls.Add(statusLabel); // 创建绘图面板容器 var plotContainer = new Panel { Dock = DockStyle.Fill, BackColor = Color.White }; mainPanel.Controls.Add(plotContainer); // 创建绘图面板(带滚动条) plotPanel = new Panel { Dock = DockStyle.Left, //Location = new Point(0, 0), Width = 365, //Height = plotContainer.Height, BackColor = Color.White }; plotContainer.Controls.Add(plotPanel); // 创建垂直滚动条 depthScrollBar = new VScrollBar { Dock = DockStyle.Right, Width = 20 }; depthScrollBar.Scroll += DepthScrollBar_Scroll; depthScrollBar.MouseDown += (s, e) => isScrollBarDragging = true; depthScrollBar.MouseUp += (s, e) => isScrollBarDragging = false; plotContainer.Controls.Add(depthScrollBar); // 创建PlotView plotView = new PlotView { Dock = DockStyle.Fill, BackColor = Color.White }; // 创建自定义控制器并禁用所有交互 var customController = new PlotController(); customController.UnbindAll(); // 解绑所有默认操作 plotView.Controller = customController; // 使用标准的MouseWheel事件处理 plotView.MouseWheel += PlotView_MouseWheel; plotPanel.Controls.Add(plotView); // 添加Resize事件处理 this.Resize += Form1_Resize; } // 优化的每米像素计算 private double CalculatePixelsPerMeter(double scaleRatio, double dpi) { // 比例尺1:100表示1厘米代表1米 // 公式: pixelsPerMeter = (100 / scaleRatio) * (dpi / 2.54) return (100.0 / scaleRatio) * (dpi / 2.54); } private void SetupScrollBar() { if (depthData.Count == 0) return; // 计算总深度范围 totalDepthRange = maxDepth - minDepth; // 设置滚动条范围 depthScrollBar.Minimum = 0; depthScrollBar.Maximum = (int)Math.Ceiling(totalDepthRange - visibleDepthRange); depthScrollBar.SmallChange = 1; // 滚动步长为1米 depthScrollBar.LargeChange = (int)visibleDepthRange; // 一页的大小为当前可见深度范围 // 设置滚动条当前位置 depthScrollBar.Value = (int)(minVisibleDepth - minDepth); } private void UpdateScrollBar() { if (depthData.Count == 0) return; // 更新滚动条范围 depthScrollBar.Maximum = (int)Math.Ceiling(totalDepthRange - visibleDepthRange); depthScrollBar.LargeChange = (int)visibleDepthRange; // 更新滚动条位置 depthScrollBar.Value = Math.Max(0, Math.Min(depthScrollBar.Maximum, (int)(minVisibleDepth - minDepth))); } private void DepthScrollBar_Scroll(object sender, ScrollEventArgs e) { // 实时更新,不再检查ScrollEventType double newMinDepth = minDepth + e.NewValue; double newMaxDepth = newMinDepth + visibleDepthRange; // 只有当深度范围确实变化时才更新 if (Math.Abs(newMinDepth - minVisibleDepth) > 0.01 || Math.Abs(newMaxDepth - maxVisibleDepth) > 0.01) { minVisibleDepth = newMinDepth; maxVisibleDepth = newMaxDepth; // 确保不超出数据范围 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; } if (minVisibleDepth < minDepth) { minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } // 立即更新图表 UpdatePlot(); // 如果正在拖动,强制重绘滚动条 if (isScrollBarDragging) { depthScrollBar.Refresh(); } } } private void Form1_Resize(object sender, EventArgs e) { // 仅重新计算可见深度范围(不改变每米像素数) CalculateVisibleDepthRange(); UpdateScrollBar(); UpdatePlot(); } private void CalculateVisibleDepthRange() { // 根据屏幕高度和固定的pixelsPerMeter计算可见深度范围 if (plotView != null && plotView.Height > 0) { // 获取绘图区域高度(像素) int plotAreaHeight = plotView.Height; // 使用固定的pixelsPerMeter计算可见深度范围(米) visibleDepthRange = plotAreaHeight / pixelsPerMeter; // 确保可见范围至少为10米 if (visibleDepthRange < 10) visibleDepthRange = 10; // 确保不超过总深度范围 if (visibleDepthRange > totalDepthRange) { visibleDepthRange = totalDepthRange; } // 更新当前可见范围 maxVisibleDepth = minVisibleDepth + visibleDepthRange; // 确保不超过最大深度 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; if (minVisibleDepth < minDepth) minVisibleDepth = minDepth; } } } private void LoadData() { try { // 使用OpenFileDialog选择CSV文件 using (var openFileDialog = new OpenFileDialog()) { openFileDialog.Filter = "CSV文件|*.csv|所有文件|*.*"; openFileDialog.Title = "选择测井数据文件"; if (openFileDialog.ShowDialog() == DialogResult.OK) { statusLabel.Text = "正在加载数据..."; Application.DoEvents(); // 使用TextFieldParser读取CSV using (var parser = new TextFieldParser(openFileDialog.FileName)) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); parser.HasFieldsEnclosedInQuotes = true; // 跳过前3行头信息 for (int i = 0; i < 4; i++) { parser.ReadLine(); } // 读取数据 while (!parser.EndOfData) { string[] fields = parser.ReadFields(); if (fields.Length >= 2) { // 修复:先声明变量再在TryParse中使用 double depth; double gamma; // 假设第一列是深度,第二列是伽马值 if (double.TryParse(fields[2], out depth) && double.TryParse(fields[6], out gamma)) { depthData.Add(depth); gammaData.Add(gamma); } } } } statusLabel.Text = $"已加载 {depthData.Count} 个数据点"; } else { statusLabel.Text = "未选择文件,使用示例数据"; GenerateSampleData(); } } if (depthData.Count > 0) { minDepth = depthData.Min(); maxDepth = depthData.Max(); totalDepthRange = maxDepth - minDepth; // 设置初始可见深度范围 CalculateVisibleDepthRange(); minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; // 确保不超过最大深度 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; if (minVisibleDepth < minDepth) minVisibleDepth = minDepth; } } } catch (Exception ex) { statusLabel.Text = $"错误: {ex.Message}"; GenerateSampleData(); } } private void GenerateSampleData() { // 生成示例数据 depthData.Clear(); gammaData.Clear(); double depth = 1000; Random rand = new Random(); for (int i = 0; i < 500; i++) { depth += 0.5; double gamma = 50 + 30 * Math.Sin(depth / 50) + rand.NextDouble() * 10; depthData.Add(depth); gammaData.Add(gamma); } // 设置示例数据的深度范围 minDepth = depthData.Min(); maxDepth = depthData.Max(); totalDepthRange = maxDepth - minDepth; // 设置初始可见深度范围 CalculateVisibleDepthRange(); minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } private void SetupPlotModel() { plotModel = new PlotModel { Title = "自然伽马测井曲线", TitleFontSize = 14, TitleFontWeight = OxyPlot.FontWeights.Bold, PlotMargins = new OxyThickness(60, 40, 60, 60) }; // 设置Y轴(深度)在右侧 - 固定网格间距 var depthAxis = new LinearAxis { Position = AxisPosition.Right, Title = "深度 (m)", Key = "Depth", StartPosition = 1, EndPosition = 0, // 反转Y轴 MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, MajorGridlineColor = OxyColor.FromRgb(180, 180, 180), MinorGridlineColor = OxyColor.FromRgb(220, 220, 220), Minimum = minVisibleDepth, Maximum = maxVisibleDepth, // 固定网格间距:每5米主网格,每1米次网格 MajorStep = 5, // 固定值,不随窗体变化 MinorStep = 1, // 固定值 // 添加以下属性确保步长绝对不变 IsPanEnabled = false, IsZoomEnabled = false, AbsoluteMaximum = double.MaxValue, AbsoluteMinimum = double.MinValue, AxislineThickness = 2, ExtraGridlineThickness = 2 }; plotModel.Axes.Add(depthAxis); // 设置X轴(伽马值)在顶部 var gammaAxis = new LinearAxis { Position = AxisPosition.Top, Title = "伽马值 (API)", Key = "Gamma", Minimum = 0, Maximum = 200, MajorStep = 20, // 10个刻度 (200/20=10) MinorStep = 5, MajorGridlineStyle = LineStyle.Solid, //MinorGridlineStyle = LineStyle.Dot, MajorGridlineColor = OxyColor.FromRgb(180, 180, 180), //MinorGridlineColor = OxyColor.FromRgb(220, 220, 220), AxislineThickness = 2 }; plotModel.Axes.Add(gammaAxis); // 添加原始数据点 //var scatterSeries = new ScatterSeries //{ // Title = "原始数据点", // MarkerType = MarkerType.Circle, // MarkerSize = 2, // MarkerFill = OxyColor.FromRgb(100, 100, 100) //}; //for (int i = 0; i < depthData.Count; i++) //{ // scatterSeries.Points.Add(new ScatterPoint(gammaData[i], depthData[i])); //} //plotModel.Series.Add(scatterSeries); plotView.Model = plotModel; } private void PlotView_MouseWheel(object sender, MouseEventArgs e) { // 计算滚轮方向 double direction = e.Delta > 0 ? -1 : 1; double scrollAmount = direction * 1; // 每次滚动1米 // 更新可见深度范围 minVisibleDepth += scrollAmount; maxVisibleDepth += scrollAmount; // 确保不超出数据范围 if (minVisibleDepth < minDepth) { minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; } // 更新滚动条位置 depthScrollBar.Value = (int)(minVisibleDepth - minDepth); // 更新图表 UpdatePlot(); } private void InterpolationComboBox_SelectedIndexChanged(object sender, EventArgs e) { UpdatePlot(); } private void UpdatePlot() { if (depthData.Count == 0) return; // 更新深度轴范围 var depthAxis = plotModel.Axes.FirstOrDefault(a => a.Key == "Depth") as LinearAxis; if (depthAxis != null) { depthAxis.Minimum = minVisibleDepth; depthAxis.Maximum = maxVisibleDepth; } // 移除之前的插值曲线 var seriesToRemove = plotModel.Series.Where(s => s is LineSeries).ToList(); foreach (var series in seriesToRemove) { plotModel.Series.Remove(series); } // 获取选择的插值方法 string method = interpolationComboBox.SelectedItem.ToString(); // 执行插值 IInterpolation interpolationFunc = CreateInterpolationFunction(depthData, gammaData, method); // 创建插值曲线 - 使用正确的平滑设置 var lineSeries = new LineSeries { Title = $"{method}插值", Color = OxyColor.FromRgb(0, 100, 200), StrokeThickness = 1.5 }; // 根据插值方法选择合适的平滑算法 if (method == "线性插值") { // 线性插值不需要额外平滑 } else { // 使用Catmull-Rom样条插值算法 lineSeries.InterpolationAlgorithm = InterpolationAlgorithms.CatmullRomSpline; // 使用CanonicalSpline并设置张力参数(如果可用) // lineSeries.InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline; // lineSeries.CanonicalSplineTension = 0.5; } // 添加当前可见范围内的点 double step = Math.Max(0.1, visibleDepthRange / 500); for (double d = minVisibleDepth; d <= maxVisibleDepth; d += step) { double gamma = interpolationFunc.Interpolate(d); lineSeries.Points.Add(new DataPoint(gamma, d)); } plotModel.Series.Add(lineSeries); plotModel.InvalidatePlot(true); // 计算5米网格线对应的像素距离 double gridSpacingPixels = 5 * pixelsPerMeter; statusLabel.Text = $"显示深度: {minVisibleDepth:F1}m - {maxVisibleDepth:F1}m ({visibleDepthRange:F1}m) | 总深度: {minDepth:F1}m - {maxDepth:F1}m | 插值: {method} | 比例: 1:{scaleRatio} | 网格间距: {gridSpacingPixels:F1}像素"; } private Tuple<List<double>, List<double>> SmoothCurve(List<double> depth, List<double> gamma, string method) { // 去除重复深度值 var uniqueDepth = new List<double>(); var uniqueGamma = new List<double>(); var seen = new HashSet<double>(); for (int i = 0; i < depth.Count; i++) { if (seen.Contains(depth[i])) continue; seen.Add(depth[i]); uniqueDepth.Add(depth[i]); uniqueGamma.Add(gamma[i]); } // 排序 var sorted = uniqueDepth.Select((d, i) => new { Depth = d, Gamma = uniqueGamma[i] }) .OrderBy(x => x.Depth) .ToList(); var sortedDepth = sorted.Select(x => x.Depth).ToArray(); var sortedGamma = sorted.Select(x => x.Gamma).ToArray(); // 生成新的深度点(用于插值) double minDepth = sortedDepth.Min(); double maxDepth = sortedDepth.Max(); int numPoints = 1000; // 插值点数 var xnew = Generate.LinearSpaced(numPoints, minDepth, maxDepth).ToList(); // 根据选择的插值方法进行插值 List<double> ynew; IInterpolation interpolation = null; switch (method) { case "线性插值": interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; case "三次样条": interpolation = Interpolate.CubicSpline(sortedDepth, sortedGamma); break; case "PCHIP": interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); break; case "Akima": interpolation = CubicSpline.InterpolateAkima(sortedDepth, sortedGamma); break; default: interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; } ynew = xnew.Select(d => interpolation.Interpolate(d)).ToList(); return Tuple.Create(xnew, ynew); } private void ExportPdfButton_Click(object sender, EventArgs e) { if (depthData.Count == 0) return; using (var saveFileDialog = new SaveFileDialog()) { saveFileDialog.Filter = "PDF文件|*.pdf"; saveFileDialog.Title = "导出PDF"; saveFileDialog.FileName = "GammaLogCurve.pdf"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { statusLabel.Text = "正在生成PDF..."; Application.DoEvents(); try { GeneratePdfReport(saveFileDialog.FileName); statusLabel.Text = $"PDF已成功导出到: {saveFileDialog.FileName}"; } catch (Exception ex) { statusLabel.Text = $"PDF导出错误: {ex.Message}"; } } } } private void GeneratePdfReport(string filePath) { try { statusLabel.Text = "正在准备PDF导出..."; Application.DoEvents(); // 获取plotPanel的宽度(365像素) double panelWidth = plotPanel.Width; // 计算每米对应的点(point)数(1点=1/72英寸) double dotsPerMeter = pixelsPerMeter * (72.0 / dpi); double minDepth = depthData.Min(); double maxDepth = depthData.Max(); double depthRange = maxDepth - minDepth; // 设置横轴宽度为plotPanel的宽度(转换为点) double gammaAxisWidth = panelWidth * (72.0 / dpi); // 保持页面宽度不变(与之前相同) double pageWidth = 500 + 100; // 伽马轴宽度500点 + 左右边距100点 // 计算图形在页面中的起始位置(居左显示) double graphStartX = 40; // 左边距40点 // 创建PDF文档 var document = new PdfDocument(); document.Info.Title = "自然伽马测井曲线报告"; document.Info.Author = "测井曲线可视化工具"; // 分页参数 const double maxPageHeight = 14400; // 200英寸 * 72点/英寸 double currentStartDepth = minDepth; int pageIndex = 0; int maxPages = 1000; // 安全限制,防止无限循环 bool isLastPage = false; // 获取插值方法 string method = interpolationComboBox.SelectedItem.ToString(); // 创建整个深度范围的插值函数 IInterpolation interpolationFunc = CreateInterpolationFunction(depthData, gammaData, method); // 计算总页数(用于页码显示) int totalPages = (int)Math.Ceiling(depthRange / (maxPageHeight / dotsPerMeter)); while (pageIndex < maxPages && !isLastPage) { statusLabel.Text = $"正在生成第 {pageIndex + 1} 页..."; Application.DoEvents(); // 计算当前页的深度范围 double currentPageDepthRange = maxPageHeight / dotsPerMeter; double currentEndDepth = currentStartDepth + currentPageDepthRange; // 检查是否是最后一页 if (currentEndDepth >= maxDepth - 0.001) // 使用容差避免浮点误差 { currentEndDepth = maxDepth; isLastPage = true; } // 计算当前页实际高度 double pageHeight = (currentEndDepth - currentStartDepth) * dotsPerMeter; // 创建页面(动态高度) var page = document.AddPage(); page.Width = pageWidth; page.Height = pageHeight; page.Orientation = PdfSharp.PageOrientation.Portrait; using (var xgraphics = XGraphics.FromPdfPage(page)) { // 设置边距 double margin = 40; double topMargin = 0; // 上边距为0 double bottomMargin = 0; // 下边距为0 double usableHeight = page.Height - topMargin - bottomMargin; // 计算比例因子 double gammaRange = 200; // 伽马值范围0-200 double gammaScale = gammaAxisWidth / gammaRange; double depthScale = usableHeight / (currentEndDepth - currentStartDepth); // 仅在第一页绘制标题 if (pageIndex == 0) { var titleFont = new XFont("Arial", 16, XFontStyle.Bold); xgraphics.DrawString("自然伽马测井曲线", titleFont, XBrushes.Navy, new XPoint(page.Width / 2, 10), new XStringFormat { Alignment = XStringAlignment.Center }); } // 绘制坐标轴框架 - 居左显示 var axisPen = new XPen(XColors.Black, 1.5); double leftAxisX = graphStartX + 30; // 左边距+标签空间 double rightAxisX = leftAxisX + gammaAxisWidth; // Y轴(深度)从顶部到底部边缘 xgraphics.DrawLine(axisPen, leftAxisX, topMargin, leftAxisX, topMargin + usableHeight); // X轴(伽马值)- 只在第一页绘制顶部轴 if (pageIndex == 0) { xgraphics.DrawLine(axisPen, leftAxisX, topMargin, rightAxisX, topMargin); } // 绘制网格线 - 深度(Y轴)延伸到页面底部 var gridPen = new XPen(XColors.LightGray, 0.3); var majorGridPen = new XPen(XColors.Gray, 0.6); // 计算起始网格深度 double startGridDepth = Math.Floor(currentStartDepth); double endGridDepth = Math.Ceiling(currentEndDepth); for (double i = startGridDepth; i <= endGridDepth; i += 1) { double y = topMargin + (i - currentStartDepth) * depthScale; // 确保y在页面范围内 if (y < topMargin || y > topMargin + usableHeight) continue; // 每5米主网格 if (i % 5 == 0) { xgraphics.DrawLine(majorGridPen, leftAxisX, y, rightAxisX, y); // 主刻度标签 var labelFont = new XFont("Arial", 8, XFontStyle.Bold); xgraphics.DrawString($"{i:F1}", labelFont, XBrushes.Black, new XPoint(leftAxisX - 5, y), new XStringFormat { Alignment = XStringAlignment.Far, LineAlignment = XLineAlignment.Center }); } else // 1米次网格 { xgraphics.DrawLine(gridPen, leftAxisX, y, rightAxisX, y); } } // 绘制网格线 - 伽马值(X轴)每20单位,延伸到页面底部 for (int i = 0; i <= 10; i++) { double gammaValue = i * 20; double x = leftAxisX + gammaValue * gammaScale; xgraphics.DrawLine(majorGridPen, x, topMargin, x, topMargin + usableHeight); // 刻度标签(顶部)- 只在第一页绘制 if (pageIndex == 0) { var labelFont = new XFont("Arial", 8); xgraphics.DrawString($"{gammaValue}", labelFont, XBrushes.Black, new XPoint(x, topMargin - 10), new XStringFormat { Alignment = XStringAlignment.Center, LineAlignment = XLineAlignment.Far }); } } // 绘制原始数据点(仅当前页范围内的点) var pointPen = new XPen(XColors.Gray, 0.5); for (int i = 0; i < depthData.Count; i++) { double depth = depthData[i]; double gamma = gammaData[i]; if (depth >= currentStartDepth && depth <= currentEndDepth) { double x = leftAxisX + gamma * gammaScale; double y = topMargin + (depth - currentStartDepth) * depthScale; // 只绘制在有效伽马值范围内的点 if (gamma >= 0 && gamma <= 200) { xgraphics.DrawEllipse(pointPen, x - 0.5, y - 0.5, 1, 1); } } } // ===== 平滑曲线绘制部分 ===== var curvePen = new XPen(XColor.FromArgb(0, 100, 200), 1.5); // 收集当前页所有有效点 var validPoints = new List<XPoint>(); // 按0.1米步长生成当前页深度范围内的点 double step = 0.1; double d = currentStartDepth; while (d <= currentEndDepth) { double gamma = interpolationFunc.Interpolate(d); if (gamma >= 0 && gamma <= 200) // 只处理有效范围内的点 { double x = leftAxisX + gamma * gammaScale; double y = topMargin + (d - currentStartDepth) * depthScale; validPoints.Add(new XPoint(x, y)); } d += step; } // 使用直线连接密集的点(点足够密时看起来是平滑的) if (validPoints.Count >= 2) { xgraphics.DrawLines(curvePen, validPoints.ToArray()); } // ===== 结束平滑曲线绘制部分 ===== // 添加坐标轴标签 var axisFont = new XFont("Arial", 10, XFontStyle.Bold); // 深度标签(垂直文本) var depthLabelState = xgraphics.Save(); xgraphics.TranslateTransform(graphStartX, topMargin + usableHeight / 2); xgraphics.RotateTransform(-90); xgraphics.DrawString("深度 (m)", axisFont, XBrushes.Black, 0, 0, new XStringFormat { Alignment = XStringAlignment.Center, LineAlignment = XLineAlignment.Center }); xgraphics.Restore(depthLabelState); // 伽马值标签 - 只在第一页绘制 if (pageIndex == 0) { xgraphics.DrawString("伽马值 (API)", axisFont, XBrushes.Black, new XPoint(leftAxisX + gammaAxisWidth / 2, topMargin - 20), new XStringFormat { Alignment = XStringAlignment.Center }); } } // 更新下一页的起始深度 currentStartDepth = currentEndDepth; pageIndex++; // 终止条件检查 if (currentStartDepth >= maxDepth - 0.001 || pageIndex >= maxPages) { isLastPage = true; } } // 保存PDF document.Save(filePath); statusLabel.Text = $"PDF导出完成,共 {pageIndex} 页"; } catch (Exception ex) { statusLabel.Text = $"PDF导出错误: {ex.Message}"; MessageBox.Show($"导出PDF时发生错误:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // 创建插值函数的方法 private IInterpolation CreateInterpolationFunction(List<double> depth, List<double> gamma, string method) { // 去除重复深度值 var uniqueDepth = new List<double>(); var uniqueGamma = new List<double>(); var seen = new HashSet<double>(); for (int i = 0; i < depth.Count; i++) { if (seen.Contains(depth[i])) continue; seen.Add(depth[i]); uniqueDepth.Add(depth[i]); uniqueGamma.Add(gamma[i]); } // 排序 var sorted = uniqueDepth.Select((d, i) => new { Depth = d, Gamma = uniqueGamma[i] }) .OrderBy(x => x.Depth) .ToList(); var sortedDepth = sorted.Select(x => x.Depth).ToArray(); var sortedGamma = sorted.Select(x => x.Gamma).ToArray(); // 根据选择的插值方法进行插值 IInterpolation interpolation = null; switch (method) { case "线性插值": interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; case "三次样条": interpolation = Interpolate.CubicSpline(sortedDepth, sortedGamma); break; case "PCHIP": interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); break; case "Akima": try { // 尝试使用Akima插值(如果可用) interpolation = CubicSpline.InterpolateAkima(sortedDepth, sortedGamma); } catch { // 如果Akima不可用,回退到PCHIP interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); } break; default: interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; } return interpolation; } } }该程序中,使用像素代表米数。当窗体变化时,网格间距也发生了变化。我们希望的是当窗体减小3米对应的像素时,图形最大值显示比之前少3米,网格线少三条,无论是主网格还是次网格,而其他的网格线数量以及网格间距不发生任何的改变
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值