Family tree Layout
Overview
In family trees (also known as a pedigree charts), there are individual nodes symbolizing each person and family nodes that link these individuals together. Connections are established through the family nodes. The structure of the graph is organized into stratified layers, each representing a different generation.
This algorithm employs a multi-stage approach to organize genealogical trees. While it shares similarities with classic tree layouts, its distinctive feature lies in the representation of a child linked to two parents instead of just one. In essence, the ‘parent node’ represents both parents of the child. This unique aspect of the layout is not readily replicated by standard algorithms, making it a singular addition to the suite of layout designs.
Tip: the isometric drawing style can help to augment the family tree layout by representing time as depth. That is, the depth or z-direction can be seen as going backwards in genealogic time. ### Features
In the intricate design of family tree layouts, the spatial separation between nodes that represent members of the same generation can be meticulously calibrated to ensure clarity and visual balance. Similarly, the gaps between individual nodes and those representing family units are adjustable to achieve an optimal arrangement.
Furthermore, nodes corresponding to the same generational tier can be methodically aligned along three distinct vertical axes—top, center, or bottom—providing a harmonious and organized depiction of the family hierarchy.
The positioning of ‘FAMILY’ nodes, which serve as pivotal junctions connecting partners, is customizable. This allows for precise placement that reflects the relational dynamics within the family structure. Additionally, the spatial configuration of these nodes in relation to parent figures can be determined independently, affording a tailored visualization of ancestral connections. This level of detail in customization facilitates a clear and coherent representation of familial relationships across the tree’s generational spectrum. ### Layout
Node Classification:
- To determine whether a node represents an individual or a partnership, an
IDataProvider
must be registered with the keyFAMILY_TYPE_DP_KEY
. - If this
IDataProvider
is missing, anArgumentError
will be thrown.
- To determine whether a node represents an individual or a partnership, an
IDataProvider Values:
- The
IDataProvider
provides the following values:MALE
: Represents a node corresponding to a male individual.FEMALE
: Represents a node corresponding to a female individual.FAMILY
: Represents a node corresponding to a family.- All other values will be interpreted as if the node represents an individual.
- The
Family Node Constraints:
- A
FAMILY
node links all members of a family. - It can only have two incoming edges from the parents.
- Two nodes of the same type (families or individuals) cannot be directly connected.
- If the input graph violates these specifications, an
InvalidGraphStructureError
exception will be thrown.
- A
Layout Calculation:
- The layout process involves two steps:
- The inner layouter arranges families in a compact manner.
- The top layout algorithm handles the relations between these “family groups,” their children, and other families.
- The layout process involves two steps:
Business Domains
Genealogy Research:
- The most common use of family tree layouts is for genealogical research. People create family trees to trace their ancestry, understand family connections, and discover their heritage.
- Researchers can input data such as names, birth dates, marriage dates, and relationships to construct a comprehensive family tree.
Medical History and Genetic Inheritance:
- Family trees are valuable for understanding medical histories and genetic inheritance patterns.
- By mapping out family members and their health conditions, individuals can identify potential genetic risks or hereditary diseases.
- Medical professionals also use family trees to study patterns of diseases within families.
Legal and Estate Planning:
- Lawyers and estate planners use family trees to determine legal relationships, inheritance rights, and beneficiaries.
- When creating wills or trusts, understanding family connections is crucial.
Social and Cultural Context:
- Family trees provide context for social and cultural studies.
- Researchers analyze family structures, migration patterns, and societal norms across different time periods and regions.
Historical Documentation:
- Family trees serve as historical records, preserving information about past generations.
- They help document lineage, marriages, and significant events within families.
Visual Representation of Relationships:
- Family trees visually represent complex relationships.
- They show parent-child connections, sibling relationships, and extended family ties.
Educational Tools:
- Teachers use family trees in classrooms to teach students about family history, genetics, and cultural diversity.
- Students can create their own family trees as part of educational projects.
Fiction and Literature:
- Writers often incorporate family trees into novels, plays, and other literary works.
- These trees enhance character development and plotlines.
Regarding data types used in family trees, it typically includes:
- Names of family members
- Birth and death dates
- Marriage dates
- Relationships (parent-child, sibling, spouse)
- Additional details like occupations, locations, and historical context.
Technical Details
The way you define nodes and link them together is crucial for this layout. For instance, one can’t have be a child of oneself. In addition:
- there are three node types which have to be defined via the
FamilyTreeLayoutData
(see the playground below) - the family node defines the hub through which parents and children are connected
- edges going into the family node are (literal) parents and the children are defined via edges from the family node to the children
- if a child is on its own married with children, you simply create a new family node.
The playground makes this logic clear and if you have some (JSON) data this can be easily implemented via the GraphBuilder
. The yFiles family tree demo has an example of this technique.
Playgrounds
▸ Basic family with three children, one of which married with child.
The family tree layout requires a particular data logic and this snippet shows how you can use a ‘knot’ to create families:
const familyKnot = new ShapeNodeStyle({
fill: 'rgb(170, 170, 170)',
stroke: 'white',
shape: ShapeNodeShape.ELLIPSE
})const female = new ShapeNodeStyle({
fill: 'pink',
stroke: 'white',
shape: ShapeNodeShape.ELLIPSE
})const male = new ShapeNodeStyle({
fill: 'lightblue',
stroke: 'white',
shape: ShapeNodeShape.ELLIPSE
})
.nodeDefaults.labels.style=new DefaultLabelStyle({
graphbackgroundFill:"transparent"
})const nodes = graph.nodes.toArray()
function createPerson(d){
const n = graph.createNode();
.tag = d;
n.addLabel(n, d.id);
graph.setStyle(n, d.type==="MALE"?male:female)
graphreturn n
}
function createKnot(d){
const n = graph.createNode();
.tag = d;
n.setStyle(n, familyKnot)
graphreturn n
}const john = createPerson({ id: 'John', type: 'MALE' })
const mary = createPerson({ id: 'Mary', type: 'FEMALE' })
const anna = createPerson({ id: 'Anna', type: 'FEMALE' })
const child1 = createPerson( { id: 'Child1', type: 'FEMALE' })
const frank = createPerson( { id: 'Frank', type: 'MALE' })
const bill = createPerson( { id: 'Bill', type: 'MALE' })
const frankjr = createPerson( { id: 'Frank Jr.', type: 'MALE' })
const fam1 = createKnot( { id: 'Family1', type: 'FAMILY' })
const fam2 = createKnot( { id: 'Family2', type: 'FAMILY' })
.createEdge(mary, fam1)
graph.createEdge(john, fam1)
graph
.createEdge(fam1, child1)
graph.createEdge(fam1, frank)
graph.createEdge(fam1, bill)
graph
.createEdge(frank, fam2)
graph.createEdge(anna, fam2)
graph.createEdge( fam2, frankjr)
graph
const familyTreeLayout = new FamilyTreeLayout({
familyNodesAlwaysBelow:true,
layoutOrientation:"top-to-bottom",
partnerlessBelow:true,
spacingBetweenFamilyMembers:100,
})const familyTreeLayoutData = new FamilyTreeLayoutData({
familyTypes: (node: INode) => {
switch (node.tag.type) {
case 'MALE':
return FamilyType.MALE
case 'FEMALE':
return FamilyType.FEMALE
case 'FAMILY':
return FamilyType.FAMILY
default:
return null
}
}
})
await graphComponent.morphLayout(familyTreeLayout, '1s', familyTreeLayoutData)