Add multiple mapbox markers from a query with Geojson using a CustomComponent

Hello,
I have been trying to duplicate what @thomaspouma had accomplished in his tread
[Update data from model in custom component without reloading entire component?] but have not been successful.

I have a query that contains the Geojson with less than 10 properties. (see geojson in image below). I have added the query reference in the custom component "model" section. And then duplicated Thomas's code, along with Kabirdas modifications, but still, no markers appear on the map.
The only other change was to modify the source id from "point" to "uid".

I've been at for two days and just don't see what I'm doing wrong. Any help would be greatly appreciated.
Thanks so much!

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>RH Maps</title>
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <link
      href="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css"
      rel="stylesheet"
    />
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }
      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>

    <script>
      mapboxgl.accessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

      const map = new mapboxgl.Map({
        container: "map",
        style: "mapbox://styles/mapbox/streets-v11",
        center: [-97.8999, 30.0005],
        zoom: 4
      });

      map.on("load", () => {
        map.loadImage(
          "https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png",
          (error, image) => {
            if (error) throw error;

            map.addImage("custom-marker", image);

            window.Retool.subscribe(function(model) {
              if (!model) {
                return;
              }

              let data = {
                type: "FeatureCollection",
                features: model.location
              };

              if (map.isSourceLoaded("uid")) {
                map.getSource("uid").setData(data);
              } else {
                map.addSource("uid", {
                  type: "geojson",
                  data
                });
              }
            });

            map.addLayer({
              id: "uid",
              type: "symbol",
              source: "points",
              layout: {
                "icon-image": "custom-marker",
                "icon-size": 0.5,
                "icon-padding": 0,
                "icon-ignore-placement": true
                // get the title name from the source's "title" property
              }
            });
          }
        );
      });
    </script>
  </body>
</html>

Hi @Phil_in_Tex!

Happy to take a look :grinning_face_with_smiling_eyes: :eyes:

The source property has to match the name you give it when you're adding the layer here

map.addLayer({
id: "uid",
type: "symbol",
source: "points",

Additionally, it looks like the object you're returning from your query is already wrapped in a FeatureCollection object so you can switch from let data = { type: "FeatureCollection", features: model.location }; to let data = model.location.

Does this work?

window.Retool.subscribe(function(model) {
              if (!model) {
                return;
              }

              let data = model.location;

              if (map.isSourceLoaded("uid_source")) {
                map.getSource("uid_source").setData(data);
              } else {
                map.addSource("uid_source", {
                  type: "geojson",
                  data
                });
              }
            });

            map.addLayer({
              id: "uid",
              type: "symbol",
              source: "uid_source",
              layout: {
                "icon-image": "custom-marker",
                "icon-size": 0.5,
                "icon-padding": 0,
                "icon-ignore-placement": true
                // get the title name from the source's "title" property
              }
            });

Hi Kabirdas,
I made the changes as you suggested but still no markers are showing on the map.

Output from the model below shows the features is an ARRAY of OBJECTS. Does that look correct? If so, then I'm still sumped.

{
location: {{Get_address_points.data}}
}

The resulting contents are as follows:

{
  "location": [
    {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [
              -98.081815,
              30.197311
            ]
          },
          "properties": {
            "mlsid": 1000612,
            "address": "206  Victoria Peak  LOOP"
          }
        },
        {
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [
              -98.07885,
              30.20159
            ]
          },
          "properties": {
            "mlsid": 1267460,
            "address": "171  Dome Peak  TER"
          }
        }
      ]
    }
  ]
}

Thanks so much for taking a look at this.

:thinking: it looks like the entire object is wrapped in an array. Try this?

{
location: {{Get_address_points.data[0]}}
}

Unfortunately, that does not seem to have worked. Would converting it to be an Object of Objects make sense to do here?

Hmm... my other guess would be that you need to do

{
location: {{Get_address_points.data.row_to_json[0]}}
}

If that doesn't work, can you hover over {{Get_address_points.data}} and expand the structure a bit? Ideally we would see something like this:

Yes I do get that with {{Get_address_points.data.row_to_json[0]}}. Still no markers though ;/
image

Weird! Would you mind posting a copy of your query data so I can test with the same data you're using? If you write into intercom I can also take a closer look there which might help to expedite things.

Absolutely, but I'm not perficient with intercom. How do I use it to send data directly to you?

It's right in your Retool app, just hit the question button in the lower right and click on 'Chat with support'! (It's getting close to the end of Friday so will be off for the weekend soon but if you write in there we'll try and help you as soon as we can :smile: )

Ok, I have sent it via chat! Have a great weekend :blush:

Circling back here to post in case anyone runs into the same problem!

Somewhere in the code edits we lost the line

map.addImage("custom-marker", image);

which meant that when mapbox tried to load the customer-marker image here

map.addLayer({
id: "uid",
type: "symbol",
source: "uid_source",
layout: {
"icon-image": "custom-marker",
"icon-size": 0.5,
"icon-padding": 0,
"icon-ignore-placement": true
// get the title name from the source's "title" property
}
});

it had nothing to load and that was causing issues.

I dont suppose you have the working code for this example @Kabirdas @Phil_in_Tex I'm currently stuck on this too atm :sweat_smile:

Hey @Muin! Here's a copy of the full HTML code that should work:

<html>

<head>
  <meta charset="utf-8">
  <title>Load data from query</title>
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <link href="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css" rel="stylesheet">
  <script src="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
  </style>
</head>

<body>
  <div id="map"></div>

  <script>
    mapboxgl.accessToken = 'xxxxxxxxxxxxxxxxxxxx';
    const map = new mapboxgl.Map({
      container: 'map', // container ID
      style: 'mapbox://styles/mapbox/streets-v11', // style URL
      zoom: 7, // starting zoom
      center: [-98.081815, 30.197311] // starting center
    });

    map.on("load", () => {
      map.loadImage(
        "https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png",
        (error, image) => {
          if (error) throw error;

          map.addImage("custom-marker", image);

          window.Retool.subscribe(function (model) {
            if (!model) {
              return;
            }

            let data = model.location;

            if (map.isSourceLoaded("uid_source")) {
              map.getSource("uid_source").setData(data);
            } else {
              map.addSource("uid_source", {
                type: "geojson",
                data,
              });
            }
          });

          map.addLayer({
            id: "uid",
            type: "symbol",
            source: "uid_source",
            layout: {
              "icon-image": "custom-marker",
              "icon-size": 0.5,
              "icon-padding": 0,
              "icon-ignore-placement": true,
              // get the title name from the source's "title" property
            },
          });
        }
      );
    });
  </script>
</body>

</html>

This assumes the custom component model is

{
location: {{locationData}}
}

and locationData is equal to something like

[
    {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [
              -98.081815,
              30.197311
            ]
          },
          "properties": {
            "mlsid": 1000612,
            "address": "206  Victoria Peak  LOOP"
          }
        },
        {
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [
              -98.07885,
              30.20159
            ]
          },
          "properties": {
            "mlsid": 1267460,
            "address": "171  Dome Peak  TER"
          }
        }
      ]
    }
  ]

Let me know if that's helpful! Otherwise we can take a look at your setup and do some debugging there as well :slightly_smiling_face:

yep thats worked thanks!