Public
Edited
May 13, 2023
2 forks
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
{
let svg = d3.select(DOM.svg(width, height));
let g = svg.append("g");

g.selectAll('path')
.data(bayAreaCounties.features)
.enter()
.append("path")
.attr("fill", "#ccc")
.attr("stroke", "#333")
.attr("d", geoPath);


// define radius in distance for areas of interest
const radiusInMiles = circleRadius;
const degreesPerMile = 1/69;
const radiusInDegrees = radiusInMiles * degreesPerMile;


// define the drag behavior
let drag = d3.drag()
.on("drag", function(event) {
// Convert the new SVG coordinates to geographic coordinates
let newCoords = bayAreaProjection.invert([event.x, event.y]);
// Update the center of the circle
let center = newCoords;

d3.select(this).attr('lng', event.y)
d3.select(this).attr('lat', event.x)
// Update the path data of the circle
d3.select(this).datum(d3.geoCircle().center(center).radius(radiusInDegrees)())
.attr("d", geoPath);

// add intersection functionality
intersectionDetection();
});
// draw first circle area of interest
let geoCircle1 = d3.geoCircle().radius(radiusInDegrees).center([-121, 37.9]); // convert radius to km for mercator projection

let circle1Path = svg.append("path")
.datum(geoCircle1)
.attr('id', 'circle1ID')
.attr("d", geoPath)
.attr('lat', bayAreaProjection([-121, 37.9])[1])
.attr('lng', bayAreaProjection([-121, 37.9])[0])
.attr("fill", "#3498DB")
.attr('stroke', '#A569BD')
.style("opacity", 0.2)
.attr('stroke-opacity', 0.3)
.call(drag);

// draw second circle area of interest
const geoCircle2 = d3.geoCircle().radius(radiusInDegrees).center([-123.5, 37.9]); // convert radius to km for mercator projection

let circle2Path = svg.append("path")
.datum(geoCircle2)
.attr('id', 'circle2ID')
.attr("d", geoPath)
.attr('lat', bayAreaProjection([-123.5, 37.9])[1])
.attr('lng', bayAreaProjection([-123.5, 37.9])[0])
.attr("fill", "#3498DB")
.attr('stroke', '#A569BD')
.style("opacity", 0.2)
.attr('stroke-opacity', 0.3)
.call(drag);


// draw the Yelp restaurants
const restaurants = svg.append('g')
.attr('id', 'dotIDs')
.selectAll("circle")
.data(dataset)
.join("circle")
.attr('class', 'restaurantDots')
.attr('cx', d => bayAreaProjection([d.latitude, d.longitude])[0])
.attr('cy', d => bayAreaProjection([d.latitude, d.longitude])[1])
.attr("r", 5)
.attr("fill", "#7C7A7A")
.attr('opacity', 0.75)
.attr('stroke', '#616161')
.attr('stroke-opacity', 0.75)
/****** show tooltip and border when hovered over ******/
.on("mouseover", function (event, d) { // <-- need to use the regular function definition to have access to "this"
svg.select("#tooltip-text")
.text(`Name: ${d.name}`)
.append("tspan")
.attr("x", 5)
.attr("dy", "1.2em")
.text(`Price: ${d.price}`)
.append("tspan")
.attr("x", 5)
.attr("dy", "1.2em")
.text(`Rating: ${d.rating}`);
let positionOffest = 8;
svg.select("#tooltip")
// move the tooltip to where the cursor is
.attr("transform", `translate(${bayAreaProjection([d.latitude, d.longitude])[0] + positionOffest}, ${bayAreaProjection([d.latitude, d.longitude])[1] + positionOffest})`)
.style("display", "block"); // make tooltip visible
d3.select(this)
.attr("stroke", "#333333")
.attr("stroke-width", 2);
})
.on("mouseout", function (event, d) {
svg.select("#tooltip").style("display", "none"); // hide tooltip
d3.select(this).attr("stroke", "none"); // undo the stroke
});

// add Tooltip to the restaurants
const tooltipGroup = svg.append("g") // the tooltip needs to be added last so that it stays on top of all circles
.attr("id", "tooltip")
.style("display", "none"); // hidden by default
tooltipGroup.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 250)
.attr("height", 60)
.attr("fill", "white")
.attr("fill", "white")
.attr("stroke", "#333")
.attr("stroke-width", 1);

// Adding text to the tooltip
tooltipGroup.append("text")
.attr("id", "tooltip-text")
.attr("x", 5)
.attr("y", 15)
.attr("font-size", "14px")
//.attr("font-weight", "bold")
.attr("fill", "black");
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more