Closed CorentinAmbroise closed 4 years ago
Are you implementing a custom composite layer? If so, can you share the code?
When you say "it used to work", what was the last working release?
It's hard to tell what's wrong from your description, but you should make sure that when you "switch", the hexagon layer and the icon layer use different ids.
Here is the Code Pen that I wrote to reproduce the bug : https://codepen.io/CorentinAmbroise/pen/yLBOxPa?editors=1011
It used to work in a another version of the framework I guess, I'll dig deeper to find it. Yes they already use different ids.
Thanks !
import React, { Component } from 'react';
import { StaticMap } from 'react-map-gl';
import DeckGL from '@deck.gl/react';
import { IconLayer } from '@deck.gl/layers';
import { HexagonLayer } from '@deck.gl/aggregation-layers';
import { json } from 'd3';
const randomName = () => {
const names = Object.keys(iconMapper);
return names[Math.floor(Math.random() * names.length)];
}
class Heatmap extends HexagonLayer {}
Heatmap.defaultProps = {
getPosition: { type: 'accessor', value: d => (d ? d.geometry.coordinates : null) },
radius: 1000,
colorRange: [
[254, 235, 226, 255 * 0.5],
[252, 197, 192, 255 * 0.6],
[250, 159, 181, 255 * 0.6],
[247, 104, 161, 255 * 0.7],
[197, 27, 138, 255 * 0.8],
[122, 1, 119, 255 * 0.8],
],
opacity: 1,
};
const iconMapper = JSON.parse('{"001-goal":{"x":1536,"y":1023,"height":512,"width":512},"002-flag":{"x":1024,"y":1023,"height":512,"width":512},"003-tasks":{"x":511,"y":0,"height":511,"width":511},"004-cloud":{"x":0,"y":1535,"height":512,"width":512},"005-presentation":{"x":1536,"y":1535,"height":512,"width":512},"006-presentation-1":{"x":512,"y":1535,"height":512,"width":512},"007-briefcase":{"x":0,"y":0,"height":511,"width":511},"008-rating":{"x":1024,"y":1535,"height":512,"width":512},"009-rating-1":{"x":512,"y":1023,"height":512,"width":512},"010-badge":{"x":1536,"y":511,"height":512,"width":512},"011-trophy":{"x":0,"y":511,"height":512,"width":512},"012-clipboard":{"x":0,"y":1023,"height":512,"width":512},"013-rating-2":{"x":1022,"y":0,"height":511,"width":511},"014-success":{"x":1024,"y":511,"height":512,"width":512},"015-rating-3":{"x":512,"y":511,"height":512,"width":512},"016-trophy-1":{"x":1533,"y":0,"height":511,"width":511}}');
const spritesheetIcons = 'https://dl.dropboxusercontent.com/s/i99zfjzliuy6rn5/spritesheetIcons.png';
const data = json('https://dl.dropbox.com/s/hng2qrjvbn6x707/randomPointsInParis.json');
class Icon extends IconLayer {}
Icon.defaultProps = {
iconAtlas: spritesheetIcons,
iconMapping: iconMapper,
dataTransform: data => data.map(feature => ({ position: feature.geometry.coordinates, icon: randomName() })),
getSize: 1,
sizeScale: 100,
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
width: window.innerWidth,
height: window.innerHeight, // - this.props.header.height,
viewState: {
longitude: 2.3442,
latitude: 48.8617,
zoom: 11.7,
},
layers: []
}
}
componentWillMount() {
this.setState({ layers: [new Heatmap({ id: 'random-icon', data })] })
}
componentWillUpdate(nextProps, nextState) {
const layers = [ ...this.state.layers ];
if (nextState.viewState.zoom > 14) {
layers[0] = new Icon({ id: 'icon-random', data });
// layers[0] = new IconLayer({
// id: 'icon-random',
// data,
// dataTransform: data => data.map(feature => ({ position: feature.geometry.coordinates, icon: randomName() })),
// iconAtlas: spritesheetIcons,
// iconMapping: iconMapper,
// getSize: 1,
// sizeScale: 100,
// });
} else {
layers[0] = new Heatmap({ id: 'random-heatmap', data });
}
if (nextState.viewState.zoom !== this.state.viewState.zoom) {
this.setState({ layers });
}
}
render() {
return (
<DeckGL
initialViewState={this.state.viewState}
viewState={this.state.viewState}
onViewStateChange={({ viewState }) => this.setState({ viewState })}
width={this.state.width}
height={this.state.height}
controller
layers={this.state.layers}
>
<StaticMap
mapStyle={`mapbox://styles/mapbox/basic-v9`}
mapboxApiAccessToken="pk.eyJ1IjoiamVhbmlwYSIsImEiOiJjanFmNzIzbXY0bWEwNDNueWxjaHViYjBpIn0.86nUQxt2lIiqQpj5puy1TQ"
/>
</DeckGL>
);
}
}
export default App;
Here is a React App that reproduces the bug if it is better for you to work with. The only other dependency, except from deck.gl and react is d3.
With the file as it is now, the bug will occur if you zoom enough on the map. To see the difference when I use the extended layer Icon instead of deck.gl's IconLayer, comment in componentWillUpdate the line 69 and uncomment lines from 70 to 78, and try again. The fact that Heatmap extends HexagonLayer doesn't change anything.
Thank you
@CorentinAmbroise In Layer.defaultProps
, each prop name maps to a prop type definition, something like {type, value, ...}
. Usually, if you use a value that is not in this shape, such as sizeScale: 1
, deck.gl will convert it to {type: 'number', value: 1}
internally. In the case of the iconAtlas
, if you want deck to automatically load an URL, the prop must be defined with async: true
. Without the option, props.iconAtlas
just returns a plain string.
To change the default prop values, you can copy a prop's definition from IconLayer.defaultProps, and modify the value
field, e.g.:
MyLayer.defaultProps = {
iconAtlas: {type: 'object', value: '<url>', async: true}
};
@CorentinAmbroise In
Layer.defaultProps
, each prop name maps to a prop type definition, something like{type, value, ...}
. Usually, if you use a value that is not in this shape, such assizeScale: 1
, deck.gl will convert it to{type: 'number', value: 1}
internally. In the case of theiconAtlas
, if you want deck to automatically load an URL, the prop must be defined withasync: true
. Without the option,props.iconAtlas
just returns a plain string.To change the default prop values, you can copy a prop's definition from IconLayer.defaultProps, and modify the
value
field, e.g.:MyLayer.defaultProps = { iconAtlas: {type: 'object', value: '<url>', async: true} };
It was indeed the issue thank you (it works If I don't use iconAtlas as a defaultProps and just pass it to the constructor) and I understand better now. Nonetheless, your fix doesn't seem to be working either, are you sure this works ? I tried it with the App that I gave you in my last reply, and it is not working
Thank you very much
Hmm looks like our async loading mechanism does not support using default values like this. When there is a default value it will skip "loading" and just use it as-is. Arguably it was never designed to be used this way... @ibgreen
That is correct (if I understand this correctly), I never anticipated that anyone would want to have a default url
rather than a default value.
While I am open to discussion, my take right now is that this seems like a rather special use case. I am not convinced it makes sense to support it unless we see that this will be more commonly useful.
It is probably not a major change, but the async props implementation is quite complex as it stands, more test cases etc would be needed.
A minor observation is that, even if we implemented this, there probably needs to be a default value anyway (that the layer sees until the URL is loaded), so we might need to add another field to the prop type.
@CorentinAmbroise I am sure you already considered this, but an atlas is just a json file so it is easy to include in your bundle if you are using webpack, no need to load it dynamically.
Description
I am developping an app which contains a map on which are displayed data. I am currently implementing a layer which is composed of a heatmap which is a simple extension of an HexagonLayer, and it is supposed to turn into an IconLayer extension when the zoom is big enough to see all the data points. So this is the same map's layer which turns from an HexagonLayer into a IconLayer, that I instantiate with a correct iconAtlas and iconMapping (it used to work, none of these has changed and it still matches the documentation requirements). The IconLayer still has some properties of the heatmap, like the radius and the onHover function. This bug occurs right when I zoom enough to change the Heatmap to the IconLayer. Both of them have data correctly formatted for their use by the layer. So the _externalTexture of the IconManager is null and the _texture is not, but it seems like its height and width are undefined, eventhough when I log them right before I get the error they are not.
If I only display the points of data from the begining, not the heatmap, so that the layer remains an IconLayer, it works fine.
Environment :
Logs
Error :
With some warnings from the WebGl :
Error: WebGL warning: texImage2D: Alpha-premult and y-flip are deprecated for non-DOM-Element uploads.
before the error with Firefox.WebGL: INVALID_OPERATION: texImage2D: no texture bound to target
after the error with both Firefox and Chrome.Thanks in advance for your help, I hope I provided enough information, ask if you need anything.