Geographic Information Systems Asked on December 20, 2020
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>
You have to combine both pages into one page and then call graph rendering function renderRelationshipGraph(data)
at the right place with the right data.
First take <style>
section from graph.html
and include it in maps.html
.
Then take <script>
section from graph.html
, leaving out renderRelationshipGraph(data)
call, and include it in maps.html
before existing code.
Then you need to create a HTML element that will receive graph inside your side bar (with id='tree'
):
<div id="sidebar">
<h1>Informations</h1>
<div class="container">
<div class="center-objs">
<div id="new-parent">
</div>
<div id="tree"></div>
</div>
</div>
</div>
All you have to do now is to modify info.update
function to include call to renderRelationshipGraph(data)
function. Since actual data for graph in props.graph
is in the form of JS variable data
definition, you have to eval
it before using data
:
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>';
}
};
Correct answer by TomazicM on December 20, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP