Deck.gl: Editable GeoJsonLayer? (Way to draw new features)

Created on 28 Jul 2017  Â·  16Comments  Â·  Source: visgl/deck.gl

First off, I'd like to thank everyone involved in the continued development of this project.
I've been using both deck.gl and react-map-gl trying to see if we can migrate to using these two from openlayers. Is there a supported/tested way to add features as such?

mapbox-gl-draw comes to mind, but there's an issue with it with regards to react-map-gl: https://github.com/uber/react-map-gl/issues/328

EDIT: I'm considering writing a deck.gl layer despite my limited knowledge, and am wondering if it's possible that the GeoJsonLayer or any related sublayer can be made as to have a mode wherein a feature can be added.

Most helpful comment

I managed to get it working, yes. I could probably spin up a sample soon, but I'm having doubts that it causes performance issues as seen here: https://github.com/uber/deck.gl/issues/936, where I cannot make use of fp64.

All 16 comments

@ralphstodomingo deck.gl layers by default refresh when the data prop changes by shallow comparison. If you add new features to the feature collection, you need to either construct a new object, or specify a dataComparator as documented here.

Thanks for the response @Pessimistress!

Got that. Is it possible to write a layer that takes mouse input? e.g. clicks on the layer register as points, or initiates drawing a line or polygon.
My initial assumption would be tapping into the layers' onClick callback and add to the data prop the point that was clicked. The onHover callback can be used when taking where the mouse pointer currently is hovering. Is this correct?

EDIT: Apparently, the layers' onClick and the DeckGL component's onLayerClick apply only to its objects. Is there a generic click or hover event?

Asking since I don't have enough WebGL knowledge. If it's possible, then I'll take the jump and try to make one for this project I'm working on.

@ralphstodomingo - if you are building "editing" functionality it might be better to use your own event handling and rely on the new queryObject function in deck.gl v4.1 (that was just released) to check what object is under your event coordinates. That way you don't have any restrictions on what browser events you can use.

@ibgreen @Pessimistress I've managed to come up with a quick-and-dirty composite layer (spun off from GeoJSONLayer), which I would be truly happy to contribute here, but I have concerns about specific things and would like to ask for your expert opinion (totally a noob when it comes to these things.):

I relied on react-map-gl's global map event handlers: onHover and onClick to update internal layer state (feature being edited, coordinates etc). This is because the layer picking methods provided by the Layer interface only applies to the features inside. I thought about making the event functions defined inside the class, but I wondered how I would access these post-class instantiation.
I might certainly be able to do:

const DrawGeoJSONLayerInstance = new DrawGeoJSONLayer();

<ReactMapGL
  ...
  onHover={DrawGeoJSONLayerInstance.onHover}
  onClick={DrawGeoJSONLayerInstance.onClick}
>
    <DeckGL
      layers={[
        DrawGeoJSONLayerInstance, 
      ]}
    >
    </DeckGL>
</ReactMapGL>

but from experience, putting out the instantiation out of the DeckGL render method causes the layer to not update at all.
The class functions mentioned only update the state of the layer (e.g. manipulate the data attribute of the underlying GeoJSONLayer).
Do I have a choice on where to attach the class functions other than react-map-gls? is there an internal layer mechanism I can use to bind to those events?

I haven't even gotten to the editing part yet, but I can surmise I would probably pass the geojson to the layer, load the layer as editing mode. Upon completion, call the provided callback function with the updated geojson.

@ralphstodomingo Here are some thoughts, I really like the direction you are going but have some concerns about this approach:
1) I don't think calling those functions like that make sense. They are supposed to be called from the deck.gl picking engine that supplies a pick info object.
2) As mentioned before you can invoke the deck.gl picking engine directly with DeckGL.queryObject - you don't need to rely on those "legacy" callbacks.
3) I am not convinced that the event handling functions in the layer are sufficient for you to fully implement editing functionality within the layer callbacks. You may need to track the click, drag, release cycle for instance, which may not be possible with current onHover/onClick.

Just like you, I'd love to see editing capabilities packaged into a reusable EditableGeoJsonLayer (or even better new GeoJsonLayer({editable: true})), but I suspect there needs to be some clever design work on the API before we get there.

I still suspect you'd be best off using external event handling and manage the editing state in the app in the initial version. Once things are working we can collaborate on extending the layer api to enable you to build more and more this directly into the layer.

CC @georgios-uber

@ibgreen
A few things left:

  1. The DrawGeoJSONLayerInstance.onHover is not the one inherited from the Layer class. It's just a custom function that updates state internally.
  2. I've seen this one and I fear I don't really understand where to use this the first time I read it. But having thought about it again, did you mean using a different means of handling events, and using DeckGL.queryObject within that?

Thank you for taking the time to reply to me, and thanks to everyone contributing to this project. It has been really informative. Wish to be able to contribute soon!

did you mean using a different means of handling events, and using DeckGL.queryObject within that?

@ralphstodomingo Yes. queryObject was added so that application's don't have to rely on deck.gl's event handling. We realized that no matter how much effort we put into the built-in event handling implementation we wouldn't be able to cover all use cases. Also, integrating deck.gl into an existing application becomes much easier when you don't have to port the existing event handling to deck.gl.

When it comes to how to actually do event handling, there are many options, perhaps the simplest is to just use react's event handling together with queryObject.

Thank you for taking the time to reply to me, and thanks to everyone contributing to this project. It has been really informative. Wish to be able to contribute soon!

No worries and looking forward to your contributions!

To anyone coming across this and thinking what to use, I used react's synthetic events like this (in case you're trying to draw new features, because you can use DeckGL.queryObject if you're editing a feature):

onClickHandler = (event) => {
  const lngLat = this.deckGLInstance.layerManager.context.viewport.unproject([event.clientX, event.clientY]); // returns coordinates clicked on screen, which you can use
}
... // render
<div
  onClick={this.onClickHandler}
>
  <DeckGL
  ...
  ref={(el) => { this.deckGLInstance = el; return this.deckGLInstance; }}
   />
</div>

Not really sure if at this moment there is another way to get the coordinates other than access the viewport itself.

@ralphstodomingo While that works, I'd like to see us offer a better API.

The picking info object returned by the picking engine does have coordinates.

onClickHandler = (event) => {
  const pickInfo = this.deckGLInstance.queryObject({x: event.clientX, y: event.clientY, ...}); 
  console.log(pickInfo.lngLat);
}

But I think this only works when something was actually picked (otherwise I believe that queryObject returns null) and so it is not enough for your use case? Sounds like we should provide an additional API or flag to better support editing?

@ibgreen

I think this only works when something was actually picked (otherwise I believe that queryObject returns null)

Yes, I can only use queryObject if I'm dealing with layer features.
I in my opinion would prefer an additional flag that lets queryObject return a picking info object regardless of whether a layer feature has been picked or not. But still, an additional API for simply returning the coordinates and viewport-relative mouse positions will be useful, too (assuming that such a function will be more performant than queryObject since it won't have to query all layers visible.)

Also, is there a cleaner way to get access to particular layers post-DeckGL initialization? Or even just the viewport: getLayers or getViewport? Constructing layers before passing them to DeckGL, when I tried that, whenever the render function is called the layers don't update like they should.
Not sure about this, though.

@pessimistress @shaojingli @1chandu see above, good input...

Closing in favor of #894.

@ralphstodomingo If you end up open sourcing your solution, drop a link! Sounds super useful.

I managed to get it working, yes. I could probably spin up a sample soon, but I'm having doubts that it causes performance issues as seen here: https://github.com/uber/deck.gl/issues/936, where I cannot make use of fp64.

@ralphstodomingo Did you manage to get through? Did you work on any sample? That would be terrific!

I did, but that was on an older version of deck.gl, and I had to write a
picking layer mechanism to highlight shapes on selection. There was also a
performance issue, where it lags so bad when I use fp64.
I keep on saying this, but I'll come around sometime soon and will try to
make a sample of it working with the latest version of deck.gl, and
hopefully take advantage of new features such as the new layer highlight
mechanism.

On Fri, May 4, 2018 at 11:13 PM, Adlen Afane notifications@github.com
wrote:

@ralphstodomingo https://github.com/ralphstodomingo Did you manage to
get through? Did you work on any sample? That would be terrific!

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/uber/deck.gl/issues/818#issuecomment-386632472, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AOoXKWLryNtxymvtGGZ8S8uxyEpw02pMks5tvHAwgaJpZM4OmFi6
.

Was this page helpful?
0 / 5 - 0 ratings