Skip to main content

Layer Styling: Visualize Tab

The UDF builder displays data from the UDF into the map view. You can change the visual representation of a UDF's output is configured under the "Visualize" tab:

You'll notice a few differences to the Editor:

  • The Visualise tab isn't written in Python, rather this is a JSON file
  • There are a few defaults namely TileLayer, rasterLayer and vectorLayer
  • "Surprise Me" button. Try it out for yourself, see what happens! (you can always Ctrl + Z to go back if you don't like it)
File

You can explore this example right here for yourself. Click on the "UDF Builder" icon on the left, just below the Fused logo to open the code editor:

Basics of Visualize Tab

The Visualize tab is built on top of DeckGL, a JavaScript front-end framework build for large dataset visualizations.

Fused works with on a either a File or Tile basis (read more about this here). The styling will differ for each:

  • Tile -> We're leveraging DeckGL's TileLayer in the Map view as a basis allowing us to render only data that is in the viewport at any given moment.
  • File -> All of the output data is in a single file, so the TileLayer part is ignored and the sub-layers vectorLayer or rasterLayer are used directly:

We have created 2 Fused-specific sub-layers:

  • rasterLayer for all raster-based visualisations (if your UDF returns a PNG for example)
  • vectorLayer for all vector-based visualisations (if your UDF returns a GeoDataFrame for example)

Under the hood Fused will use whichever sublayer fits your UDF output, but keep in mind that both are defined in each UDF:

// psuedo-code overview of Visualize tab parameters
{
"tileLayer": {
"@@type": "TileLayer",
...
},
// This is a Fused-specific sublayer for all raster outputs
"rasterLayer": {
"@@type": "BitmapLayer"
...
},
// This is a Fused-specific sublayer for all vector outputs
"vectorLayer": {
"@@type": "GeoJsonLayer",
...
}
}

Depending on what your UDF returns, you can use different layer types (this is the current supported list):

  • Vector H3HexagonLayers for UDFs returning a JSON with a column containing H3 indices
  • Vector GeoJsonLayer for UDFs returning a GeoDataFrame (or any DataFrame with a geometry column)
  • Raster BitmapLayer for UDFs returning an array

Their visualization styles can be configured with DeckGL properties.

tip

You can hold Cmd on MacOS or Ctrl on Windows / Linux to tilt the map view.

You can try it out in the map right below this in the "Vector H3HexagonLayer" section 👇

Vector H3HexagonLayer

At the moment, any pd.DataFrame will be rendered using the vectorLayer config. If your DataFrame does have H3 indices you can use H3HexagonLayer to display those as hexagon vectors.

In this case, the config column getHexagon should be set with the name of the DataFrame column of H3 indices. The rendered hexagons can be styled by setting values from a different column in getFillColor & getElevation.

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 19,
"tileSize": 256,
"pickable": true
},
"vectorLayer": {
"@@type": "H3HexagonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"extruded": false,
"opacity": 1,
"coverage": 0.9,
"lineWidthMinPixels": 5,
// This assumes your UDF returns a DataFrame with a column called 'hex' containing all the H3 indices
"getHexagon": "@@=properties.hex",
"getLineColor": {
"@@function": "hasProp",
"property": "metric",
"present": "@@=[(1 - properties.metric/500) * 255, 0, 255]",
"absent": [200, 200, 200]
},
"getFillColor": {
"@@function": "hasProp",
"property": "metric",
"present": "@@=[255, (1 - properties.metric/500) * 255, 0]",
"absent": [220, 255, 100]
},
"getElevation": {
"@@function": "hasProp",
"property": "metric",
"present": "@@=properties.metric",
"absent": 1
},
"elevationScale": 10
}
}

Vector GeoJsonLayer

The visualization of the output of a UDF that returns a DataFrame or GeoDataFrame can be configured dynamically based on column values. Attributes of the vectorLayer can be set to use either hardcoded values or column values, such as:

  • Line color (getLineColor) and line width (getLineWidth)
  • Elevation (getElevation) with extruded set to true
  • lineWidthUnits helps maintain visual consistency across zoom levels when set to pixels
File
Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 15,
"tileSize": 256,
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"extruded": true,
"getElevation": "@@=properties.stats*1",
"lineWidthMinPixels": 1,
"getLineWidth": "@@=properties.stats*10",
"getLineColor": {
"@@function": "hasProp",
"property": "stats",
"present": "@@=[properties.stats*5, properties.stats*3, properties.stats*2]",
"absent": [255, 0, 255]
},
"getFillColor": "@@=[properties.stats*5, properties.stats*3, properties.stats*2]"
}
}

Color styling

There are 4 ways to set the color for the stroke (getLineColor) and fill (getFillColor) of a GeoJsonLayer. These examples show how to set it for the fill with getFillColor, and the same syntax applies for the stroke with getLineColor. They all modify the visualization config for this UDF.

@fused.udf
def udf(
bbox: fused.types.Bbox = None,
table_path: str = "s3://fused-asset/infra/building_msft_us/",
):
import numpy as np
import random
utils = fused.load("https://github.com/fusedio/udfs/tree/eda5aec/public/common/").utils

# Load data
gdf=utils.table_to_tile(bbox, table=table_path)

# Assign random numbers
gdf['value'] = np.random.randint(0,10, len(gdf))

# Assign random classes
categories = ['residential', 'commercial', 'health', 'public']
gdf['class'] = [random.choice(categories) for _ in range(len(gdf))]

print(gdf)
return gdf

With a single hardcoded color

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 19,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"lineWidthMinPixels": 1,
"pointRadiusMinPixels": 1,
"getFillColor": [20,200,200,100]
}
}
File

Based on a property value

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 15,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"extruded": true,
"getElevation": "@@=properties.stats*1",
"lineWidthMinPixels": 1,
"getFillColor": "@@=[properties.value*50, properties.value*30, properties.value*2]"
}
}
File

Alternatively, to support a default color when a value is absent.

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 15,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"extruded": true,
"getElevation": "@@=properties.stats*1",
"lineWidthMinPixels": 1,
"getFillColor": {
"@@function": "hasProp",
"property": "value",
"present": "@@=[properties.value*50, properties.value*3, properties.value*2]",
"absent": [
255,
0,
255
]
}
}
}

Using colorCategories

To set the color with colorCategories, use the attr property to specify the table column for the values, and the colors property to define the desired color palette.

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 19,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"lineWidthMinPixels": 1,
"pointRadiusMinPixels": 1,
"getFillColor": {
"@@function": "colorCategories",
"attr": "class",
"domain": [
"residential",
"commercial",
"health",
"public"
],
"colors": "Bold"
}
}
}
File
warning

Note that unexpected behaviors may arise if too many domains are used.

Using colorContinuous

To set the color with colorContinuous, use the attr property to specify the table column for the values, and the colors property to define the desired color palette.

Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 19,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"lineWidthMinPixels": 1,
"pointRadiusMinPixels": 1,
"getFillColor": {
"@@function": "colorContinuous",
"attr": "value",
"domain": [
0,
10
],
"colors": "Peach",
"nullColor": [
184,
184,
184
]
}
}
}
File

Raster BitmapLayer

Raster layers can be set to display a tooltip on hover by setting the pickable property to true. See DeckGL documentation.

File
Expand to see Visualise code
{
"tileLayer": {
"@@type": "TileLayer",
"minZoom": 0,
"maxZoom": 19,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
}
}
tip

The transparency of raster images can be set in two ways:

  1. In RGB images, the color black (0,0,0) is automatically set to full transparency.

If a 4-channel array is passed, i.e. RGBA, the value of the 4th channel is the transparency.

Custom loadingLayer and errorLayer

When tileLayer has "@@type": "DebugTileLayer" set loadingLayer and errorLayer can be configured to show the user that the UDF is still processing or that an error occurred. This is helpful for debugging.

Expand to see Visualise code
{
"tileLayer": {
"@@type": "DebugTileLayer",
"minZoom": 0,
"maxZoom": 15,
"tileSize": 256,
"pickable": true
},
"rasterLayer": {
"@@type": "BitmapLayer",
"pickable": true
},
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"extruded": true,
"getElevation": "@@=properties.stats*1",
"lineWidthMinPixels": 1,
"getLineColor": {
"@@function": "hasProp",
"property": "stats",
"present": "@@=[properties.stats*5, properties.stats*3, properties.stats*2]",
"absent": [
255,
0,
255
]
},
"getFillColor": "@@=[properties.stats*5, properties.stats*3, properties.stats*2]"
},
"loadingLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": false,
"pickable": true,
"lineWidthMinPixels": 10,
"getLineColor": [
25,
55,
0,
255
],
"getFillColor": [
5,
20,
255,
40
]
},
"errorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": true,
"pickable": true,
"lineWidthMinPixels": 10,
"getLineColor": [
255,
255,
0,
255
],
"getFillColor": [
255,
20,
255,
40
]
}
}