I'm currently trying to implement a 3d globe that has COVID-19 information for each country that is coming from a JSON file. Whenever I click on a specific country, or mesh, on the globe, it shows that the userdata doesn't have the information which means that it wasn't able to retrieve the data from the JSON file or it wasn't able to match it with the GEOJSON file of the world map.
Here are relevant code snippets that may help:
That's the code snippet for what should happen when you click on the globe:
private onGlobeClick(event: MouseEvent): void {
console.log('Globe clicked');
event.preventDefault();
const rect = this.globeContainer.nativeElement.getBoundingClientRect();
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
// Perform raycasting to detect intersected objects
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
console.log('Intersects:', intersects);
if (intersects.length > 0) {
const intersectedObject = intersects[0].object;
console.log('Intersected Object:', intersectedObject);
// Check if the intersected object has userData
if (intersectedObject.userData) {
console.log('User Data:', intersectedObject.userData);
// Fetch more information about the country and display it
if (intersectedObject.userData['type'] === 'polygon') {
const countryName = intersectedObject.userData['countryName'];
console.log(`Clicked on: ${countryName}`);
this.fetchCountryData(countryName).subscribe(data => {
if (data) {
console.log(`Country: ${data.name}`);
console.log(`Confirmed: ${data.confirmed}`);
console.log(`Recovered: ${data.recovered}`);
console.log(`Deaths: ${data.deaths}`);
} else {
console.log('Country data not available.');
}
});
} else {
console.log('Unknown object type clicked.');
}
} else {
console.log('No user data.');
}
} else {
console.log('No intersection detected.');
}
}
Here's the globe rendering code snippet:
private createGlobe(): void {
forkJoin({
countries: this.http.get<any>('https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson', { responseType: 'json' }),
covidData: this.http.get<any[]>('assets/latest.json', { responseType: 'json' })
}).subscribe(({ countries, covidData }) => {
const parsedCovidData = covidData;
const covidDataMap = new Map(Object.entries(parsedCovidData));
const colorScale = d3.scaleLog()
.domain([1, d3.max(Object.values(parsedCovidData), data => data['details']['confirmed']) || 1])
.range([0, 1]);
const colorInterpolate = d3.interpolateRgb('white', 'red');
const globe = new ThreeGlobe()
.globeImageUrl('//unpkg.com/three-globe/example/img/earth-dark.jpg')
.polygonsData(countries.features.filter((d: any) => d.properties.ISO_A2 !== 'AQ'))
.polygonCapColor((d: any) => {
const countryName = d.properties.ADMIN;
const countryData = covidDataMap.get(countryName);
const severity = countryData ? countryData['details']['confirmed'] : 0;
const color = colorScale(severity);
const userData = { countryName, type: 'polygon', properties: d.properties, severity };
d.userData = userData;
return color !== null && color !== undefined ? colorInterpolate(color) : 'white';
})
.polygonSideColor(() => 'transparent') // Adjust as needed
.polygonStrokeColor(() => '#111');
this.scene.add(globe);
this.globe = globe;
// Add event listeners
this.globeContainer.nativeElement.addEventListener('mousemove', this.onMouseMove.bind(this), false);
this.globeContainer.nativeElement.addEventListener('click', this.onGlobeClick.bind(this), false);
});
}
Here's how the data is fetched:
private fetchCountryData(countryName: string): Observable<any> {
return this.http.get<any>('assets/latest.json').pipe(
map(data => {
const countryData = data[countryName];
console.log(`Searching for: ${countryName}`);
console.log(`Found:`, countryData);
if (countryData) {
return {
name: countryName,
confirmed: countryData.details['confirmed'],
deaths: countryData.details['deaths'],
recovered: countryData.details['recoveries']
};
} else {
return {
name: undefined,
confirmed: undefined,
deaths: undefined,
recovered: undefined
};
}
})
);
}
Here's as well the code snippet for forming the globe:
private createGlobe(): void {
forkJoin({
countries: this.http.get<any>('https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson', { responseType: 'json' }),
covidData: this.http.get<any[]>('assets/latest.json', { responseType: 'json' })
}).subscribe(({ countries, covidData }) => {
const parsedCovidData = covidData;
const covidDataMap = new Map(Object.entries(parsedCovidData));
const colorScale = d3.scaleLog()
.domain([1, d3.max(Object.values(parsedCovidData), data => data['details']['confirmed']) || 1])
.range([0, 1]);
const colorInterpolate = d3.interpolateRgb('white', 'red');
const globe = new ThreeGlobe()
.globeImageUrl('//unpkg.com/three-globe/example/img/earth-dark.jpg')
.polygonsData(countries.features.filter((d: any) => d.properties.ISO_A2 !== 'AQ'))
.polygonCapColor((d: any) => {
const countryName = d.properties.ADMIN;
const countryData = covidDataMap.get(countryName);
const severity = countryData ? countryData['details']['confirmed'] : 0;
const color = colorScale(severity);
const userData = { countryName, type: 'polygon', properties: d.properties, severity };
d.userData = userData;
return color !== null && color !== undefined ? colorInterpolate(color) : 'white';
})
.polygonSideColor(() => 'transparent') // Adjust as needed
.polygonStrokeColor(() => '#111');
this.scene.add(globe);
this.globe = globe;
// Add event listeners
this.globeContainer.nativeElement.addEventListener('mousemove', this.onMouseMove.bind(this), false);
this.globeContainer.nativeElement.addEventListener('click', this.onGlobeClick.bind(this), false);
});
}
Here's the format of the json file:
{
"Afghanistan": {
"details": {
"confirmed": 40200,
"recoveries": 33614,
"deaths": 1492
}
},
"Albania": {
"details": {
"confirmed": 17055,
"recoveries": 10071,
"deaths": 451
}
},
When all of that runs, it does show the globe as is intended but whenever I click on any country to try to print out it's details in the console, it's not able to detect it as a country:
globe.component.ts:63 Globe clicked
globe.component.ts:75 Intersects: (20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
globe.component.ts:80 Intersected Object: Mesh {isObject3D: true, uuid: '0be13669-ba55-47ff-b5da-c7aca8eb6787', name: '', type: 'Mesh', parent: Group, …}
globe.component.ts:84 User Data: {}
globe.component.ts:103 Unknown object type clicked.
I would really appreciate if I can get any assistance with it. Also if any more information about the project, here's the github link for the project so far: https://github.com/CGUltimateno/Covid-globe