diff --git a/src/js/nfdimap.js b/src/js/nfdimap.js
index 8a19121..6475704 100644
--- a/src/js/nfdimap.js
+++ b/src/js/nfdimap.js
@@ -1,68 +1,87 @@
Promise.all([
- d3.json("/data/nfdimap/germany.json"),
+ d3.json("/data/nfdimap/germany.json"),
d3.buffer("/data/nfdimap/participantsM.xlsx")
-]).then( ([germany, buffer]) => {
+]).then(([germany, buffer]) => {
- const tmp = XLSX.read(buffer, { type: 'array'})
+ const tmp = XLSX.read(buffer, { type: 'array'});
return {
- people: XLSX.utils.sheet_to_json(tmp.Sheets.People),
- places: XLSX.utils.sheet_to_json(tmp.Sheets.Places),
+ people: XLSX.utils.sheet_to_json(tmp.Sheets.Peoples),
+ projects: XLSX.utils.sheet_to_json(tmp.Sheets.project),
+ places: XLSX.utils.sheet_to_json(tmp.Sheets.Places),
germany: germany
}
-}).then( data => {
+}).then(data => {
- // determine how to show a person's name
+ // Utility: format a person's name with their role
formatName = function(d) {
- return d.Name + (d.Role ? ` (${d.Role})` : '')
- }
+ return d.Name + (d.Role ? ` (${d.Role})` : '');
+ };
- // utility: make a HTML list with a heading
+ // Utility: make an HTML list with a heading (for People and Projects)
makeList = function(heading, entries) {
- return `
${heading}
` +
- '' +
- entries.map(e => `- ${e}
`).join('') +
- '
'
- }
+ if (entries.length === 0) return ''; // Do not show if no entries
+ return `${heading}
` +
+ '' +
+ entries.map(e => `- ${e}
`).join('') +
+ '
';
+ };
- // determine the class attributes for a given place from its persons
- placeClass = function(entries) {
- const allRoles = String(entries.map(d => d.Role).join('')).toLowerCase();
+ // Combine data for each institution (people + projects)
+ function combineInstitutionData(institution, people, projects) {
+ const peopleList = people.map(formatName);
+ const projectList = projects.map(p => p.Project);
- // simple logic for now
- if( allRoles.includes('spokesperson') )
- return 'applicant'
+ // Show the institution name as a header (h4), and then lists of people and projects
+ const combinedEntries = [
+ `${institution}
`, // Display institution name as a header
+ makeList('People', peopleList),
+ makeList('Projects', projectList) // Only show Projects if they exist
+ ];
- return 'normal'
+ return combinedEntries.join('');
+ }
+
+ // Function to determine the class attributes for a given place based on people
+ function placeClass(entries) {
+ const allRoles = String(entries.map(d => d.Role).join('')).toLowerCase();
+ return allRoles.includes('spokesperson') ? 'applicant' : 'normal';
}
- // pre-process places
+ // Pre-process places
const projection = d3.geoConicConformal().center([20.22, 49.3]).scale(2750);
- const placeIndex = d3.index(data.places, d => d.Name)
-
+ const placeIndex = d3.index(data.places, d => d.Name);
+
const places = d3.groups(
- data.people,
- p => p.City,
- ).map( ([place, people]) => ({
- name: place,
- info: d3.groups( people, p => p.Institution )
- .map( ([inst, people]) => {
- return makeList( inst, people.map(formatName) )
- })
- .join(''),
- coord: (d => projection([d.Longitude, d.Latitude]))(placeIndex.get(place)),
- type: placeClass(people)
- }))
-
+ data.people,
+ p => p.City,
+ ).map(([place, people]) => {
+ // Group projects by institution as well
+ const projectsByInstitution = d3.group(data.projects, p => p.Institution);
+
+ // Group people by institution
+ const peopleByInstitution = d3.group(people, p => p.Institution);
+
+ return {
+ name: place,
+ info: [...peopleByInstitution.entries()].map(([inst, people]) => {
+ const projects = projectsByInstitution.get(inst) || []; // Get projects for this institution, or empty array if none
+ return combineInstitutionData(inst, people, projects); // Combine people and projects per institution
+ }).join(''),
+ coord: (d => projection([d.Longitude, d.Latitude]))(placeIndex.get(place)),
+ type: placeClass(people) // determine class based on people roles
+ };
+ });
+
const path = d3.geoPath().projection(projection);
-
- // begin map drawing
+
+ // Begin map drawing
const svg = d3
.select("#dataplant-map")
.append("svg")
.attr("viewBox", [0, 0, 300, 400]);
- // draw base map
+ // Draw base map
svg
.append("g")
.selectAll("path")
@@ -71,6 +90,7 @@ Promise.all([
.attr("d", path)
.classed("map", true);
+ // Draw places
const placesnodes = svg
.append("g")
.selectAll("circle")
@@ -80,15 +100,17 @@ Promise.all([
.attr("cx", d => d.coord[0])
.attr("cy", d => d.coord[1])
.attr("r", () => 4)
- .attr('data-tippy-content', d=> d.info)
- .attr('data-place-type', d => d.type)
+ .attr('data-tippy-content', d => d.info)
+ .attr('data-place-type', d => d.type);
+ // Add tooltips using tippy.js
tippy(placesnodes.nodes(), {
appendTo: (reference) => document.querySelector('#dataplant-map'),
interactive: true,
maxWidth: 'none',
allowHTML: true,
theme: 'custom',
- delay: [100, 1500]
- })
-})
+ delay: [100, 100]
+ });
+});
+