I build a Leaflet
map and display data from a GeoJSON
. I add a Leaflet-Sidebar
to my map, and embed the info control inside this sidebar. When a user hovering polygons on the map, raw values are dynamically displayed in the sidebar.
In my GeoJSON
I also have a field with JSON
formated values to build a D3 graph. I wan't to add a D3
graph in the sidebar/info control. Graph will dynamicaly change when user hovering polygons.
Info is an HTML
structure but D3
is using JS
so I don't know how to add a dynamic content. In this case I want to replace var data
in my graph.html example with props.graph in my map.html.
How can add a D3
graph in a Lealflet-Sidebar
with data from my GeoJSON
?
map.html
<html>
<head>
<meta charset="utf-8">
<title>My Map</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.0/spin.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2/leaflet.spin.js"></script>
<script type="text/javascript" src="https://raw.githubusercontent.com/Turbo87/leaflet-sidebar/master/src/L.Control.Sidebar.js"></script>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.css">
<link rel="stylesheet" href="https://raw.githubusercontent.com/Turbo87/leaflet-sidebar/master/src/L.Control.Sidebar.css">
<style media="screen">
#map {width:100%; height:100%;}
</style>
</head>
<body>
<!-- Sidebar -->
<div id="sidebar"><h1>Informations</h1>
<div class="container">
<div class="center-objs">
<div id="new-parent">
<!-- Graph here -->
</div>
</div>
</div>
</div>
<!-- Map -->
<div id="map"></div>
<script type="text/javascript">
var map = L.map('map').setView([43.446577,6.079717], 9);
// Baselayer
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var esri = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
var google = L.tileLayer('http://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}');
var baseLayers = {
"Carte OSM": osm,
"Photo ESRI": esri,
"Google Hybrid": google
};
L.control.layers(baseLayers).addTo(map);
// Style
function styleBasique (feature) {
return {
color: '#5f5f5f',
fillOpacity: 0
};
}
// Listeners
function highlightFeature(cadastre) {
var cadastreSurvol = cadastre.target;
cadastreSurvol.setStyle({
color: '#fc0000'
});
if (!L.Browser.ie && !L.Browser.opera) {
cadastreSurvol.bringToFront(cadastre.target);
}
info.update(cadastreSurvol.feature.properties);
}
function resetHighlight(e) {
cadastre.resetStyle(e.target);
info.update();
}
// Over
function onEachFeatureSurvol(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight
});
}
// Infos
var info = L.control({position: 'topleft'});
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = (props ?
'<b>' + props.reference_cadastrale + '</b><br><br>'
// D3 here ?
+ props.graph
: '<i>Move...</i>');
};
info.addTo(map);
// Container for sidebar
var htmlObject = info.getContainer();
var a = document.getElementById('new-parent');
function setParent(el, newParent) {
newParent.appendChild(el);
}
setParent(htmlObject, a);
// Sidebar
var sidebar = L.control.sidebar('sidebar', {
closeButton: true,
position: 'left'
});
map.addControl(sidebar);
// Layer
// JSON
var dataurl = 'https://carto.cocktaildata.fr/py/parcelles.data.json';
// Creation
var cadastre = L.geoJson(false, {
//onEachFeature: onEachFeature,
onEachFeature: onEachFeatureSurvol,
style: styleBasique
}).addTo(map);
// Spinner
cadastre.fire('data:loading');
// Loading data
$.getJSON(dataurl, function (data) {
// Stop spinner
cadastre.fire('data:loaded');
// Add data
cadastre.addData(data);
// Zoom
map.fitBounds(cadastre.getBounds());
// Sidebar on clic
cadastre.on('click', function () {
sidebar.toggle();
});
});
</script>
</body>
</html>
graph.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Filiation</title>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<style>
rect {
fill: #66bfff;
cursor: pointer;
}
.active {
fill: orange;
stroke: orange;
}
.activelink {
fill: none;
stroke: orange;
stroke-width: 2.5px;
}
.label {
fill: white;
font-family: sans-serif;
pointer-events: none;
font-size: 16px;
}
.label_date {
fill: white;
font-family: sans-serif;
pointer-events: none;
font-style: italic;
font-size: 12px;
}
.link {
fill: none;
stroke: #66bfff;
stroke-width: 2.5px;
}
</style>
</head>
<body>
<div id="tree"></div>
<script>
var width = 1000,
height = 800,
boxWidth = 150,
boxHeight = 35,
gap = {
width: 50,
height: 12
},
margin = {
top: 16,
right: 16,
bottom: 16,
left: 16
},
svg;
// Data
var data = {
"Nodes": [
{"lvl": 0, "name": "830560000D1980", "date_modification": ""},
{"lvl":1, "name": "830560000D1954", "date_modification": "2018-08-06"},
{"lvl":2, "name": "830560000D1586", "date_modification": "2016-05-09"},
{"lvl":3, "name": "830560000D1582", "date_modification": "1997-10-14"},
{"lvl":4, "name": "830560000D0239", "date_modification": "1997-06-03"},
{"lvl":4, "name": "830560000D0240", "date_modification": "1997-06-03"},
{"lvl":4, "name": "830560000D0241", "date_modification": "1997-06-03"},
{"lvl":4, "name": "830560000D0246", "date_modification": "1997-06-03"},
{"lvl":4, "name": "830560000D0247", "date_modification": "1997-06-03"}
],
"links": [
{"source": "830560000D1980", "target": "830560000D1954"},
{"source": "830560000D1954", "target": "830560000D1586"},
{"source": "830560000D1586", "target": "830560000D1582"},
{"source": "830560000D1582", "target": "830560000D0239"},
{"source": "830560000D1582", "target": "830560000D0240"},
{"source": "830560000D1582", "target": "830560000D0241"},
{"source": "830560000D1582", "target": "830560000D0246"},
{"source": "830560000D1582", "target": "830560000D0247"}
]
};
// Layout
var Nodes = [];
var links = [];
var lvlCount = 0;
var diagonal = d3.svg.diagonal()
.projection(function (d) {
"use strict";
return [d.y, d.x];
});
function find(text) {
"use strict";
var i;
for (i = 0; i < Nodes.length; i += 1) {
if (Nodes[i].name === text) {
return Nodes[i];
}
}
return null;
}
function mouse_action(val, stat, direction) {
"use strict";
d3.select("#" + val.id).classed("active", stat);
links.forEach(function (d) {
if (direction == "root") {
if (d.source.id === val.id) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
if (d.target.lvl < val.lvl)
mouse_action(d.target, stat, "left");
else if (d.target.lvl > val.lvl)
mouse_action(d.target, stat, "right");
}
if (d.target.id === val.id) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
if (direction == "root") {
if(d.source.lvl < val.lvl)
mouse_action(d.source, stat, "left");
else if (d.source.lvl > val.lvl)
mouse_action(d.source, stat, "right");
}
}
}else if (direction == "left") {
if (d.source.id === val.id && d.target.lvl < val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl < val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}else if (direction == "right") {
if (d.source.id === val.id && d.target.lvl > val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl > val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}
});
}
function unvisite_links() {
"use strict";
links.forEach(function (d) {
d.visited = false;
});
}
function renderRelationshipGraph(data) {
"use strict";
var count = [];
data.Nodes.forEach(function (d) {
count[d.lvl] = 0;
});
lvlCount = count.length;
data.Nodes.forEach(function (d, i) {
d.x = margin.left + d.lvl * (boxWidth + gap.width);
d.y = margin.top + (boxHeight + gap.height) * count[d.lvl];
d.id = "n" + i;
count[d.lvl] += 1;
Nodes.push(d);
});
data.links.forEach(function (d) {
links.push({
source: find(d.source),
target: find(d.target),
id: "l" + find(d.source).id + find(d.target).id
});
});
unvisite_links();
svg.append("g")
.attr("class", "nodes");
var node = svg.select(".nodes")
.selectAll("g")
.data(Nodes)
.enter()
.append("g")
.attr("class", "unit");
node.append("rect")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.attr("id", function (d) { return d.id; })
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "node")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function () {
mouse_action(d3.select(this).datum(), true, "root");
unvisite_links();
})
.on("mouseout", function () {
mouse_action(d3.select(this).datum(), false, "root");
unvisite_links();
});
// Référence cadastrale
node.append("text")
.attr("class", "label")
.attr("x", function (d) { return d.x + 14; })
.attr("y", function (d) { return d.y + 18; })
.text(function (d) { return d.name; });
// Date de modification
node.append("text")
.attr("class", "label_date")
.attr("x", function (d) { return d.x + 43; })
.attr("y", function (d) { return d.y + 30; })
.text(function (d) {return d.date_modification; });
links.forEach(function (li) {
svg.append("path", "g")
.attr("class", "link")
.attr("id", li.id)
.attr("d", function () {
var oTarget = {
x: li.target.y + 0.5 * boxHeight,
y: li.target.x
};
var oSource = {
x: li.source.y + 0.5 * boxHeight,
y: li.source.x
};
if (oSource.y < oTarget.y) {
oSource.y += boxWidth;
} else {
oTarget.y += boxWidth;
}
return diagonal({
source: oSource,
target: oTarget
});
});
});
}
svg = d3.select("#tree").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
renderRelationshipGraph(data);
</script>
</body>
</html>
Mixed files
<html>
<head>
<meta charset="utf-8">
<title>My Map</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.0/spin.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2/leaflet.spin.js"></script>
<script type="text/javascript" src="https://raw.githubusercontent.com/Turbo87/leaflet-sidebar/master/src/L.Control.Sidebar.js"></script>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.6.0/leaflet.css">
<link rel="stylesheet" href="https://raw.githubusercontent.com/Turbo87/leaflet-sidebar/master/src/L.Control.Sidebar.css">
<style media="screen">
#map {width:100%; height:100%;}
rect {
fill: #66bfff;
cursor: pointer;
}
.active {
fill: orange;
stroke: orange;
}
.activelink {
fill: none;
stroke: orange;
stroke-width: 2.5px;
}
.label {
fill: white;
font-family: sans-serif;
pointer-events: none;
font-size: 16px;
}
.label_date {
fill: white;
font-family: sans-serif;
pointer-events: none;
font-style: italic;
font-size: 12px;
}
.link {
fill: none;
stroke: #66bfff;
stroke-width: 2.5px;
}
</style>
</head>
<body>
<!- Graph -->
<script>
var width = 1000,
height = 800,
boxWidth = 150,
boxHeight = 35,
gap = {
width: 50,
height: 12
},
margin = {
top: 16,
right: 16,
bottom: 16,
left: 16
},
svg;
// Layout
var Nodes = [];
var links = [];
var lvlCount = 0;
var diagonal = d3.svg.diagonal()
.projection(function (d) {
"use strict";
return [d.y, d.x];
});
function find(text) {
"use strict";
var i;
for (i = 0; i < Nodes.length; i += 1) {
if (Nodes[i].name === text) {
return Nodes[i];
}
}
return null;
}
function mouse_action(val, stat, direction) {
"use strict";
d3.select("#" + val.id).classed("active", stat);
links.forEach(function (d) {
if (direction == "root") {
if (d.source.id === val.id) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
if (d.target.lvl < val.lvl)
mouse_action(d.target, stat, "left");
else if (d.target.lvl > val.lvl)
mouse_action(d.target, stat, "right");
}
if (d.target.id === val.id) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
if (direction == "root") {
if(d.source.lvl < val.lvl)
mouse_action(d.source, stat, "left");
else if (d.source.lvl > val.lvl)
mouse_action(d.source, stat, "right");
}
}
}else if (direction == "left") {
if (d.source.id === val.id && d.target.lvl < val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl < val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}else if (direction == "right") {
if (d.source.id === val.id && d.target.lvl > val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl > val.lvl) {
d3.select("#" + d.id).classed("activelink", stat); // change link color
d3.select("#" + d.id).classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}
});
}
function unvisite_links() {
"use strict";
links.forEach(function (d) {
d.visited = false;
});
}
function renderRelationshipGraph(data) {
"use strict";
var count = [];
data.Nodes.forEach(function (d) {
count[d.lvl] = 0;
});
lvlCount = count.length;
data.Nodes.forEach(function (d, i) {
d.x = margin.left + d.lvl * (boxWidth + gap.width);
d.y = margin.top + (boxHeight + gap.height) * count[d.lvl];
d.id = "n" + i;
count[d.lvl] += 1;
Nodes.push(d);
});
data.links.forEach(function (d) {
links.push({
source: find(d.source),
target: find(d.target),
id: "l" + find(d.source).id + find(d.target).id
});
});
unvisite_links();
svg.append("g")
.attr("class", "nodes");
var node = svg.select(".nodes")
.selectAll("g")
.data(Nodes)
.enter()
.append("g")
.attr("class", "unit");
node.append("rect")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.attr("id", function (d) { return d.id; })
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "node")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function () {
mouse_action(d3.select(this).datum(), true, "root");
unvisite_links();
})
.on("mouseout", function () {
mouse_action(d3.select(this).datum(), false, "root");
unvisite_links();
});
// Référence cadastrale
node.append("text")
.attr("class", "label")
.attr("x", function (d) { return d.x + 14; })
.attr("y", function (d) { return d.y + 18; })
.text(function (d) { return d.name; });
// Date de modification
node.append("text")
.attr("class", "label_date")
.attr("x", function (d) { return d.x + 43; })
.attr("y", function (d) { return d.y + 30; })
.text(function (d) {return d.date_modification; });
links.forEach(function (li) {
svg.append("path", "g")
.attr("class", "link")
.attr("id", li.id)
.attr("d", function () {
var oTarget = {
x: li.target.y + 0.5 * boxHeight,
y: li.target.x
};
var oSource = {
x: li.source.y + 0.5 * boxHeight,
y: li.source.x
};
if (oSource.y < oTarget.y) {
oSource.y += boxWidth;
} else {
oTarget.y += boxWidth;
}
return diagonal({
source: oSource,
target: oTarget
});
});
});
}
svg = d3.select("#tree").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
// Active in info.update
//renderRelationshipGraph(data);
</script>
<!-- Sidebar -->
<div id="sidebar"><h1>Informations</h1>
<div class="container">
<div class="center-objs">
<div id="new-parent"></div>
<!-- Graph here -->
<div id="tree"></div>
</div>
</div>
</div>
<!-- Map -->
<div id="map"></div>
<script type="text/javascript">
var map = L.map('map').setView([43.446577,6.079717], 9);
// Baselayer
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var esri = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
var google = L.tileLayer('http://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}');
var baseLayers = {
"Carte OSM": osm,
"Photo ESRI": esri,
"Google Hybrid": google
};
L.control.layers(baseLayers).addTo(map);
// Style
function styleBasique (feature) {
return {
color: '#5f5f5f',
fillOpacity: 0
};
}
// Listeners
function highlightFeature(cadastre) {
var cadastreSurvol = cadastre.target;
cadastreSurvol.setStyle({
color: '#fc0000'
});
if (!L.Browser.ie && !L.Browser.opera) {
cadastreSurvol.bringToFront(cadastre.target);
}
info.update(cadastreSurvol.feature.properties);
}
function resetHighlight(e) {
cadastre.resetStyle(e.target);
info.update();
}
// Over
function onEachFeatureSurvol(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight
});
}
// Infos
var info = L.control({position: 'topleft'});
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
if (props) {
this._div.innerHTML = props.reference_cadastrale;
if (props.graph) {
eval(props.graph);
renderRelationshipGraph(data);
document.getElementById('tree').style.display = 'block';
}
else {
document.getElementById('tree').style.display = 'none';
}
}
else {
document.getElementById('tree').style.display = 'none';
this._div.innerHTML = '<i>Move...</i>';
}
};
info.addTo(map);
// Container for sidebar
var htmlObject = info.getContainer();
var a = document.getElementById('new-parent');
function setParent(el, newParent) {
newParent.appendChild(el);
}
setParent(htmlObject, a);
// Sidebar
var sidebar = L.control.sidebar('sidebar', {
closeButton: true,
position: 'left'
});
map.addControl(sidebar);
// Layer
// JSON
var dataurl = 'https://carto.cocktaildata.fr/py/parcelles.data.json';
// Creation
var cadastre = L.geoJson(false, {
//onEachFeature: onEachFeature,
onEachFeature: onEachFeatureSurvol,
style: styleBasique
}).addTo(map);
// Spinner
cadastre.fire('data:loading');
// Loading data
$.getJSON(dataurl, function (data) {
// Stop spinner
cadastre.fire('data:loaded');
// Add data
cadastre.addData(data);
// Zoom
map.fitBounds(cadastre.getBounds());
// Sidebar on clic
cadastre.on('click', function () {
sidebar.toggle();
});
});
</script>
</body>
</html>
graph.html
is complete independent page. How/where do you display it? I don't see any<iframe>
tag inmap.html
. – TomazicM Jul 30 '20 at 21:55<embed>
withgraph.html
, it works with static data but how to dynamically setvar data
? I think I must mixmap.html
andgraph.html
. – GeoGyro Jul 31 '20 at 6:55