Skip to main content

Fused Map

Fused Map is a map widget in Fused. It draws geospatial data from a UDF, a SQL query, or a tile URL: points, polygons, H3 hexagons, and raster tiles, with many layers on one map.

Its type is fused-map. It works like any other widget: configure it in JSON, connect UDFs with edges, and share it as a URL. To see working examples, open the Fused Map example canvas. For the props, see the Fused Map API reference.

Add a Fused Map widget​

  1. In the Workbench canvas, select the Widget icon in the toolbar.
  2. In the Widget configuration panel, select Preset, then Maps and geo, then Fused map.
  3. Connect the UDF node to the widget node with an edge.
  4. In the JSON, set udf to the name of the UDF in double braces, for example {{my_udf}}.

For a faster way, drag an edge from the output handle of a UDF node to an empty spot on the canvas, then select Fused map in the menu. Workbench creates a Fused Map widget that is already connected to the UDF and draws its result.

Drag an edge from a UDF node and select Fused map

note

The UDF must be on the same canvas. The name in {{ }} must match the UDF name exactly, with the same case.

Layer types and data sources​

Set the type of each layer to geojson, h3, raster, mvt, heatmap, scatterplot, arc, or deck-geojson. Each layer reads its data in one of four ways:

PropertyWhat the map does
udfRuns the UDF one time and draws the whole result.
udf and tile: trueRuns the UDF for each map tile. The UDF gets the tile bounds.
sqlRuns a DuckDB query on a UDF result, then draws the rows.
tileUrlReads tiles from a URL with {z}, {x}, and {y} placeholders.

The UDF returns a GeoDataFrame for geojson layers, a DataFrame with an H3 column for h3 layers, and an image array for raster layers. A DataFrame with latitude and longitude columns also works for geojson layers: set latColumn and lngColumn. Every other column is a property of the feature. The tooltip, the filters, and the data-driven colors use these properties.

Name the first argument bounds to load data per tile

With tile: true, the map passes each tile's area as [west, south, east, north] in EPSG:4326. Return only the rows inside bounds. The type doesn't matter, only the name.

@fused.udf
def udf(bounds):
...
tileFirst argument is boundsResult
trueYesEach tile gets its own data.
trueNoEvery tile gets the same result.
falseEitherThe UDF runs once and bounds is ignored.

To make a layer dynamic, pass a widget input's value to the UDF from the layer's udf in the Fused Map widget JSON. Write {{my_udf?<udf_argument>=$<param>}}, where <param> is the input's param name. For example, with an input whose param is band, "udf": "{{my_udf?band_index=$band}}" passes its value to the UDF's band_index argument and re-runs the UDF when it changes.

Example​

This map shows San Francisco Airbnb listings as points on top of a Bay Area seismic hazard raster.

Show the widget JSON
{
"type": "fused-map",
"props": {
"basemap": "mapbox://styles/mapbox/dark-v11",
"centerLng": -122.3,
"centerLat": 37.7,
"zoom": 9.5,
"autoFit": false,
"layers": [
{
"id": "seismic",
"name": "Bay Area seismic hazard (raster)",
"type": "raster",
"udf": "{{bay_area_seismic_raster}}",
"tile": true,
"minZoom": 0,
"maxZoom": 19,
"style": { "opacity": 0.6 }
},
{
"id": "sf_listings",
"name": "SF Airbnb listings",
"type": "geojson",
"udf": "{{sf_listings}}",
"latColumn": "latitude",
"lngColumn": "longitude",
"tooltip": ["name", "price_in_dollar", "room_type"],
"legend": true,
"style": {
"fillColor": { "type": "categorical", "attr": "room_type", "palette": "Bold" },
"pointRadius": 4,
"opacity": 0.85
}
}
]
}
}
Show the bay_area_seismic_raster UDF

The raster layer uses tile: true, so the first argument is bounds.

@fused.udf
def udf(
bounds: fused.types.Bounds = [-122.6, 37.2, -121.7, 38.0],
path: str = "s3://fused-asset/demos/seismic_data/v2023_1_pga_475_rock_3min.tif",
):
common = fused.load("https://github.com/fusedio/udfs/tree/fbf5682/public/common/")
tile = common.get_tiles(bounds, zoom=common.estimate_zoom(bounds))
arr = common.read_tiff(tile, input_tiff_path=path)
return common.arr_to_plasma(arr.filled(0), min_max=(0, 1.5))
Show the sf_listings UDF

The points layer runs once and has no bounds argument.

@fused.udf
def udf(path: str = "s3://fused-sample/demo_data/airbnb_listings_sf.parquet"):
import geopandas as gpd
import pandas as pd

df = pd.read_parquet(path)
df = df.dropna(subset=["latitude", "longitude", "price_in_dollar"])
gdf = gpd.GeoDataFrame(
df[["name", "neighbourhood_cleansed", "room_type", "price_in_dollar"]],
geometry=gpd.points_from_xy(df["longitude"], df["latitude"]),
crs="EPSG:4326",
)
return gdf

The Fused Map example canvas has working UDF and widget examples. For the style, tooltip, legend, and filter props, see the Fused Map API reference.

The sidebar​

The sidebar lets the viewer change the style, the filters, and the SQL of each layer in the browser. Each layer has a Style tab and a Filters tab. In Style, set the layer type, the fill mode (for example By value with a property, a palette, steps, and a domain), the stroke, and the opacity.

The Fused Map sidebar next to the map and the widget configuration

These changes are temporary. To keep them, select Save to widget to write them into the widget configuration, or select Copy config and paste the result into the JSON. Select Reset to undo the changes.