Two X-Trace on Easy Chart

Hello,

Is there a way I can get two X trace on easy chart? I need to see two values on two time stamp at the same time.

I am sure someone must have done this in past. Let me know.

Thank you.

No, sorry. It has been on my to-do list for my NoteChart module for ages.

Well, this is Vision, so just about anything that is not included with the module is theoretically possible if you are willing to build it yourself. The only question is how deep down the rabbit hole a developer is willing to go to get the result they want.

In this case, the crosshair could be created with a value marker, and the labels could be created with xy text annotations. If you only ever need two x traces at the same time, then I would create a custom property on the parent container of the easy chart to store the current xtrace crosshair location and associated properties. Then, anytime the crosshair is moved, use the data to create a second crosshair in the old location before replacing the data with the current crosshair location. Messing around with this from the mouse clicked event handler of the easy chart, I was able to achieve this result:

To do this, I created custom property with a dataset datatype on the parent container called easyChartXTrace:
image

Here is the script I developed for the mouseClicked event handler:

# If this is a left click
if event.button == 1:
	# Get the jfreechart [panel] and its plot
	# event.source.getComponent(0).getComponent(0) is the way most people do this, but I'm in the habit of not trusting subcomponent heiarchies to be static
	chartField = event.source.getClass().getDeclaredField('chart')
	chartField.setAccessible(True)
	easyChart = chartField.get(event.source)
	plot = easyChart.chart.plot
	
	# If the chart is in XTrace mode:
	if plot.annotationMode == 2:
	
		# If the easyChartXTrace dataset exists, add the crosshair at the location the dataset provides:
		if event.source.parent.easyChartXTrace:
			event.source.addSecondaryCrosshair(plot)
			
		# Get the data for the current xtrace location,
		# ...and save it to the easyChartXTrace custom property on the parent container
		event.source.setCrosshairData(easyChart, plot)
	else:
		# If the chart is not in xtrace mode, delete any previous xtrace position data
		event.source.parent.easyChartXTrace = None

If there is a left click and the chart is in xtrace mode, the mouse clicked event handler calls on two custom methods that I added to the easy chart.
image

Note the required parameters for each one:
image

image

Here is the setCrosshairData script:

#def setCrosshairData(self, easyChart, plot):
	# Get the xtrace crosshair location
	chartX = plot.domainCrosshairValue
	
	# Gtet the bounds for the default range axis
	displayedUpperBound = plot.rangeAxis.upperBound
	displayedLowerBound = plot.rangeAxis.lowerBound
	
	# Create an empty list and headers for the easyChartXTrace custom property dataset
	data = []
	headers = 'X', 'Y', 'TEXT', 'COLOR'
	
	# Iterate through each plot dataset
	# There will be a seperate dataset for each range axis that is displayed in the chart
	for datasetIndex in range(plot.getDatasetCount()):
		dataset = plot.getDataset(datasetIndex)
		
		# Get the upper and lower bounds for the current dataset's range axis
		# for scaling purposes
		rangeAxis = plot.getRangeAxisForDataset(datasetIndex)
		upperBound = rangeAxis.upperBound
		lowerBound = rangeAxis.lowerBound
		
		# Locate the relevent item in the dataset for the crosshair location and retrieve the y value
		for seriesIndex in range(dataset.seriesCount):			

			# Grab the pen name from the dataset and iterate through get the pen color from whichever pen dataset the chart is using
			penName = dataset.getSeriesKey(seriesIndex)
			tagPens = self.tagPens # Change this to the relevent dataset if needed
			for row in range(tagPens.rowCount):
				if tagPens.getValueAt(row, 'NAME') == penName:
					color = tagPens.getValueAt(row, 'COLOR')
			
			for itemIndex in range(dataset.getItemCount(seriesIndex)):
				if chartX == dataset.getXValue(seriesIndex, itemIndex):
					yValue = dataset.getYValue(seriesIndex, itemIndex)
					
					# Scale the y value, so it is displayed correctly on the default range axis
					yPercent = (yValue - lowerBound) / (upperBound - lowerBound)
					scaledYValue = displayedLowerBound + yPercent * (displayedUpperBound - displayedLowerBound)
					
					# Convert the annotation to a string, and limit it to two decimal places
					annotationText = "%s: %f" % (penName, yValue)
					
					# Add all the collected data as a row to the dataset data and stop iterating through the item indexes
					data.append([chartX, scaledYValue, annotationText, color])
					break
		
		# Create a dataset with the relevent data, and assign it to the parent container's easyChartXTrace custom property
		self.parent.easyChartXTrace = system.dataset.toDataSet(headers, data)

Here is the addSecondaryCrosshair script:

#def addSecondaryCrosshair(self, plot):
	# Import the stuff needed to make this happen
	from org.jfree.chart.plot import ValueMarker
	from org.jfree.chart.annotations import XYTextAnnotation
	from java.awt import BasicStroke, Font
	
	# Get the dataset and the crosshair location
	dataset  = self.parent.easyChartXTrace
	chartX = dataset.getValueAt(0, 'X')
	
	# Clear any existing marker and annotations
	plot.clearDomainMarkers()
	plot.clearAnnotations()
	
	# Dashed Crosshiar Stroke
	boldDashed = BasicStroke(
		1.0,					# Width of the line
		BasicStroke.CAP_ROUND,	# Line cap style
		BasicStroke.JOIN_ROUND,	# Line join style
		1.0,					# Miter Limit
		[2.0, 2.0],				# Dash Pattern (2 pixels on, 2 pixels off)
		0.0						# Dash Phase
		)
	
	# Set the crosshair and its properties
	crosshair = ValueMarker(chartX)
	crosshair.setStroke(boldDashed)
	crosshair.setPaint(system.gui.color('black'))
	plot.addDomainMarker(crosshair)
	
	# Adjust the font size of the annotations
	annotationFont = Font(Font.MONOSPACED, Font.PLAIN, 13)
	
	# Iterate through the dataset, and use the row values to set the annotations
	for row in range(dataset.rowCount):
		chartY = dataset.getValueAt(row, 'Y')
		annotationText = dataset.getValueAt(row, 'TEXT')
		color = dataset.getValueAt(row, 'COLOR')
		
		# Create a border effect with an underlaid annotation
		borderAnnotation = XYTextAnnotation(u'█' * len(annotationText), chartX, chartY)
		borderAnnotation.setFont(annotationFont)  # Slightly larger font size
		borderAnnotation.setPaint(color)		
		plot.addAnnotation(borderAnnotation)
		
		# Underlay the annotation text with the plot background, so the pen lines don't make the letters hard to see
		backgroundAnnotation = XYTextAnnotation(u'█' * len(annotationText), chartX, chartY)
		backgroundAnnotation.setPaint(self.plotBackground)
		backgroundAnnotation.setFont(annotationFont)
		plot.addAnnotation(backgroundAnnotation)
		
		# Set the actual annotation
		annotation = XYTextAnnotation(annotationText, chartX, chartY)
		annotation.setPaint(color)
		annotation.setFont(annotationFont)
		plot.addAnnotation(annotation)
3 Likes