Click and drag a fixed node move it to a new location.
Combine user interactions like click and dragEnd with the pin() function to allow users to move geolocated nodes.
Click and drag a fixed node move it to a new location.
Combine user interactions like onClick and dragEnd with the pin prop to allow users to move geolocated nodes.
- Event Handling docs
import { MapWeave } from "mapweave/mapbox";
import { NetworkLayer } from "mapweave/layers";
import { networkData } from "./data";
let currentNetworkData = networkData;
const mapweave = new MapWeave({
container: "mw",
options: { accessToken: VITE_MAPBOX_API_KEY },
});
const networkLayer = new NetworkLayer({ data: currentNetworkData });
mapweave.addLayer(networkLayer);
let currentPinned = networkLayer.pin();
mapweave.fitBounds({ padding: 200 });
mapweave.on("click", ({ id, item }) => {
if (id !== null && item?.latitude) {
freeNode(id);
} else {
lockNodePositions();
}
});
mapweave.on("drag-end", (e) => {
if (e.position === null || e.id === null) {
return;
}
networkLayer.pin({ ...currentPinned, [e.id]: e.position });
currentPinned = networkLayer.pin();
});
function freeNode(nodeId) {
// Copy the network and replace the clicked fixed node with a node without a lat/lon
const dataCopy = {
...currentNetworkData,
[nodeId]: {
type: "node",
color: "#0FAB7C",
size: 9,
label: {
backgroundColor: "#0FAB7C",
text: "Drag to move me",
position: currentNetworkData[nodeId].label.position,
},
},
};
// Get the lat/lon from the clicked fixed node and use them to set the pinned position of the new free node
let { latitude, longitude } = currentNetworkData[nodeId];
networkLayer.data(dataCopy);
networkLayer.pin({
...currentPinned,
[nodeId]: { latitude, longitude, altitude: 0 },
});
currentPinned = networkLayer.pin();
currentNetworkData = dataCopy;
}
function lockNodePositions() {
// Get the positions of any pinned free nodes and replace the free nodes with fixed nodes at those positions
const dataCopy = Object.fromEntries(
Object.entries(currentNetworkData).map(([key]) => [
key,
currentPinned[key] ? { ...networkData[key], ...currentPinned[key] } : currentNetworkData[key],
])
);
networkLayer.data(dataCopy);
// Then reset pinned nodes
networkLayer.pin({});
currentPinned = networkLayer.pin();
currentNetworkData = dataCopy;
} const labelText = "Click to free me";
export const networkData = {
Cambridge: {
type: "node",
color: "#FF1764",
latitude: 52.205325,
longitude: 0.118264,
size: 9,
label: { text: labelText, position: "nw", backgroundColor: "#FF1764" },
},
Colchester: {
type: "node",
color: "#FF1764",
latitude: 51.891048,
longitude: 0.902992,
size: 9,
label: { text: labelText, position: "e", backgroundColor: "#FF1764" },
},
London: {
type: "node",
color: "#FF1764",
latitude: 51.507387,
longitude: -0.127663,
size: 9,
label: { text: labelText, position: "sw", backgroundColor: "#FF1764" },
},
linkCambridgeLondon: {
type: "link",
id: "linkCambridgeLondon",
id1: "London",
id2: "Cambridge",
color: "white",
width: 2,
},
linkCambridgeColchester: {
type: "link",
id: "linkCambridgeColchester",
id1: "Colchester",
id2: "Cambridge",
color: "white",
width: 2,
},
linkLondonColchester: {
type: "link",
id: "linkLondonColchester",
id1: "Colchester",
id2: "London",
color: "white",
width: 2,
},
}; <!doctype html>
<html>
<head>
<link rel="stylesheet" href="@ci/theme/mw/css/examples.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="mw">
<div class="example__infobox controls">
<div class="container">
<div class="legend fixed-node"></div>
<div>Fixed node</div>
<div class="divider"></div>
<div class="legend free-node"></div>
<div>Pinned free node</div>
</div>
</div>
</div>
<script type="module" src="./code.js"></script>
</body>
</html> #mw {
height: 100vh;
position: relative;
overflow: hidden;
}
body {
margin: 0;
}
.controls {
position: absolute;
border-radius: 0;
display: flex;
z-index: 1;
width: fit-content;
justify-content: space-around;
}
.container {
display: flex;
width: inherit;
justify-content: space-around;
}
.controls .container button {
background: none;
border: none;
cursor: pointer;
font-family: monospace;
color: #fff;
}
.controls .container .divider {
width: 1px;
background-color: #ccc;
align-self: stretch;
margin: 0 10px;
}
.legend {
display: block;
height: 18px;
width: 18px;
margin: 0 5px 0 5px;
border-radius: 50%;
}
.fixed-node {
background-color: #ff1764;
}
.free-node {
background-color: #0fab7c;
} import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { MapWeave } from "mapweave/react/mapbox";
import { NetworkLayer } from "mapweave/react/layers";
import { networkData } from "./data";
const mapWeaveOptions = { accessToken: VITE_MAPBOX_API_KEY };
function Demo() {
const [currentNetworkData, setCurrentNetworkData] = useState(networkData);
const [pinnedNodes, setPinnedNodes] = useState({});
const clickHandler = ({ id, item }) => {
if (id !== null && item?.type === "node" && item?.latitude) {
freeNode(id);
} else {
lockNodePositions();
}
};
const dragEndHandler = ({ id, position }) => {
if (id === null || position === null) {
return;
}
setPinnedNodes({ ...pinnedNodes, [id]: position });
};
function freeNode(nodeId) {
// Copy the network and replace the clicked fixed node with a node without a lat/lon
const dataCopy = {
...currentNetworkData,
[nodeId]: {
type: "node",
color: "#0FAB7C",
size: 9,
label: {
backgroundColor: "#0FAB7C",
text: "Drag to move me",
position: currentNetworkData[nodeId].label.position,
},
},
};
// Get the lat/lon from the clicked fixed node and use them to set the pinned position of the new free node
let { latitude, longitude } = currentNetworkData[nodeId];
setCurrentNetworkData(dataCopy);
setPinnedNodes({ ...pinnedNodes, [nodeId]: { latitude, longitude, altitude: 0 } });
}
function lockNodePositions() {
// Get the positions of any pinned free nodes and replace the free nodes with fixed nodes at those positions
const dataCopy = Object.fromEntries(
Object.entries(currentNetworkData).map(([key]) => [
key,
pinnedNodes[key] ? { ...networkData[key], ...pinnedNodes[key] } : currentNetworkData[key],
])
);
setCurrentNetworkData(dataCopy);
// Then reset pinned nodes
setPinnedNodes({});
}
return (
<>
<div class="example__infobox controls">
<div class="container">
<div class="legend fixed-node"></div>
<div>Fixed node</div>
<div class="divider"></div>
<div class="legend free-node"></div>
<div>Pinned free node</div>
</div>
</div>
<MapWeave options={mapWeaveOptions} onClick={clickHandler} onDragEnd={dragEndHandler}>
<NetworkLayer data={currentNetworkData} pin={pinnedNodes} />
</MapWeave>
</>
);
}
const root = createRoot(document.getElementById("mw"));
root.render(<Demo />); const labelText = "Click to free me";
export const networkData = {
Cambridge: {
type: "node",
color: "#FF1764",
latitude: 52.205325,
longitude: 0.118264,
size: 9,
label: { text: labelText, position: "nw", backgroundColor: "#FF1764" },
},
Colchester: {
type: "node",
color: "#FF1764",
latitude: 51.891048,
longitude: 0.902992,
size: 9,
label: { text: labelText, position: "e", backgroundColor: "#FF1764" },
},
London: {
type: "node",
color: "#FF1764",
latitude: 51.507387,
longitude: -0.127663,
size: 9,
label: { text: labelText, position: "sw", backgroundColor: "#FF1764" },
},
linkCambridgeLondon: {
type: "link",
id: "linkCambridgeLondon",
id1: "London",
id2: "Cambridge",
color: "white",
width: 2,
},
linkCambridgeColchester: {
type: "link",
id: "linkCambridgeColchester",
id1: "Colchester",
id2: "Cambridge",
color: "white",
width: 2,
},
linkLondonColchester: {
type: "link",
id: "linkLondonColchester",
id1: "Colchester",
id2: "London",
color: "white",
width: 2,
},
}; <!doctype html>
<html>
<head>
<link rel="stylesheet" href="@ci/theme/mw/css/examples.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="mw"></div>
<script type="module" src="./code.jsx"></script>
</body>
</html> #mw {
height: 100vh;
position: relative;
overflow: hidden;
}
body {
margin: 0;
}
.controls {
position: absolute;
border-radius: 0;
display: flex;
z-index: 1;
width: fit-content;
justify-content: space-around;
}
.container {
display: flex;
width: inherit;
justify-content: space-around;
}
.controls .container button {
background: none;
border: none;
cursor: pointer;
font-family: monospace;
color: #fff;
}
.controls .container .divider {
width: 1px;
background-color: #ccc;
align-self: stretch;
margin: 0 10px;
}
.legend {
display: block;
height: 18px;
width: 18px;
margin: 0 5px 0 5px;
border-radius: 50%;
}
.fixed-node {
background-color: #ff1764;
}
.free-node {
background-color: #0fab7c;
}