Skip to content

Instantly share code, notes, and snippets.

@stevenwmarks
Last active January 4, 2018 04:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevenwmarks/ec701718dfe74fbee433d0d76f67f819 to your computer and use it in GitHub Desktop.
Save stevenwmarks/ec701718dfe74fbee433d0d76f67f819 to your computer and use it in GitHub Desktop.
Family Tree on Map
license: mit
border: no
height: 600
scrolling: yes

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.

city lat lon
London 51.51 -0.132
Bath 51.382 -2.368
Portsmouth 50.86 -1.09
Oxford 51.752 -1.258
WALES 52.13 -3.783
SCOTLAND 56.491 -4.203
Canterbury 51.28 1.079
Cambridge 52.205 0.122
Exeter 50.718 -3.534
gen surname name1 birthYr death birthPlace county spouse lat lon
mother Arnold Mavis Lillian 1920 1978 West Ham Essex Jack Charles Marks 51.558 0.015
grandfather Arnold Walter 1894 1967 Wakefield Mews London Lillian Mary Fulcher 51.527 -0.124
great-grandfather Arnold William Henry 1862 1897 Islington London Eliza Sooby 51.547 0.106
g-g-grandfather Arnold Edward 1837 1900 Islington London Mary Thomas 51.547 0.166
g-g-g-grandfather Arnold John 1803 unknown Chilham Kent Ann Allard 51.244 0.966
father Marks Jack Charles 1915 1998 Woodford Essex Mavis Lillian Arnold 51.592 0.092
grandfather Marks William Charles 1878 1956 Woodford Essex Jessie Rowlinson 51.592 0.042
great-grandfather Marks Henry Charles 1847 1878 Haggerston Middlesex Elizabeth Ann Saveall 51.522 -0.039
g-g-grandfather Marks Benjamin 1819 1891 Frome Somerset Elizabeth Watts 51.229 -2.318
g-g-g-grandfather Marks Edward 1781 1825 North Petherton Somerset Rachel [unknown surname] 51.042 -2.969
g-g-g-g-grandfather Marks Edward 1742 unknown North Petherton Somerset Joan Parish 51.042 -3.019
g-g-g-g-g-grandfather Marks Thomas 1702 unknown North Petherton Somerset Joane Higgins 51.092 -2.969
g-g-g-g-g-g-grandfather Marks Edward 1673 unknown North Petherton Somerset Jane Brain 51.092 -3.019
g-g-g-g-g-g-g-grandfather Markes John 1647 unknown North Petherton Somerset Lucretia Mitchell 51.092 -3.069
grandmother Fulcher Lillian Mary 1900 1980 Southend Essex Walter Arnold 51.546 0.708
great-grandfather Fulcher Frank 1874 1966 Herne Hill London Ada Louisa Walker 51.455 0.097
g-g-grandfather Fulcher Henry 1835 1893 Newbourn Suffolk Charlotte Childs 52.04 1.314
g-g-g-grandfather Fulcher James 1797 1855 Newbourn Suffolk Mary Little 52.04 1.264
grandmother Rowlinson Jessie 1881 1955 Great Wratting Suffolk William Charles Marks 52.059 0.461
great-grandfather Rowlinson William 1857 1926 Great Wratting Suffolk Ellen Cornell 52.059 0.411
g-g-grandfather Rowlingson Richard 1829 1900 Great Wratting Suffolk Elizabeth Cornwell 52.109 0.461
g-g-g-grandfather Rollingson Richard 1791 1858 Great Wratting Suffolk Martha Browne 52.109 0.411
g-g-g-g-grandfather Rollinson Richard 1761 unknown Great Wratting Suffolk Hannah Smith 52.109 0.361
great-grandmother Saveall Elizabeth Ann 1846 1922 Wanstead Essex Henry Charles Marks 51.567 0.033
g-g-grandfather Saveall William 1821 1888 Woodford Bridge Essex Sarah [unknown surname] 51.606 0.056
great-grandmother Cornell Ellen 1857 unknown Great Wratting Suffolk William Rowlinson 52.059 0.361
g-g-grandfather Cornell Samuel 1826 1904 Barnardiston Suffolk Elizabeth Coe 52.112 0.499
g-g-g-grandfather Cornell Samuel 1800 1870 Barnardiston Suffolk Ann Hammond 52.112 0.459
g-g-g-g-grandfather Cornell Daniel 1771 1843 Haverhill Suffolk Mary Mansfield 52.081 0.445
g-g-g-g-g-grandfather Cornell Samuel 1740 1788 Little Wratting Suffolk Mary Filney 52.098 0.46
g-g-g-g-g-g-grandfather Cornell John 1703 1761 Withersfield Suffolk Ann Gowers 52.1 0.403
great-grandmother Sooby Eliza 1867 1898 St. Pancras London William Henry Arnold 51.531 0.126
g-g-grandfather Sooby John 1821 1908 St. Pancras London Caroline Elizabeth Newton 51.531 0.076
great-grandmother Walker Ada Louisa 1873 1935 Old Ford Middlesex Frank Fulcher 51.534 -0.026
g-g-grandfather Walker Joseph Ebenezer 1828 1919 Bethnal Green London Mary Daycock 51.527 0.067
g-g-g-grandfather Walker John 1802 1867 Bethnal Green London Eliza Taylor 51.527 0.017
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment