Taylor diagrams

I am faced with the task of producing some Taylor diagrams, a concept that I first came across yesterday
https://climatedataguide.ucar.edu/climate-data-tools-and-analysis/taylo…

Does this functionality exist in Igor? I can only get so far using the Polar Graphs package - for example, I can't do is user ticks from waves (the angle data will need to be non-linear).

Thanks
If the polar graphs package *did* have a user ticks from wave, would that be useful for this?

Or would there be another hurdle to overcome?

--Jim Prouty
Software Engineer, WaveMetrics, Inc.
Here's something to start you off; it draws a blank Taylor diagram. You'll need to change the parameters to match your application of course.

Take a look at the attached screen shot of the Taylor diagram graph.

--Jim


#pragma rtGlobals=3		// Use modern global access method and strict wave access.

Macro RedrawTaylorDiagramTicksGrids()
	RedrawCorrelationTicks()
	RedrawRadiusGrids()
	RedrawIsoLines()
End

Proc RedrawCorrelationTicks()
	Variable labelFontSize=12
	Variable drawMajorCorrelationsGrid=1
	Variable majorCorrelationsTo=0.25
	TopTaylorDiagram(1)
	GetAxis/Q bottom
	Variable ticksAtRadius= V_Max

	fReDrawCorrelationTicks(ticksAtRadius, labelFontSize, drawMajorCorrelationsGrid, majorCorrelationsTo)
End

Proc RedrawRadiusGrids()
	Variable firstRadius = 0.25
	Variable radiusInc = 0.25
	Variable maxRadius = 1.5
	TopTaylorDiagram(1)
	fRedrawRadiusGrids(firstRadius, radiusInc, maxRadius)
End

Proc RedrawIsoLines()
	Variable centerX= 1.15
	Variable centerY= 0
	Variable isoRadiusIncrement= 0.4
	Variable numberOfLines= 4
	TopTaylorDiagram(1)
	fRedrawIsoLines(centerX, centerY, isoRadiusIncrement,numberOfLines)
End

Macro ClearTaylorDiagramTicksAndGrids()

	TopTaylorDiagram(1)
	SetDrawEnv push
	SetDrawLayer/K ProgAxes	// IsoLines
	SetDrawLayer/K ProgBack	// RadiusGrids
	SetDrawLayer/K UserAxes	// CorrelationTicks
	SetDrawEnv pop
End

Function/S TopTaylorDiagram(bringToFront)
	Variable bringToFront
	
	String taylorGraphs= WinList("TaylorDiagram*",";","WIN:1,VISIBLE:1")
	String graphName=StringFromList(0,taylorGraphs)	// first one is the topmost one
	if( strlen(graphName) && bringToFront )
		DoWindow/F $graphName
	endif
	return graphName
End


Function fRedrawIsoLines(centerX, centerY, isoRadiusIncrement,numberOfLines)
	Variable centerX, centerY, isoRadiusIncrement,numberOfLines

	SetDrawEnv push
	SetDrawLayer/K ProgAxes	// Note: different layer than fReDrawCorrelationTicks and fRedrawRadiusGrids
	SetDrawEnv xcoord= bottom,ycoord= left,dash= 0,linefgc=(0,65535,0), fillpat=0,save	// solid, green
	
	// indicate center with a drawn marker
	SetDrawEnv textxjust=1, textyjust=1, textrgb=(0,65535,0)
	DrawText centerX, centerY,"o"
	GetAxis/Q bottom
	Variable radiusMax= V_Max
	Variable i
	for(i=0; i<numberOfLines; i+=1)
		Variable radius= (i+1)*isoRadiusIncrement
		Variable startDegrees=StartAngleFor(centerX, radius, radiusMax)
		Variable endDegrees=StopAngleFor(centerX, radius, radiusMax)
		DrawArc/X/Y centerX, centerY, radius, startDegrees, endDegrees

		Variable labelX, labelY
		FindIntersectionOfArcWithRadius(centerX,radius, labelX, labelY)
		Variable labelRadians= atan2(labelY,centerx-labelx)
		Variable labelDegrees= 90-labelRadians * 180 / pi
		SetDrawEnv textrot=labelDegrees, textxjust=1,textyjust=1,textrgb=(0,65535,0)
		String labelText= num2str(radius)
		DrawText labelX, labelY,labelText
	endfor
	
	SetDrawEnv pop
End

// put label at the intersection of the iso circle
// and a circle centered at the origin with radiusFromZero
Function FindIntersectionOfArcWithRadius(radiusFromZero,radiusFromIso, labelX, labelY)
	Variable radiusFromZero, radiusFromIso
	Variable &labelX, &labelY	// outputs
	
	// math.stackexchange.com/questions/256100/how-can-i-find-the-points-at-which-two-circles-intersect
	Variable x1= 0, y1=0, r1=radiusFromZero				// circle1 centered at the origin with radiusFromZero
	Variable x2= radiusFromZero, y2=0, r2= radiusFromIso	// circle2 at radiusFromZero,0 with radiusFromIso
	
	// -2x(x1-x2) - 2y(y1-y2) = (r1^2-r2^2) - (x1^2 - x2^2) - (y1^2 - y2^2)
	// in our case both y1 and y2 are zero, so we solve for x:
	// -2x(x1-x2) = (r1^2-r2^2) - (x1^2 - x2^2)
	//
	// or x = [(r1^2-r2^2) - (x1^2 - x2^2)]/(2x2-2x1)
	labelX= ((r1*r1-r2*r2) - (x1*x1 - x2*x2))/(2*x2-2*x1)
	labelY= sqrt(radiusFromZero*radiusFromZero - labelX*labelX)
End
	

// returns degrees, presumes the graph's angle range is 0-90 degrees, and the arc center is on the x axis between 0 and radiusMax
Function StartAngleFor(centerX, radius, radiusMax)
	Variable centerX, radius, radiusMax
	
	Variable angleInDegrees= 0
	if( (centerX + radius) > radiusMax )
		// intersects along radiusMax, use Law of Cosines since we know all three side lengths
		Variable numerator= centerX*centerX+radius*radius-radiusMax*radiusMax
		Variable denominator= 2*centerX*radius
		Variable ratio= numerator/denominator
		Variable complementInRadians= acos(ratio)
		angleInDegrees= 180/pi*(pi-complementInRadians)
	endif
	
	return angleInDegrees	
End

// returns degrees, presumes the graph's angle range is 0-90 degrees, and the arc center is on the x axis between 0 and radiusMax
Function StopAngleFor(centerX,  radius, radiusMax)
	Variable centerX,  radius, radiusMax
	
	Variable angleInDegrees= 180
	if( (centerX - radius) < 0 )
		// intersects along x=0
		Variable numerator= centerX
		Variable denominator= radius
		Variable ratio= numerator/denominator
		Variable complementInRadians= acos(ratio)
		angleInDegrees= 180/pi*(pi-complementInRadians)
	endif
	
	return angleInDegrees	
End


Function fRedrawRadiusGrids(firstRadius, radiusInc, maxRadius)
	Variable firstRadius, radiusInc, maxRadius
	
	SetDrawEnv push
	SetDrawLayer/K ProgBack	// Note: different layer than fReDrawCorrelationTicks and fRedrawIsoLines
	SetDrawEnv xcoord= bottom,ycoord= left,dash= 3,save

	// always draw a grid at radius=1 as solid black
	Variable radius= 1.0
	SetDrawEnv xcoord= bottom,ycoord= left,dash= 0,linefgc=(0,0,0),fillpat= 0, save
	DrawArc /X/Y 0,0,radius,0,90
	
	// Draw the other grids as dashed blue
	SetDrawEnv dash=3, linefgc=(32768,40777,65535),save	// light blue
	for( radius= firstRadius; radius <= maxRadius; radius += radiusInc )
		if( abs(radius - 1.0) > radiusInc/4 )	// don't draw over the black grid
			DrawArc /X/Y 0,0,radius,0,90
		endif
	endfor
	SetDrawEnv pop
End

Function fReDrawCorrelationTicks(ticksAtRadius, labelFontSize, drawMajorCorrelationsGrid, majorCorrelationsTo)
	Variable ticksAtRadius
	Variable labelFontSize
	Variable drawMajorCorrelationsGrid
	Variable majorCorrelationsTo
	
	// the following should perhaps be inputs instead of being hard-coded here
	Make/O majorTicks={0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1.0 }
	Make/O minorTicks={0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.91, 0.92, 0.93, 0.94, 0.96, 0.97, 0.98}
	Variable majorTickLen = 0.04	// radius units
	Variable minorTickLen = majorTickLen * 0.5	// radius units

	SetDrawEnv push
	SetDrawLayer/K UserAxes
	SetDrawEnv xcoord= bottom,ycoord= left,dash= 0,fsize=labelFontSize,fillpat=0,save
	DrawArc /X/Y 0,0,ticksAtRadius,0,90	// draw axis

	Variable i, n, innerRadius, correlation, x1, x2, y1, y2, radiansForCorrelation, yScale, labelDegrees, labelRadius

	// draw major ticks
	n= numpnts(majorTicks)
	innerRadius= ticksAtRadius - majorTickLen
	for(i=0; i<n; i+=1 )
		correlation= majorTicks[i]
		x1 = innerRadius * correlation
		x2 = ticksAtRadius * correlation
		radiansForCorrelation = acos(correlation)
		yScale= sin(radiansForCorrelation)
		y1 = innerRadius * yScale
		y2 = ticksAtRadius * yScale
		if( drawMajorCorrelationsGrid && correlation != 0 && correlation != 1.0 )
			SetDrawEnv push
			SetDrawEnv dash=3, linefgc=(32768,40777,65535)	// light blue
			DrawLine majorCorrelationsTo*correlation,majorCorrelationsTo* yScale,x1,y1
			SetDrawEnv pop
		endif
		DrawLine x1,y1,x2,y2
		String labelText= num2str(correlation)
		labelDegrees= radiansForCorrelation * 180 / pi
		labelRadius= ticksAtRadius + majorTickLen
		SetDrawEnv push
		SetDrawEnv rotate=labelDegrees, textyjust=1
		DrawText labelRadius,0,labelText
		SetDrawEnv pop
	endfor

	// draw minor ticks
	n= numpnts(minorTicks)
	innerRadius= ticksAtRadius - minorTickLen
	for(i=0; i<n; i+=1 )
		correlation= minorTicks[i]
		x1 = innerRadius * correlation
		x2 = ticksAtRadius * correlation
		radiansForCorrelation = acos(correlation)
		yScale= sin(radiansForCorrelation)
		y1 = innerRadius * yScale
		y2 = ticksAtRadius * yScale
		DrawLine x1,y1,x2,y2
	endfor

	SetDrawEnv pop
End

Macro NewTaylorDiagram() : Graph
	PauseUpdate; Silent 1		// building window...
	Make/O/N=1 invisible=NaN
	Display /W=(239,135,706,576)/N=TaylorDiagram invisible as "Taylor Diagram"
	ModifyGraph margin(left)=64,margin(bottom)=47,margin(top)=41,margin(right)=50,width={Plan,1,bottom,left}
	ModifyGraph standoff=0
	ModifyGraph manTick(left)={0,0.25,0,2},manMinor(left)={0,50}
	ModifyGraph manTick(bottom)={0,0.25,0,2},manMinor(bottom)={0,50}
	SetAxis left 0,1.7
	SetAxis bottom 0,1.7
	TextBox/C/N=text0/O=-50/F=0/A=MC/X=32.29/Y=26.91 "Correlation"
EndMacro


--Jim Prouty
Software Engineer, WaveMetrics, Inc.
TaylorDiagram.png (51.67 KB)