Power Apps Tracks Santa
Tags: powerapps, powerplatform, lowcode, learning
Published: 2023-12-18 07:26:11


![santa tracker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnvb082oktifxatzhrp7.gif)

Ive had a couple of apps where positioning of mulitple images is dynamic, this can be painful. Think of a carpark map that you want to place an X on the space, there are a 100 spaces and you need to map the X,Y corodinates in the app, this could take forever. So I wanted to figure the best way, and as everyone knows I like to learn by doing, often with a crazy project.

Its almost Christmas so I was inspired by my childrens favourite Christmas site [Norad Tracks Santa](https://www.noradsanta.org/en), could Power Apps Track Santa? Additionally not just any locations in the world, just the ones my children would love 😎

So in my mind I had 2 options, zoom in on the map, position Santa in the middle and move the map, or full screen the map and move Santa. The better way was the first, but due to time constraints and what I wanted to learn I went with the latter.

- The plan was, plot all of the locations in a SharePoint list
- On a timer move Santa from the current location to the next
- Start the movement at midnight on internal dateline
- Finsish the move after 24hours

After a little fun (and some pain, timezones suck), I learned a great technique for plotting coordinates, and just about understood dealing with timezones (who Im lying to, still no idea).

![map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1t3pwt8wx7pvvjroxuvp.png)

## Plotting Multiple Coordinates
My old way was to position the component, read its X & Y values, then add that to a SharePoint list. What I wanted to do was to automatically record the X & Y values as I drag the icon around, but for security I dont approve custom components, so I have no drag and drop. Then I realised I do have drag and drop, in the maker studio.

So I created an Icon that on click patches the coordinates, in the studio I drag the icon to the position, and then hold the 'Alt' button (to activate test mode) and click it.

![studio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7r0hrhhelg38z9ixnld0.gif)

To make it a little smarter I added 2 input boxes, one to record the location name that I needed, and second the row id. That way I couldnot just add (if Id input was empty) but also modify (if valid Id).

![inputs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u4wgv6fmzirio85t1mg.png)

**Icon onSelect**
```
If(IsBlank(inID.Text),
    Patch('List-SantaTracker',{},
        {
            Title:inLocation.Text,
            X:Self.X+(Self.Width/2),
            Y:Self.Y+(Self.Height/2)
        }
    )
,
    Patch('List-SantaTracker',{ID:Value(inID.Text)},
        {
            Title:inLocation.Text,
            X:Self.X+(Self.Width/2),
            Y:Self.Y+(Self.Height/2)
        }
    )
)
```
_Incase you didnt know, you dont need to use Defaults() to create, just an empty object, and you dont need to use the LookUp() to modify, just the ID in an object_

For a little extra functionality I also added a second Icon that had the current ID as its X & Y, so that I could check to see if the values were correct.

```
LookUp('List-SantaTracker',ID=Value(inID.Text)).X-(Self.Width/2)
```

```
LookUp('List-SantaTracker',ID=Value(inID.Text)).Y-(Self.Height/2)
```

As I now had a load of admin components that I didnt want to show in the live app I needed a way to hide them. I was orgionally going to have a hard coded boolean variable, but I knew I would forget one time. So I used a neat little trick, the SaveData function errors in the maker studio, so I could use that error handling to set the variable.

```
IfError(SaveData([],"makerStudio"),Set(vbAdmin,true),Set(vbAdmin,false));
```

## Time
There is not a single developer in the world that likes timezones, me included. I wanted the Santa to only move when it midnight on the international date line, but this was harder then you thought because Power Apps always reads your devices location and shows the time as local. Luckily you can force UTC time, its excatly 12 hours behind so I just had to move the date time to middday Xmas Eve. To do it you just use the Zulo time format "yyyy-MM-ddThh:mm:ss"

```
Set(vsStarttime,DateTimeValue("2023-12-24T12:00:00Z"));
```

Now I need to compare my local time to the UTC time, I was going to use the Timezone function, but I found a quicker way, the Text function has timezone as a format. So I did the old stringfy/parse trick and converted to a string and back to a date.

```
DateTimeValue(Text(Now(), DateTimeFormat.UTC))
```

I then would check to see if the local time was after midday UTC time.

The final thing I wanted was a countdown to when it started, this was a monster of an expression that hurts my head when I tried to describe so will just show you.

```
Set(voCountDown,
    {
        days: RoundDown(
                DateDiff(DateTimeValue(Text(Now(), DateTimeFormat.UTC))
                , 
                    vsStarttime
                , 
                    TimeUnit.Hours
                ) 
            / 24, 0
            ),
        hours: Mod(
                RoundDown(
                    DateDiff(DateTimeValue(Text(Now(),DateTimeFormat.UTC))
                    ,
                        vsStarttime
                    ,
                        TimeUnit.Minutes
                    )
                / 60, 0), 24
            ),
        minutes: Mod(
                DateDiff(DateTimeValue(Text(Now(), DateTimeFormat.UTC))
                ,
                    vsStarttime
                ,
                    TimeUnit.Minutes
                )
            , 
                60
            )
    }
);
```

## Position

**X Positon**
X was easy, as this is a simple linear movement. Santa would move from right to left at the same speed. I needed to convert a minute to horizontal pixels. This was calcualted by distance divided by time, so screen width / 1440 minutes in a day.

```
Set(viXstep,App.DesignWidth/1440);
```

To get the current position based on time I mulitplied the minutes passed by the viXstep.

```
Set(viXposition,
    App.DesignWidth-
        (
            DateDiff(vsStarttime,
                DateTimeValue(Text(Now(), DateTimeFormat.UTC))
            ,
                TimeUnit.Minutes
            )
            *viXstep
        )
);
```

**Y Positon**
Y was a little harder, as the end/target position was always moving. I calcualted the viYstep by getting the number of X steps between the current and target location, then dividing the difference between the current and target Y distance.

```
 Set(viYstep,(
        LookUp(colLocations,ID=voSanta.id).Y-voTarget.Y)
    /
        (
            (LookUp(colLocations,ID=voSanta.id).X-voTarget.X)
        *
            viXstep
        )
    );
```

Whats nice about this is the steps works even when the Santa changes direction, as if it is moving down it will generate a negative step value, so Santa still moves in the right direction.

## Other Stuff
There is lots more to the App but sure you have dealt with it before, but here are a few toplines:

To make the run now button work I cheated and changed the vsStarttime to Now(), then every loop deducted a minute from the vsStarttine, so increasing the time passed by making the start older, not the now in the future.

```
If(vbRunNow,Set(vsStarttime,DateAdd(vsStarttime,-1,TimeUnit.Minutes)));
```

I used to 2 main objects, one for the current Santa position and one for the next/target, this relied on the SharePoint list ID being sequential (if you are deleting rows just add your own sequenced column and use that instead).

I cached the SharePoint list to a local collection then added a 0 ID record for the start location and an end record at X=0 to ensure the Santa started and finished in right places.

---

The solution is available [here](https://github.com/wyattdave/Power-Platform/tree/main/Power%20App%20Artifacts) to download if you want to see more, you just need a sahrepoint list like this:

![sahrepoint lsit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvozwplcb0bcz7m1l3hm.png)