chart.ctChart.plotArea.scatterChartList.lastOrNull()?.let { sc ->
// markers only
val st = sc.scatterStyle ?: sc.addNewScatterStyle()
st.`val` = STScatterStyle.MARKER
sc.serList.forEachIndexed { i, ser ->
// κρύψε γραμμή
val sp = if (ser.isSetSpPr) ser.spPr else ser.addNewSpPr()
val ln = if (sp.isSetLn) sp.ln else sp.addNewLn()
if (!ln.isSetNoFill) ln.addNewNoFill()
// ΣΚΛΗΡΟ reset dLbls
if (ser.isSetDLbls) ser.unsetDLbls()
val dLbls = ser.addNewDLbls()
// ΜΟΝΟ ένα point label στο idx=0, με ρητό κείμενο από helper κελί
val dl = dLbls.addNewDLbl()
dl.addNewIdx().`val` = 0L
val ref = CellReference(drawSheet.sheetName, legendRow0 + i, legendCol, true, true)
dl.addNewTx().addNewStrRef().f = ref.formatAsString()
// όλα τα flags OFF
dl.addNewShowSerName().`val` = false
dl.addNewShowVal().`val` = false
dl.addNewShowCatName().`val` = false
dl.addNewShowLegendKey().`val` = false
dl.addNewShowPercent().`val` = false
dl.addNewShowBubbleSize().`val` = false
// θέση μόνο για αυτό το point
dl.addNewDLblPos().`val` = STDLblPos.L
}
// καθάρισε chart-level dLbls για σιγουριά
if (sc.isSetDLbls) sc.unsetDLbls()
}
< /code>
Этот код добавляет метки для каждой точки каждой серии. < /p>
fun eventsTimelineScatter(
drawSheet: XSSFSheet, // ΠΟΥ ζωγραφίζει (π.χ. "Charts")
dataSheet: XSSFSheet, // ΑΠΟ ΠΟΥ διαβάζει (π.χ. "PlotData")
lastRow: Int,
title: String,
anchorCols: IntRange,
anchorRows: IntRange,
xCol: Int, // στήλη χρόνου (Excel serial, numeric)
typeCol: Int // στήλη κατηγορίας (String)
) {
data class Cat(val key: String, val display: String)
fun norm(raw: String?): Cat {
val d = (raw ?: "Unknown").trim().replace(Regex("\\s+"), " ")
return Cat(d.lowercase().ifEmpty { "unknown" }, d.ifEmpty { "Unknown" })
}
val byType = mutableMapOf>()
val keyToDisplay = mutableMapOf()
val laneOf = mutableMapOf()
fun lane(k: String) = laneOf.getOrPut(k) { laneOf.size + 1 }.toDouble()
var xMin = Double.POSITIVE_INFINITY
var xMax = Double.NEGATIVE_INFINITY
for (r in 1..lastRow) {
val row = dataSheet.getRow(r) ?: continue
val cat = norm(row.getCell(typeCol)?.toString())
val x = row.getCell(xCol)?.numericCellValue ?: continue
byType.getOrPut(cat.key) { mutableListOf() }.add(x to lane(cat.key))
keyToDisplay.putIfAbsent(cat.key, cat.display)
if (x < xMin) xMin = x
if (x > xMax) xMax = x
}
if (byType.isEmpty()) return
// legend helper cells (τίτλοι σειρών από κελί)
val legendCol = (anchorCols.last + 2).coerceAtLeast(24)
val legendRow0 = anchorRows.first
val keysInOrder = keyToDisplay.keys.sortedBy { keyToDisplay[it]!!.lowercase() }
keysInOrder.forEachIndexed { i, k ->
val row = drawSheet.getRow(legendRow0 + i) ?: drawSheet.createRow(legendRow0 + i)
row.createCell(legendCol).setCellValue(keyToDisplay[k])
}
val drawing = drawSheet.createDrawingPatriarch() as XSSFDrawing
val anchor = drawing.createAnchor(0,0,0,0, anchorCols.first, anchorRows.first, anchorCols.last+1, anchorRows.last+1)
val chart = drawing.createChart(anchor) as XSSFChart
val xAxis = chart.createValueAxis(AxisPosition.BOTTOM).apply {
crosses = AxisCrosses.MIN
majorTickMark = AxisTickMark.OUT
minorTickMark = AxisTickMark.NONE
tickLabelPosition = AxisTickLabelPosition.LOW
}
val yAxis = chart.createValueAxis(AxisPosition.LEFT).apply {
majorTickMark = AxisTickMark.NONE
minorTickMark = AxisTickMark.NONE
tickLabelPosition = AxisTickLabelPosition.NONE
}
val data = chart.createData(ChartTypes.SCATTER, xAxis, yAxis) as XDDFScatterChartData
data.setVaryColors(false)
try { data.style = ScatterStyle.MARKER } catch (_: Throwable) {}
// σειρές (μία ανά κατηγορία)
keysInOrder.forEachIndexed { i, k ->
val pts = byType[k]!!.sortedBy { it.first }
val xs = XDDFDataSourcesFactory.fromArray(pts.map { it.first }.toTypedArray())
val ys = XDDFDataSourcesFactory.fromArray(pts.map { it.second }.toTypedArray())
val s = data.addSeries(xs, ys) as XDDFScatterChartData.Series
val ref = CellReference(drawSheet.sheetName, legendRow0 + i, legendCol, true, true)
s.setTitle(null, ref)
s.setMarkerStyle(MarkerStyle.CIRCLE)
s.setMarkerSize(7)
s.isSmooth = false
}
chart.plot(data)
chart.setTitleText(title)
chart.setTitleOverlay(false)
// --- CT tweaks ---
// 5) CT: markers only + 1 label (series title) ΜΟΝΟ στο πρώτο point κάθε σειράς
// 5) CT: markers only + 1 label (from cell) ΜΟΝΟ στο πρώτο point κάθε σειράς
chart.ctChart.plotArea.scatterChartList.lastOrNull()?.let { sc ->
// markers only
val st = sc.scatterStyle ?: sc.addNewScatterStyle()
st.`val` = STScatterStyle.MARKER
sc.serList.forEachIndexed { i, ser ->
// κρύψε γραμμή
val sp = if (ser.isSetSpPr) ser.spPr else ser.addNewSpPr()
val ln = if (sp.isSetLn) sp.ln else sp.addNewLn()
if (!ln.isSetNoFill) ln.addNewNoFill()
// ΣΚΛΗΡΟ reset dLbls
if (ser.isSetDLbls) ser.unsetDLbls()
val dLbls = ser.addNewDLbls()
// ΜΟΝΟ ένα point label στο idx=0, με ρητό κείμενο από helper κελί
val dl = dLbls.addNewDLbl()
dl.addNewIdx().`val` = 0L
val ref = CellReference(drawSheet.sheetName, legendRow0 + i, legendCol, true, true)
dl.addNewTx().addNewStrRef().f = ref.formatAsString()
// όλα τα flags OFF
dl.addNewShowSerName().`val` = false
dl.addNewShowVal().`val` = false
dl.addNewShowCatName().`val` = false
dl.addNewShowLegendKey().`val` = false
dl.addNewShowPercent().`val` = false
dl.addNewShowBubbleSize().`val` = false
// θέση μόνο για αυτό το point
dl.addNewDLblPos().`val` = STDLblPos.L
}
// καθάρισε chart-level dLbls για σιγουριά
if (sc.isSetDLbls) sc.unsetDLbls()
}
// Legend
chart.getOrAddLegend().apply { position = LegendPosition.TOP_RIGHT; isOverlay = false }
// Gridlines στον Χ
chart.ctChart.plotArea.valAxList.forEach { ax ->
if (ax.axPos.`val` == STAxPos.B && !ax.isSetMajorGridlines) ax.addNewMajorGridlines()
}
// X axis: datetime format + λίγα ticks
chart.ctChart.plotArea.valAxList.firstOrNull { it.axPos.`val` == STAxPos.B }?.let { xAx ->
val nf = if (xAx.isSetNumFmt) xAx.numFmt else xAx.addNewNumFmt()
nf.formatCode = "yyyy-mm-dd HH:mm:ss"; nf.sourceLinked = false
val range = if (xMax.isFinite() && xMin.isFinite() && xMax > xMin) xMax - xMin else 1.0
val desiredTicks = 6
val step = range / (desiredTicks - 1)
val sca = if (xAx.scaling!=null) xAx.scaling else xAx.addNewScaling()
if (sca.isSetMin) sca.unsetMin()
if (sca.isSetMax) sca.unsetMax()
sca.addNewMin().setVal(if (xMin.isFinite()) xMin else 0.0)
sca.addNewMax().setVal(if (xMax.isFinite()) xMax else 1.0)
if (xAx.isSetMajorUnit) xAx.unsetMajorUnit()
xAx.addNewMajorUnit().setVal(step)
}
// Y axis: lanes 1..N, χωρίς labels, tick ανά lane
val lanes = max(laneOf.size, 1)
chart.ctChart.plotArea.valAxList.firstOrNull { it.axPos.`val` == STAxPos.L }?.let { yAx ->
(if (yAx.isSetDelete) yAx.delete else yAx.addNewDelete()).setVal(false)
val sca = if (yAx.scaling!=null) yAx.scaling else yAx.addNewScaling()
if (sca.isSetMin) sca.unsetMin()
if (sca.isSetMax) sca.unsetMax()
sca.addNewMin().setVal(0.5)
sca.addNewMax().setVal(lanes + 0.5)
if (yAx.isSetMajorUnit) yAx.unsetMajorUnit()
yAx.addNewMajorUnit().setVal(1.0)
}
// (προαιρετικά: “ψημένα” Y labels σε κελιά αριστερά — το έχεις ήδη commented)
}
Я хочу заменить значение индекса y, но этот код, который я написал, добавьте метки в каждой точке вместо этого только для первой точки.
Любой совет?>
[code]chart.ctChart.plotArea.scatterChartList.lastOrNull()?.let { sc -> // markers only val st = sc.scatterStyle ?: sc.addNewScatterStyle() st.`val` = STScatterStyle.MARKER
sc.serList.forEachIndexed { i, ser -> // κρύψε γραμμή val sp = if (ser.isSetSpPr) ser.spPr else ser.addNewSpPr() val ln = if (sp.isSetLn) sp.ln else sp.addNewLn() if (!ln.isSetNoFill) ln.addNewNoFill()
// ΣΚΛΗΡΟ reset dLbls if (ser.isSetDLbls) ser.unsetDLbls() val dLbls = ser.addNewDLbls()
// ΜΟΝΟ ένα point label στο idx=0, με ρητό κείμενο από helper κελί val dl = dLbls.addNewDLbl() dl.addNewIdx().`val` = 0L
val ref = CellReference(drawSheet.sheetName, legendRow0 + i, legendCol, true, true) dl.addNewTx().addNewStrRef().f = ref.formatAsString()
// όλα τα flags OFF dl.addNewShowSerName().`val` = false dl.addNewShowVal().`val` = false dl.addNewShowCatName().`val` = false dl.addNewShowLegendKey().`val` = false dl.addNewShowPercent().`val` = false dl.addNewShowBubbleSize().`val` = false
// θέση μόνο για αυτό το point dl.addNewDLblPos().`val` = STDLblPos.L }
// καθάρισε chart-level dLbls για σιγουριά if (sc.isSetDLbls) sc.unsetDLbls() } < /code> Этот код добавляет метки для каждой точки каждой серии. < /p> fun eventsTimelineScatter( drawSheet: XSSFSheet, // ΠΟΥ ζωγραφίζει (π.χ. "Charts") dataSheet: XSSFSheet, // ΑΠΟ ΠΟΥ διαβάζει (π.χ. "PlotData") lastRow: Int, title: String, anchorCols: IntRange, anchorRows: IntRange, xCol: Int, // στήλη χρόνου (Excel serial, numeric) typeCol: Int // στήλη κατηγορίας (String) ) { data class Cat(val key: String, val display: String) fun norm(raw: String?): Cat { val d = (raw ?: "Unknown").trim().replace(Regex("\\s+"), " ") return Cat(d.lowercase().ifEmpty { "unknown" }, d.ifEmpty { "Unknown" }) }
val byType = mutableMapOf>() val keyToDisplay = mutableMapOf() val laneOf = mutableMapOf() fun lane(k: String) = laneOf.getOrPut(k) { laneOf.size + 1 }.toDouble()
var xMin = Double.POSITIVE_INFINITY var xMax = Double.NEGATIVE_INFINITY for (r in 1..lastRow) { val row = dataSheet.getRow(r) ?: continue val cat = norm(row.getCell(typeCol)?.toString()) val x = row.getCell(xCol)?.numericCellValue ?: continue byType.getOrPut(cat.key) { mutableListOf() }.add(x to lane(cat.key)) keyToDisplay.putIfAbsent(cat.key, cat.display) if (x < xMin) xMin = x if (x > xMax) xMax = x } if (byType.isEmpty()) return
// legend helper cells (τίτλοι σειρών από κελί) val legendCol = (anchorCols.last + 2).coerceAtLeast(24) val legendRow0 = anchorRows.first val keysInOrder = keyToDisplay.keys.sortedBy { keyToDisplay[it]!!.lowercase() } keysInOrder.forEachIndexed { i, k -> val row = drawSheet.getRow(legendRow0 + i) ?: drawSheet.createRow(legendRow0 + i) row.createCell(legendCol).setCellValue(keyToDisplay[k]) }
val drawing = drawSheet.createDrawingPatriarch() as XSSFDrawing val anchor = drawing.createAnchor(0,0,0,0, anchorCols.first, anchorRows.first, anchorCols.last+1, anchorRows.last+1) val chart = drawing.createChart(anchor) as XSSFChart
// --- CT tweaks --- // 5) CT: markers only + 1 label (series title) ΜΟΝΟ στο πρώτο point κάθε σειράς // 5) CT: markers only + 1 label (from cell) ΜΟΝΟ στο πρώτο point κάθε σειράς chart.ctChart.plotArea.scatterChartList.lastOrNull()?.let { sc -> // markers only val st = sc.scatterStyle ?: sc.addNewScatterStyle() st.`val` = STScatterStyle.MARKER
sc.serList.forEachIndexed { i, ser -> // κρύψε γραμμή val sp = if (ser.isSetSpPr) ser.spPr else ser.addNewSpPr() val ln = if (sp.isSetLn) sp.ln else sp.addNewLn() if (!ln.isSetNoFill) ln.addNewNoFill()
// ΣΚΛΗΡΟ reset dLbls if (ser.isSetDLbls) ser.unsetDLbls() val dLbls = ser.addNewDLbls()
// ΜΟΝΟ ένα point label στο idx=0, με ρητό κείμενο από helper κελί val dl = dLbls.addNewDLbl() dl.addNewIdx().`val` = 0L
val ref = CellReference(drawSheet.sheetName, legendRow0 + i, legendCol, true, true) dl.addNewTx().addNewStrRef().f = ref.formatAsString()
// όλα τα flags OFF dl.addNewShowSerName().`val` = false dl.addNewShowVal().`val` = false dl.addNewShowCatName().`val` = false dl.addNewShowLegendKey().`val` = false dl.addNewShowPercent().`val` = false dl.addNewShowBubbleSize().`val` = false
// θέση μόνο για αυτό το point dl.addNewDLblPos().`val` = STDLblPos.L }
// καθάρισε chart-level dLbls για σιγουριά if (sc.isSetDLbls) sc.unsetDLbls() }
// Gridlines στον Χ chart.ctChart.plotArea.valAxList.forEach { ax -> if (ax.axPos.`val` == STAxPos.B && !ax.isSetMajorGridlines) ax.addNewMajorGridlines() }
// X axis: datetime format + λίγα ticks chart.ctChart.plotArea.valAxList.firstOrNull { it.axPos.`val` == STAxPos.B }?.let { xAx -> val nf = if (xAx.isSetNumFmt) xAx.numFmt else xAx.addNewNumFmt() nf.formatCode = "yyyy-mm-dd HH:mm:ss"; nf.sourceLinked = false
val range = if (xMax.isFinite() && xMin.isFinite() && xMax > xMin) xMax - xMin else 1.0 val desiredTicks = 6 val step = range / (desiredTicks - 1) val sca = if (xAx.scaling!=null) xAx.scaling else xAx.addNewScaling() if (sca.isSetMin) sca.unsetMin() if (sca.isSetMax) sca.unsetMax() sca.addNewMin().setVal(if (xMin.isFinite()) xMin else 0.0) sca.addNewMax().setVal(if (xMax.isFinite()) xMax else 1.0) if (xAx.isSetMajorUnit) xAx.unsetMajorUnit() xAx.addNewMajorUnit().setVal(step) }
// Y axis: lanes 1..N, χωρίς labels, tick ανά lane val lanes = max(laneOf.size, 1) chart.ctChart.plotArea.valAxList.firstOrNull { it.axPos.`val` == STAxPos.L }?.let { yAx -> (if (yAx.isSetDelete) yAx.delete else yAx.addNewDelete()).setVal(false) val sca = if (yAx.scaling!=null) yAx.scaling else yAx.addNewScaling() if (sca.isSetMin) sca.unsetMin() if (sca.isSetMax) sca.unsetMax() sca.addNewMin().setVal(0.5) sca.addNewMax().setVal(lanes + 0.5) if (yAx.isSetMajorUnit) yAx.unsetMajorUnit() yAx.addNewMajorUnit().setVal(1.0) }
// (προαιρετικά: “ψημένα” Y labels σε κελιά αριστερά — το έχεις ήδη commented) }
[/code] Я хочу заменить значение индекса y, но этот код, который я написал, добавьте метки в каждой точке вместо этого только для первой точки. Любой совет?>