snMapbox = {
const options = {
accessToken: 'pk.eyJ1IjoieWlhbm5pLXZlcnZlcmlzIiwiYSI6ImNrcWF0azdnejBjdm4yd3M3ajBmb2hpeGkifQ.rl7QWaaMtqRYNJ-vMIMoOA',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-60, 20],
zoom: 2,
pitch: 0,
bearing: 0,
circleRadius: 8,
circleOpacity: 1,
tooltip: (obj) => `
<div>Gender: ${obj.gender}</div>
<div>Age Bucket: ${obj.AgeBucket}</div>
`,
createLayers: true,
flyTo: {
center: [-74.50, 40],
zoom: 4,
speed: 0.3,
curve: 1,
easing(t) {
return t;
}
},
palette: [
'#3399CC',
'#CC6666',
]
};
return {
qae: {
properties: {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [{ qWidth: 5, qHeight: 2000 }],
qSuppressZero: true,
qSuppressMissing: true,
},
showTitles: true,
title: 'US Data',
subtitle: 'Random gender / age buckets',
footnote: 'Data is random, for this example only.',
},
data: {
targets: [
{
path: '/qHyperCubeDef',
dimensions: {
min: 1,
max: 5,
},
measures: {
min: 0,
max: 0,
},
},
],
},
},
component() {
const element = stardust.useElement();
const layout = stardust.useLayout();
const qData = layout.qHyperCube?.qDataPages[0];
const qMatrix = qData.qMatrix.filter(row => row.some(el => el.qNum !== "NaN"))
const property = layout.qHyperCube?.qDimensionInfo[3]?.qFallbackTitle;
const property2 = layout.qHyperCube?.qDimensionInfo[4]?.qFallbackTitle;
const [instance, setInstance] = stardust.useState();
let GeoJSON, map = null;
let mapData = [];
const propertyChildren = [...new Set(qMatrix.map((array) => array[3].qText))];
const propertyChildrenWithColors = propertyChildren.reduce((r, e, i) => r.push(e, options.palette[i]) && r, []);
// Create the Mapbox features based on our HyperCube data
const buildFeatures = (obj) => {
const featureObj = {
type: 'Feature',
properties: {
count: 1,
userID: obj.id,
[property]: obj[property],
},
geometry: {
type: 'Point',
coordinates: [obj.lng, obj.lat],
},
};
if (options.tooltip !== null) {
featureObj.properties.description = options.tooltip(obj);
}
return featureObj;
}
// Convert our HyperCube data into a GeoJSON for Mapbox
const buildGeoJSON = () => {
const goodGeoJSON = {
type: 'FeatureCollection',
features: [],
};
qMatrix.map((array) => {
if (typeof array[1].qNum !== 'number' || typeof array[2].qNum !== 'number') return false;
const obj = {
id: Number(array[0].qNum),
lat: Number(array[1].qNum),
lng: Number(array[2].qNum),
};
obj[property] = array[3].qText;
obj[property2] = array[4].qText;
const feature = buildFeatures(obj);
goodGeoJSON.features.push(feature);
return obj;
});
return goodGeoJSON;
}
// Create the layer that will hold the dots
const buildLayer = () => {
const match = ['match', ['get', property], ...propertyChildrenWithColors, '#FFF'];
const layer = {
id: 'dots',
type: 'circle',
source: 'hyperCubeData',
paint: {
'circle-stroke-width': 0,
'circle-radius': options.circleRadius,
'circle-color': match,
'circle-opacity': options.circleOpacity,
},
};
return layer;
}
// Create the map
const buildMap = () => {
// Add HyperCube data as GeoJSON
map.addSource('hyperCubeData', {
type: 'geojson',
data: GeoJSON,
});
// Create the layer
const layer = buildLayer();
map.addLayer(layer);
if (options.extraLayers && options.extraLayers.length) {
options.extraLayers.map((_layer) => map.addLayer(_layer));
}
// Create Tooltips and the triggering events
if (options.tooltip !== null) {
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false,
className: 'sn-mapbox-tooltip',
});
map.on('mouseenter', 'dots', (e) => {
map.getCanvas().style.cursor = 'pointer';
const coordinates = e.features[0].geometry.coordinates.slice();
const { description } = e.features[0].properties;
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
popup
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
map.on('mouseleave', 'dots', () => {
map.getCanvas().style.cursor = '';
popup.remove();
});
}
};
// Update layer data upon HyperCube change
const updateLayers = () => {
const nextChunk = qMatrix.map((array) => {
const obj = {
id: Number(array[0].qNum),
lat: Number(array[1].qNum),
lng: Number(array[2].qNum),
[property]: array[3].qText,
};
return buildFeatures(obj);
});
if (GeoJSON) {
GeoJSON = { ...GeoJSON, features: [...GeoJSON.features, ...nextChunk] };
map.getSource('hyperCubeData').setData(GeoJSON);
} else {
GeoJSON = buildGeoJSON();
buildMap();
}
};
stardust.useEffect(() => {
mapboxgl.accessToken = options.accessToken;
if (!map) {
// Initialize mapbox GL
map = new mapboxgl.Map({
container: element,
...options,
});
// Add layer with data
map.on('load', () => {
updateLayers(qData); // Draw the first set of data, in case we load all
mapData = [...mapData, ...qMatrix];
});
// Add intro animation
if (options.flyTo) {
map.flyTo(options.flyTo);
}
}
}, [layout]);
},
};
}