import {tip, showMainTip, clearMainTip} from "/src/scripts/tooltips"; export function editRoute(onClick) { if (customization) return; if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return; closeDialogs(".stable"); if (!layerIsOn("toggleRoutes")) toggleRoutes(); $("#routeEditor").dialog({ title: "Edit Route", resizable: false, position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"}, close: closeRoutesEditor }); debug.append("g").attr("id", "controlPoints"); const node = onClick ? elSelected.node() : d3.event.target; elSelected = d3.select(node).on("click", addInterimControlPoint); drawControlPoints(node); selectRouteGroup(node); viewbox.on("touchmove mousemove", showEditorTips); if (onClick) toggleRouteCreationMode(); if (fmg.modules.editRoute) return; fmg.modules.editRoute = true; // add listeners document.getElementById("routeGroupsShow").addEventListener("click", showGroupSection); document.getElementById("routeGroup").addEventListener("change", changeRouteGroup); document.getElementById("routeGroupAdd").addEventListener("click", toggleNewGroupInput); document.getElementById("routeGroupName").addEventListener("change", createNewGroup); document.getElementById("routeGroupRemove").addEventListener("click", removeRouteGroup); document.getElementById("routeGroupsHide").addEventListener("click", hideGroupSection); document.getElementById("routeElevationProfile").addEventListener("click", showElevationProfile); document.getElementById("routeEditStyle").addEventListener("click", editGroupStyle); document.getElementById("routeSplit").addEventListener("click", toggleRouteSplitMode); document.getElementById("routeLegend").addEventListener("click", editRouteLegend); document.getElementById("routeNew").addEventListener("click", toggleRouteCreationMode); document.getElementById("routeRemove").addEventListener("click", removeRoute); function showEditorTips() { showMainTip(); if (routeNew.classList.contains("pressed")) return; if (d3.event.target.id === elSelected.attr("id")) tip("Click to add a control point"); else if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point"); } function drawControlPoints(node) { const l = node.getTotalLength(); const increment = l / Math.ceil(l / 4); for (let i = 0; i <= l; i += increment) { const point = node.getPointAtLength(i); addControlPoint([point.x, point.y]); } routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value; } function addControlPoint(point, before = null) { debug .select("#controlPoints") .insert("circle", before) .attr("cx", point[0]) .attr("cy", point[1]) .attr("r", 0.6) .call(d3.drag().on("drag", dragControlPoint)) .on("click", clickControlPoint); } function addInterimControlPoint() { const point = d3.mouse(this); const controls = document.getElementById("controlPoints").querySelectorAll("circle"); const points = Array.from(controls).map(circle => [+circle.getAttribute("cx"), +circle.getAttribute("cy")]); const index = getSegmentId(points, point, 2); addControlPoint(point, ":nth-child(" + (index + 1) + ")"); redrawRoute(); } function dragControlPoint() { this.setAttribute("cx", d3.event.x); this.setAttribute("cy", d3.event.y); redrawRoute(); } function redrawRoute() { lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const points = []; debug .select("#controlPoints") .selectAll("circle") .each(function () { points.push([this.getAttribute("cx"), this.getAttribute("cy")]); }); elSelected.attr("d", round(lineGen(points))); const l = elSelected.node().getTotalLength(); routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value; if (fmg.modules.elevation) showEPForRoute(elSelected.node()); } function showElevationProfile() { fmg.modules.elevation = true; showEPForRoute(elSelected.node()); } function showGroupSection() { document.querySelectorAll("#routeEditor > button").forEach(el => (el.style.display = "none")); document.getElementById("routeGroupsSelection").style.display = "inline-block"; } function hideGroupSection() { document.querySelectorAll("#routeEditor > button").forEach(el => (el.style.display = "inline-block")); document.getElementById("routeGroupsSelection").style.display = "none"; document.getElementById("routeGroupName").style.display = "none"; document.getElementById("routeGroupName").value = ""; document.getElementById("routeGroup").style.display = "inline-block"; } function selectRouteGroup(node) { const group = node.parentNode.id; const select = document.getElementById("routeGroup"); select.options.length = 0; // remove all options routes.selectAll("g").each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } function changeRouteGroup() { document.getElementById(this.value).appendChild(elSelected.node()); } function toggleNewGroupInput() { if (routeGroupName.style.display === "none") { routeGroupName.style.display = "inline-block"; routeGroupName.focus(); routeGroup.style.display = "none"; } else { routeGroupName.style.display = "none"; routeGroup.style.display = "inline-block"; } } function createNewGroup() { if (!this.value) { tip("Please provide a valid group name"); return; } const group = this.value .toLowerCase() .replace(/ /g, "_") .replace(/[^\w\s]/gi, ""); if (document.getElementById(group)) { tip("Element with this id already exists. Please provide a unique name", false, "error"); return; } if (Number.isFinite(+group.charAt(0))) { tip("Group name should start with a letter", false, "error"); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; const basic = ["roads", "trails", "searoutes"].includes(oldGroup.id); if (!basic && oldGroup.childElementCount === 1) { document.getElementById("routeGroup").selectedOptions[0].remove(); document.getElementById("routeGroup").options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); document.getElementById("routeGroupName").value = ""; return; } const newGroup = elSelected.node().parentNode.cloneNode(false); document.getElementById("routes").appendChild(newGroup); newGroup.id = group; document.getElementById("routeGroup").options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); document.getElementById("routeGroupName").value = ""; } function removeRouteGroup() { const group = elSelected.node().parentNode.id; const basic = ["roads", "trails", "searoutes"].includes(group); const count = elSelected.node().parentNode.childElementCount; alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${ basic ? "all elements in the group" : "the entire route group" }?

Routes to be removed: ${count}`; $("#alert").dialog({ resizable: false, title: "Remove route group", buttons: { Remove: function () { $(this).dialog("close"); $("#routeEditor").dialog("close"); hideGroupSection(); if (basic) routes .select("#" + group) .selectAll("path") .remove(); else routes.select("#" + group).remove(); }, Cancel: function () { $(this).dialog("close"); } } }); } function editGroupStyle() { const g = elSelected.node().parentNode.id; editStyle("routes", g); } function toggleRouteSplitMode() { document.getElementById("routeNew").classList.remove("pressed"); this.classList.toggle("pressed"); } function clickControlPoint() { if (routeSplit.classList.contains("pressed")) splitRoute(this); else { this.remove(); redrawRoute(); } } function splitRoute(clicked) { lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const group = d3.select(elSelected.node().parentNode); routeSplit.classList.remove("pressed"); const points1 = [], points2 = []; let points = points1; debug .select("#controlPoints") .selectAll("circle") .each(function () { points.push([this.getAttribute("cx"), this.getAttribute("cy")]); if (this === clicked) { points = points2; points.push([this.getAttribute("cx"), this.getAttribute("cy")]); } this.remove(); }); elSelected.attr("d", round(lineGen(points1))); const id = getNextId("route"); group.append("path").attr("id", id).attr("d", lineGen(points2)); debug.select("#controlPoints").selectAll("circle").remove(); drawControlPoints(elSelected.node()); } function toggleRouteCreationMode() { document.getElementById("routeSplit").classList.remove("pressed"); document.getElementById("routeNew").classList.toggle("pressed"); if (document.getElementById("routeNew").classList.contains("pressed")) { tip("Click on map to add control points", true); viewbox.on("click", addPointOnClick).style("cursor", "crosshair"); elSelected.on("click", null); } else { clearMainTip(); viewbox.on("click", clicked).style("cursor", "default"); elSelected.on("click", addInterimControlPoint).attr("data-new", null); } } function addPointOnClick() { // create new route if (!elSelected.attr("data-new")) { debug.select("#controlPoints").selectAll("circle").remove(); const parent = elSelected.node().parentNode; const id = getNextId("route"); elSelected = d3.select(parent).append("path").attr("id", id).attr("data-new", 1); } addControlPoint(d3.mouse(this)); redrawRoute(); } function editRouteLegend() { const id = elSelected.attr("id"); editNotes(id, id); } function removeRoute() { alertMessage.innerHTML = "Are you sure you want to remove the route?"; $("#alert").dialog({ resizable: false, title: "Remove route", buttons: { Remove: function () { $(this).dialog("close"); elSelected.remove(); $("#routeEditor").dialog("close"); }, Cancel: function () { $(this).dialog("close"); } } }); } function closeRoutesEditor() { elSelected.attr("data-new", null).on("click", null); clearMainTip(); routeSplit.classList.remove("pressed"); routeNew.classList.remove("pressed"); debug.select("#controlPoints").remove(); unselect(); } }