30 Day Map Challenge - Day 18 - Out of this World

It's day 18 of the 30 Day Map Challenge and today's theme is "Out of this World".

Map something non-Earth: a fantasy realm, an exoplanet, the Moon, Mars, deep space, or a purely conceptual place (e.g., a mental map).

Not every map uses a real world background. You might be creating a Power BI report about a planned development, the inside of a building or vehicle or as we have here a theme park - and still overlay polygons, images, circles, lines etc as you would over a real base map.

So for today's challenge, I've chosen to create an interactive map of a theme park. I asked ChatGPT to create a space themed theme park map based on a real theme park location - can you guess which one?

Theme Park Map

I took the generated image, imported into QGIS and then used gdal_translate to convert it to a cloud optimized geotiff (COG) - (see the blog post from day 9).

Then in Icon Map Slicer I turned off the background map and added the COG image as a raster overlay. This forms the base of our theme park map. But as it's Power BI, I wanted to show some metrics on the map. I imagined I was running a theme park and had KPIs for queue length, throughput and visitor satisfaction - as well as whether the park was open or not.

To display these metrics I've created SVG images in a DAX measure. This might sound complicated, but actually they're easy to create. I have two version of the image - a detailed one for when the map is showing a subset of rides and there's more space, and a simplified one, just using colours to indicate the KPIs.

To create these I started in PowerPoint and mocked them up using PowerPoint shapes:

PowerPoint

I then grouped all the elements in each image, and then used the right click menu to save them as SVG images.

I then substituted the hard coded colours and text with the values from my Power BI table. Here's the simpler image as an example:

Stats Image Compact = 
"<svg width=""1090"" height=""94"" xmlns=""http://www.w3.org/2000/svg"" xmlns:xlink=""http://www.w3.org/1999/xlink"" xml:space=""preserve"" overflow=""hidden"">
    <g transform=""translate(0,31.5) scale(1, 0.17816)"">
        <g transform=""translate(-640 -1229)"">
            <path d=""M655 1248.21C655 1245.89 656.887 1244 659.215 1244L1705.79 1244C1708.11 1244 1710 1245.89 1710 1248.21L1710 1358.79C1710 1361.11 1708.11 1363 1705.79 1363L659.215 1363C656.887 1363 655 1361.11 655 1358.79Z"" stroke=""#163E64"" stroke-width=""13.75"" stroke-miterlimit=""8"" fill=""#163E64"" fill-rule=""evenodd""/>
            <text fill=""#FFFFFF"" font-family=""Aptos,Aptos_MSFontService,sans-serif"" font-weight=""400"" font-size=""63"" transform=""matrix(1 0 0 1 789.392 1330)"">" &
                MAX ( space_ride_metrics[Ride Name] ) &
            "</text>
            <text fill=""" & MAX ( space_ride_metrics[Queue Length Colour] ) & """ font-family=""Segoe Fluent Icons,Segoe Fluent Icons_MSFontService,sans-serif"" font-weight=""400"" font-size=""55"" transform=""matrix(1 0 0 1 1497.31 1333)""></text>
            <text fill=""" & MAX ( space_ride_metrics[Satisfaction Colour] ) & """ font-family=""Segoe Fluent Icons,Segoe Fluent Icons_MSFontService,sans-serif"" font-weight=""400"" font-size=""55"" transform=""matrix(1 0 0 1 1569.17 1333)""></text>
            <text fill=""" & MAX ( space_ride_metrics[Throughput Colour] ) & """ font-family=""Segoe Fluent Icons,Segoe Fluent Icons_MSFontService,sans-serif"" font-weight=""400"" font-size=""55"" transform=""matrix(1 0 0 1 1641.04 1333)""></text>
            <text fill=""" & MAX ( space_ride_metrics[Status Colour] ) & """ font-family=""Segoe Fluent Icons,Segoe Fluent Icons_MSFontService,sans-serif"" font-weight=""400"" font-size=""73"" transform=""matrix(1 0 0 1 684.495 1340)""></text>
        </g>
    </g>
</svg>"

With both images created as measures, I then created an additional measure to determine whether to display the full version of the simpler version depending on whether a subset of rides was selected in the table visual.

Stats Image = 
VAR TotalRides =
    CALCULATE(
        DISTINCTCOUNT( space_ride_metrics[Ride Name] ),
        ALL( space_ride_metrics[Ride Name] )              -- ignore all filters on Ride Name
    )
VAR VisibleRides =
    CALCULATE(
        DISTINCTCOUNT( space_ride_metrics[Ride Name] ),
        ALLSELECTED( space_ride_metrics[Ride Name] )      -- respect page/visual interactions
    )
RETURN
IF (
    VisibleRides < TotalRides,   -- something is filtering the rides (e.g. table selection)
    
    [Full Stats Image],
    [Stats Image Compact]
)

We can now display these at the ride locations on the map using Icon Map Slicer's image layer.

I then added an additional overlay showing linestrings of the maintenance routes, which is optionally displayed based on the slicer selection.

Here's the Power BI report to see it in action:

And the Power BI .pbix file to you can dissect the report and see in detail how it was put together.