Excel XY图(散点图)数据标签无重叠 [英] Excel XY Chart (Scatter plot) Data Label No Overlap

查看:1803
本文介绍了Excel XY图(散点图)数据标签无重叠的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在过去一周一直在努力。虽然它不能做奇迹,可以说我有一个很好的结果:



我只是想要把这个代码放在那里,像我这样的那些正在寻找某种vba宏的穷人,可以帮助他们避免在分散情况下的标签重叠,因为在进行关于这个问题的研究时,我无法找到任何有用的东西。

So I've been working on this for the past week. Although it can't do miracles, I can say I've got a pretty good result:
I just wanted to put this code out there for all the poor souls like me that are looking for some kind of vba macro that helps them avoid label overlaps in a scatter plot, because while doing my research on the subject, I wasn't able to find anything helpful.

推荐答案

Const PIXEL_TO_POINT_RATIO As Double = 0.72 '1 Pixel = 72/96*1 Point
Const tStep As Double = 0.1
Const rStep As Double = 0.1
Dim pCount As Integer

Sub ExampleMain()

        RearrangeScatterLabels Sheet5 

        RearrangeScatterLabels Sheet25

End Sub

Sub RearrangeScatterLabels(sht As Worksheet)
    Dim plot As Chart
    Dim sCollection As SeriesCollection
    Dim dLabels() As DataLabel
    Dim dPoints() As Point
    Dim xArr(), yArr(), stDevX, stDevY As Double
    Dim x0, x1, y0, y1 As Double
    Dim temp() As Double
    Dim theta As Double
    Dim r As Double
    Dim isOverlapped As Boolean
    Dim safetyNet, validEntry, currentPoint As Integer

    Set plot = sht.ChartObjects(1).Chart 'XY chart (scatter plot)
    Set sCollection = plot.SeriesCollection 'All points and labels
    safetyNet = 1
    pCount = (sCollection.Count - 1)

    ReDim dLabels(1 To 1)
    ReDim dPoints(1 To 1)
    ReDim xArr(1 To 1)
    ReDim yArr(1 To 1)

    For pt = 1 To sCollection(1).Points.Count
        For i = 1 To pCount
            If sCollection(i).Points.Count <> 0 Then
                'Dynamically expand the arrays
                validEntry = validEntry + 1
                If validEntry <> 1 Then
                    ReDim Preserve dLabels(1 To UBound(dLabels) + 1)
                    ReDim Preserve dPoints(1 To UBound(dPoints) + 1)
                    ReDim Preserve xArr(1 To UBound(xArr) + 1)
                    ReDim Preserve yArr(1 To UBound(yArr) + 1)
                End If

                Set dLabels(i) = sCollection(i).Points(pt).DataLabel 'Store all label objects
                Set dPoints(i) = sCollection(i).Points(pt)           'Store all point objects
                temp = getElementDimensions(, dPoints(i))
                xArr(i) = temp(0) 'Store all points x values
                yArr(i) = temp(2) 'Store all points y values
            End If
        Next
    Next

    If UBound(dLabels) < 2 Then Exit Sub

    pCount = UBound(dLabels)
    stDevX = Application.WorksheetFunction.StDev(xArr) 'Get standard deviation for x
    stDevY = Application.WorksheetFunction.StDev(yArr) 'Get standard deviation for y
    If stDevX = 0 Then stDevX = 1
    If stDevY = 0 Then stDevY = 1
    r = 0

    For currentPoint = 1 To pCount
        theta = Rnd * 2 * Application.WorksheetFunction.Pi()
        x0 = xArr(currentPoint)
        y0 = yArr(currentPoint)
        x1 = xArr(currentPoint)
        y1 = yArr(currentPoint)
        isOverlapped = True

        Do Until Not isOverlapped
            safetyNet = safetyNet + 1

            If safetyNet < 500 Then
                If Not checkForOverlap(dLabels(currentPoint), dLabels, dPoints, plot) Then
                    'No label is within bounds and not overlapping
                    isOverlapped = False
                    r = 0
                    theta = Rnd * 2 * Application.WorksheetFunction.Pi()
                    safetyNet = 1
                Else
                    'Move label so it does not overlap
                    theta = theta + tStep
                    r = r + rStep * tStep / (2 * Application.WorksheetFunction.Pi())
                    x1 = x0 + stDevX * r * Cos(theta)
                    y1 = y0 + stDevY * r * Sin(theta)
                    dLabels(currentPoint).Left = x1
                    dLabels(currentPoint).Top = y1
                End If
            Else
                safetyNet = 1
                Exit Do
            End If
        Loop
    Next
End Sub

Function checkForOverlap(ByRef dLabel As DataLabel, ByRef dLabels() As DataLabel, ByRef dPoints() As Point, ByRef dChart As Chart) As Boolean
    checkForOverlap = False 'Return false by default

    'Detect label going over chart area
    If detectOverlap(dLabel, , , dChart) Then
        checkForOverlap = True
        Exit Function
    End If

    'Detect labels overlap
    For i = 1 To pCount
        If Not dLabel.Left = dLabels(i).Left Then
            If detectOverlap(dLabel, dLabels(i)) Then
                checkForOverlap = True
                Exit Function
            End If
        End If
    Next

    'Detect label overlap with point
    For i = 1 To pCount
        If detectOverlap(dLabel, , dPoints(i)) Then
            checkForOverlap = True
            Exit Function
        End If
    Next
End Function

Function getElementDimensions(Optional dLabel As DataLabel, Optional dPoint As Point, Optional dChart As Chart) As Double()
    'Get element dimensions and compensate slack
    Dim eDimensions(3) As Double

    'Working in IV quadrant
    If dPoint Is Nothing And dChart Is Nothing Then
        'Get label dimensions and compensate padding
        eDimensions(0) = dLabel.Left + PIXEL_TO_POINT_RATIO * 3                'Left
        eDimensions(1) = dLabel.Left + dLabel.Width - PIXEL_TO_POINT_RATIO * 3 'Right
        eDimensions(2) = dLabel.Top + PIXEL_TO_POINT_RATIO * 6                 'Top
        eDimensions(3) = dLabel.Top + dLabel.Height - PIXEL_TO_POINT_RATIO * 3 'Bottom
    End If
    If dLabel Is Nothing And dChart Is Nothing Then
        'Get point dimensions
        eDimensions(0) = dPoint.Left - PIXEL_TO_POINT_RATIO * 5 'Left
        eDimensions(1) = dPoint.Left + PIXEL_TO_POINT_RATIO * 5 'Right
        eDimensions(2) = dPoint.Top - PIXEL_TO_POINT_RATIO * 5  'Top
        eDimensions(3) = dPoint.Top + PIXEL_TO_POINT_RATIO * 5  'Bottom
    End If
    If dPoint Is Nothing And dLabel Is Nothing Then
        'Get chart dimensions
        eDimensions(0) = dChart.PlotArea.Left + PIXEL_TO_POINT_RATIO * 22                         'Left
        eDimensions(1) = dChart.PlotArea.Left + dChart.PlotArea.Width - PIXEL_TO_POINT_RATIO * 22 'Right
        eDimensions(2) = dChart.PlotArea.Top - PIXEL_TO_POINT_RATIO * 4                           'Top
        eDimensions(3) = dChart.PlotArea.Top + dChart.PlotArea.Height - PIXEL_TO_POINT_RATIO * 4  'Bottom
    End If

    getElementDimensions = eDimensions 'Return dimensions array in Points
End Function

Function detectOverlap(ByVal dLabel1 As DataLabel, Optional ByVal dLabel2 As DataLabel, Optional ByVal dPoint As Point, Optional ByVal dChart As Chart) As Boolean
    'Left, Right, Top, Bottom
    Dim AxL, AxR, AyT, AyB As Double 'First label coordinates
    Dim BxL, BxR, ByT, ByB As Double 'Second label coordinates
    Dim eDimensions() As Double 'Element dimensions

    eDimensions = getElementDimensions(dLabel1)
    AxL = eDimensions(0)
    AxR = eDimensions(1)
    AyT = eDimensions(2)
    AyB = eDimensions(3)

    If dPoint Is Nothing And dChart Is Nothing Then
        'Compare with another label
        eDimensions = getElementDimensions(dLabel2)
    End If
    If dLabel2 Is Nothing And dChart Is Nothing Then
        'Compare with a point
        eDimensions = getElementDimensions(, dPoint)
    End If
    If dPoint Is Nothing And dLabel2 Is Nothing Then
        'Compare with chart area
        eDimensions = getElementDimensions(, , dChart)
    End If
    BxL = eDimensions(0)
    BxR = eDimensions(1)
    ByT = eDimensions(2)
    ByB = eDimensions(3)

    If dChart Is Nothing Then
        detectOverlap = (AxL <= BxR And AxR >= BxL And AyT <= ByB And AyB >= ByT) 'Reverse De Morgan's Law
    Else
        detectOverlap = Not (AxL >= BxL And AxR <= BxR And AyT >= ByT And AyB <= ByB) 'Is in chart bounds (working in IV quadrant)
    End If
End Function


我意识到代码很粗糙,没有优化,但是我不能花更多的时间在这个项目上。我已经留下了不少笔记来帮助阅读,如果有人选择继续这个项目。希望这有帮助。

最好的祝福,Schadenfreude。


I realize the code is kinda rough and not optimized, but I can't spend more time on this project. I've left quite a few notes around to help read it, should anyone choose to continue this project.

Hope this helps.
Best wishes, Schadenfreude.

这篇关于Excel XY图(散点图)数据标签无重叠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆