vasturiano / globe.gl

UI component for Globe Data Visualization using ThreeJS/WebGL
https://vasturiano.github.io/globe.gl/example/world-population/
MIT License
2.03k stars 301 forks source link

Cannot Update Arcs Data #13

Closed ttK5050 closed 4 years ago

ttK5050 commented 4 years ago

Describe the bug Hi! I'm having a problem with my Globe.gl application that I'm hoping you can help me with. I cannot seem to be able to pass new JSON data to - or modify at all - the arcs data of my application.

To Reproduce

  1. Initialize some arcs data based off a JSON array: world.arcsData(usersJSONObjects).arcLabel(() => '').arcStartLat(() => "42.9814").arcStartLng(() => "-70.9478").arcColor(() => ['#ff0000', '#ffffff']).arcEndLat(arcs => arcs.loc_lat).arcEndLng(arcs => arcs.loc_lon).arcStroke(() => 0.025);
  2. Then, change an attribute later on (in my case, in my onZoom() function to make the arc bigger): world.arcStroke(() => null);
  3. I get the following error: Uncaught TypeError: Cannot read property 'uniforms' of undefined at updateObj (three-globe.module.js:856) at data-joint.module.js:288 at Array.forEach (<anonymous>) at updateObjs (data-joint.module.js:283) at viewDigest (data-joint.module.js:264) at threeDigest (three-globe.module.js:519) at Function.update (three-globe.module.js:841) at kapsule.module.js:112 at later (index.js:27)

Expected behavior Oddly enough, this functionality works with labels: for example, I can later change the size of my labels and such, but for arcs, an error is given if I attempt to change anything beyond the initialization. The following code works:

  1. Initialize some arcs data based off a JSON array: world.arcsData(usersJSONObjects).arcLabel(() => '').arcStartLat(() => "42.9814").arcStartLng(() => "-70.9478").arcColor(() => ['#ff0000', '#ffffff']).arcEndLat(arcs => arcs.loc_lat).arcEndLng(arcs => arcs.loc_lon).arcStroke(() => 0.025);
  2. Then, change an attribute later on (in my case, in my onZoom() function to make the label size bigger): world.labelSize(() => 0.06)
  3. Works great! 😃

Desktop (please complete the following information): Not sure if this is relevant because this crops up across all platforms I've tried this on, but Mac OS 10.14.6, Chrome Version 83.0.4103.116.

Additional context Your work is amazing! Thanks so much for all your help and support in maintaining this project - having this be open-source must be an incredible commitment, but I can tell you that this framework is helping many people like me!

vasturiano commented 4 years ago

@ttK5050 thanks for reporting this!

Would you mind making a simple reproducible example with your case on https://codepen.io/ . That'll make it easier to debug the issue. 👍

ttK5050 commented 4 years ago

Hi @vasturiano, thanks for the reply! Attached below is a simple example of the problem: if you would like, I could try to put it on code sandbox or codepen but I'm not very familiar with them. Hope this is okay, and thanks again for maintaining this!

<!DOCTYPE html>
<html>
  <head>
    <title>Arc Issue Demo</title>
    <!-- IMPORT GLOBE.GL -->
  </head>

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

    <script>
      //some sample data: two objects with some basic attributes
      var sampleJSONData = [
        {
          first_name: "Mexico City",
          loc_lat: "19.4326",
          loc_lon: "-99.1332"
        },
        {
          first_name: "Seattle",
          loc_lat: "47.6062",
          loc_lon: "-122.3321"
        }
      ];

      //create a basic globe
      var world = Globe({ waitForGlobeReady: true });
      world = world(document.getElementById("globeViz"))
        .globeImageUrl(
          "https://unpkg.com/three-globe@2.7.2/example/img/earth-night.jpg"
        )
        .pointOfView({ lat: 0, lng: 0, altitude: 0.1 }, 3000)
        .showGraticules(true);

      //add the objects as labels to the globe
      //this is to show how the feature should work
      //note that the label size is set to 0.3
      world
        .labelsData(sampleJSONData)
        .labelLat(labels => labels.loc_lat)
        .labelLng(labels => labels.loc_lon)
        .labelText(labels => labels.first_name)
        .labelAltitude(() => 0.001)
        .labelColor(labels => "white")
        .labelSize(() => 0.3)
        .labelDotRadius(() => 0.3)
        .labelDotOrientation(() => "bottom");

      //now, we add add arcs from a predifined "hub" to these cities
      //note that the stroke is set to 0.005
      world
        .arcsData(sampleJSONData)
        .arcLabel(() => "")
        .arcStartLat(() => "42.9814")
        .arcStartLng(() => "-70.9478")
        .arcColor(() => ["#ff0000", "#cfcfcf"])
        .arcEndLat(arcs => arcs.loc_lat)
        .arcEndLng(arcs => arcs.loc_lon)
        .arcStroke(() => 0.005);

      //great, setup is complete!

      //now, 5 seconds after load, let's try to change the size of the labels
      setTimeout(function() {
        alert("changing label size");
        world.labelSize(() => 0.06);
        alert("finished changing label size");
      }, 5000);

      //then, 10 seconds after load, try to change the arc stroke
      setTimeout(function() {
        alert("changing ARC STROKE size");
        world.arcStroke(() => 0.1);
        alert("finished changing ARC STROKE");
      }, 10000);

    </script>
  </body>
</html>
lslzl3000 commented 4 years ago

@ttK5050 please try to use codepan or something to show the full demo. I have tested changing arcStroke dynamically in my code. It works fine with my data or the demo data. It should not have any problems. My best guess is that there is something wrong with your data format... anyway, please give us a full demo to test.

ttK5050 commented 4 years ago

Hi @lslzl3000, thanks for the feedback. I suppose the main trouble for me is how to import and get all the dependencies to work on Codepen... if you could give me a quick pointer on how best to do that I'd much appreciate it 🙂 and get it done right away.

I have to admit, the sample code I posted actually does seem to work on my system as well. I should have tested it better, my bad. Some explanation about my actual setup. I create a new var globe object upon the loading of the page. I immediately create a set of arcs data and a set of labels data. I then bind an onZoom function to the globe so that I can dynamically scale the size of my labels and the user zooms in and out. Here's the simple function:

function updateSizes(pos){
            if (pos.altitude>0.4) {
                world.labelSize(() => (0)).labelDotRadius(() => (0.06+pos.altitude/2.5));

                //world.arcStroke(() => 1);
                //above line should work, but causes error shown in OP
            } else if (pos.altitude<0.07) {
                world.labelSize(() => 0.06).labelDotRadius(() => 0.02).labelAltitude(() => 0.001);
            } else {
                 world.labelSize(() => 0.06+pos.altitude/2.5).labelDotRadius(() => 0.06+pos.altitude/2.5);
            }
        }

I tweaked around with my code and tried changing the arc stroke at a different point in my code (say, right after initializing the arc objects) - and it worked as you said it would. That made me further confused about what was causing the error because when I try changing the arc stroke of the world object right next to where I'm changing the label size in the onZoom function, it gives me the error I mentioned.

But I must be missing something or doing something wrong elsewhere that isn't clear through snippets, so I'll post a full code example when I can and after a helping hand 😉. Thanks so much for all your help!

ttK5050 commented 4 years ago

Sorry for the lack of a working example, still am unsure of how to do so properly - but I noticed something interesting that may help in pinning down the problem.

I am using both Arcs data and Labels Data in my project, something I don't think I saw in the examples (i.e. using multiple forms of objects at the same time). The first static initialization of both forms work great, something like this:

            world.labelsData(finalJSON)
            .labelLng(labels => labels.loc_lon)
            .labelText(labels => labels.first_name)
            .labelAltitude(() => 0.001)
            .labelColor(labels => setColor(labels))
            .labelSize(() => 0.3)
            .labelDotRadius(() => 0.3)
            .labelDotOrientation(() => "bottom")
            .onLabelHover(onHover)
            .onLabelClick(onClick).arcLabel(() => '');

            world.arcsData(usersJSONObjects)
            .arcStartLat(arcs => +arcs.start_lat)
            .arcStartLng(arcs => +arcs.start_lon)
            .arcEndLat(arcs => +arcs.loc_lat)
            .arcEndLng(arcs => +arcs.loc_lon)
            .arcColor(() => ['#ff0000', '#cfcfcf'])
            .arcStroke(() => 0.015);

Editing them on the fly is where the issue arises. Oddly, if I only include arcs objects and leave out the labels objects, the arcs respond just fine to having their properties change later (e.g. onZoom() function). Likewise for the labels objects, when initialized on their own without arcs, there is no problem in adjusting them later.


       world.onZoom(function onZoom (pos) {

            //this line works fine - only when the labels don't exist
            world.arcStroke(() => (pos.altitude/8.5));

            //changing the label size seems to work with or without arcs data being present, adding to the mystery... :)
            if (pos.altitude>0.4) {
                world.labelSize(() => (0)).labelDotRadius(() => (0.06+pos.altitude/2.5));
            } else if (pos.altitude<0.07) {
                world.labelSize(() => 0.06).labelDotRadius(() => 0.02).labelAltitude(() => 0.001);
            } else {
                 world.labelSize(() => 0.06+pos.altitude/2.5).labelDotRadius(() => 0.06+pos.altitude/2.5);
            }

        });

It's only when I have two types of objects on the Globe that the framework has a problem.

Note that I set the initial arc stroke to 0.015. Here, in my onZoom function, I try to scale it up to 0.5 for the sake of testing. It seems to work, changing the stroke to 0.5. However, after that first change to the stroke size from the onZoom function, every call after begins to throw the 'Uncaught TypeError' as before and refuses to adjust the arc stroke further.

Again, I understand that you can't provide proper assistance without a working example, as my case may be unique to my setup or data, etc. But I thought that I would let you know that it seems to only fail when I have two different types of objects (arcs and labels), so it might have some roots in the underlying framework...

Thanks so much!

vasturiano commented 4 years ago

@ttK5050 thanks for looking deeper into this.

I've tested the two layers together (arcs and labels) and am unable to reproduce the issue. I've actually put together a codepen for it: https://codepen.io/vasturiano/pen/gOPNVdO

It would be great if you could extend that codepen to a point that it reproduces the problem, otherwise it's quite difficult to troubleshoot it.

ttK5050 commented 4 years ago

@vasturiano Thanks very much for your help - just solved the issue after working a bit with your example!

As @lslzl3000 pointed out, it was in fact some sort of data issue. I'm using Django as a backend framework, and I pass a JSON "dump" string to my frontend webpage where I parse it into a data structure using JSON.parse(). I'm still not sure why this particular instance of receiving and parsing JSON data caused such distress (and if it will to anyone else), especially because it worked without trouble when used statically.

In any case, I used a snippet of your code to take my (faulty?) JSON data and put what I need in a new data structure...

const arcsData = [...Array(usersJSONObjects.length).keys()].map(x => ({
          startLat: 42.98,
          startLng: -70.94,
          endLat: usersJSONObjects[x].loc_lat,
          endLng: usersJSONObjects[x].loc_lon
}));

...and it works! I should have thought to look into this deeper earlier, so thanks so much for your patience! Your framework rocks 😄!