function drawGraticuleTicks(
svgNode,
{
step = [1, 1],
fontSize = 10,
fontFamily = "inherit",
fontFill = "black",
fontStroke = "white",
fontStrokeWidth = 2,
tickSize = 0,
tickPadding = 4,
tickStroke = "black",
tickStrokeWidth = 0.75,
position = "inside",
closenessPrecision = 0.001,
debug = false
} = {}
) {
const {
projection,
height,
width,
marginTop,
marginRight,
marginLeft,
marginBottom
} = svgNode.props;
const boundsGeo = viewportAsGeo({
projection,
width,
height,
marginTop,
marginRight,
marginBottom,
marginLeft
});
const pos = graticuleLabelPositions(boundsGeo, { step });
const P = pos.map((p) => {
const [x, y] = projection(p);
p.x = x;
p.y = y;
return p;
});
const g = d3.select(svgNode).append("g").attr("class", "graticule-ticks");
const leftMost = d3.min(P, (p) => p.x);
let leftTicks = pos.filter(
(p) => Math.abs(p.x - leftMost) <= closenessPrecision
);
leftTicks = leftTicks.filter((t) => t.type === "latitude");
const bottomMost = d3.max(P, (p) => p.y);
let bottomTicks = pos.filter(
(p) => Math.abs(p.y - bottomMost) <= closenessPrecision
);
bottomTicks = bottomTicks.filter((t) => t.type === "longitude");
// Left ticks
const leftTickG = g
.append("g")
.attr("class", "left-graticules-ticks")
.selectAll(".tick")
.data(leftTicks)
.join("g")
.attr("class", "tick")
.attr("transform", (d) => `translate(${d.x},${d.y})`);
leftTickG
.append("line")
.attr("class", "tick-line")
.attr("x2", (position === "outside" ? -1 : 1) * tickSize);
leftTickG
.append("text")
.attr("dy", (position === "outside" ? -1 : 1) * (tickSize + tickPadding))
.attr("dominant-baseline", position === "outside" ? "auto" : "hanging")
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)");
// Bottom Ticks
const bottomTickG = g
.append("g")
.attr("class", "bottom-graticules-ticks")
.selectAll(".tick")
.data(bottomTicks)
.join("g")
.attr("class", "tick")
.attr("transform", (d) => `translate(${d.x},${d.y})`);
bottomTickG
.append("line")
.attr("class", "tick-line")
.attr("y2", (position === "outside" ? 1 : -1) * tickSize);
bottomTickG
.append("text")
.attr("dy", (position === "outside" ? 1 : -1) * (tickSize + tickPadding))
.attr("text-anchor", "middle")
.attr("dominant-baseline", position === "outside" ? "hanging" : "auto");
g.selectAll(".tick-line")
.attr("stroke", tickStroke)
.attr("stroke-width", tickStrokeWidth);
// Add label text
g.selectAll("text")
.attr("font-family", fontFamily)
.attr("font-size", fontSize)
.attr("fill", fontFill)
.attr("stroke", fontStroke)
.attr("stroke-width", fontStrokeWidth)
.attr("paint-order", "stroke")
.text((d) =>
d.type === "longitude" ? formatLongitude(d[0]) : formatLatitude(d[1])
);
if (debug) {
g.append("g")
.selectAll(".edge-points")
.data(boundsGeo.coordinates[0])
.join("circle")
.attr("class", "edge-points")
.attr("r", ".75")
.attr("fill", "#0ff")
.each(function (d) {
const [x, y] = projection(d);
d3.select(this).attr("cx", x).attr("cy", y);
});
g.append("g")
.attr("class", "graticules-intersection-points")
.selectAll(".graticules-intersection-point")
.data(pos)
.join("circle")
.attr("class", "graticules-intersection-point")
.attr("r", 4)
.attr("fill", "none")
.attr("stroke-width", 2)
.each(function (d) {
const [cx, cy] = projection(d);
d3.select(this)
.attr("cx", cx)
.attr("cy", cy)
.attr("stroke", d.type === "longitude" ? "red" : "orange");
});
}
}