Spatial filters

Spatial filters allows you to filter CARTO charts and widgets by a geospatial polygon. This is typically used to synchronize widgets with a map (so that the widget is filtered as the user pans or zooms in) but it can also be used alongside other geospatial inputs, such as a draw a polygon mechanism.

Adding a spatial filter

To filter a widget by a geospatial polygon, simply use thespatialFilter property. This property is available in all models, and expects a valid GeoJSON polygon or multipolygon.

Example

const categories = await dataSource.widgetSource.getCategories({
  column: 'group_name',
  operation: 'count',
  spatialFilter: polygon // GeoJSON.Polygon | GeoJSON.MultiPolygon; 
});

Synchronizing widgets with a deck.gl map

The most common use case is to create a CARTO + deck.gl map and then have the widgets be filtered. In this case, the question is: how do I pass the current viewport to the widgets?

  1. Use the onViewStateChange property in deck.gl to obtain the latest viewState

Your code probably already looks similar to this, where you're using map.jumpTo to sync layers with the viewState in deck.gl. In any case, you want to do the same for your widgets. Our recommendation is to send that viewState to a function that will create the spatialFilter out of the viewState.

import {Deck} from '@deck.gl/core';

deck.setProps({
  onViewStateChange: ({viewState}) => {
    const {longitude, latitude, ...rest} = viewState;
    map.jumpTo({center: [longitude, latitude], ...rest});
    debouncedUpdateSpatialFilter(viewState);
  }
});

We strongly recommend debouncing all actions downstream, at least for 100ms. From updating the spatialFilter to rendering new widgets, it all should probably wait until the user has finished interacting with the map. This will improve the performance of your application and reduce the amount of queries to your data warehouse.

  1. Create a function that turns a viewState into a spatialFilter, using createViewportSpatialFilter

Turning a deck.gl viewState into a valid GeoJSON polygon is not a trivial task. It involves unprojecting the viewState and making sure the resulting polygon is a valid GeoJSON, even when crossing the antimeridian for low zoom levels.

That's why we provide the createViewportSpatialFilter, where all those operations are already available and ready to use.

import {WebMercatorViewport, Deck} from '@deck.gl/core';
import {createViewportSpatialFilter} from '@carto/api-client';
// import {debounce} from './utils'; — Your favorite debounce method

let viewportSpatialFilter;

const debouncedUpdateSpatialFilter = debounce(viewState => {
  const viewport = new WebMercatorViewport(viewState);
  viewportSpatialFilter = createViewportSpatialFilter(viewport.getBounds());
  renderWidgets();
}, 300);
  1. Use your updated spatial filter in your CARTO widgets

// import and set up dataSource...

const categories = await dataSource.widgetSource.getCategories({
    column: 'group_name',
    operation: 'count',
    spatialFilter: viewportSpatialFilter // this is now in-sync with the map!
});

createViewportSpatialFilter

Returns a valid SpatialFilter for a given viewport, typically obtained from deck.gl's viewport.getBounds() method ([west, south, east, north]).

  • If the viewport covers the entire world (to some margin of error in Web Mercator space), undefined is returned instead.

  • If the viewport extends beyond longitude range [-180, +180], the polygon may be reformatted for compatibility with CARTO APIs.

Usage

import {createViewportSpatialFilter} from '@carto/api-client';

createViewportSpatialFilter(viewport.getBounds());

Last updated