在散点图上绘制置信椭圆

时间:2016-09-21 13:12:31

标签: ios swift core-plot

我正在开发一个iOS应用程序,我正在使用CorePlot库(2.1版)来绘制散点图。我的散点图绘制得很好,在下一步中我想在绘图上绘制一个半透明的置信椭圆。我写了一个计算主轴和短轴以及椭圆所需旋转角度的类。我的ConfidenceEllipse类实现了一个getPath()方法,该方法返回一个表示要绘制的椭圆的CGPath。

func getPath() -> CGPath
{
var ellipse: CGPath

var transform = CGAffineTransformIdentity
transform = CGAffineTransformTranslate (transform, CGFloat(-self.meanX), CGFloat(-self.meanY))
transform = CGAffineTransformRotate (transform, CGFloat(self.angle))
transform = CGAffineTransformTranslate (transform, CGFloat(self.meanX), CGFloat(self.meanY))

ellipse = CGPathCreateWithEllipseInRect(CGRectMake (CGFloat(-self.mainHalfAxis), CGFloat(-self.minorHalfAxis), CGFloat(2 * self.mainHalfAxis), CGFloat(2 * self.minorHalfAxis)),&transform)

return ellipse
}

在网上搜索了一段时间后,看来Annotations是要走的路,所以我尝试了这个:

let graph     = hostView.hostedGraph!
let space     = graph.defaultPlotSpace    
let ellipse = ConfidenceEllipse(chiSquare: 5.991)
ellipse.analyze(self.samples)

let annotation = CPTPlotSpaceAnnotation (plotSpace: space!, anchorPlotPoint: [0,0])
let overlay = CPTBorderedLayer (frame: graph.frame)
overlay.outerBorderPath = ellipse.getPath()

let fillColor = CPTColor.yellowColor()
overlay.fill = CPTFill (color: fillColor)

annotation.contentLayer = overlay
annotation.contentLayer?.opacity = 0.5
graph.addAnnotation(annotation)

这样做,会给我以下内容 Screenshot 正如您所看到的,叠加占用了帧的完整大小,这似乎是合乎逻辑的,因为我在创建 CPTBorderedLayer 对象时传递了帧维度。我也尝试将构造函数留空,但是叠加层根本没有显示。所以我想知道,这里有什么我想念的吗?

2 个答案:

答案 0 :(得分:0)

您需要缩放椭圆以匹配绘图。使用绘图区域bounds作为注记图层的框架,并将注释附加到绘图区域。在x和y方向上缩放椭圆,以匹配绘图空间使用的变换,以拟合绘图区域中的绘图。

编辑: 在研究了边界层的工作方式之后,我意识到上面的建议不会起作用。每当图层边界发生变化时,CPTBorderedLayer会自动设置outerBorderPath。不要试图影响图层边框,而是将椭圆绘制到图像中,并将其用作边框图层的fill。您应该调整图层的大小,使椭圆恰好适合内部。

答案 1 :(得分:0)

在未能使Annotations正常工作之后,我决定走另一条路。我的最终解决方案是将原始散点图与第二个散点图重叠,后者仅包含一个数据点,即置信椭圆的中心。这是代码

    func drawConfidenceEllipse () {

    let graph     = hostView.hostedGraph!
    let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace

    let scaleX = (graph.bounds.size.width - graph.paddingLeft - graph.paddingRight) / CGFloat(plotSpace.xRange.lengthDouble)
    let scaleY = (graph.bounds.size.height - graph.paddingTop - graph.paddingBottom) / CGFloat(plotSpace.yRange.lengthDouble)

    let analysis = ConfidenceEllipse(chiSquare: 5.991)
    analysis.analyze(self.samples)
    let unscaledPath = analysis.getPath()

    let bounds = CGPathGetBoundingBox(unscaledPath)

    var scaler = CGAffineTransformIdentity
    scaler = CGAffineTransformScale (scaler, scaleX, scaleY)
    scaler = CGAffineTransformTranslate (scaler, CGFloat (-bounds.origin.x), CGFloat (-bounds.origin.y))
    let scaledPath   = CGPathCreateCopyByTransformingPath (unscaledPath, &scaler)
    let scaledBounds = CGPathGetPathBoundingBox(scaledPath)

    let symbol = CPTPlotSymbol ()
    symbol.symbolType = CPTPlotSymbolType.Custom
    symbol.customSymbolPath = scaledPath
    symbol.fill = CPTFill (color: CPTColor.yellowColor().colorWithAlphaComponent(0.25))
    symbol.size = CGSize (width: scaledBounds.size.width, height: scaledBounds.size.height)

    let lineStyle = CPTMutableLineStyle()
    lineStyle.lineWidth = 1
    lineStyle.lineColor = CPTColor.yellowColor()

    symbol.lineStyle = lineStyle

    let ellipse = CPTScatterPlot (frame: hostView.frame)
    ellipse.title = "Confidence Ellipse"
    ellipse.delegate = self
    ellipse.dataSource = self
    ellipse.plotSymbol = symbol
    ellipse.dataLineStyle = nil

    graph.addPlot(ellipse)

}

以下是最终结果的屏幕截图:

95% Confidence Ellipse on top of scatter plot

希望这有帮助