30 Day Map Challenge - Day 30 - Makeover

We made it! It's the last day, day 30 of the 30 Day Map Challenge and today's topic is "Makeover"

Take a map you made during the month or an older piece and redesign it. Focus on improving the aesthetics, clarity, or data communication.

I've decided not to focus on appearance for this challenge, but actually how an existing map functions within the Power BI report. Just before the 30 Day Map Challenge started, I posted a blog about how to present Telecoms Cell Tower KPIs.

Day 30

The original report had a DAX UDF function that generated SVG images for each cell tower, with wedges for each cell. This worked really well and generated the required result.

However, as each cell tower is a single image, it wasn't possible to select an individual wedge to filter the report, and as the map is zoomed out all the towers overlap and the report wasn't readable.

Therefore I thought for the makeover I'd try a slightly different approach. Rather than generating an SVG image using DAX, this time I'm generating a WKT polygon representing a single wedge.

This means that they can be individually selected, and as the coordinates are locations on the map, as the maps is zoomed out, the wedges scale at the same rate meaning they get smaller and don't overlap.

Now we have 2 different solutions according to what's required.

Here's the alternative DAX measure:

Cell Feature WKT = 
VAR lon = SELECTEDVALUE('Cells'[Longitude])
VAR lat = SELECTEDVALUE('Cells'[Latitude])
VAR az  = SELECTEDVALUE('Cells'[Azimuth])
VAR bw  = SELECTEDVALUE('Cells'[Beamwidth])

-- defaults 
VAR minR = 20          -- meters (0 => wedge closes at center)
VAR maxR = 150         -- meters
VAR steps = 64         -- arc smoothness
VAR useCompass = 1     -- 1 => RF bearings: 0°=North, clockwise

RETURN
IF(
    OR(ISBLANK(lon), ISBLANK(lat)),
    BLANK(),
    VAR halfBW = bw / 2.0
    VAR a0 = az - halfBW
    VAR a1 = az + halfBW

    VAR segs = MAX ( INT(steps), 6 )
    VAR step = bw / segs

    VAR R = 6378137.0
    VAR latRad = lat * PI() / 180.0

    /* ---------- outer arc a0 -> a1, at maxR ---------- */
    VAR OuterAngles =
        ADDCOLUMNS(
            GENERATESERIES(0, segs, 1),
            "ang", a0 + [Value] * step
        )
    VAR OuterCoordsWKT =
        CONCATENATEX(
            OuterAngles,
            VAR thetaDeg = IF(useCompass = 1, 90.0 - [ang], [ang])
            VAR theta    = thetaDeg * PI() / 180.0
            VAR dx       = maxR * COS(theta)
            VAR dy       = maxR * SIN(theta)
            VAR dLonDeg  = (dx / (R * COS(latRad))) * 180.0 / PI()
            VAR dLatDeg  = (dy / R) * 180.0 / PI()
            VAR x        = lon + dLonDeg
            VAR y        = lat + dLatDeg
            VAR xFmt     = FORMAT(x, "0.########")
            VAR yFmt     = FORMAT(y, "0.########")
            VAR xTxt     = IF(RIGHT(xFmt,1)=".", LEFT(xFmt, LEN(xFmt)-1), xFmt)
            VAR yTxt     = IF(RIGHT(yFmt,1)=".", LEFT(yFmt, LEN(yFmt)-1), yFmt)
            RETURN xTxt & " " & yTxt,
            ", ",
            [Value],   -- stable ordering
            ASC
        )

    /* ---------- inner arc a1 -> a0 at minR (or center if minR=0) ---------- */
    VAR InnerAngles =
        ADDCOLUMNS(
            GENERATESERIES(0, segs, 1),
            "ang", a1 - [Value] * step
        )
    VAR InnerCoordsWKT =
        IF(
            minR <= 0,
            VAR xFmt = FORMAT(lon, "0.########")
            VAR yFmt = FORMAT(lat, "0.########")
            VAR xTxt = IF(RIGHT(xFmt,1)=".", LEFT(xFmt, LEN(xFmt)-1), xFmt)
            VAR yTxt = IF(RIGHT(yFmt,1)=".", LEFT(yFmt, LEN(yFmt)-1), yFmt)
            RETURN xTxt & " " & yTxt,
            CONCATENATEX(
                InnerAngles,
                VAR thetaDeg = IF(useCompass = 1, 90.0 - [ang], [ang])
                VAR theta    = thetaDeg * PI() / 180.0
                VAR dx       = minR * COS(theta)
                VAR dy       = minR * SIN(theta)
                VAR dLonDeg  = (dx / (R * COS(latRad))) * 180.0 / PI()
                VAR dLatDeg  = (dy / R) * 180.0 / PI()
                VAR x        = lon + dLonDeg
                VAR y        = lat + dLatDeg
                VAR xFmt     = FORMAT(x, "0.########")
                VAR yFmt     = FORMAT(y, "0.########")
                VAR xTxt     = IF(RIGHT(xFmt,1)=".", LEFT(xFmt, LEN(xFmt)-1), xFmt)
                VAR yTxt     = IF(RIGHT(yFmt,1)=".", LEFT(yFmt, LEN(yFmt)-1), yFmt)
                RETURN xTxt & " " & yTxt,
                ", ",
                [Value],
                ASC
            )
        )

    /* ---------- first coordinate (for closure) at maxR @ a0 ---------- */
    VAR FirstCoordWKT =
        VAR thetaDeg = IF(useCompass = 1, 90.0 - a0, a0)
        VAR theta    = thetaDeg * PI() / 180.0
        VAR dx       = maxR * COS(theta)
        VAR dy       = maxR * SIN(theta)
        VAR dLonDeg  = (dx / (R * COS(latRad))) * 180.0 / PI()
        VAR dLatDeg  = (dy / R) * 180.0 / PI()
        VAR x        = lon + dLonDeg
        VAR y        = lat + dLatDeg
        VAR xFmt     = FORMAT(x, "0.########")
        VAR yFmt     = FORMAT(y, "0.########")
        VAR xTxt     = IF(RIGHT(xFmt,1)=".", LEFT(xFmt, LEN(xFmt)-1), xFmt)
        VAR yTxt     = IF(RIGHT(yFmt,1)=".", LEFT(yFmt, LEN(yFmt)-1), yFmt)
        RETURN xTxt & " " & yTxt

    /* ---------- assemble WKT ring ---------- */
    VAR Ring = OuterCoordsWKT & ", " & InnerCoordsWKT & ", " & FirstCoordWKT

    RETURN
        "POLYGON((" & Ring & "))"
)

And the Power BI report:

And the .pbix file to download.