ChartsOrg / ChartsOrg/Charts

Y Axis value repeat in BarChart and Zero Y Axis position is not there in Positive and Negative BarChart.

Open
#5,066 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
28k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

We are facing mainly two issue
1. Y Axis value is repeat for large number in Bar Chart despite we are using granularityEnable and setting value as well. We have use below OIAnalysisLargeValueFormatter but it seems it is working but granularity is not taking correct value.

Screenshot 2023-06-02 at 12 01 52 PM

`final class OIAnalysisLargeValueFormatter: AxisValueFormatter {

var suffix = ["", "k", "L", "Cr"]

public init() { }

fileprivate func format(value: Double) -> String {
var sig = value
var length = 3
var fraction = 0

let numberFormatter = NumberFormatter()
numberFormatter.usesGroupingSeparator = true
numberFormatter.negativePrefix = "-"

if abs(sig) >= 10000000 {
sig = (Double(sig) / 100000000)
sig *= 10
length -= 0
numberFormatter.maximumFractionDigits = 1
} else if abs(sig) >= 100000 {
sig = (Double(sig) / 1000000).rounded()
sig *= 10
length -= 1
numberFormatter.maximumFractionDigits = 0
} else if abs(sig) >= 10000 {
sig = (Double(sig) / 10000).rounded() //' K'
length -= 2
} else {
length -= 3 // ""
}

let number = NSNumber(value: sig)

numberFormatter.maximumFractionDigits = fraction
numberFormatter.positiveSuffix = suffix[length]
numberFormatter.negativeSuffix = suffix[length]

guard let string = numberFormatter.string(from: number) else {
return NumberFormatter.localizedString(from: number, number: .decimal)
}

return string
}

func stringForValue(
_ value: Double, axis: AxisBase?) -> String {
return format(value: value)
}

func stringForValue(
_ value: Double,
entry: ChartDataEntry,
dataSetIndex: Int,
viewPortHandler: ViewPortHandler?) -> String {
return format(value: value)
}

}`

# 2. In positive negative chart it is not showing Zero position as Axis in between.

Screenshot 2023-06-02 at 12 02 42 PM

'Data Building :- >
OiAnalysisChartDataBuilder
func processOIChartDataSet(strikes: [Strike], spotPrice: Double) -> ViewController.Content {
var dataEntries = [[BarChartDataEntry]]()
var callEntries = [BarChartDataEntry]()
var putEntries = [BarChartDataEntry]()
var xAxisValues = [String]()

strikes.enumerated().forEach {
let data = OiAnalysisToolTipModel(chartType: .oi,
strikePrice: $0.element.strikePrice,
callOi: $0.element.calloi,
putOi: $0.element.putoi,
firstColorView: Constants.colorSets[0] ?? .green,
secondColorView: Constants.colorSets[1] ?? .red)
xAxisValues.append(String(Int($0.element.strikePrice)))
callEntries.append(BarChartDataEntry(x: $0.element.strikePrice, y: $0.element.calloi, data: data))
putEntries.append(BarChartDataEntry(x: $0.element.strikePrice, y: $0.element.putoi, data: data))
}

callEntries.insert(BarChartDataEntry(x: 0, y: 0), at: 0)
putEntries.insert(BarChartDataEntry(x: 0, y: 0), at: 0)
dataEntries.append(callEntries)
dataEntries.append(putEntries)
return processChartResponse(chartType: .oi,
barData: dataEntries,
title: OiFilterTabs.OiFilterSecondaryFilter.oi.title,
spotPrice: spotPrice,
xAxisBarValues: xAxisValues)
}'

CHART CODE:

'extension ViewController {

struct Content {

let chartType: OiFilterTabs.OiFilterSecondaryFilter
let chartDataSets: [OiFNOChartDataSet]
let spotPrice: Double
let xAxisBarValues: [String]
weak var chartViewDelegate: ChartViewDelegate?

init(chartType: OiFilterTabs.OiFilterSecondaryFilter,
chartDataSets: [OiFNOChartDataSet],
spotPrice: Double = 0.0,
xAxisBarValues: [String] = [],
chartViewDelegate: ChartViewDelegate? = nil) {
self.chartType = chartType
self.chartDataSets = chartDataSets
self.spotPrice = spotPrice
self.xAxisBarValues = xAxisBarValues
self.chartViewDelegate = chartViewDelegate
}

}

struct OiFNOChartDataSet {

let entries: [ChartDataEntry]
let barChartEntries: [BarChartDataEntry]
let color: UIColor
let title: String

}

func setUpChart() {
let legend = barChartView.legend
legend.enabled = false

barChartView.xAxis.drawGridLinesEnabled = false
barChartView.xAxis.labelPosition = .bottom
barChartView.xAxis.labelTextColor = .gray
barChartView.xAxis.centerAxisLabelsEnabled = true
barChartView.xAxis.axisLineColor = .gray
barChartView.xAxis.granularityEnabled = true
barChartView.xAxis.enabled = true

barChartView.leftAxis.drawGridLinesEnabled = false
barChartView.leftAxis.drawAxisLineEnabled = false
barChartView.leftAxis.labelTextColor = .gray
barChartView.leftAxis.drawZeroLineEnabled = true
barChartView.leftAxis.valueFormatter = OIAnalysisLargeValueFormatter()
barChartView.leftAxis.granularityEnabled = true
barChartView.leftAxis.enabled = true

barChartView.rightAxis.drawAxisLineEnabled = false
barChartView.rightAxis.drawGridLinesEnabled = true
barChartView.rightAxis.drawLabelsEnabled = false
barChartView.rightAxis.gridColor = .gray

barChartView.pinchZoomEnabled = false
barChartView.dragXEnabled = true
barChartView.setScaleEnabled(false)
barChartView.scaleXEnabled = false
barChartView.scaleYEnabled = false
barChartView.extraRightOffset = Constants.defaultMargin
barChartView.extraLeftOffset = Constants.defaultMargin
}

func configure(_ content: Content) {

barChartView.delegate = content.chartViewDelegate
let dataSets = content.chartDataSets.enumerated().map {
getBarChartDataSet($0.element.barChartEntries, color: $0.element.color, label: $0.element.title, highlightEnabled: content.chartViewDelegate != nil)
}

let chartData = BarChartData(dataSets: dataSets)

barChartView.leftAxis.setLabelCount(Constants.leftAxisOICount, force: true)
barChartView.rightAxis.setLabelCount(Constants.leftAxisOICount, force: true)

barChartView.xAxis.valueFormatter = OiBarChartValueXAxisFormatter(values: content.xAxisBarValues)

chartData.barWidth = Constants.barWidth

barChartView.xAxis.granularity = barChartView.xAxis.axisMaximum / Double(content.xAxisBarValues.count)

barChartView.leftAxis.spaceBottom = 0.0
barChartView.xAxis.axisMinimum = 0.0
barChartView.xAxis.axisMaximum = 0.0 + chartData.groupWidth(groupSpace: Constants.groupSpace, barSpace: Constants.barSpace) * Double(content.xAxisBarValues.count)

chartData.groupBars(fromX: 0.0, groupSpace: Constants.groupSpace, barSpace: Constants.barSpace)

barChartView.data = chartData

self.getBarGranuraltiy(axisCount: Constants.leftAxisOICount, chartMax: barChartView.chartYMax)
barChartView.notifyDataSetChanged()
barChartView.setVisibleXRangeMaximum(Constants.xRangeMaximum)
barChartView.animate(yAxisDuration: Constants.yAxisDuration, easingOption: .linear)

addSpotPriceLine(spotPrice: content.spotPrice, values: content.xAxisBarValues)

barChartView.xAxisRenderer = LimitLineLabelRenderer(
viewPortHandler: barChartView.viewPortHandler,
axis: barChartView.xAxis,
transformer: barChartView.getTransformer(forAxis: .left)
)

}

func getBarGranuraltiy(axisCount: Int, chartMax: Double) -> Double {
let count = String(Int(chartMax)).count - 1
let c = NSDecimalNumber(decimal: pow(10, count)).doubleValue
let d = (chartMax / c).rounded() * c

let granuraltiy = (d / Double(axisCount)).rounded(.toNearestOrAwayFromZero)

let granuraltiyCount = String(Int(granuraltiy)).count - 1
let granuraltiyNumber = NSDecimalNumber(decimal: pow(10, granuraltiyCount)).doubleValue
let finalValue = (granuraltiy / granuraltiyNumber).rounded() * granuraltiyNumber
debugPrint(finalValue)
return finalValue < (20 * 100000) ? (20 * 100000) : finalValue
}

}

// MARK: - Constant Helper

private typealias ConstantHelper = ViewController
private extension ConstantHelper {

enum Constants {

static let defaultMargin: CGFloat = 16.0

static let dashLineWidth: CGFloat = 1.0
static let xOffsetLimitLine: CGFloat = 50.0
static let yOffsetLimitLine = 4.0

// Line Chart
static let chartViewHeight: CGFloat = 315.0
static let lineWidth: CGFloat = 2.0
static let rightLeftAxisCount: Int = 8
static let chartMaxRange: Int = 120

// Bar Chart
static let granularity = 50.0
static let leftAxisOICount: Int = 6
static let groupSpace = 0.4
static let barWidth = 0.2
static let barSpace = 0.03
static let xRangeMaximum = 5.0
static let yAxisDuration = 0.5
static let barDashPhase = 5.0
static let barDashLineWidth = 1.0

static var xAxisValueForPCRAndMAX: [String] = {
["9:15", "10:15", "11:15", "12:15", "01:15", "02:15", "03:15"]
}()

}

}

// MARK: - OiLineChartValuexAxisFormatter

private class OiLineChartValuexAxisFormatter: AxisValueFormatter {

func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let index = Int(value) < titleValues.count ? Int(value) : Int(value / 20)
return titleValues[index]
}

var titleValues = [String]()

init(values: [String]) {
self.titleValues = values
}

}

// MARK: - OiBarChartValueXAxisFormatter

private class OiBarChartValueXAxisFormatter: AxisValueFormatter {

var titleValues = [String]()

init(values: [String]) {
self.titleValues = values
}

func stringForValue(_ value: Double, axis: AxisBase?) -> String {

let count = self.titleValues.count
guard let axis = axis, !self.titleValues.isEmpty else { return "" }

let factor = axis.axisMaximum / Double(count)
let index = Int((value / factor).rounded())

if index >= 0 && index < count {
return self.titleValues[index]
}
return ""
}

}

// MARK: - ChartDataHelper Helper

private typealias ChartDataHelper = ViewController
private extension ChartDataHelper {

func getBarChartDataSet(_ chartDataEntries: [BarChartDataEntry],
color: UIColor,
label: String,
highlightEnabled: Bool) -> BarChartDataSet {
let chartDataSet = BarChartDataSet(entries: chartDataEntries, label: label)
chartDataSet.colors = [color]
chartDataSet.drawIconsEnabled = false
chartDataSet.drawValuesEnabled = false
chartDataSet.highlightEnabled = highlightEnabled
chartDataSet.highlightColor = .clear
chartDataSet.highlightLineWidth = Constants.dashLineWidth
chartDataSet.highlightLineDashPhase = Constants.barDashPhase
chartDataSet.highlightLineDashLengths = [Constants.barDashPhase, Constants.barDashPhase]
return chartDataSet
}

func addSpotPriceLine(spotPrice: Double, values: [String]) {
guard let value = values.first?.toDouble, let value2 = values[1].toDouble else { return }
let limitPoint = ((spotPrice - value) / (value2 - value)) - 1.0
let limitLine = ChartLimitLine(limit: limitPoint, label: "Spot price: " + String(spotPrice))
limitLine.lineWidth = Constants.barDashLineWidth
limitLine.lineColor = UIColor.gray
limitLine.valueTextColor = UIColor.gray
limitLine.lineDashLengths = [Constants.barDashPhase, Constants.barDashPhase]
limitLine.labelPosition = .rightTop
limitLine.xOffset = -Constants.xOffsetLimitLine
limitLine.yOffset = -Constants.yOffsetLimitLine

barChartView.xAxis.removeAllLimitLines()
barChartView.xAxis.addLimitLine(limitLine)
barChartView.xAxis.gridLineDashLengths = [5, 5]
barChartView.xAxis.drawLimitLinesBehindDataEnabled = true
barChartView.centerViewTo(xValue: limitPoint, yValue: 0, axis: .left)
}

}'

## Charts Environment

##Chart Version : 4.1.0
##Xcode : 14.2
##5.6
##iPhone

## Demo Project
## https://github.com/lokesh-vyas/DemoChartBar

ℹ Please link to or upload a project we can download that reproduces the issue.

1. Open App
2. In OI tab yAxis value is repeating.
3. in Change OI and PCR Zero yAxis is not showing while added 0 data in ChartDataBuilder.

Contributor guide

Open the contributing guide

Research direction

Start in the ViewController chart setup and configuration, especially setUpChart(), configure(), getBarGranuraltiy(), and the axis formatters; also inspect OiAnalysisChartDataBuilder.processOIChartDataSet(). Reproduce both reported BarChart cases and verify that Y-axis labels do not repeat and that the zero position appears between positive and negative bars.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
data-visualization, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.