{
const svg = d3.create("svg").attr("width", width).attr("height", height);
const stationsToShow = ["서울", "수원", "대전", "동대구", "울산", "부산"];
const stationGrid = svg
.selectAll(".stationGrid")
.data(stationsKorean)
.enter()
.append("line")
.attr("y1", margin.top)
.attr("y2", height - margin.bottom)
.attr("x1", (d, i) => xScale(i))
.attr("x2", (d, i) => xScale(i))
.style("stroke", (d) => (stationsToShow.includes(d) ? "#bbb" : "#efefef"));
const stationLabels = svg
.selectAll(".stationLabels")
.data(stationsKorean)
.enter()
.append("text")
.text((d) => d)
.attr("x", (d, i) => xScale(i))
.attr("y", margin.top - 10)
.attr("class", "stationLabels")
.style("display", (d) => (stationsToShow.includes(d) ? "block" : "none"))
.style("fill", (d) => (d == "서울" || d == "부산" ? "#222" : "#222"));
const hoursGrid = svg
.selectAll(".hoursGrid")
.data(hours)
.enter()
.append("line")
.attr("y1", (d) => yScale(timeParse(d)))
.attr("y2", (d) => yScale(timeParse(d)))
.attr("x1", margin.left)
.attr("x2", margin.left - 5)
.style("stroke", (d) =>
getTime(d) == "12:00" || getTime(d) == "24:00" ? "#777" : "#777"
)
.style("stroke-dasharray", (d) =>
getTime(d) == "12:00" || getTime(d) == "24:00" ? "3, 3" : "3, 0"
);
const hoursLabel = svg
.selectAll(".hoursLabel")
.data(hours)
.enter()
.append("text")
.text((d) => timeFormat(timeParse(d)))
.attr("x", margin.left - 8)
.attr("y", (d) => yScale(timeParse(d)) + 4)
.attr("class", "hoursLabel")
.style("fill", (d) =>
getTime(d) == "12:00" || getTime(d) == "24:00" ? "#222" : "#222"
)
.style("font-weight", (d) =>
getTime(d) == "12:00" || getTime(d) == "24:00" ? "600" : "400"
);
const currentTimeLine = svg
.append("line")
.attr("x1", xScale(0))
.attr("x2", xScale(stations.length - 1))
.attr("y1", yScale(timeParse("2024-05-01, 04:30")))
.attr("y2", yScale(timeParse("2024-05-01, 04:30")))
.style("stroke", "#000")
.style("stroke-width", 0.75);
let isClicked = false;
const rectForInteraction = svg
.append("rect")
// .attr("x", xScale(0))
// .attr("y", yScale(timeParse("2024-05-01, 03:00")))
// .attr("width", xScale(stations.length - 1) - xScale(0))
// .attr(
// "height",
// yScale(timeParse("2024-05-02, 03:00")) -
// yScale(timeParse("2024-05-01, 03:00"))
// )
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("fill", "rgba(0,0,0,0)")
.on("mousemove", function (event, d) {
// mouse의 page 내 좌표 -> event.pageX, event.pageY
// currentTimeLine.attr("y1", event.pageY).attr("y2", event.pageY);
// mouse의 svg 내 좌표 -> [xPos, yPos] = d3.pointer(event)
const [xPos, yPos] = d3.pointer(event);
if (xPos > xScale(0) && xPos < xScale(stations.length - 1)) {
if (
yPos > yScale(timeParse("2024-05-01, 03:00")) &&
yPos < yScale(timeParse("2024-05-02, 03:00"))
) {
currentTimeLine.attr("y1", yPos).attr("y2", yPos);
const currentTime = timeFormat(yScale.invert(yPos));
currentTimeLabel.attr("y", yPos + 4).text(currentTime);
currentTimeLabelBG.attr("y", yPos + 4).text(currentTime);
}
}
})
.on("click", function () {
isClicked = false;
seoulToBusan.map((train) =>
train.style("opacity", 0.9).style("stroke-width", 1.2)
);
busanToSeoul.map((train) =>
train.style("opacity", 0.9).style("stroke-width", 1.2)
);
});
//////// Line function
const lineFunc = d3
.line()
.x((d) => xScale(d.stationIndex))
.y((d) => yScale(d.time))
.curve(d3.curveLinear);
// .curve(d3.curveCatmullRom);
// .curve(d3.curveCardinal);
//////// Seoul to Busan
let seoulToBusan = [];
data.map((d, i) => {
const stationData = stations
.map((s, i) => {
const obj = {};
obj.time = d[s] ? d[s] : null;
obj.station = s;
obj.stationIndex = stations.indexOf(s);
return obj;
})
.filter((d) => d.time != null);
seoulToBusan[i] = svg
.append("path")
.datum(stationData)
.attr("d", lineFunc)
.attr("fill", "none")
.attr("stroke", lineColor)
.style("opacity", 0.9)
.attr("stroke-width", 1.2)
.style("display", direction != "Busan to Seoul" ? "block" : "none")
.on("mouseover", function () {
if (!isClicked) {
d3.select(this).style("stroke-width", 2.8);
}
})
.on("mouseout", function () {
if (!isClicked) {
d3.select(this).style("stroke-width", 1.2);
}
})
.on("click", function () {
isClicked = true;
seoulToBusan.map((train) =>
train.style("opacity", 0.2).style("stroke-width", 1.2)
);
busanToSeoul.map((train) =>
train.style("opacity", 0.2).style("stroke-width", 1.2)
);
d3.select(this).style("opacity", 0.9).style("stroke-width", 2.8);
});
});
//////// Busan to Seoul
let busanToSeoul = [];
dataBusan.map((d, i) => {
const stationData = stations
.map((s, i) => {
const obj = {};
obj.time = d[s] ? d[s] : null;
obj.station = s;
obj.stationIndex = stations.indexOf(s);
return obj;
})
.filter((d) => d.time != null);
busanToSeoul[i] = svg
.append("path")
.datum(stationData)
.attr("d", lineFunc)
.attr("fill", "none")
.attr("stroke", "#E87200")
.style("opacity", 0.9)
.attr("stroke-width", 1.2)
.style("display", direction != "Seoul to Busan" ? "block" : "none")
.on("mouseover", function () {
if (!isClicked) {
d3.select(this).style("stroke-width", 2.8);
}
})
.on("mouseout", function () {
if (!isClicked) {
d3.select(this).style("stroke-width", 1.2);
}
})
.on("click", function () {
isClicked = true;
seoulToBusan.map((train) =>
train.style("opacity", 0.2).style("stroke-width", 1.2)
);
busanToSeoul.map((train) =>
train.style("opacity", 0.2).style("stroke-width", 1.2)
);
d3.select(this).style("opacity", 0.9).style("stroke-width", 2.8);
});
});
const currentTimeLabelBG = svg
.append("text")
.attr("x", xScale(stations.length - 1) + 5)
.attr("y", yScale(timeParse("2024-05-01, 04:30")) + 4)
.text(timeFormat(timeParse("2024-05-01, 04:30")))
.attr("class", "timeLabel")
.style("font-weight", 800)
.style("fill", "white")
.style("stroke", "white")
.style("stroke-width", 3);
const currentTimeLabel = svg
.append("text")
.attr("x", xScale(stations.length - 1) + 5)
.attr("y", yScale(timeParse("2024-05-01, 04:30")) + 4)
.text(timeFormat(timeParse("2024-05-01, 04:30")))
.attr("class", "timeLabel");
return svg.node();
}