Skip to main content

Fused Map

Using an AI agent?

Give your agent the Fused Map skill so it can inspect UDF outputs, choose the right layer types, and wire map parameters. For canvas and widget work in general, install the Workbench skills from fusedio/skills. Or paste this prompt to build a small example map:

Read https://docs.fused.io/skills/FUSED_MAP.md. Create a Fused canvas named sf_listings_map. Add a UDF sf_listings that reads s3://fused-sample/demo_data/airbnb_listings_sf.parquet and returns name, room_type, price_in_dollar, latitude and longitude. Add a Fused Map that shows the listings as points on a dark basemap, colored by room_type with a legend, and shows name and price_in_dollar in the tooltip. Validate the widget, push the canvas and give me its link.

Fused Map is a map widget in Fused. It draws geospatial data from a UDF, an h3-query widget or the H3 Data Retrieval REST API: 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.

Get data onto the map​

Each layer reads its rows from one of three sources:

SourceSet on the layerUse it for
A UDF"udf": "{{my_udf}}"Anything a UDF returns: points, polygons, H3 cells or a raster image.
An h3-query widget"data": "{{name}}", with an edge from the h3-query node to the mapThe rows of one query on an indexed H3 dataset, loaded in the browser.
The H3 Data Retrieval REST API"type": "fused-hex" and "source": {"path": "<dataset>"}A whole indexed dataset, fetched tile by tile as you pan and zoom, with no UDF.

H3 Visualization has working examples of the h3-query and fused-hex sources, and the Fused Map API reference lists every layer type and prop.

To pass a widget input's value to a UDF, write {{my_udf?<udf_argument>=$<param>}}, where <param> is the input's param name. For example, "udf": "{{my_udf?band_index=$band}}" passes the value of an input whose param is band to the UDF's band_index argument, and runs the UDF again when it changes.

Load a UDF per tile​

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.

Example: a raster and points​

This map shows San Francisco Airbnb listings as points on top of a Bay Area seismic hazard raster. A raster layer draws the image a UDF returns. With tile: true and a bounds argument, each tile reads only its window of the GeoTIFF.

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():
import pandas as pd

path = "s3://fused-sample/demo_data/airbnb_listings_sf.parquet"
df = pd.read_parquet(path)
df = df.dropna(subset=["latitude", "longitude", "price_in_dollar"])
return df[["name", "latitude", "longitude", "price_in_dollar", "room_type"]]

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.

Send map values to other widgets​

A map can send its viewport bounds (param), the clicked feature (clickParam), the hovered feature (hoverParam), its box selection (selectParam), its camera (stateParam) and a per-layer row version (sourceParam) to canvas parameters. Other widgets and UDFs read them as $<name>. See the Fused Map API reference for each one.

In this widget the map sends its bounds to $map_bounds with "param" and "autoSend": true. The bar chart below it reads $map_bounds[1] to $map_bounds[4] in its SQL and counts the San Francisco Airbnb listings in view by room type, so the chart follows the map as you pan and zoom. Both read the sf_listings UDF from the example above.

Show the widget JSON
{
"type": "div",
"props": {
"style": "display:flex;flex-direction:column;gap:8px;height:100%"
},
"children": [
{
"type": "fused-map",
"props": {
"style": "flex:1;min-height:0",
"basemap": "mapbox://styles/mapbox/dark-v11",
"centerLng": -122.43,
"centerLat": 37.76,
"zoom": 12,
"param": "map_bounds",
"autoSend": true,
"layers": [
{
"id": "listings",
"name": "SF Airbnb listings",
"type": "geojson",
"udf": "{{sf_listings}}",
"latColumn": "latitude",
"lngColumn": "longitude",
"tooltip": ["name", "room_type", "price_in_dollar"],
"style": {
"fillColor": { "type": "categorical", "attr": "room_type", "palette": "Bold" },
"pointRadius": 3
}
}
]
}
},
{
"type": "bar-chart",
"props": {
"title": "Listings in view by room type",
"sql": "SELECT room_type AS label, COUNT(*) AS value FROM {{sf_listings}} WHERE longitude BETWEEN $map_bounds[1] AND $map_bounds[3] AND latitude BETWEEN $map_bounds[2] AND $map_bounds[4] GROUP BY 1 ORDER BY 2 DESC",
"style": "height:200px"
}
}
]
}

For maps that send their view and selections to charts, see cross-filtering.

The sidebar​

The sidebar lists the map's layers. For each one, the viewer can change the type, the style, the filters and the SQL 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.