Public
Edited
Nov 22, 2023
Paused
Fork of TimeTravel
Insert cell
Insert cell
Insert cell
chart = d3
.create('svg')
.attr("viewBox", [0, 0, width, height])
.attr("cursor", "pointer")
.call(domainZoom()
.x(xScale)
.axisX(timeline)
.link(updateData))
.call(drawData)
.node()
Insert cell
drawData = function(svg) {
svg.append("text")
.join("tspan")
.attr("x", margin.left)
.attr("y", (height * 3 / 5) + 30)
.text("Researchers")
.style('font-family', '"Open Sans", sans-serif');
//SimulationForce
var simulation = d3.forceSimulation(data.object)
.force('x', d3.forceX((d) => xScale(parseTime(d.date))))
.force('y', d3.forceY((d) => y(d.domain)))
.force('collide', d3.forceCollide(d => d.radius))
.on("tick", tick);

console.log(data)
var simulation1 = d3.forceSimulation(data.person)
.force('forceX', d3.forceX(width / 2).strength(3))
.force('forceY', d3.forceY(height2 + 250).strength(10))
.force('collide', d3.forceCollide(30).strength(1))
.on('tick', ticked)


//ForceSimulation Ticks
function tick() {
svg.selectAll('#circle1')
.attr('class', 'dataPoint')
.data(data.object)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
}


function ticked() {
svg.selectAll('#circle2')
.data(data.person)
.attr('cx', d => d.x)
.attr('cy', d => d.y);
}

const g = svg.append("g");


let links = [];
let links_object_list = [];

for (let i = 0; i < data.object.length; i++) {
for (let p = 0; p < data.person.length; p++) {
for (let j = 0; j < data.links.length; j++) {
if (data.object[i].id == data.links[j].target && data.person[p].id == data.links[j].source) {

const coor = d3.linkVertical()({
source: [data.person[p].x, data.person[p].y],
target: [data.object[i].x, data.object[i].y]
}),

link_object = {
color: data.object[i].Color,
coord: coor,
source_x: data.person[p].x,
source_y: data.person[p].y,
target_x: data.object[i].x,
target_y: data.object[i].y,
source: [data.person[p].x, data.person[p].y],
target: [data.object[i].x, data.object[i].y],
id_source: data.person[p].id,
id_target: data.object[i].id
};

links.push(coor)
links_object_list.push(link_object)
}
}
}
}


var link = d3.linkVertical()
.source(function (d) {
return [d.source[0], d.source[1]];
})
.target(function (d) {
return [d.target[0], d.target[1]];
});

const gradient1 = g.append("g")
.selectAll('linearGradient')
.data(links_object_list)
.enter().append('linearGradient')
.attr("id", d => "gradient1" + d.color)
.attr("spreadMethod", "reflect")
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('r', 600)

gradient1
.append("stop")
.attr("offset", "30%")
.attr("stop-color", "#ee2681")

gradient1
.append("stop")
.attr("offset", "70%")
.attr("stop-color", d => d.color);

gradient1.append("animate")
.attr("attributeName", "x1")
.attr("values", "0%;-200%")
.attr("dur", "7s")
.attr("repeatCount", "indefinite");

gradient1.append("animate")
.attr("attributeName", "x2")
.attr("values", "-100%;-300%")
.attr("dur", "7s")
.attr("repeatCount", "indefinite")

g.selectAll("path")
.data(links_object_list)
.join('path')
.attr("d", link)
.attr('stroke-opacity', '0')
.attr('stroke', d => `url(#gradient1${d.color})`)
.attr('id', d => "path" + d.id_source)
.attr('class', d => "path" + d.id_target)
.attr('fill', 'none');

//Vertical Lines
svg.selectAll('.line-decade')
.data(xScale.ticks())
.join('line')
.attr('class', 'line-decade')
.attr('x1', d => xScale(d))
.attr('x2', d => xScale(d))
.attr('y1', 100)
.attr('y2', height * 3 / 5 - margin.top)
.attr('stroke-width', 1)
.attr('stroke', 'lightgray').select(".domain").style('stroke-opacity', '0');

const node = g.append("g")
.selectAll("g")
.data(data.person)
.enter().append("g")

node.append('pattern')
.attr("id", d => "image"+ d.id)
.attr("width", 1)
.attr("height", 1)
.append("svg:image")
.attr("xlink:href", d => d.url)
.attr("width", 40)
.attr("height", 40);

//BeeSwarm
svg.selectAll("circle")
.data(data.object)
.join("circle")
.attr('class', d => "circle2" + d.year + "+" + d.id)
.attr("id", "circle1")
.attr("r", d => d.radius)
.attr("fill", d => d.Color)
.on('click', function (d) {})
.on("mouseover", function (d, data) {
tooltipcircle
.html("<link rel=preconnect href=https://fonts.gstatic.com> <link href=https://fonts.googleapis.com/css2?family=Montserrat&display=swap rel=stylesheet><div class=full-width><h1 class=h1full-width>" + plus + "Score: 0.0</h1></div><h1 class=h1tooltipcircle>Date</h1><hr><p>" + data.date + "</p><h1 class=h1tooltipcircle>Title</h1><hr><p>" + data.name + "</p><h1 class=h1tooltipcircle>DOI</h1><hr><p>" + data.DOI + "</p><h1 class=h1tooltipcircle>Knowlage Domain</h1><p>" + data.domain + "</p><h1 class=h1tooltipcircle>Keywords</h1><p>" + data.keywords + "</p><h1 class=h1tooltipcircle>Authors</h1><p>" + data.authors + "</p>")
.style('visibility', 'visible');
})
.on('mousemove', function (d) {
if (d.clientX > width / 2) {
tooltipcircle
.style('top', d.clientY + 10 + 'px')
.style('left', d.clientX - 425 + 'px')
.style("display", "inline-block");
} else {
tooltipcircle
.style('top', d.clientY + 10 + 'px')
.style('left', d.clientX + 20 + 'px')
.style("display", "inline-block");
}
})
.on('mouseout', function () {
tooltipcircle.style('visibility', 'hidden');
});

console.log(data)
var most_recent = ''

const datalinks = data

node.append("circle")
//.attr('r', d => Math.random() * 30)
.attr('r', 20)
.attr("id", "circle2")
.attr('class', d => "circle" + d.profile)
.attr("fill", d => `url(#image${d.id})`)
//.attr("fill", "#d7467e")
.on('click', function (d, data) {
if (most_recent == data.profile) {
d3.selectAll('circle').style("opacity", 1);
d3.selectAll('#circle2').style("opacity", 0.6);
d3.select(this).attr("stroke", "black").style("opacity", 1)
d3.selectAll('#circle2').on('mouseout', function (d, data) {
tooltip.style('visibility', 'hidden');
d3.selectAll("path").style('stroke-opacity', '0')
d3.selectAll("circle")
.attr("stroke", "white")
.style("opacity", 1)
});
d3.selectAll('#circle2').on('mouseover', function (d, data) {
let links3 = [];
const links_object_list3 = [];

for (let i = 0; i < datalinks.object.length; i++) {
for (let p = 0; p < datalinks.person.length; p++) {
for (let j = 0; j < datalinks.links.length; j++) {
if (datalinks.object[i].id == datalinks.links[j].target && datalinks.person[p].id == datalinks.links[j].source) {

const coor3 = d3.linkVertical()({
source: [datalinks.person[p].x, datalinks.person[p].y],
target: [datalinks.object[i].x, datalinks.object[i].y]
}),

link_object3 = {
color: datalinks.object[i].Color,
coord: coor3,
source_x: datalinks.person[p].x,
source_y: datalinks.person[p].y,
target_x: datalinks.object[i].x,
target_y: datalinks.object[i].y,
source: [datalinks.person[p].x, datalinks.person[p].y],
target: [datalinks.object[i].x, datalinks.object[i].y],
id_source: datalinks.person[p].id,
id_target: datalinks.object[i].id
};

links3.push(coor3)
links_object_list3.push(link_object3)
}
}
}
}

var link3 = d3.linkVertical()
.source(function (d) {
return [d.source[0], d.source[1]];
})
.target(function (d) {
return [d.target[0], d.target[1]];
});

g.selectAll("path")
.data(links_object_list3)
.join('path')
.attr("d", link3)
.attr('stroke-opacity', '0')
.attr('stroke', d => `url(#gradient1${d.color})`)
.attr('id', d => "path" + d.id_source)
.attr('class', d => "path" + d.id_target)
.attr('fill', 'none');
var image = "<img src=" + data.url + "/>"
tooltip
.html("<h1>" + facebook + twitter + chat + person + "Score: 0.0 </h1><hr>" + image + "<br/><h1>" + data.name + "</h1><h2>Ph.D.</h2><h1>ORCID</h1><p>" + data.profile + "</p>")
.style('visibility', 'visible')
filterOver("circle" + data.profile, data.id)
d3.select(this).attr("stroke", "black").style("opacity", 1)
d3.select(this).style("cursor", "pointer");
})
most_recent = ""
} else {
most_recent = data.profile
d3.selectAll('circle')
.style("opacity", 0)
d3.select(this)
.style("opacity", 1)
d3.selectAll('#circle1')
.style("opacity", 1)
d3.selectAll('#path' + data.profile)
.style("opacity", 1)
d3.selectAll('#circle2')
.on('mouseout', null)
d3.selectAll('#circle2')
.on('mouseover', null)
most_recent = data.profile
tooltip.html(``)
.style('visibility', 'hidden')
d3.select(this)
.style("cursor", "pointer")
d3.select(this).attr("stroke", "black").style("opacity", 1)
};
})
.on('mouseover', function (d, c) {
let links3 = [];
const links_object_list3 = [];

for (let i = 0; i < datalinks.object.length; i++) {
for (let p = 0; p < datalinks.person.length; p++) {
for (let j = 0; j < datalinks.links.length; j++) {
if (datalinks.object[i].id == datalinks.links[j].target && datalinks.person[p].id == datalinks.links[j].source) {

const coor3 = d3.linkVertical()({
source: [datalinks.person[p].x, datalinks.person[p].y],
target: [datalinks.object[i].x, datalinks.object[i].y]
}),

link_object3 = {
color: datalinks.object[i].Color,
coord: coor3,
source_x: datalinks.person[p].x,
source_y: datalinks.person[p].y,
target_x: datalinks.object[i].x,
target_y: datalinks.object[i].y,
source: [datalinks.person[p].x, datalinks.person[p].y],
target: [datalinks.object[i].x, datalinks.object[i].y],
id_source: datalinks.person[p].id,
id_target: datalinks.object[i].id
};

links3.push(coor3)
links_object_list3.push(link_object3)
}
}
}
}

var link3 = d3.linkVertical()
.source(function (d) {
return [d.source[0], d.source[1]];
})
.target(function (d) {
return [d.target[0], d.target[1]];
});

g.selectAll("path")
.data(links_object_list3)
.join('path')
.attr("d", link3)
.attr('stroke-opacity', '0')
.attr('stroke', d => `url(#gradient1${d.color})`)
.attr('id', d => "path" + d.id_source)
.attr('class', d => "path" + d.id_target)
.attr('fill', 'none');

var image = "<img src=" + c.url + "/>"
tooltip
.html("<h1>" + facebook + twitter + chat + person + "Score: 0.0 </h1><hr>" + image + "<br/><h1>" + c.name + "</h1><h2>Ph.D.</h2><h1>ORCID</h1><p>" + c.profile + "</p>")
.style('visibility', 'visible')
filterOver("circle" + c.profile, c.id)
d3.select(this).attr("stroke", "black").style("opacity", 1)
d3.select(this).style("cursor", "pointer");

})
.on('mouseout', function (d, data) {
d3.selectAll("#path" + data.id).style('stroke-opacity', '0')
filterOut("circle" + data.profile)
d3.select(this).attr("stroke", "white").style("opacity", 1)
tooltip.style('visibility', 'hidden');
})
.on('mousemove', function (d, data) {
if (d.clientX > width / 2) {
tooltip
.style('top', d.clientY + 10 + 'px')
.style('left', d.clientX - 375 + 'px')
.style("display", "inline-block");
} else {
tooltip
.style('top', d.clientY + 10 + 'px')
.style('left', d.clientX + 20 + 'px')
.style("display", "inline-block");
}
});


/*node.append('text')
.text(d => d.name.slice(0, 2))
.attr('x', d => d.x)
.attr('y', d => d.y)
.style('fill', '#fff')
.attr("transform" , `translate(${-10}, ${6.5})`)
.attr("font-weight", "bold")*/


//AxisTop
/*svg.append('g').call(xAxis).selectAll("text")
.attr("transform", "translate(25,10)rotate(270)")
.attr("color", "black");*/



d3.select('#clusterButton').on('click', () => {

d3.selectAll("path").style('stroke-opacity', '0')

d3.selectAll("#circle2").attr("pointer-events", "all");

simulation = d3.forceSimulation(data.object)
.force('forceX', d3.forceX((d) => xScale(parseTime(d.date))).strength(1))
.force('forceY', d3.forceY((d) => y(d.domain)))
.force('collide', d3.forceCollide(d => d.radius))
.restart()
.on("tick", tick);

document.getElementById("AllButton").disabled = false;
});

d3.select('#groupButton').on('click', () => {

d3.selectAll("#cicle2").attr("pointer-events", "none");

d3.selectAll("path").style('stroke-opacity', '0')

simulation = d3.forceSimulation(data.object)
.force('forceX', d3.forceX((d) => xScale(parseTime(d.date))).strength(6))
.force('forceY', d3.forceY(height / 4).strength(1))
.force('collide', d3.forceCollide(d => d.radius))
.on("tick", tick);

//document.getElementById("AllButton").disabled = true;
});


d3.select('#NoOneButton').on('click', () => {

d3.selectAll('path')
.style("stroke-opacity", 0);


});

d3.select('#AllButton').on('click', () => {

d3.selectAll('path')
.style("stroke-opacity", 1);
d3.select(".domain").style('stroke-opacity', '0')
let links2 = [];
const links_object_list2 = [];

for (let i = 0; i < data.object.length; i++) {
for (let p = 0; p < data.person.length; p++) {
for (let j = 0; j < data.links.length; j++) {
if (data.object[i].id == data.links[j].target && data.person[p].id == data.links[j].source) {

const coor2 = d3.linkVertical()({
source: [data.person[p].x, data.person[p].y],
target: [data.object[i].x, data.object[i].y]
}),

link_object2 = {
color: data.object[i].Color,
coord: coor2,
source_x: data.person[p].x,
source_y: data.person[p].y,
target_x: data.object[i].x,
target_y: data.object[i].y,
source: [data.person[p].x, data.person[p].y],
target: [data.object[i].x, data.object[i].y],
id_source: data.person[p].id,
id_target: data.object[i].id
};

links2.push(coor2)
links_object_list2.push(link_object2)
}
}
}
}

var link2 = d3.linkVertical()
.source(function (d) {
return [d.source[0], d.source[1]];
})
.target(function (d) {
return [d.target[0], d.target[1]];
});

g.selectAll("path")
.data(links_object_list2)
.join('path')
.attr("d", link2)
.attr('stroke-opacity', '0')
.attr('stroke', d => `url(#gradient1${d.color})`)
.attr('id', d => "path" + d.id_source)
.attr('class', d => "path" + d.id_target)
.attr('fill', 'none');

});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
multiFormat = ["shortYear", "fullYear", "decade"]
Insert cell
xIntercept = 50
Insert cell
vOffset = 20
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more