Vision Chart (not EasyChart) Min & Max Date

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:

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

propertyChange event handler
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
#def getXTraceLabel(self, chart, penName, yValue):
	return ''