Built with blockbuilder.org Credit for the genealogical research goes entirely to my brother. Credit for learning D3 goes to the incredible community. Next step is to clean up the code, and learn some more JavaScript.
xxxxxxxxxx
<html lang="en">
<!-- One-page version. Code still needs a lot of cleaning up. -->
<head>
<meta charset="utf-8">
<title>Marks/Arnold Family Tree</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
font-family: Verdana, Tahoma, sans-serif;
font-size: 11pt;
}
#container {
width: 960px;
height: 600px;
margin: 0 auto;
position: relative;
border-left: 1px solid gray;
border-bottom: 1px solid gray;
}
#map {
width: 740px;
float: left;
}
#legend {
float: right;
width: 200px;
height: 360px;
font-size: 10pt;
text-align: center;
padding: 25px 10px;
background: lightgray;
border: 0px;
border-radius: 8px;
}
h1 {
font-size: 36pt;
color: gold;
text-shadow: 5px 5px black;
text-align: right;
position: absolute;
top: 445px;
left: 480px;
}
h5 {
font-size; 8pt;
font-style: italic;
position: absolute;
top: 435px;
left: 850px;
}
#cities {
font-size: 10pt;
font-weight: bold;
}
div.tooltip {
position: absolute;
top: 70px;
left: 160px;
text-align: center;
width: 275px;
height: 130px;
padding: 2px;
font: 16px sans-serif;
font-weight: bold;
background: lightgray;
opacity: .9;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
#m {
background-color: red;
}
#a {
background-color: blue;
}
#r {
background-color: orange;
}
#f {
background-color: indigo;
color: gray;
}
#s {
background-color: yellow;
}
#sb {
background-color: violet;
}
#c {
background-color: orangeRed;
}
#w {
background-color: royalBlue;
}
.lineM {
fill: none;
stroke: red;
stroke-width: 2px;
}
</style>
</head>
<body>
<div id="container">
<h1 id="hede">england's green<br/>
and pleasant land</h1>
<h5 id ="subhede">for my family</h5>
<div id="map">
<script type="text/javascript">
//Width and height
var w = 740;
var h = 600;
// create tooltip div
var div = d3.select("#map").append("div")
.attr("class", "tooltip")
.html( "<br/>" + "Ancestor Facts");
// Define map projection
var projection = d3.geoAlbers()
// Somerset to London
// .scale([10000])
.rotate( [0.2,0] )
.center([-0.8, 51.5])
.translate( [w/2, h/2] );
//Define path generator
var path = d3.geoPath()
.projection(projection);
//Create SVG element
var svg1 = d3.select("#map")
.append("svg")
.attr("width", w)
.attr("height", h);
// define what to do when panning or zooming
var zooming = function(d) {
// new offset array
var offset = [d3.event.transform.x, d3.event.transform.y];
// calculate new scale
var newScale = d3.event.transform.k * 1300; // probably need to change this
// update projection with new offset and scale
projection.translate(offset)
.scale(newScale);
// update all paths and circles
svg1.selectAll("path")
.attr("d", path);
svg1.selectAll("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
});
svg1.selectAll("text") // city labels update
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("dx", function(d) {
if (d.city === "London")
return "-0.5em";
else
return ".5em";
})
.attr("text-anchor", function(d) {
if (d.city === "London")
return "end";
else
return "start";
})
.text(function(d) {
return d.city;
});
var branchLine = d3.line() // branchLines update
.x(function(d) { return projection([d.lon, d.lat])[0]; })
.y(function(d) { return projection([d.lon, d.lat])[1]; });
svg1.selectAll("path.lineM")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
};
// define zoom behavior
var zoom = d3.zoom()
.on("zoom", zooming);
// create container in which all zoom-able elements will live
var mapHolder = svg1.append("g")
.attr("id", "mapHolder")
.call(zoom) //Bind the zoom behavior
.call(zoom.transform, d3.zoomIdentity //Then apply the initial transform
.translate(w/2, h/2)
.scale(8)
.translate(0, 0));
// Create a new, invisible background rect to catch zoom events
mapHolder.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", w)
.attr("height", h)
.attr("opacity", 0);
//Load in GeoJSON data
d3.json("GBR_adm2-1.json", function(json) {
//Bind data and create one path per GeoJSON feature
mapHolder.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", function(d) {
if (d.properties.NAME_1 === "Wales" || d.properties.NAME_1 === "Scotland")
return "gray";
else if (d.properties.NAME_1 === "Northern Ireland")
return "white";
else
return "green";
})
.attr("stroke", "white")
.attr("stroke-width", "1");
// load in cities data
d3.csv("citiesGBR1.csv", function(data) {
mapHolder.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", function(d) {
if (d.city === "WALES" || d.city === "SCOTLAND") // ugly, but I didn't want a separate country file with just three elements
return 0;
else
return 3;
});
// add city names
mapHolder.selectAll("cities")
.data(data)
.enter()
.append("text")
.attr("id", "cities")
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("dx", function(d) {
if (d.city === "London")
return "-0.5em";
else
return ".5em";
})
.attr("text-anchor", function(d) {
if (d.city === "London")
return "end";
else
return "start";
})
.text(function(d) {
return d.city;
});
}); // end cities.csv
// load in family data
d3.csv("famTree1.csv", function(data) {
// filter for family branches
var dataM = data.filter(function(d) {return (d.surname === "Marks" || d.surname === "Markes"); });
var dataA = data.filter(function(d) {return (d.surname === "Arnold"); });
var dataR = data.filter(function(d) {return (d.surname === "Rowlinson" || d.surname === "Rowlingson" || d.surname === "Rollingson" || d.surname === "Rollinson"); });
var dataF = data.filter(function(d) {return (d.surname === "Fulcher"); });
var dataS = data.filter(function(d) {return (d.surname === "Saveall"); });
var dataSB = data.filter(function(d) {return (d.surname === "Sooby"); });
var dataC = data.filter(function(d) {return (d.surname === "Cornell"); });
var dataW = data.filter(function(d) {return (d.surname === "Walker"); });
//define line
var branchLine = d3.line()
.x(function(d) { return projection([d.lon, d.lat])[0]; })
.y(function(d) { return projection([d.lon, d.lat])[1]; });
// create paths for family branches
mapHolder.selectAll("pathM")
.data([dataM])
.enter()
.append("path")
.attr("class", "lineM")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataA])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "blue")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataR])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "orange")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataF])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "indigo")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataS])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "yellow")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataSB])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "violet")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataC])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "orangeRed")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
mapHolder.selectAll("pathM")
.data([dataW])
.enter()
.append("path")
.attr("class", "lineM")
.style("stroke", "royalBlue")
.attr("d", branchLine)
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
});
// create circles for locations
mapHolder.selectAll("dots")
.data(data)
.enter()
.append("circle")
.attr("r", 5)
.attr("stroke", "white")
.attr("stroke-width", 1)
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("fill", function(d) {
if (d.surname === "Marks" || d.surname === "Markes")
return "red";
else if (d.surname === "Arnold")
return "blue";
else if (d.surname === "Rowlinson" || d.surname === "Rowlingson" || d.surname === "Rollingson" || d.surname === "Rollinson")
return "orange";
else if (d.surname === "Fulcher")
return "indigo";
else if (d.surname === "Saveall")
return "yellow";
else if (d.surname === "Cornell")
return "orangeRed";
else if (d.surname === "Sooby")
return "violet";
else (d.surname === "Walker")
return "royalBlue";
})
.on("mouseover", function(d) {
div.html("<br/>" + d.gen + "<br/>" + d.name1 + " " + d.surname
+ "<br/>" + d.birthPlace + ", " + d.county + "<br/>" + d.birthYr +
" - " + d.death + "<br/>" + "Spouse: " + d.spouse)
})
.on("mouseout", function(d) {
div.html("<br/>" + "Ancestor Facts")
});
}); //end famTree.csv
}); // end GeoJSON
</script>
</div> <!-- end of div map -->
<div id="legend">
<h3>Legend</h3>
<span id='m'>Marks</span><br/>
<span id='r'>Rowlinson</span><br/>
<span id='s'>Saveall</span><br/>
<span id='c'>Cornell</span><br/><br/>
<span id='a'>Arnold</span><br/>
<span id='f'>Fulcher</span><br/>
<span id='sb'>Sooby</span><br/>
<span id='w'>Walker</span><br/><br/>
<p>To see more information about an ancestor, put your cursor over one of the colored circles.</p>
<p>Use your mouse wheel and left button, to zoom and drag the map.</p>
</div>
</div> <!-- end of div container -->
</body>
</html>
https://d3js.org/d3.v4.min.js