chart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
const radius = 3.5;
let overviewMarker = {
x: 0,
y: 0,
w: width,
h: height
};
let transform = d3.zoomIdentity;
let zoom = d3
.zoom()
.extent([[0, 0], [width, height]])
.translateExtent([[0, 0], [width, height]])
.scaleExtent([1, 8])
.on("zoom", zoomed);
const detailview = svg.append("g");
detailview
.append("rect")
.attr("transform", transform)
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "#ECEFF1");
detailview
.selectAll("circle")
.data(data)
.join("circle")
.attr("fill", "#263238")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateCool(i / data.length))
.attr("stroke-width", 1)
.attr("stroke", "#263238");
//Task 2
//Hint: Create a zoomIdentity object and set the translate
//and scale acoording to overviewSize, width, and height values
const overviewTransform = d3.zoomIdentity.scale(overviewSize/height)
.translate( 3 * width - overviewSize, overviewSize);
const overview = svg
.append("g")
.attr("transform", overviewTransform)
.attr("viewBox", [0, 0, width, height]);
overview
.append("rect")
.attr("x", 0 )
.attr("y", 0 )
// Hint: Set width, heigth, Fill, StrokeWidth, Stroke Color
.attr("width", width)
.attr("height", height)
.attr("fill", "#ECEFF1")
.attr("stroke-width", 5)
.attr("stroke", "#263238");
// Hint: Append the data points in the overview object
overview
.selectAll("circle")
.data(data)
.join("circle")
.attr("fill", "#263238")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateCool(i / data.length))
.attr("stroke-width", 1)
.attr("stroke", "#263238");
function zoomed() {
transform = d3.event.transform; // retrieve the zoom state
}
const markerRect = overview
.append("rect")
.datum(overviewMarker) //
//Hint: Set x, y, width, height and opacity
.attr("x", 0 )
.attr("y", 0 )
.attr("width", width)
.attr("height", height)
.attr("fill-opacity", 0)
//Set stroke width, stroke color
.attr("stroke-width", 5)
.attr("stroke", "#FF0000");
//Hint: check the difference between datum and data
/*
source: https://stackoverflow.com/questions/13728402/what-is-the-difference-d3-datum-vs-data
.data(data) : only binds the entirety of data to the first element in overview
.datum(data) : no data-join process , if overview contains more than one element the
datum(data) will bind the entirety of data to every single element in overview
*/
function updateMarkerRect() {
overviewMarker.x = transform.invertX(0);
overviewMarker.y = transform.invertY(0);
overviewMarker.w = transform.invertX(width) - overviewMarker.x;
overviewMarker.h = transform.invertY(height) - overviewMarker.y;
markerRect
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("width", d => d.w)
.attr("height", d => d.h);
}
function updateMarkerDrag(d) {
// Task 3
// Hint: Use the overview and overviewMarkers parameter
// to set a min and Max constrain for the adjustment of the overviewMarker
let mouse_drag_x = d3.event.x;
let mouse_drag_y = d3.event.y;
if(mouse_drag_x > width - d.w){mouse_drag_x = width - d.w}
if(mouse_drag_y > height - d.h){mouse_drag_y = height - d.h}
if(mouse_drag_x < 0){mouse_drag_x = 0}
if(mouse_drag_y < 0){mouse_drag_y = 0}
d3.select(this)
.attr("x", d.x = mouse_drag_x ) // d.x = overviewMarker.x
.attr("y", d.y = mouse_drag_y );
//console.log( d.x + " " + overviewMarker.x + " " + height + " " + overviewMarker.h);
// Hint: use d3.zoomIdentity to scale and translate. Substitute 1 with the correct values
// transform with scale(k) and translate(t_x, t_y)
let t = d3.zoomIdentity.scale(zoomSlider.value)
.translate(- mouse_drag_x , - mouse_drag_y);
// console.log(zoomSlider.value);
detailview.call(zoom.transform, t);
detailview.attr("transform", transform);
}
function startMarkerDrag(d) {
// Task 3
// Change the apearance of the marker when the user interact with it
// source: https://observablehq.com/@d3/click-vs-drag
d3.select(this).attr("stroke-width", 15);
}
function endMarkerDrag(d) {
// Task 3
// Return the marker to its static view
d3.select(this).attr("stroke-width", 5);
}
let markerDrag = d3
.drag()
.on("start", startMarkerDrag)
.on("drag", updateMarkerDrag)
.on("end", endMarkerDrag);
markerRect.call(markerDrag);
// Task 1:
// Hint: set the slider step, min and max (scaleExtent) parameters using the zoom parameter
// zoomSlider;
// d3 accesses the HTML-slider and sets the attributes for type = "range"
// scale increases the value if value > 0
d3.select(zoomSlider)
.attr("min", zoom.scaleExtent()[0])
.attr("max", zoom.scaleExtent()[1])
.attr("step", 0.1);
function sliderZoom() {
// Task 1-2:
// Hint task 2: Use the overview and overviewMarkers parameter
// to set a min and Max constrain for the adjustment of the overviewMarker when the slider value changes
//overviewMarker.x = 0;
//overviewMarker.y = 0;
if(overviewMarker.x > width - overviewMarker.w){overviewMarker.x = width - overviewMarker.w}
if(overviewMarker.y > height - overviewMarker.h){overviewMarker.y = height - overviewMarker.h}
if(overviewMarker.x < 0){overviewMarker.x = 0}
if(overviewMarker.y < 0){overviewMarker.y = 0}
let zoomIntensity = zoomSlider.value;
// console.log("zoomIntensity " + zoomIntensity)
let zoomIntensityD3 = d3.select(this).property("value");
// console.log("zoomIntensityD3: " + zoomIntensityD3)
// Hint task 1: use d3.zoomIdentity to scale and translate. Substitute 1 with the real values
// transform with scale(k) & translate(t_x, t_y)
let t = d3.zoomIdentity.scale(zoomIntensityD3)
.translate( width * (1 - zoomIntensityD3) / (2 * zoomIntensityD3),
height * (1 - zoomIntensityD3) / (2 * zoomIntensityD3));
// .translate(-overviewMarker.x , - overviewMarker.y);
// console.log("after transformation: " + t.k + " " + t.x + " " + t.y)
detailview.call(zoom.transform, t);
detailview.attr("transform", transform);
updateMarkerRect();
// Hint: First achieve to scale properly the detail view and then show that on the overViewmarker, details can be done later, like center the scaling or add constrains to the overviewMarker.
}
d3.select(zoomSlider)
.on("input", sliderZoom);
return svg.node();
}