From Complex to Simple using Power Apps Functions

Single Power Apps statement for a complex request

Just as I “walk away” from a series of sessions that I’ve presented during the last 2 months, I’ve ended it with one that generated lots of interest. The “I’ve lost my car, help me find it (using AI Builder)!” presentation at Maple Power 2020 that focused on the Text Recognition scenario had me work on enhancing the performance of my sample License Plate Reader canvas app.
This article is an extract of how I’ve converted a process that transformed the Use eXperience from waiting over 15 seconds for a result to an average of merely 3-4 seconds, using basic Power Apps functions in a single script/statement.
The objective of this article is to help you consider the potential of this lo-code/no-code tool even for what can sometimes be considered a complex scenario.

Background

Almost a year ago, I built a License Plate Reader app that was leveraging the Text Recognition scenario available from AI Builder. Due to limitations that were present back then, I was using a (then called) CDS Entity that would trigger a Power Automate flow to process an image taken from a Power Apps canvas app. The available component was not providing all information about the detected text and allowing to determine what was the ‘biggest’ part of it (the point being on a license plate, the actual number is what’s standing out / the biggest) and that’s why I was going the long way out.

License Plate Reader – the original version

Updated version

The Text Recognizer component that is now available for canvas apps, provides all the details required to process the content directly within the app. Removing the need to go through a flow to do the magic.

License Plate Reader – the updated version

The recipe

In order to get the biggest part of the recognized text, we must consider the following steps:

  • loop through the results
  • evaluate each result – position in the image & height
  • determine the biggest section
  • for all results that are within a specified threshold append the detected text to the final result

The process steps

The structure (TextRecognizer.Results)

The Text Recognizer component returns a series of values where we find the one that is of interest in this scenario: ResultsThe issue is that we can’t easily process this structure as it contains a record as one of its values (BoundingBox).

Results structure with BoundingBox value

The conversion (ForAll)

Using the ForAll function we can work around this constraint and assign the BoundingBox record fields at the same level as the main Results fields.

ForAll()

Find the biggest text (First & SortByColumns)

Using the First function on the sorted result from the previous step with the help of SortByColumns function we can find the text that has the biggest height value.

First() & SortByColumns()

Keep only the results within the biggest text range (Filter & SortByColumns)

At this point, we want to sort the results based on their position in the image. We’ll use the PageNumber, then the XPos (reading left to right) properties to do this sorting. Then either via a fixed value or as in the demo app a slider control, we will Filter the results that have a height that is within a defined range of the biggest text we found in the previous step.

Filter() & SortByColumns()

Assemble the text together (Concat)

The final step is to assemble all the filtered results into a single string using the Concat function. Simply extracting the Text property and inserting a space character in between each result.

Concat()

The whole script / statement

For ease of copy/paste, here’s the single statement that provides all the magic:

Concat(
    Filter(
        SortByColumns(
            ForAll(
                TextRecognizer1.Results,
                {
                    Text: Text,
                    PageNumber: PageNumber,
                    XPos: BoundingBox.Left,
                    YPos: BoundingBox.Top,
                    Height: BoundingBox.Height
                }
            ),
            "PageNumber",
            Ascending,
            "XPos",
            Ascending
        ),
        Height >= (First(
            SortByColumns(
                ForAll(
                    TextRecognizer1.Results,
                    {
                        Text: Text,
                        PageNumber: PageNumber,
                        XPos: BoundingBox.Left,
                        YPos: BoundingBox.Top,
                        Height: BoundingBox.Height
                    }
                ),
                "Height",
                Descending
            )
        ).Height) * (sldRatio / 100)
    ),
    Text,
    " "
)

Summary

So, in this article, we’ve looked into using a combination of Power Apps functions to process a fairly complex scenario into a single statement. It allowed, in this case, to highly improve the end-user experience by having from a 15-20 seconds process to a 3-4 seconds process to determine the license plate number for a picture.

Sources

The app and related components for both versions are available as a solution on my Github site: PowerApps/LicensePlateReaderLite_1_4.zip
The session deck from Maple Power 2020 conference is also available on my Github: Session-Decks/Maple Power 2020 – I’ve lost my car.pdf

About me

I’m Éric Sauvé MCT & MVP, aka ZePowerDiver, Power Platform practice lead, solution architect, and consulting service director at XRM Vision. I’m a community leader for TDGi‘s global hackathons in Canada. Power Platform enthusiast, delivering App in a Day sessions, leading local community events such as Montreal Power Platform Happy Hour, actively participating in Montreal Business Application User Group, and speaking in Power Platform and Dynamics CE related conferences.

You can reach me on:

LinkedIn: https://www.linkedin.com/in/zepowerdiver
Twitter: https://twitter.com/ZePowerDiver
Facebook: https://www.facebook.com/ZePowerDiver/
Instagram: https://www.instagram.com/ZePowerDiver/
YouTube: https://www.youtube.com/c/ZePowerDiver
Blogger: https://www.zepowerdiver.com/
Github: https://github.com/ZePowerdiver/

Keep on Diving!

About the Author:

I’m Éric Sauvé, aka ZePowerDiver, solution architect and team lead at XRM Vision. I’m a community leader for TDGi‘s global hackathons in Canada. Power Platform enthusiast, delivering App in a Day sessions, leading local community events such as Montreal Power Platform Happy Hour, actively participating in Montreal Business Application User Group and speaking in Power Platform and Dynamics CE related conferences.

You can reach me on:

LinkedIn: https://www.linkedin.com/in/zepowerdiver
Twitter: https://twitter.com/ZePowerDiver
Facebook: https://www.facebook.com/ZePowerDiver/
YouTube: https://www.youtube.com/c/ZePowerDiver
Blogger: https://www.zepowerdiver.com/

Reference:

Sauvé, É. (2020). From Complex to Simple using Power Apps Functions. Available at: http://www.zepowerdiver.com/2020/11/from-complex-to-simple-using-power-apps.html [Accessed: 4th March 2021].

Find more great Power Platform content here.

Share this on...

Rate this Post:

Share: