# Vision Chart (not EasyChart) Min & Max Date

**URL:** <https://forum.inductiveautomation.com/t/vision-chart-not-easychart-min-max-date/70606>\
**Category:** Ignition\
**Tags:** ignition81, vision, scripting\
**Created:** [March 3, 2023, 8:10pm UTC](https://forum.inductiveautomation.com/t/vision-chart-not-easychart-min-max-date/70606 "2023-03-03T20:10:30Z")\
**Posts on this page:** 1\
**Showing post:** 12

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 9, 2023, 10:01am UTC](https://forum.inductiveautomation.com/t/vision-chart-not-easychart-min-max-date/70606/12 "2023-03-09T10:01:03Z")

</div>

The above script produced a result that looked good, but after playing around with it, I didn't like how the labels overlapped making some of them unreadable, so for fun, I developed the script further. It now adjusts the label positions slightly if they overlap.

**Here is the result:**

[https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/9/19d5722627d2bee26c32f8f7fca038d5d9125a9f.mp4](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/9/19d5722627d2bee26c32f8f7fca038d5d9125a9f.mp4)

**Here are the updated scripts:** (In case anybody wants to use them or develop them further)

> **propertyChange event handler**
>
> ```auto
> if event.propertyName == 'selectedXValue':
> from org.jfree.chart.annotations import XYTextAnnotation
> from java.awt.font import FontRenderContext, TextLayout
> from java.awt import Color, Font
> plot = event.source.chart.plot
> data = event.source.Data #or whatever the custom data property is
> backgroundFont = Font(Font.MONOSPACED, Font.PLAIN, 18)
> annotationFont = Font(Font.MONOSPACED, Font.BOLD, 18)
> def getYValues(layout, yMidpoint):
> rangeAxis = plot.rangeAxis
> rangeHeight = float(rangeAxis.upperBound - rangeAxis.lowerBound)
> chartHeight = float(event.source.chartRenderingInfo.chartArea.height)
> labelHeight = float(layout.bounds.height)
> labelRange = (labelHeight/chartHeight) * rangeHeight
> y1 = float(yMidpoint) - (labelRange * .5)
> y2 = float(yMidpoint) + (labelRange * .5)
> return [y1, y2]
> def getAnnotationX(layout, domainLocation): #Ensures labels stay within the bounds of the chart even when crosshair is near the edge
> domainAxis = plot.domainAxis
> domainWidth = domainAxis.upperBound - domainAxis.lowerBound
> midPoint = domainAxis.lowerBound + (domainWidth * .5)
> chartWidth = event.source.chartRenderingInfo.chartArea.width
> pixelOffset = layout.bounds.width *.5
> offsetRatio = pixelOffset/float(chartWidth)
> domainOffset = domainWidth * offsetRatio
> if domainLocation > midPoint:
> return (domainLocation - domainOffset)
> else:
> return (domainLocation + domainOffset)
> def getAnnotationText(row, column):
> penName = data.getColumnName(column)
> penValue = data.getValueAt(row, column)
> text = penName + ': ' +str(penValue)
> return text
> def adjustAnnotations(annotations):
> adjustedHeaders = ['column', 'text', 'x', 'y1', 'y2']
> adjustedData = []
> sortedData = system.dataset.sort(annotations, 3)
> lowerBound = plot.rangeAxis.lowerBound
> upperBound = plot.rangeAxis.upperBound
> previousY2 = None
> previousY1 = None
> for row in range(sortedData.rowCount):
> column = sortedData.getValueAt(row, 0)
> text = sortedData.getValueAt(row, 1)
> x = sortedData.getValueAt(row, 2)
> y1 = sortedData.getValueAt(row, 3)
> y2 = sortedData.getValueAt(row, 4)
> if y2 > lowerBound and y1 < upperBound:
> if row == 0:
> labelHeight = y2 - y1
> lowerLabelY1 = float(lowerBound)
> lowerLabelY2 = lowerLabelY1 + (2 * labelHeight)
> if y1 < lowerLabelY2:
> yOffset = float(lowerLabelY2 - y1)
> offsetY1 = y1 + yOffset
> offsetY2 = y2 + yOffset
> adjustedData.append([column, text, x, offsetY1, offsetY2])
> previousY2 = offsetY2
> else:
> adjustedData.append([column, text, x, y1, y2])
> previousY2 = y2
> else:
> if y1 < previousY2:
> yOffset = float(previousY2 - y1)
> offsetY1 = y1 + yOffset
> offsetY2 = y2 + yOffset
> adjustedData.append([column, text, x, offsetY1, offsetY2])
> previousY2 = offsetY2
> else:
> adjustedData.append([column, text, x, y1, y2])
> previousY2 = y2
> adjustedDataset = system.dataset.toDataSet(adjustedHeaders, adjustedData)
> finalSortedData = system.dataset.sort(adjustedDataset, 3, False)
> finalizedData = []
> for row in range(finalSortedData.rowCount):
> column = finalSortedData.getValueAt(row, 0)
> text = finalSortedData.getValueAt(row, 1)
> x = finalSortedData.getValueAt(row, 2)
> y1 = finalSortedData.getValueAt(row, 3)
> y2 = finalSortedData.getValueAt(row, 4)
> if row == 0:
> if y2 > upperBound:
> yOffset = float(y2 - upperBound)
> offsetY1 = y1 - yOffset
> offsetY2 = y2 - yOffset
> finalizedData.append([column, text, x, offsetY1, offsetY2])
> previousY1 = offsetY1
> else:
> finalizedData.append([column, text, x, y1, y2])
> previousY1 = y1
> else:
> if y2 > previousY1:
> yOffset = float(y2 - previousY1)
> offsetY1 = y1 - yOffset
> offsetY2 = y2 - yOffset
> finalizedData.append([column, text, x, offsetY1, offsetY2])
> previousY1 = offsetY1
> else:
> finalizedData.append([column, text, x, y1, y2])
> previousY1 = y1
> return system.dataset.toDataSet(adjustedHeaders, finalizedData)
> def getAnnotations():
> domainLocation = plot.domainCrosshairValue
> headers = ['column', 'text', 'x', 'y1', 'y2']
> subData = []
> for row in range(data.rowCount):
> if data.getValueAt(row, 0).getTime() == domainLocation:
> for column in range(1, data.columnCount):
> text = getAnnotationText(row, column)
> layout = TextLayout(u'█' * len(text), annotationFont, FontRenderContext(None, False, False))
> x = getAnnotationX(layout, domainLocation)
> yMidpoint = data.getValueAt(row, column)
> yValues = getYValues(layout, yMidpoint)
> y1 = yValues[0]
> y2 = yValues[1]
> subData.append([column, text, x, y1, y2])
> annotationData = system.dataset.toDataSet(headers, subData)
> annotations = adjustAnnotations(annotationData)
> return annotations			
> def setAnnotation():
> annotations = getAnnotations()
> for row in range(annotations.rowCount):
> column = annotations.getValueAt(row, 0)
> text = annotations.getValueAt(row, 1)
> x = annotations.getValueAt(row, 2)
> y1 = annotations.getValueAt(row, 3)
> y2 = annotations.getValueAt(row, 4)
> y = y1 + ((y2 - y1) * .5)
> background = XYTextAnnotation(u'█' * len(text), x, y)
> background.setPaint(Color.WHITE)
> background.setFont(backgroundFont)
> plot.addAnnotation(background)		
> annotation = XYTextAnnotation(text, x, y)
> textColor = plot.renderer.getSeriesPaint(column-1)
> annotation.setPaint(textColor)
> annotation.setFont(annotationFont)
> plot.addAnnotation(annotation)
> for annotation in plot.getAnnotations():
> if isinstance(annotation, XYTextAnnotation):
> plot.removeAnnotation(annotation)
> if event.source.chart.plot.annotationMode == 2: #This line ensures that the labels will only be added in XTrace Mode
> setAnnotation()
> 
> ```

> **getXTraceLabel extension function**
>
> ```auto
> #def getXTraceLabel(self, chart, penName, yValue):
> return ''
> 
> ```

---

_[View the full topic](https://forum.inductiveautomation.com/t/vision-chart-not-easychart-min-max-date/70606)._
