Original question splitted in 2 parts --> at the suggestion of Frenchy
1) How can I display a 2-digit abbreviation for all touched states within a clipPath, not hidden by the route line?
// --- clear svg -----------------------------------------
d3.select("svg").remove();
// --- get modal size --------------------------------------------------------
document.querySelector(".modal").addEventListener("shown.bs.modal", function (e) {
let modalWidth = document.querySelector(".modal-dialog").clientWidth;
let modalHeight = document.querySelector(".modal-dialog").clientHeight;
// --- load both json files, V7 method -----------------
Promise.all([
d3.json("https://gist.githubusercontent.com/manu-75/06b031548def239d7038e7157a3c204c/raw/8da47d28b846b6b232cad57692d737e05ddebee3/USA_CAN.geojson"),
d3.json(`https://gist.githubusercontent.com/manu-75/06b031548def239d7038e7157a3c204c/raw/8da47d28b846b6b232cad57692d737e05ddebee3/${currentTopo}.topojson`),
]).then(function (data) {
let baseMap = data[0];
let tourMap = data[1];
let geojsonTour = topojson.feature(tourMap, tourMap.objects[currentTopo]);
// --- get bbox of tourMap -----------------------------
let tourMapBbox = d3.geoBounds(geojsonTour);
// --- get aspectRatio from topojson -------------------
let widthBbox = tourMapBbox[1][0] - tourMapBbox[0][0];
let heightBbox = tourMapBbox[1][1] - tourMapBbox[0][1];
let aspectRatio = widthBbox / heightBbox;
let tourHeightCalc = Math.round(modalWidth / aspectRatio);
// --- margins, width, height --------------------------
let margin = { top: 20, right: 20, bottom: 20, left: 20 },
width = modalWidth - margin.left - margin.right,
height = tourHeightCalc - margin.top - margin.bottom;
// --- svg -------------------------------------------
let svg = d3
.select("#tourMap")
.append("svg")
.attr("width", width)
.attr("height", height);
// --- projection ------------------------------------
let projection = d3.geoMercator().fitSize([width, height], geojsonTour);
// --- path ------------------------------------------
let path = d3.geoPath().projection(projection);
// --- svg append tour -------------------------------
svg
.append("path")
.datum(geojsonTour)
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "#ff674d")
.attr("stroke-width", 1.5);
// --- svg append origin circle ----------------------
svg
.append("circle")
.attr("cx", projection(destArray)[0])
.attr("cy", projection(destArray)[1])
.attr("r", 4)
.attr("fill", "#338bff");
// --- clipPath defined by tourMapBbox ----------------
const clipPath = svg
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", projection(tourMapBbox[0])[0])
.attr("y", projection(tourMapBbox[1])[1])
.attr("width", projection(tourMapBbox[1])[0] - projection(tourMapBbox[0])[0])
.attr("height", projection(tourMapBbox[0])[1] - projection(tourMapBbox[1])[1]);
// --- append clipPath -------------------------------
const g = svg.append("g").attr("clip-path", "url(#clip)");
// --- abbr name for states within clipPath ????
// --- use "properties":{"name":"Washington","id":"US-WA" ....
// --- last 2 characters from the id, in this example 'WA'
// --- svg append baseMap ----------------------------
svg
.append("path")
.datum(baseMap)
.attr("d", path)
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-opacity", 0.5)
.attr("stroke-width", 0.3);
});
}, { once: true });
here's my full jsfiddle example
Any help appreciated!
to have a complete circle, you have to check if the circle is outside of the svg dimension:
for the first button, the circle is outside the svg , so you adapt the height like this:
// --- svg append origin circle ----------------------
svg
.append("circle")
.attr("cx", projection(destArray)[0])
.attr("cy", projection(destArray)[1])
.attr("r", 4)
.attr("fill", "#338bff");
const r = parseInt(svg.select("circle").node().getAttribute("r"));
const cy = parseInt(projection(destArray)[1]) + 1;
const tcy = r + cy;
if (tcy > height)
svg.node().setAttribute("height",tcy);
if needed, you could do the same operation with the width....
you could check the fiddle