Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc issues + Rectangular or lasso selection is not available out of the box, right? #2658

Closed
mccalluc opened this issue Feb 7, 2019 · 7 comments
Assignees

Comments

@mccalluc
Copy link
Contributor

mccalluc commented Feb 7, 2019

First, in the docs for "Adding Interactivity", the links here

The picking engine is exposed through the DeckGL.pickObject and DeckGL.pickObjects methods.

should probably go to

instead of

because the picking methods are covered in the docs for Deck, but not in the docs for the DeckGL component.


Second, in the Deck docs there is some confusion:

pickMultipleObjects

Performs deep picking. Finds all close pickable and visible object at the given screen coordinate, even if those objects are occluded by other objects.

deck.pickObject({x, y, radius, layerIds, depth})

Should the example there be about pickMultipleObjects rather than pickObject?


Finally, in early 2017 it looks like there was an idea of supporting rectangle or lasso selection. I don't think lasso was ever implemented, and for rectangular region selection, an earlier questioner was pointed to the pickObjects docs #1683.

I wanted to confirm that there's no built-in notion of selecting a set of objects. I've started doing something like this...

export default class SelectablePolygonLayer extends PolygonLayer {
  renderLayers() {
    var {id, getFillColor, data, isSelected, ...rest} = this.props;
    const selectedOverlayProps = {
      id: `selected-${id}`,
      getFillColor: [64,64,64],
      data: data.filter(isSelected),
      ...rest
    };

    const overlay = new PolygonLayer(selectedOverlayProps)
    return [...super.renderLayers(), overlay];
  }
}

and then, using my particular data structures...

          isSelected: cellEntry => this.state.selectedCellIds[cellEntry[0]],
          onClick: info => {
            const cellId = info.object[0];
            if (this.state.selectedCellIds[cellId]) {
              this.setState((state) => {
                delete state.selectedCellIds[cellId];
                return {selectedCellIds: state.selectedCellIds}
              })
            } else {
              this.setState((state) => {
                state.selectedCellIds[cellId] = true;
                return {selectedCellIds: state.selectedCellIds}
              })
            }
          }

... which lets me select individual objects, but maybe there's a better way? Thanks!

@Pessimistress
Copy link
Collaborator

Correct, deck.gl does not have the notion of "selection". Rectangular selection can be implemented with deck.pickObjects, and lasso (picking by arbitrary bounds) is going to be tricky.

@ibgreen
Copy link
Collaborator

ibgreen commented Feb 11, 2019

I've started doing something like this...

I believe "lasso" type selections are indeed implemented in some of our internal deck.gl (or rather nebula.gl) applications the way I believe you are proposing, by creating a non-pickable "mask" layer with a polygon that covers the screen and has a lasso-shaped hole.

Then any non-hidden objects can be picked with Deck.pickObjects.

However, I don't think the technique required any custom/subclassed layers, the standard polygon layer did the trick.

@georgios-uber am I getting this right? Does nebula.gl itself contains any code or example of this technique?

@mccalluc
Copy link
Contributor Author

Oh, thanks: That sounds like an interesting approach. For now I'll try to get the rectangular selection first, and then look at the lasso: If I get something tidy, I'll post an example here.

(For me, making a composite layer seemed like an easy way to distinguish the behavior and style for the selected and unselected objects, but I could see putting if-thens in the properties, too.)

Feel free to close?

@georgios-uber
Copy link
Contributor

I've started doing something like this...

I believe "lasso" type selections are indeed implemented in some of our internal deck.gl (or rather nebula.gl) applications the way I believe you are proposing, by creating a non-pickable "mask" layer with a polygon that covers the screen and has a lasso-shaped hole.

Then any non-hidden objects can be picked with Deck.pickObjects.

However, I don't think the technique required any custom/subclassed layers, the standard polygon layer did the trick.

@georgios-uber am I getting this right? Does nebula.gl itself contains any code or example of this technique?

Yes, nebula uses standard deck methods to implement selection.

@mccalluc
Copy link
Contributor Author

@georgios-uber : Thanks! I'll look deeper into that. I filed some small PRs on the docs, but I think I'm still confused by the relationship between Deck and DeckGL. At the end, the DeckGL docs say:

All Deck methods are available on the DeckGL component.

I may be interpreting it wrong, but it doesn't seem to work that way for me:

    ...
    this.deck = <DeckGL {...props}/>;
    console.log('pickObjects?', this.deck.pickObjects);
    console.log('other properties:', this.deck);

produces:

pickObjects? undefined
other properties: 
    {$$typeof: Symbol(react.element), type: ƒ, key: null, ref: null, props: {…}, …}
    $$typeof: Symbol(react.element)
    key: null
    props: {controller: {…}, getCursor: ƒ, onDrag: ƒ, onDragStart: ƒ, onDragEnd: ƒ, …}
    ref: null
    type: ƒ DeckGL(props)
    _owner: FiberNode {tag: 1, key: null, elementType: ƒ, type: ƒ, stateNode: Spatial, …}
    _store: {validated: false}
    ...

The pickObjects method is not available there.

I've also tried something like nebula, here, but for me the context is undefined, and even if it weren't, the layerManager docs make it sound like an internal I should not need to touch.

@ibgreen
Copy link
Collaborator

ibgreen commented Feb 13, 2019

Your JSX stanza (this.deck = <DeckGL {...props}/>;) is only going to return a tiny ReactElement descriptor, which holds the props and is later used during the React diffing process to create/update the actual React component instance.

You need to get a ref to the actual DeckGL React component, that ref will have the functions you seek.

https://github.com/uber/deck.gl/blob/master/modules/react/src/deckgl.js#L79

Google "React refs" to see how this is done, i.e. <DeckGL ref={....

@mccalluc
Copy link
Contributor Author

That solves that problem. Thank you! I'll close this.

There are still issues, but it's on my side: My data has coordinates from 0-50000, and if I try too large of a region I have an error in drawAndSamplePickingBuffer:

var pickedColors = new Uint8Array(width * height * 4);
Uncaught RangeError: Invalid typed array length: 8644546848

Figuring out exactly what that limit is:

> new Uint8Array(Math.pow(2,31))
  Uncaught RangeError: Invalid typed array length: 2147483648
> new Uint8Array(Math.pow(2,31)-1).byteLength
  2147483647
> new Uint16Array(Math.pow(2,31)-1).byteLength
  Uncaught RangeError: Array buffer allocation failed
> new Uint16Array(Math.pow(2,30)).byteLength // Might squeeze out a little more memory by using a different type? Not sure what's going on here.
  2147483648

So, for me, I think I'll keep my coordinates as they are, and implement my own region selection, but may revisit this in the future. Thank you again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants