1

For simplicty, referencing this example:

https://carto.com/developers/carto-vl/examples/#example-load-external-geojson-layer

I would like to have this bit of code behave the same as through it were an in-line GeoJSON declaration

fetch('https://libs.cartocdn.com/carto-vl/assets/stations.geojson')
  .then(response => response.json())
  .then(function(data){
      // Define layer
      const source = new carto.source.GeoJSON(data);
      const viz = new carto.Viz();
      const layer = new carto.Layer('layer', source, viz);

      layer.addTo(map, 'watername_ocean');
      layer.on('loaded', hideLoader);
  });

For example (this code works, although for brevity only includes one coordinate):

const stations =
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "Van Dorn Street",
        "marker-color": "#0000ff",
        "marker-symbol": "rail-metro",
        "line": "blue"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -77.12911152370515,
          38.79930767201779
        ]
      }
    },
  ]
};

const source = new carto.source.GeoJSON(stations);
const viz = new carto.Viz();
const layer = new carto.Layer('layer', source, viz);
layer.addTo(map, 'watername_ocean');

The above is an in-line example which avoids issues relating to global variables versus a variable defined within a function.

What I'm trying to figure out to no avail is how to do use fetch to define a global variable, with the same ease as defining it in-line.

This code does not work:

let stations;    
fetch('https://libs.cartocdn.com/carto-vl/assets/stations.geojson').then(
        function(u){ return u.json();}
      ).then(
        function(json){
          stations = json;
        }
      )

const source = new carto.source.GeoJSON(stations);
const viz = new carto.Viz();
const layer = new carto.Layer('layer', source, viz);
layer.addTo(map, 'watername_ocean');

Generates the following errors:

Error: [Missing required property] 'data' carto-vl.min.js:6:3429

1 Answer 1

3

The issue in your code is that 'fetch' is asynchronous. Therefore, when creating the GeoJSON source, the 'stations' variable doesn't have yet any value. In this case, you'd need to use Promises, async-await or a callback.

From the example above:

  1. Using a callback:
    fetch('https://libs.cartocdn.com/carto-vl/assets/stations.geojson')
      .then((response) => response.json())
      .then((data) => init(data));

    function init(data) {
      const map = new mapboxgl.Map({
        container: 'map',
        style: carto.basemaps.voyager,
        center: [0, 0],
        zoom: 1,
        scrollZoom: false
      });

      const nav = new mapboxgl.NavigationControl({
        showCompass: false
      });
      map.addControl(nav, 'top-left');
      map.addControl(new mapboxgl.FullscreenControl(), 'top-left');

      // Define user
      carto.setDefaultAuth({
        username: 'cartovl',
        apiKey: 'default_public'
      });

      const source = new carto.source.GeoJSON(data);
      const viz = new carto.Viz();
      const layer = new carto.Layer('layer', source, viz);

      layer.addTo(map, 'watername_ocean');
      layer.on('loaded', hideLoader);

      function hideLoader() {
        document.getElementById('loader').style.opacity = '0';
      }
    }
  1. Using async-await
    async function getStations() {
      return fetch('https://libs.cartocdn.com/carto-vl/assets/stations.geojson')
        .then((response) => response.json());
    }

    async function init() {
      const map = new mapboxgl.Map({
        container: 'map',
        style: carto.basemaps.voyager,
        center: [0, 0],
        zoom: 1,
        scrollZoom: false
      });

      const nav = new mapboxgl.NavigationControl({
        showCompass: false
      });
      map.addControl(nav, 'top-left');
      map.addControl(new mapboxgl.FullscreenControl(), 'top-left');

      // Define user
      carto.setDefaultAuth({
        username: 'cartovl',
        apiKey: 'default_public'
      });

      const data = await getStations();
      const source = new carto.source.GeoJSON(data);
      const viz = new carto.Viz();
      const layer = new carto.Layer('layer', source, viz);

      layer.addTo(map, 'watername_ocean');
      layer.on('loaded', hideLoader);

      function hideLoader() {
        document.getElementById('loader').style.opacity = '0';
      }
    }

    init();

To access the variables, you can return them and use it only when the request has finished:

  async function getStations() {
    return fetch('https://libs.cartocdn.com/carto-vl/assets/stations.geojson')
      .then((response) => response.json());
  }

  async function init() {
    const map = new mapboxgl.Map({
      container: 'map',
      style: carto.basemaps.voyager,
      center: [0, 0],
      zoom: 1,
      scrollZoom: false
    });

    // ...

    const data = await getStations();
    const source = new carto.source.GeoJSON(data);
    const viz = new carto.Viz();
    const layer = new carto.Layer('layer', source, viz);

    layer.addTo(map, 'watername_ocean');
    layer.on('loaded', hideLoader);

    // ...

    return { data, source, viz, layer, map };
  }

  // deconstruct the objec to create the variables

  const { data, source, viz, layer, map } = await init();

  // Use the variables here!
3
  • Thank you Elena. The more I read up on this issue the more I understand the fundamental problem, I was just really hoping I could establish those items as global variables for future use rather than in functions. What is the flexibility within Carto to push new data to a layer, If I first set it up with blank data in the body, and then “push” the new data after the callback has finished? This way even if “data” is a local variable, perhaps I could maintain source, viz, and layer as global variables.
    – smarchan
    Commented Jun 25, 2019 at 16:07
  • I added a new example in the answer that explains how you can initialize the variables once the request has finished, hope it helps. Commented Jun 27, 2019 at 10:46
  • 1
    Thank you for this Elena. I will play around with this tonight and let you know how I make out. I think your last example will certainly help others in the future who are running into this issue.
    – smarchan
    Commented Jun 28, 2019 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.