Creating an SVG-based temperature color band in Power Apps

Note: Apparently, WordPress and SVG code do NOT go together well. I'm going to try and figure out a solution but, in the meantime, I will put in screen shots of the SVG related code so it can be seen at least. Also, it's all in the sample app which you can download at the bottom of the post.

Note #2: I couldn't find a great solution to display the SVG code so I am providing the Word document where I drafted this post. It has the code so you can copy and paste if you want to build the app in the way described. Again, my apologies for the less the stellar experience! I will be learning some good lessons for the next time I post about SVGs!

One of my all-time favorite apps is the iOS weather app. The way that it communicates the current and upcoming weather conditions is nothing short of beautiful. While the “live weather” animations at the top are awesome, what I really like are the temperature gradient bars for the 10-day forecast.

At this point, you are likely thinking (sarcastically), “Wow wyotim, with a favorite app like that you must be great fun at parties.” And you are not wrong; I’m not much fun at parties…but I am pretty okay-ish at creating a color band to represent temperatures, which is what this post will be about. 😁

Let’s look at what we are going to make:

We’ll start by looking at the SVG that makes it possible, dive in to how we can create a single temperature band, and then how to create two temperature bands that scale relative to the two temperature ranges. Pretty exciting stuff, right? I bet you are rethinking that whole “fun at parties” thing now, huh? No? That’s okay, you are still right. Anyway, let’s get started!

The temperature band SVG

Let’s look at the SVG that will make all this possible.

To be fair, that is a raster image of the SVG, but I digress. What this represents is a temperature range of (from left to right) -100F to 200F, or for the more civilized types, -73.33C to 93.33C.

This temperature range is on purpose as it gives a wide enough range to cover all the temperatures that a person will experience (hopefully! 😬), and it gave an even range of 300 to help me sort out how to make this thing work. The changes in colors are strategic as well, representing relative comfort ranges. I made this a while ago and didn’t take good notes on the source that I used for these ranges. I was unable to find it at the time of writing, but if I can track it down, I will cite it here later.

Here is the code for the SVG itself, in which you can see where the color breaks happen.

Here are the temperature ranges that the color changes represent, in the same order that they appear in the SVG code:

Gradient OffsetColorLow Temp, °FHigh Temp, °FLow Temp, °CHigh Temp, °C
0%Light pink-100-41-73.33-41
20%Pink-40-17-40-28
28%Purple-168-27-14
36.33%Blue931-13-1
44%Light blue324808
49.67%Cyan4958914
53%Seafoam green59651518
55.22%Green66711921
57.44%Yellow green72782225
59.67%Yellow79882631
63%Orange89993237
66.67%Red1001133845
71.33%Dark red1141994693.32
100%Darker red20020093.3393.33

One item of note: the color space I used is HSL, which stands for Hue, Saturation, and Lightness. This choice was, again, intentional as it allows three useful functions.

First, setting the colors (i.e. the Hues) in the gradient is super simple as it is based on degrees around the color wheel with red being at 0.

Second, it allows an easy way to add lighter or darker colors (i.e. the Lightness), which is done at the extremes of the color band. A pure color is at 50% in HSL, so you can set higher or lower values to darken or lighten the hue, respectively. A color space like HSB wouldn’t allow that as you really can only darken colors in the same hue due to the pure color being at 100% of the Brightness, so more complex adjustments would need to come in to play.

Third, it is possible to fit the color band to the existing color scheme of an app by adjusting the Saturation value to the same (or a similar value) as a color said scheme, like a primary color for instance. This means that the color band can have a defined structure that can make visual sense in the app it is being used in. I won’t cover this in this post, but I’m happy to write up how that would work in a later post if anyone were interested.

Now that we have a way to show off a range of temperatures, how do we make it show the temperature range we currently want to see? To do this, we need to define a space that the range will exist in. For a single temperature range, this is a trivial step: the space is just the size that you want the temperature range to exist in, such as the space between the high and low temperatures in the example picture. With two or more ranges, this gets a bit more complex as we probably want the temperature bands to line up so that we can compare the differences in the temperature extremes between the ranges and still have the colors line up properly so that the temperature color on one corresponds with the same temperature on the other(s). We’ll start with the single range.

Single range gradient

We’ll begin by creating a new, blank Power App (either tablet or phone will work but I will be using tablet) and adding three controls: two text input controls and one image control. Size and arrange them like the image below.

Rename the left-most text input to txt_LowTemp, the right-most text input to txt_HiTemp, and the image control to img_TempBackground. The exact size and spacing doesn’t matter too much; it’s the gradient that we are after. The sizes I used are 80 wide and 40 high for the text inputs and 300 wide and 40 high for the image.

Next, set the Format properties for both text input controls to Number. We will be using these to simulate the temperature inputs that we would get from a weather data source, such as the MSN Weather connector which is a standard connector and free to use in any Power App. If you want to enter some default numbers just to have them ready, feel free to do so. Just make sure that the number on the left side is smaller than the number on the right side; our temperature gradient goes from cold to hot in a left to right fashion. I’ll enter 15 on the left and 25 on the right.

For the image control, set each of the corner radius properties (RadiusBottomLeft, RadiusBottomRight, RadiusTopLeft, RadiusTopRight) to the following:

Self.Height / 2

This is the formula for circularly rounded corners.

We will also remove the SampleImage text from the Image property and leave it blank for now, then set the Fill property to:

RGBA(0,0,0,0.05)

We are going to use this image control as a background as it allows us to easily create a shape with rounded corners. We don’t need it for the single range version, but now we will have it to copy and paste for the two-range version we will create later.

Having done these steps, we should have something that looks like this:

Now copy and paste the image control, setting the Fill property to:

RGBA(0,0,0,0)

and set the position properties (Height, Width, X, and Y) to match the first image control. Rename it to img_TempGradient.

In the Image property, input the following:

This is our previous SVG code with some changes. Essentially, we have set the width and height properties of the SVG itself, the SVG viewBox, and the rectangle that houses the color gradient to the same as the image control it resides in. After doing this, we should see the following:

Now comes the hard part. We want the color gradient to only show the range set by the low temperature on the left and the high temperature on the right. In addition to that, we want it to fit exactly in the image control so that the width of the gradient range we are using fits the full width and height. To do this, we need to scale the height and width of the SVG and rectangle object inside the SVG, as well as adjust the viewBox of the SVG. Just to give some information about the viewBox, it represents a viewable area within the SVG. Most of the time, we want to see the whole SVG so it often has the same dimensions as the SVG itself. Not so in our case, as we want to only see the section that represents the given temperature range. I’m going to dump the code to put in the Image property and then explain what is happening.

First off, the top With function is setting the endpoints of our temperature gradient range based on whether we are using Imperial (°F) or Metric (°C) units. I am using Imperial units, so I set wth_MinTemp to -100 and wth_MaxTemp to 200.

The next With function is setting the scaled width for the SVG and rectangle in the SVG by using the width of img_TempBackground and the given temperature values to create a ratio. One note on this: we could replace img_TempBackground.Width with Self.Width but, again, setting it up this way will help us in the two-range version.

The third With function is doing two things: scaling the height relative to the width so that the resulting shape fits the Image control and setting the starting point for the viewBox so that we see just the area of the SVG that fits the temperature range.

To summarize these changes, we made the SVG big enough that our chosen viewBox is exactly the width and height of the Image control it is represented by and then made it so the viewBox starts at the low temperature and ends at the high temperature.

It should look thusly:

Pretty cool right? How about we test to make sure that it works though. Let’s enter 80 for the high temp. We should see this:

And just to make doubly sure, let’s set the low temp to -30. We should then see:

Success! Now, let’s make it work for two temperature ranges.

Two-range gradients

We’re going to start by copying and pasting all four existing controls twice and positioning them like shown below. We’ll also set the low and high temperatures for the bottom set of controls (txt_LoTemp_2 and txt_HiTemp_2) to 20 and 35, respectively.

We will replace the Image property for the upper temperature gradient image (img_TempGradient_1) with:

Basically what is happening here is that we are now scaling our SVG with respect to the maximum high temperature and the minimum low temperature.

We will do the same in the Image property of img_TempGradient_2, which will be replaced with:

We won’t notice much of a change just yet, but we will once we make a couple more additions. Let’s make the following changes to img_TempGradient_1:

Width: With(
    {
        wth_TempSpread: txt_HiTemp_1.Text - txt_LoTemp_1.Text,
        wth_MaxSpread: Max(txt_HiTemp_1.Text, txt_HiTemp_2.Text) - Min(txt_LoTemp_1.Text, txt_LoTemp_2.Text)
    },
    img_TempBackground_1.Width * wth_TempSpread / wth_MaxSpread
)

X: With(
    {
        wth_TempLo: txt_LoTemp_1.Text,
        wth_MinLo: Min(txt_LoTemp_1.Text, txt_LoTemp_2.Text)
    },
    With(
        {
            wth_MaxSpread: Max(txt_HiTemp_1.Text, txt_HiTemp_2.Text) – wth_MinLo
        },
        img_TempBackground_1.X + ((wth_TempLo – wth_MinLo) / wth_MaxSpread) * img_TempBackground_1.Width
    )
)

And similarly, let’s change the Width and X of img_TempGradient_2 to:

Width: With(
    {
        wth_TempSpread: txt_HiTemp_2.Text - txt_LoTemp_2.Text,
        wth_MaxSpread: Max(txt_HiTemp_1.Text, txt_HiTemp_2.Text) - Min(txt_LoTemp_1.Text, txt_LoTemp_2.Text)
    },
    img_TempBackground_2.Width * wth_TempSpread / wth_MaxSpread
)

X: With(
    {
        wth_TempLo: txt_LoTemp_2.Text,
        wth_MinLo: Min(txt_LoTemp_1.Text, txt_LoTemp_2.Text)
    },
    With(
        {
            wth_MaxSpread: Max(txt_HiTemp_1.Text, txt_HiTemp_2.Text) – wth_MinLo
        },
        img_TempBackground_2.X + ((wth_TempLo – wth_MinLo) / wth_MaxSpread) * img_TempBackground_2.Width
    )
)

So…what did we do there exactly? Three things: first, we are treating the temperature backgrounds (img_TempBackground_1 and img_TempBackground_2) as placeholders for the total temperature spread (i.e. the difference between the max high temp and min low temp).

Second, we are scaling the width of each temperature gradient (img_TempGradient_1 and img_TempGradient_2) to fit its individual temperature spread relative to the total temperature spread.

And third, we are adjusting the starting point of each temperature gradient, again relative to the total temperature spread.

It should look like this:

Again, let’s test this out to make sure it is working properly. Set the low and high temperatures of the upper set of text input controls (txt_LoTemp_1 and txt_HiTemp_1) to -10 and 65, respectively. Here’s what we should see:

And let’s change the bottom set of text input controls (txt_LoTemp_2 and txt_HiTemp_2) to -15 and 75, respectively. It should look like so:

And once again, we have success!

Final thoughts

By replacing the text inputs with data from a weather API, we can have a nice UI for our users to enjoy. Furthermore, because the SVG can be used with both Imperial and Metric units, it can be easy to localize by setting up a condition where, based on the units used, it will set the wth_MinTemp and wth_MaxTemp to the necessary values.

In using the MSN Weather connector for instance, we can find the unit system and then use that. If we used the MSNWeather.TodaysForecast call and set it in a global variable called glb_TodaysForecast, we could access it like so:

glb_TodaysForecast.units.system

In my case, it would return “Imperial”. So, setting the wth_MinTemp and wth_MaxTemp to the following should localize things nicely:

// For imperial units, use -100 for min, 200 for max
// For metric units, use -73.33 for min, 93.33 for max
wth_MinTemp: If(
    glb_TodaysForecast.units.system = “Imperial”,
    -100,
    -73.33
),
wth_MaxTemp: If(
    glb_TodaysForecast.units.system = “Imperial”,
    200,
    93.33
)

As with everything, there are more ways to accomplish this task, but this is one decent way.

Another idea is to do this with more than two temperature ranges. The main changes that would need to be made would be to take the min and maxes of all the temperatures and replace all the places that do that with just two (i.e. the X, Width, and wth_ScaledWidth and wth_StartingPosition in the SVG code for each of the temperature gradient image controls).

To further extend the use of this idea, here is a screenshot of one possibility for using the MSN Weather connector and this temperature gradient to show temperature data in apps.

(Just so it’s known, I do understand that using modals in Power Apps in the way shown isn’t great for screen readers. I can feel a finger wag of consternation coming across the pond from my buddy Sancho Harker. Also, this is an uncommonly nice couple of days for February in Wyoming. T-shirts and shorts all around! 😁)

I hope you have enjoyed this little journey into the realm of SVG color gradients and temperature. I will have a link to download the example app we built at the bottom of this post. It will be an app, not a solution, so import accordingly! If you have any questions or comments, feel free to share them here or hit me up on social media. Thank you for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *