visgl / deck.gl

WebGL2 powered visualization framework
https://deck.gl
MIT License
12.16k stars 2.08k forks source link

Layer update only on map drag with mouse. #724

Closed Nedo93 closed 7 years ago

Nedo93 commented 7 years ago

Hi guys, we are facing an issue using deck.gl We are trying to draw a scatterplot layer dinamically; when coordinates change the layer should update and simply move the circle on the map. The problem is that the circle won't move (aka the layer won't be redrawed) until we drag the map with mouse. We are kinda stucked here so if somebody can help us, would be really appreciated. I'm attaching the main files, if it's not enough to understand the problem, let me know it what else you need. Thank you in advance.

App.js :

/* global window,document */
import React, {Component} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import DeckGLOverlay from './deckgl-overlay.js';
import {json as requestJson} from 'd3-request';

// Set your mapbox token here

const MAPBOX_TOKEN = 'pk.eyJ1IjoibmVkbzkzIiwiYSI6ImNqMjRxOXl0dTAwMjcycW41cHUzanFqeDEifQ.SeVTw4r_BC3xL3S30XfumQ';

class Root extends Component {

  constructor(props) {
    super(props);
    this.state = {
      viewport: {
        ...DeckGLOverlay.defaultViewport,
        width: 500,
        height: 500
      },
      //Add another instance here for every new layer      
      scatters: null,
      pos: null,
      icon: null,
      paths: null,    
      time: 0
    };

    //Add another instance of requestJson for every new layer    

    requestJson('./data/scatters.json', (error, response) => {
      if (!error) {
        this.setState({scatters: response});
      }
    }); 
    requestJson('./data/pos.json', (error, response) => {
      if (!error) {
        this.setState({pos: response});
      }
    }); 
    requestJson('./data/icon.json', (error, response) => {
      if (!error) {
        this.setState({icon: response});
      }
    }); 
    requestJson('./data/paths.json', (error, response) => {
      if (!error) {
        this.setState({paths: response});
      }
    });    
  }

  componentDidMount() {
    window.addEventListener('resize', this._resize.bind(this));
    this._resize();
    this._animate();
  }

  _resize() {
    this._onChangeViewport({
      width: 1024, //window.innerWidth,
      height: 720 //window.innerHeight
    });
  }

  _onChangeViewport(viewport) {
    this.setState({
      viewport: {...this.state.viewport, ...viewport}
    });
  }
    //Add in const { }, new layers's ID
  render() {
    const {viewport, scatters, pos, icon, paths, time} = this.state;

    return (    
    <div className="mapCont">
        <div className="Map">
            <MapGL
                {...viewport}
                mapStyle="mapbox://styles/mapbox/dark-v9"
                perspectiveEnabled={true}
                onChangeViewport={this._onChangeViewport.bind(this)}
                mapboxApiAccessToken={MAPBOX_TOKEN}>
                <DeckGLOverlay viewport={viewport}
                //Add another layere here:              
                scatters = {scatters}
                pos={pos}               
                paths={paths}                               
                trailLength={180}
                time={time}
                />
            </MapGL>
        </div>
    </div>
    );
  } 
}

render(<Root />, document.body.appendChild(document.createElement('div')));

deckgl-overlay.js :

import React, {Component} from 'react';
import DeckGL, {PathLayer} from 'deck.gl';
import DeckGL, {ScatterplotLayer} from 'deck.gl';
import $ from 'jquery'; 

var tableJSONStringed = null;
var posJSON = [];

//Position tracker

window.setInterval(function(){

        var request = $.ajax({
            url: "https://nedo93.000webhostapp.com/phpgetcoords.php",                 
            method: "POST",               
            dataType: "json"    
        });         

        request.done(function( msg ) {  
            posJSON = [];       
            tableJSONStringed = JSON.stringify(msg, null, " ");
            //var count = Object.keys(msg).length;  
            //alert( tableJSONStringed);

            for(var i=0; i < Object.keys(msg).length; i++) {
                posJSON.push({
                    "position": [msg[i].Longitude, msg[i].Latitude],
                    "radius": 0.05,
                    "color": [255, 255, 0, 255]
                });
            }

            console.log(JSON.stringify(posJSON));

        });              
        request.fail(function( jqXHR, textStatus ) {
            //alert( "Request failed: " + textStatus );
        });
}, 5000);

export default class DeckGLOverlay extends Component {  

  static get defaultViewport() {
    return {
      longitude: 11.25,
      latitude: 43.77,
      zoom: 16,
      maxZoom: 18,
      pitch: 40, //viewport tilt
      bearing: 0
    };
  }

  _initialize(gl) {
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);
  }
    //Add in const { }, new layers's ID
  render() {
    const {viewport, scatters, pos, icon, paths, trailLength, time} = this.props;   

    //Add another instance of a new layer here:

    const layers = [      
      new ScatterplotLayer({
        id: 'scatters',
        data: scatters,     
        radiusScale: 100,
        outline: false
       }),
      new ScatterplotLayer({
        id: 'pos',
        data: posJSON,      
        radiusScale: 100,
        outline: false
       }),    
       new PathLayer({
        id: 'paths',
        data: paths,        
        rounded: true,
        getColor: paths => [255, 0, 0, 255],
        getWidth: paths => 0.03,
        widthScale: 100
      })
    ];  

    return (
      <DeckGL {...viewport} layers={layers} onWebGLInitialized={this._initialize} />
    );      
  }    
}

index.html

<!doctype html>
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta charset='UTF-8' />
        <title>MRT</title>
        <style>
            body {margin: 0; padding: 0; <!-->overflow: hidden;</!-->}
        </style>
    </head>
    <body>              
        <div id= "subBar"></div>        
            <div class="App">
                <div class="App-logo">
                    <img src="pic/logo.png" alt="Logo" style="width:583px;height:116px;">                       
                </div>
                <div class="App-title">
                    <h1>MARATHON RUN TRACKER</h1>
                </div>
            </div>      
        <div id= "subBar"></div>
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> 
        <div class="mapBox">            
            <script src='bundle.js'></script>               
        </div>
        <div id="subBar"></div>         
    </body>
</html>
ibgreen commented 7 years ago

@Nedo93 - I think this may be related to the fact that deck.gl uses shallow equality on the data prop by default - and as far as I can tell, your interval mutates the data array instead of creating a new one.

You can use the dataComparator props or an updateTriggers: {all: ...} to trigger a vertex attribute update. See the base Layer docs.

Nedo93 commented 7 years ago

But where (physically in the code) should we implement the updateTriggers?

ibgreen commented 7 years ago

It is a property on the layers. Search the deck.gl repo for updateTriggers, they are use in many of the examples.

Or just supply dataComparator: deepEqual (e.g. from lodash) to the layers.

Nedo93 commented 7 years ago

Ok thank you, we will take a look at it

Nedo93 commented 7 years ago

Oh thank you, couldn't find that specific page, thank you very much