# Gauge properties

**URL:** <https://forum.inductiveautomation.com/t/gauge-properties/75335>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [June 21, 2023, 1:12pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335 "2023-06-21T13:12:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![dan\_mueller](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@dan\_mueller](https://forum.inductiveautomation.com/u/dan_mueller)\
**Post date:** [June 21, 2023, 1:12pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/1 "2023-06-21T13:12:22Z")

</div>

Is it possible to add an additional value to a gauge? I want to add another needle.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/4/7470f87fdf995c6262fa689a6660e968bdb0fa63.png)

---

<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:** [June 21, 2023, 4:32pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/2 "2023-06-21T16:32:42Z")

</div>

I love this idea. What kind of gauge is this? Is this Perspective?

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [June 21, 2023, 4:35pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/3 "2023-06-21T16:35:35Z")

</div>

You can do, just about anything you want with an SVG in perspective. And Vision is possibly easier.

Perspective:

> [@Perspective, faster page](https://forum.inductiveautomation.com/t/perspective-faster-page/69885/35):
>
> Okay, I had a bit of free time today, so here is a complete SVG solution. You could do the Label as SVG text as well, or a gauge needle if you wanted, I haven't gone that far. Just a little example of the power of using the Ignition Designer to add SVG elements. I didn't include the label for the example. Note: All of this (outside of the calculations needed to find the points for the shape) was done inside of the designer (yes I can be somewhat of a masochist at times. rofl). First a s…

Vision:

> [@Rotating graphic is changing sizes at random](https://forum.inductiveautomation.com/t/rotating-graphic-is-changing-sizes-at-random/71822/26):
>
> Well if you want it, here is what I threw together. You of course can add more customization if needed or wanted. 
> 
> > **repaint script**
> >
> > from java.awt import Color from java.awt import RadialGradientPaint from java.awt import MultipleGradientPaint as mgp from java.awt.geom import Rectangle2D from java.awt.geom import Ellipse2D from java.awt.geom import Line2D from java.awt.geom import Point2D from java.awt import BasicStroke from java.awt.geom import Path2D import math g = event.graphics #Watch Body …

Perhaps if I have some free time, I'll build one with two needles. Sounds fun.

---

<div class="post-metadata">

**Author:** ![dan\_mueller](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@dan\_mueller](https://forum.inductiveautomation.com/u/dan_mueller)\
**Post date:** [June 21, 2023, 4:43pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/4 "2023-06-21T16:43:33Z")

</div>

yes

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [July 10, 2023, 6:10pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/5 "2023-07-10T18:10:00Z")

</div>

Okay, so I may have gotten a little sniped with this, but it was fun once I got into it.

The below image shows two SVG elements that are exactly the same as far as the programming goes, but the configuration is different using custom properties.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/a/2a75178a1c32b04c023d84923837d0bedb73e3df.png)

This was accomplished with a property change event on a custom property and a couple of property bindings with script transforms. If I were planning to do this in a production environment I would move the scripting to a project library at a minimum, there are probably some other optimizations that could be had.

The property change refreshes the bindings so any change to custom property results in a change to the SVG.

Here is the structure for the custom property. Be aware that as you add things to the config you will see some errors until the entire structure for each matches. The simplest thing is to duplicate the objects and then change the values to your needs.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/b/0b5b412dd55ffd8c73d6667e49778862202b0772.png)

> **Property Change Script**
>
> ```auto
> import math
> majTickDegrees = self.custom.config.gauge.ticks.major.degreesPerTick
> tickRange = self.custom.config.gauge.ticks.scale.max - self.custom.config.gauge.ticks.scale.min
> degBetweenTicks = 1
> 	
> center = {'x':self.custom.config.gauge.center.x,'y':self.custom.config.gauge.center.y}
> 	
> tickStartY = center['y'] + self.custom.config.gauge.radius
> tickEndY = tickStartY + self.custom.config.gauge.ticks.major.length
> 	
> minTickDegrees = majTickDegrees / float(self.custom.config.gauge.ticks.minor.graduations)
> gaugeHeight = self.custom.config.gauge.radius * math.cos(math.radians(self.custom.config.gauge.endAngle) - math.pi/2) + center['y']
> 	
> if majTickDegrees and majTickDegrees != self.custom.config.gauge.ticks.scale.max:
> degBetweenTicks = (self.custom.config.gauge.endAngle - self.custom.config.gauge.startAngle) / (tickRange / float(majTickDegrees))
> 	
> if self.custom.config.gauge.ticks.major.visible:
> self.props.elements[1].elements[0].elements = [{
> 'type':'line',
> 'stroke':{'paint': self.custom.config.gauge.ticks.major.color},
> 'x1':center['x'],
> 'y1':tickStartY,
> 'x2':center['x'],
> 'y2':tickEndY,
> 'style':{'transform':'rotate({}deg)'.format((degBetweenTicks * thisTick) + self.custom.config.gauge.startAngle),
> 'transform-origin':'{x}px {y}px'.format(**center)}
> } for thisTick in range((tickRange / majTickDegrees) + 1)]
> else:
> self.props.elements[1].elements[0].elements = []
> 	
> if self.custom.config.gauge.ticks.text.visible:
> self.props.elements[1].elements[2].elements = [{
> 'type':'text',
> 'fill':{'paint':self.custom.config.gauge.ticks.text.color},
> 'text': '{:d}'.format(int(thisTick * majTickDegrees + self.custom.config.gauge.ticks.scale.min)),
> 'text-anchor':'middle',
> 'text-align':'center',
> 'x': center['x'],
> 'y': center['y'] + self.custom.config.gauge.radius,
> 'style':{'transform':'translate({}px, {}px)'.format(-(self.custom.config.gauge.radius+self.custom.config.gauge.ticks.text.offset) * math.cos(math.radians((degBetweenTicks * thisTick) + self.custom.config.gauge.startAngle) - math.pi/2),-self.custom.config.gauge.radius + 5 - (self.custom.config.gauge.radius+self.custom.config.gauge.ticks.text.offset)*math.sin(math.radians((degBetweenTicks * thisTick) + self.custom.config.gauge.startAngle) - math.pi / 2)),
> 'transform-origin': '{x}px {y}px'.format(**center),
> 'font-family':self.custom.config.gauge.ticks.text.font.family,
> 'font-size': self.custom.config.gauge.ticks.text.font.size}
> } for thisTick in range((tickRange/majTickDegrees) + 1)]
> else:
> self.props.elements[1].elements[2].elements = []
> 		
> 	
> tickEndY = tickStartY + self.custom.config.gauge.ticks.minor.length
> 	
> if minTickDegrees and minTickDegrees != self.custom.config.gauge.ticks.scale.max:
> degBetweenTicks = (self.custom.config.gauge.endAngle - self.custom.config.gauge.startAngle) / (tickRange / float(minTickDegrees))
> 		
> if self.custom.config.gauge.ticks.minor.visible:
> self.props.elements[1].elements[1].elements = [{
> 'type':'line',
> 'stroke':{'paint':self.custom.config.gauge.ticks.minor.color},
> 'x1':center['x'],
> 'y1':tickStartY,
> 'x2':center['x'],
> 'y2':tickEndY,
> 'style':{'transform':'rotate({}deg)'.format((degBetweenTicks * thisTick) + self.custom.config.gauge.startAngle),
> 'transform-origin':'{x}px {y}px'.format(**center)}
> } for thisTick in range(int((majTickDegrees/minTickDegrees) * (tickRange/majTickDegrees)) + 1) if (thisTick % self.custom.config.gauge.ticks.minor.graduations)]
> else:
> self.props.elements[1].elements[1].elements = []
> 		
> degBetweenTicks = (self.custom.config.gauge.endAngle - self.custom.config.gauge.startAngle)/float(tickRange)
> 	
> if self.custom.config.gauge.needles:
> elems = []
> for needle in self.custom.config.gauge.needles:
> elems.append({
> 'type':'circle',
> 'fill':{'paint':needle.color},
> 'cx':center['x'],
> 'cy':center['y'],
> 'r':3										
> })
> elems.append({'type':'path',
> 'fill':{'paint':needle.color},
> 'stroke':{'paint':needle.color,'width':1.0},
> 'd':'M{},{} m{},{} L{},{} L{},{}'.format(center['x'],center['y'],0,self.custom.config.gauge.radius,center['x'] + 1.5,center['y'],center['x'] - 1.5,center['y']),
> 'style':{'transform':'rotate({}deg)'.format((degBetweenTicks * needle.value) + self.custom.config.gauge.startAngle),
> 'transform-origin':'{x}px {y}px'.format(**center)}
> })
> self.props.elements[3].elements = [{'type':'group',
> 'elements':elems
> }]
> else:
> self.props.elemenst[3].elements = []
> self.refreshBinding('props.elements[0].elements')
> self.refreshBinding('props.elements[2].d')
> 
> ```

Binding on `props.elements[0].elements`. Bound to `this.custom.config.gauge.fill` this draws the filled areas in the gauge.

> **Script Transform for Blocks**
>
> ```auto
> import math
> blocks = []
> startAngle = self.custom.config.gauge.startAngle
> gaugeRadius = self.custom.config.gauge.radius - value.gap
> gaugeScale = (self.custom.config.gauge.endAngle - self.custom.config.gauge.startAngle) / float(self.custom.config.gauge.ticks.scale.max - self.custom.config.gauge.ticks.scale.min)
> for block in value.blocks:
> l1x1 = value.insideRadius * math.cos(math.radians(block.startValue * gaugeScale + startAngle) + math.pi/2)
> l1y1 = value.insideRadius * math.sin(math.radians(block.startValue * gaugeScale + startAngle) + math.pi/2)
> l1x2 = gaugeRadius * math.cos(math.radians(block.startValue * gaugeScale + startAngle) + math.pi/2)
> l1y2 = gaugeRadius * math.sin(math.radians(block.startValue * gaugeScale + startAngle) + math.pi/2)
> l2x1 = gaugeRadius * math.cos(math.radians(block.endValue * gaugeScale + startAngle) + math.pi/2)
> l2y1 = gaugeRadius * math.sin(math.radians(block.endValue * gaugeScale + startAngle) + math.pi/2)
> l2x2 = value.insideRadius * math.cos(math.radians(block.endValue * gaugeScale + startAngle) + math.pi/2)
> l2y2 = value.insideRadius * math.sin(math.radians(block.endValue * gaugeScale + startAngle) + math.pi/2)
> 		
> blocks.append( 'M{12},{13} m{0},{1} l{2},{3} a{4},{4} 0 0,1 {5},{6} l{7},{8} a{9},{9} 0 0,0 {10},{11}'.format(
> l1x1,
> l1y1,
> l1x2-l1x1,
> l1y2-l1y1,
> gaugeRadius,
> l2x1 - l1x2,
> l2y1 -l1y2,
> l2x2 - l2x1,
> l2y2 - l2y1,
> value.insideRadius,
> l1x1 - l2x2,
> l1y1 - l2y2,
> self.custom.config.gauge.center.x,
> self.custom.config.gauge.center.y))
> return	[{'type':'path',
> 'fill':{'paint':value.blocks[i].color},
> 'd':block} for i,block in enumerate(blocks)]
> 
> ```

Binding on `props.elements[2].d`. Bound to `this.custom.config.gauge` this draws the arc that is the base for the tick marks.

> **Script Transform for Baseline**
>
> ```auto
> import math
> 	
> startAngle = self.custom.config.gauge.startAngle
> gaugeRadius = self.custom.config.gauge.radius
> 	
> x1 = -gaugeRadius * math.cos(math.radians(startAngle) - math.pi/2)
> y1 = -gaugeRadius * math.sin(math.radians(startAngle) - math.pi/2)
> x2 = -gaugeRadius * math.cos(math.radians(self.custom.config.gauge.endAngle) - math.pi/2)
> y2 = -gaugeRadius * math.sin(math.radians(self.custom.config.gauge.endAngle) - math.pi/2)
> 	
> return 'M{0},{1} m{2},{3} a{4},{4} 0 1,1 {5},{6}'.format(self.custom.config.gauge.center.x,self.custom.config.gauge.center.y,x1,y1,gaugeRadius,x2-x1,y2-y1)
> 
> ```

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [July 10, 2023, 6:52pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/6 "2023-07-10T18:52:18Z")

</div>

I don't think there's need to develop anything special.  
This demo uses three gauges superimposed in a coordinate container.

![Triple gauge](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/3/0337abb5aca82014b1fcdcca7e85bce73cba14f9.gif)

Just set the color of anything causing a problem to transparent by adding a `00` to the end of the hex color. e.g., `#aa000000` will give transparent red.

I'd suggest going a bit less garish with your colors. Have a look at high-performance HMI standards to give you some ideas. i.e., your gauge would have been considered great in the early 1980s but looks way out of date now. Search on web or YouTube for ISA101. They have fascinating guidelines on the use (and misuse) of color and how it affects situational awareness.

For example:  
 ![HP HMI guages](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/2/d2a715d78cd84635cde6b0b911c51313e902dadf.png)

_Image source: [World of Instrumentation](https://worldofinstrumentation.com/how-to-upgrade-your-standard-hmi-to-high-performance-hmi/)_.

How long did it take you to read six gauges and spot the tank running low and the tank empty?

Be cool, man. Be cool!

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [July 10, 2023, 7:15pm UTC](https://forum.inductiveautomation.com/t/gauge-properties/75335/7 "2023-07-10T19:15:39Z")

</div>

> [@Transistor](#):
>
> I don't think there's need to develop anything special.  
> This demo uses three gauges superimposed in a coordinate container.

Except for the performance loss, particularly if you're going to have many of these on a single view.
