fetchMap

fetchMap is a function designed to allow developers to retrieve layers and other details from a CARTO Builder map, in order to integrate them into custom applications. Developers can then choose to:

  • Replicate the map nearly exactly as configured in CARTO

  • Integrate those layers in the application with customizations

  • Use this information in any other way (eg: storing map metadata in a database)

Usage

import {Deck} from '@deck.gl/core';
import {fetchMap} from '@carto/api-client';

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';
const mapData = await fetchMap({cartoMapId, credentials, autoRefresh, onNewData});

Options

type FetchMapOptions = {
  apiBaseUrl?: string;
  cartoMapId: string;
  accessToken?: string;
  autoRefresh?: number;
  onNewData?: (map: any) => void;
  clientId?: string;
  headers?: Record<string, string>;
};
  • accessToken (optional): When the map that you're requesting is private/shared (in other words, not public) you will be required to pass a valid OAuth Access Token that represents a user with access to the map. Learn more about OAuth Access Tokens.

  • apiBaseUrl (optional): The base URL that hosts your APIs. It varies depending on your CARTO region/deployment method. By default it will use https://gcp-us-east1.api.carto.com which is the URL for the CARTO cloud US-based tenant, but it will be different if you operate in another region or in your own deployment. Learn how to obtain your API Base URL.

  • autoRefresh (optional): Interval in seconds at which to autoRefresh the data from the map. If provided, onNewData must also be provided. Make sure your map's data freshness settings are consistent with the desired experience.

  • onNewData (optional): Callback function that will be invoked whenever data in layers is changed. If provided, autoRefresh must also be provided.

  • clientId (optional): An arbitrary string that can be used to identify the application, area or functionality that is performing the requests. It will be reflected later in the CARTO Activity Data, allowing developers to track usage in their applications.

  • headers (optional): An array of valid HTTP headers that will be attached to all requests originating from this source. This is useful to send any extra header or to control the cache in your sources.

Response

The response of fetchMap will be a long, structured JSON that contains all the necessary information in order for you to integrate that map in your own CARTO + deck.gl application.

type FetchMapResult = {
  id: string; // ID of the map in CARTO
  title: string;
  description?: string;
  createdAt: string;
  updatedAt: string;
  basemap: Basemap | null; // basemap object (based on Maplibre GL or Google Maps) compatible with https://deck.gl/docs/get-started/using-with-map 
  initialViewState: ViewState; // see deck.gl viewState
  layers: LayerDescriptor[]; // array of layer properties following the types below. You can build deck.gl layers by transforming these properties.
  popupSettings: any; // typically an object containing the user-defined settings for popup, hover, and tooltip interactions
  token: string; // for private maps, the API Access Token that needs to be used for subsequent API queries. 
  stopAutoRefresh?: () => void; // Stops the map from auto-refreshing (e.g., polling)
};

type LayerDescriptor = {
  type: LayerType;
  props: Record<string, any>;
  filters?: Filters; // check Filters section in this documentation
  scales: Record<ScaleKey, Scale>;
};

type Scale = {
  field: VisualChannelField;
  domain: (string | number)[];
  range: (string | number)[];
  type: ScaleType;
};

type VisualChannelField = {
  name: string;
  type: string;
  colorColumn?: string;
};

type LayerType = 'clusterTile' | 'h3' | 'heatmapTile' | 'mvt' | 'quadbin' | 'raster' | 'tileset';
type ScaleKey = 'fillColor' | 'lineColor' | 'pointRadius' | 'elevation' | 'weight';
type ScaleType = 'linear' | 'ordinal' | 'log' | 'point' | 'quantile' | 'quantize' | 'sqrt' | 'custom' | 'identity';

Basic examples

Static display — prototyping with no changes

You can use the response from fetchMap directly in a deck.gl map, to achieve a static display of the map layers exactly as configured in Builder.

import {Deck} from '@deck.gl/core';
import {fetchMap} from '@carto/api-client';
import {layerFactory} from './utils';

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';

const deck = new Deck({
  // deck initial setup
});

fetchMap({cartoMapId}).then(cartoMap => deck.setProps({
  layers: layerFactory(cartoMap.layers)
}););
Accessing map metadata

You can easily inspect a particular part of the map metadata (such as styling properties) by accessing the fetchMap response object, following the reference above. For example, we can easily count the number of layers that have been configured in the map, and show the title of the map.

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

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';
fetchMap({cartoMapId}).then(map => {
  const layers = map.layers || [];
  console.log(`Map Title: ${map.title}`);
  console.log(`Number of Layers: ${layers.length}`);
});
Modifying layer styles

Most developers will want to integrate the layers from fetchMap into their own deck.gl application, harmonizing the properties with the ones used in the application.

Below is an example demonstrating how to initialize the layers, override the visibility property of one layer, adjust the getRadius property of another, and leave a third layer unchanged:

import {Deck} from '@deck.gl/core';
import {fetchMap} from '@carto/api-client';
import {layerFactory} from './utils';

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';

fetchMap({cartoMapId}).then(map => {
  const layers = map.layers || [];

  // Override the visibility property of the first layer
  if (layers[0]) {
    layers[0].visible = false; // Example: hides the first layer
  }

  // Override the getRadius property of the second layer
  if (layers[1]) {
    layers[1].getRadius = (d) => d.properties.radius * 2; // Example: adjusts the radius
  }
  
  // LayerFactory

  // The third layer remains unchanged

  new Deck({
    initialViewState: map.initialViewState,
    controller: true,
    layers: layerFactory(layers)
  });
});
Modifying interactions, tooltips, and popups

Using the popupSettings from the fetchMap response we can understand how the interactions were configured in the original map, and use that information to create our own interactions, having full control of the final UX.

import {Deck} from '@deck.gl/core';
import {fetchMap} from '@carto/api-client';
import {layerFactory} from './utils';

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';

fetchMap({cartoMapId}).then(map => {
  const layers = map.layers || [];
  const tooltipSettings = map.popupSettings || [];

  // Clone and customize the first layer, rebuilding its tooltip
  const customizedLayers = layers[0] ? [{
    ...layers[0],
    getTooltip: info => {
      const property = tooltipSettings.title || 'name';
      return info.object ? {text: info.object.properties[property]} : null;
    }
  }] : [];
  
  // LayerFactory

  new Deck({
    initialViewState: map.initialViewState,
    controller: true,
    layers: layerFactory(customizedLayers)
  });
});
Re-building legends from your map

Using the scales property available in each layer of the fetchMap response, we can easily build a legend that matches very closely the legend found in CARTO Builder, or create our own completely custom legend for our application. Here's a basic example that accesses those properties:

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

const cartoMapId = 'ff6ac53f-741a-49fb-b615-d040bc5a96b8';

fetchMap({cartoMapId}).then(map => {
  const layers = map.layers || [];

  // Assuming you have a div with id="legend"
  const legendContainer = document.getElementById('legend'); 
  if (legendContainer) {
    legendContainer.innerHTML = '<h3>Map Legend</h3>';
    layers.forEach(layer => {
      if (layer.scales && layer.scales.fillColor) {
        const scale = layer.scales.fillColor;
        const legendItem = document.createElement('div');
        legendItem.innerHTML = `<b>${layer.props.id || 'Unnamed Layer'} (fillColor)</b>: <br/>`;
        scale.domain.forEach((domainItem, i) => {
          legendItem.innerHTML += `<span style="color:${scale.range[i]}">${domainItem}</span> <br/>`;
        });
        legendContainer.appendChild(legendItem);
      }
    });
  }
});

Complete example

A fully working example of a simple application using fetchMap can be found in our Examples gallery.

Examples

Last updated

Was this helpful?