Public
Edited
Mar 2
Paused
Fork of D3 U.S. map
Insert cell
Insert cell
map2 = {
// hopefully the projection is specified, but this might work if it is not
const projection = d3.geoIdentity()
.reflectY(true) // if needed.
.fitSize([chartStyle.width, chartStyle.height],cd_geo)
const pathGenerator = d3.geoPath(projection);

// this creates a div in the HTML DOM with an HTML template string
// https://observablehq.com/documentation/misc/standard-library#html-string
// the tooltipTemplate variable is also an HTML template
const container = d3.select(
html`<div style="position:relative;">${tooltipTemplate}</div>`
);

const tooltipDiv = container.select(".tooltip");

// this creates a child svg element in the previously defined div container
const svg = container.append('svg')
.attr("viewBox", [0, 0, chartStyle.width, chartStyle.height])
.attr("width", chartStyle.width)
.attr("height", chartStyle.height)
.attr("style", "max-width: 100%; height: auto;");

// this creates a child rect element as the background
svg.append("rect")
.attr("width", chartStyle.width)
.attr("height", chartStyle.height)
.attr('fill', chartStyle.backgroundColor);

// this creates child path elements for each data feature
// https://bost.ocks.org/mike/join/
svg.selectAll('path')
.data(cd_geo.features)
.join('path')
.attr('d', pathGenerator)
.attr('fill', chartStyle.landColor)
.attr('stroke', chartStyle.landStroke)
.attr('stroke-width', 1)
.call(tooltip, tooltipDiv);
return container.node();
}
Insert cell
Insert cell
chartStyle = ({
height: 581,
width: 928,
landColor: "#09A573",
backgroundColor: "#EAF2FA",
landStroke: "#FCF5E9",
hoverColor: "magenta"
})
Insert cell
function resetStyle(selection) {
selection.attr("fill", chartStyle.landColor);
};


Insert cell
function setStyle(selection) {
selection.attr("fill", chartStyle.hoverColor);
};
Insert cell
Insert cell
cd_geo = FileAttachment("cd_hex.json").json()

Insert cell
Insert cell
// I think this gives each tooltip a custom id to prevent styling issues
tooltipId = DOM.uid().id
Insert cell
// This is an observable html template
// https://observablehq.com/documentation/misc/standard-library#html-string
tooltipTemplate = html`<div class="tooltip tooltip-${tooltipId}">
${tooltipStyles}
<div class="tooltip-contents"></div>
</div>`
Insert cell
Insert cell
// this is a dangerous combination of arrow functions and `this`
tooltip = (selectionGroup, tooltipDiv) => {
selectionGroup.each(function () {
d3.select(this)
.on("mouseover.tooltip", handleMouseover)
.on("mousemove.tooltip", handleMousemove)
.on("mouseleave.tooltip", handleMouseleave);
});

function handleMouseover() {
// show/reveal the tooltip, set its contents,
// style the element being hovered on
showTooltip();
setContents(d3.select(this).datum(), tooltipDiv);
setStyle(d3.select(this));
}

function handleMousemove(event) {
// update the tooltip's position
const [mouseX, mouseY] = d3.pointer(event, this);
// add the left & top margin values to account for the SVG g element transform
setPosition(mouseX + margin.left, mouseY + margin.top);
}

function handleMouseleave() {
// do things like hide the tooltip
// reset the style of the element being hovered on
hideTooltip();
resetStyle(d3.select(this)); // I don't love that `this`, inside the tooltip code is the element and not the tooltip
}

function showTooltip() {
tooltipDiv.style("display", "block");
}

function hideTooltip() {
tooltipDiv.style("display", "none");
}

function setPosition(mouseX, mouseY) {
tooltipDiv
.style(
"top",
mouseY < chartStyle.height / 2 ? `${mouseY + MOUSE_POS_OFFSET}px` : "initial"
)
.style(
"right",
mouseX > chartStyle.width / 2
? `${chartStyle.width - mouseX + MOUSE_POS_OFFSET}px`
: "initial"
)
.style(
"bottom",
mouseY > chartStyle.height / 2
? `${chartStyle.height - mouseY + MOUSE_POS_OFFSET}px`
: "initial"
)
.style(
"left",
mouseX < chartStyle.height / 2 ? `${mouseX + MOUSE_POS_OFFSET}px` : "initial"
);
}
}
Insert cell
Insert cell
function setContents(datum, tooltipDiv) {
// customize this function to set the tooltip's contents however you see fit
tooltipDiv
.selectAll("p")
.data(Object.entries(datum.properties)) // I had to add properties here bc my json structure was different
.join("p")
.filter(([key, value]) => value !== null && value !== undefined)
.html(
([key, value]) =>
`<strong>${key}</strong>: ${
typeof value === "object" ? value.toLocaleString("en-US") : value
}`
);
};


Insert cell
// Another observable html template
// https://observablehq.com/documentation/misc/standard-library#html-string
tooltipStyles = htl.html`<style>
/* modify these styles to however you see fit */
div.tooltip-${tooltipId} {
box-sizing: border-box;
position: absolute;
display: none;
top: 0;
left: -100000000px;
padding: 8px 12px;
font-family: sans-serif;
font-size: 12px;
color: #333;
background-color: #fff;
border: 1px solid #333;
border-radius: 4px;
pointer-events: none;
z-index: 1;
}
div.tooltip-${tooltipId} p {
margin: 0;
}
</style>`
Insert cell
Insert cell
MOUSE_POS_OFFSET = 8
Insert cell
margin = ({ top: 10, right: 10, bottom: 10, left: 10 })
Insert cell
Insert cell
d3 = require("d3@7", "d3-geo@3", "d3-selection@2")
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