Data Modeling and Visualization for a 16th-Century French Royal Engraving Collection

    This article shows how a historical catalogue of French monarchs, based on engravings associated with Nicolas de Larmessin, can be converted into a structured dataset for querying, verification, and extension. It covers entity modeling for persons, reigns, dynasties, kingdoms, portraits, and evidence; techniques for handling reign intervals, overlapping rulers, conflicting dates, uncertain chronology, and data quality; API design, analytical queries, visualization patterns, and a phased delivery plan. It is intended for developers and data engineers working on historical, genealogical, or cultural-heritage data projects.

    1. Source Characteristics and Requirements

    The engraving set is based on engravings by the French engraver Nicolas de Larmessin. The portraits run from Faramund, the legendary first king of the Franks, to Louis XIV of the Bourbon dynasty, covering most French monarchs in between as well as several royal family members. The original catalogue lists figures by lineage and uses ordinal numbers and short annotations to connect the Merovingian, Carolingian, and Capetian dynasties across several centuries.

    For readers, this list has an obvious limitation: it does not provide consistent answers to questions such as “How many years did a particular king reign?”, “Which monarchs were ruling at the same time?”, or “Who was whose father?”

    This article does not focus on art history. Instead, it is aimed at developers and data engineering practitioners. Using this engraving set as a prototype, it explains how a historical list of persons can be transformed into a structured dataset. The article covers:

    • designing a data model based on the characteristics of the source material;
    • handling reign intervals, multi-source conflicts, and date annotation;
    • designing query APIs and visualization options;
    • establishing data-quality rules and a delivery path.

    The goal is to turn “portraits plus annotations” into a queryable, verifiable, and extensible knowledge asset. The SQL and JSON examples in this article are illustrative, and fields can be adapted to the needs of a specific project.

    1.1 Three characteristics of the source material

    The engraving collection can be abstracted as a composite data source consisting of “persons + relationships + images”:

    • The person list is semi-structured. Ordinal numbers imply an ordering, while annotations mix historical facts, evaluations, and speculation.
    • The relationship network is dense. Father-son relations, sibling relations, partition, reunification, deposition, and marriage alliances are intertwined across several centuries.
    • Images are unique and carry date information. Each engraving corresponds to one person, and the production date of the engraving itself is also important metadata.

    If the list is simply imported into a database, the result is little more than a searchable name list, with limited query capability and limited knowledge value. The requirements should therefore be refined into five categories:

    1. Retrieval by dimension: dynasty, period, identity status, and reign duration.
    2. Interval-based questions: which monarchs had overlapping reigns; when the Frankish kingdom was unified and when it split again.
    3. Kinship and succession reconstruction: whether succession chains are continuous; how many sons a particular monarch had.
    4. Traceable evidence: every conclusion should be traceable to a specific engraving or historical source.
    5. Extensibility: royal family members, queens, regents, and capital cities can later be added without restructuring existing tables.

    1.2 Five modeling challenges

    First, ordinal position does not equal chronology. In the list, Childeric I appears before the Merovingian dynasty heading. He was the father of Clovis I and an ancestor of the Merovingian family that later founded the dynasty, so the catalogue effectively treats him as a “pre-dynastic figure.” However, what most people refer to as the “Merovingian dynasty” often includes Childeric I. The Carolingian section is more complicated: Charles the Fat was originally king of East Francia, while Eudes and Raoul were not Carolingian by blood. Dynasty classification therefore directly affects results such as “count monarchs by dynasty.” Classification rules should be defined separately and stored as metadata.

    Second, one name may correspond to multiple identities. Both Louis I and Charles II appear in the Carolingian section, but they are not simply two “Louis” or two “Charles” entries within the same dynasty. Louis I is Louis the Pious, counted as the first Louis in the traditional French sequence. Charles II is Charles the Bald; he is Charles II in the West Frankish sequence, but he is often counted as Charles I when all Carolingian rulers are considered together. Charlemagne is also sometimes referred to as Charles I in certain numbering systems. If a localized name is treated as the primary key, gaps or conflicts may appear between entries such as Louis I and Louis V. Any name with an ordinal should therefore be split into two fields: a base name and an ordinal. A numbering-convention note should also be attached.

    Third, a reign interval is itself a modeling object. The source contains at least four forms of reign interval:

    • Chlothar I “reunited the kingdom” in 558 and, after his death in 561, the kingdom was divided again;
    • Chlothar II was initially limited to Neustria and only unified all of Francia in 613;
    • Louis III and Carloman II appear together as co-rulers;
    • Charles the Fat ruled across East Francia and West Francia, representing succession across kingdoms.

    There are also cases involving temporal adjacency, such as Clovis II preceding Chlothar III; confusable same names, such as Chlothar I, II, and III; and periods with excluded rulers, such as the predecessor of Dagobert III. These cannot be fully represented by a single “start year/end year” field.

    Fourth, “unification” and “division” are computable states. Terms such as “unified,” “divided,” and “inherited a kingdom” are essentially changes in the overall state of the Frankish kingdom over time. Once each person’s reign interval is stored accurately, periods of unification and division can be derived through interval-coverage calculations rather than maintained manually.

    Fifth, information quality varies. The source explicitly notes that Faramund is “very likely a fictional figure” and that records about Clodion are “still sparse.” The entry for Childeric I contains only one father-son relationship, and after Dagobert I many entries contain only names. The model must therefore allow “identity without dates” and use a confidence field to distinguish historical fact, inference, and legend.

    2. Data Model Design

    2.1 Core entities and relationships

    A hybrid approach is recommended: a relational database as the core, with a graph query layer, such as a property graph, for relationship exploration.

    • Entities: Person, Reign, Dynasty, Kingdom, Portrait, Source.
    • Relationships: reign, father/son, brother, succession, marriage, evidence.

    Making Reign a separate entity is the most important modeling decision. The reasons are:

    • the same person may reign multiple times in different kingdoms or under different titles;
    • multiple rulers may reign concurrently during the same period;
    • each reign can have its own evidence and confidence metadata.
    1. erDiagram
    2. PERSON ||--o{ REIGN : "holds"
    3. DYNASTY ||--o{ PERSON : "member_of"
    4. REIGN }o--|| KINGDOM : "rules"
    5. REIGN }o--o{ EVIDENCE : "supported_by"
    6. PORTRAIT ||--|| EVIDENCE : "visual_source"
    7. SOURCE ||--o{ EVIDENCE : "text_source"
    8. PERSON {
    9. string person_id PK
    10. string base_name "Base name, e.g. Chlothar"
    11. int ordinal "Ordinal, e.g. 1"
    12. string identity_status "attested / thin / legendary"
    13. float confidence "0 to 1"
    14. }
    15. REIGN {
    16. int reign_id PK
    17. string person_id FK
    18. string kingdom_code FK
    19. int start_date "YYYYMMDD"
    20. int end_date "YYYYMMDD"
    21. string span_type "sole / co_ruler / sub_king"
    22. string source_note "Original annotation"
    23. }
    24. KINGDOM {
    25. string kingdom_code PK
    26. string display_name "e.g. Soissons"
    27. string full_name "e.g. West Francia"
    28. }

    2.2 Person table: identity status and name decomposition

    1. -- identity_status: attested (supported by sources) / thin (sparse records) / legendary (legendary or fictional)
    2. CREATE TABLE person (
    3. person_id VARCHAR(32) PRIMARY KEY,
    4. base_name VARCHAR(64) NOT NULL, -- Base name, e.g. Chlothar
    5. ordinal SMALLINT, -- 1/2/3; leave NULL for figures such as Faramund
    6. display_name VARCHAR(96) NOT NULL, -- e.g. Chlothar I
    7. name_variants JSONB, -- Variants: {"la":"Chlothar I","en":"Chlothar I","alias":["Chlothar I"]}
    8. dynasty_code VARCHAR(32) NOT NULL, -- Classification rules are defined separately
    9. identity_status VARCHAR(16) NOT NULL CHECK (identity_status IN ('attested','thin','legendary')),
    10. confidence NUMERIC(3,2) NOT NULL DEFAULT 0.80,
    11. portrait_id VARCHAR(32), -- Linked entry in the portrait catalogue
    12. CONSTRAINT uq_person_name UNIQUE (base_name, ordinal)
    13. );

    Two design points are important:

    • base_name and ordinal are jointly unique, which fundamentally prevents duplicate entries such as “two Clovis I.”
    • name_variants stores alternate names in JSON, including Latin, English, and localized variants. Different translations may not be consistent, so a variant dictionary is also useful for synonym search.

    2.3 Reign table: one model for multiple interval types

    1. CREATE TABLE kingdom (
    2. kingdom_code VARCHAR(32) PRIMARY KEY, -- FRANCIA / PARIS / SOISSON / NEUST / AUST / WEST_FRANCIA / EAST_FRANCIA
    3. display_name VARCHAR(32) NOT NULL,
    4. full_name VARCHAR(64) NOT NULL, -- e.g. West Francia
    5. sort_order SMALLINT NOT NULL
    6. );
    7. CREATE TABLE reign (
    8. reign_id SERIAL PRIMARY KEY,
    9. person_id VARCHAR(32) NOT NULL REFERENCES person(person_id),
    10. kingdom_code VARCHAR(32) NOT NULL REFERENCES kingdom(kingdom_code),
    11. span_type VARCHAR(16) NOT NULL CHECK (span_type IN ('sole','co_ruler','sub_king')),
    12. -- Dates are stored as YYYYMMDD integers. Missing parts are padded with 0, e.g. 0558 or 0481 when only the year is known.
    13. start_date INT NOT NULL,
    14. end_date INT NOT NULL,
    15. seq_in_kingdom SMALLINT, -- Sequence within the same kingdom
    16. notes TEXT, -- Original annotations, preserved as-is
    17. source_ref VARCHAR(64), -- Evidence reference, linked to the evidence table
    18. CONSTRAINT ck_reign_range CHECK (start_date <= end_date)
    19. );
    20. CREATE INDEX idx_reign_kingdom_range ON reign (kingdom_code, start_date, end_date);
    21. CREATE INDEX idx_reign_person ON reign (person_id);

    The original entries can be mapped as follows:

    Source entry Person record Reign record
    Clovis I (reigned from 481, died in 511, kingdom divided among four sons) Clovis/1 FRANCIA, sole, 0481–0511
    Childebert I, inherited the Kingdom of Paris Childebert/1 PARIS, sub_king, 0511–0558
    Chlothar I, inherited the Kingdom of Soissons; reunited Francia in 558 Chlothar/1 SOISSON, sub_king, 0511–0558; FRANCIA, sole, 0558–0561
    Charles the Fat, briefly unified Francia Charles/3 under Carolingian numbering EAST_FRANCIA, sole, 0881–0887; FRANCIA, sole, 0884–0887
    Louis III and Carloman II Louis/3, Carloman/2 WEST_FRANCIA, co_ruler, 0879–0882; 0879–0884

    The dates in this table are approximate values added for illustration. Details such as the exact month in which Charles the Fat took control of West Francia, and the death year of Louis III, differ across sources. When building a production dataset, these should be checked item by item and their sources preserved.

    Two modeling decisions deserve explanation:

    • Charles the Fat has multiple relevant status points. He was king of East Francia, was crowned emperor in 881, and succeeded to the West Frankish throne in 884. He should not be recorded only as “King of West Francia.”
    • Entries without an explicit kingdom are not necessarily kingdom-agnostic. For example, Chlothar II was only king in Neustria before 613 and became sole king of all Francia only after 613. This information must be filled in during modeling; otherwise, overlap calculations may produce incorrect unified periods.

    2.4 Dual timeline: historical validity and record validity

    Historical data will continue to be corrected. A bitemporal modeling approach is useful:

    • Valid time: the historical period itself, represented by start_date and end_date.
    • Transaction time: the time when the record was written, revised, or retired in the database, represented by audit fields such as created_at, valid_from, and valid_to.

    With this approach, any query can be interpreted in two ways: “according to current understanding” or “according to the understanding at a particular point in time.” Revisions are not lost, and traceability and audit requirements can be satisfied.

    2.5 Kinship and unified periods

    Kinship should be stored in a separate table to avoid duplicating father-son and brother relations inside the person table.

    1. CREATE TABLE kinship (
    2. from_person VARCHAR(32) REFERENCES person(person_id),
    3. to_person VARCHAR(32) REFERENCES person(person_id),
    4. relation VARCHAR(16) NOT NULL CHECK (relation IN ('father','mother','son','brother')),
    5. PRIMARY KEY (from_person, to_person, relation)
    6. );

    “Unified periods” do not need to be maintained manually. They can be derived from reign intervals using SQL. The algorithm is based on a classic sweep-line approach: each reign interval is split into two events, “start +1” and “end -1.” After sorting events by date and accumulating them, the number of concurrent rulers at any point can be obtained. Continuous intervals where the count is exactly 1 correspond to periods with a single ruler.

    The following example treats end_date as an open interval. Each end event is shifted by adding 0101 to avoid incorrectly marking the death day itself as “already departed.”

    1. WITH events AS (
    2. SELECT start_date AS d, 1 AS delta, reign_id FROM reign
    3. WHERE kingdom_code = 'FRANCIA'
    4. UNION ALL
    5. SELECT end_date + 101, -1, reign_id FROM reign
    6. WHERE kingdom_code = 'FRANCIA'
    7. ),
    8. scan AS (
    9. SELECT
    10. d,
    11. SUM(delta) OVER (ORDER BY d, delta
    12. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS concurrent_kings,
    13. SUM(CASE WHEN delta = 1 THEN 1 ELSE 0 END) OVER (ORDER BY d, delta
    14. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS opens
    15. FROM events
    16. )
    17. SELECT
    18. d - 1 AS seg_start, -- Segment start: the day before the current event
    19. LEAD(d) OVER (ORDER BY d) - 1 AS seg_end, -- Segment end: the day before the next event
    20. concurrent_kings,
    21. opens -- 1 means a new ruler begins in this segment
    22. FROM scan
    23. WHERE concurrent_kings = 1; -- One ruler corresponds to a unified period

    The scan output can then be aggregated into continuous unified periods with Python:

    1. def merge_unified_periods(segments):
    2. """Merge single-point segments into continuous unified periods.
    3. segments: [(seg_start, seg_end, concurrent_kings, opens), ...]"""
    4. periods, current = [], None
    5. for start, end, concurrent, opens in segments:
    6. if concurrent != 1:
    7. current = None
    8. continue
    9. # opens == 1 means a new ruler acceded, so this is treated as a new unified segment even if it touches the previous one.
    10. if current and not opens and current[1] + 1 == start:
    11. current = (current[0], end)
    12. else:
    13. if current:
    14. periods.append(current)
    15. current = (start, end)
    16. if current:
    17. periods.append(current)
    18. return periods

    In this illustrative dataset, the timeline can be bounded by the accession of Clovis I and the death of Louis V. The approximate unified periods are as follows:

    Unified period Approximate interval Sole ruler(s) Reason for ending
    1 481–511 Clovis I Died; kingdom divided among four sons
    2 558–561 Chlothar I Died; kingdom divided among sons
    3 613–639 Chlothar II, then Dagobert I Dagobert I died
    4 639–657 Clovis II Died
    5 673–691 Theuderic III Died
    6 691–714 Clovis IV, Childebert III, Dagobert III Dagobert III died; period of civil conflict followed
    7 721–737 Theuderic IV Died; vacancy followed
    8 743–751 Childeric III Deposed
    9 751–768 Pepin Died; Francia divided between two sons
    10 771–840 Charlemagne, Louis I Louis I died
    11 843–855 Charles the Bald in West Francia Associated with the death of Lothair I
    12 861–863 Charles the Bald in West Francia Louis II moved south
    13 866–875 Charles the Bald in West Francia Louis II died
    14 884–887 Charles the Fat Deposed

    Minor timing details, such as the early death of Carloman II and the elevation of Robertian claimants, are not included here. In production data, they should be verified one by one.

    Unified periods can directly validate claims in the original catalogue because the scan logic itself provides evidence:

    • Periods 4 through 8 provide a precise definition of “reunification.” Chlothar II became sole king of all Francia in 613, and later periods of unity are continuations of that state.
    • The claim that Charles the Fat’s unification was brief is supported. He held both East Francia and West Francia only during 884–887.
    • Between periods 3 and 8, there were also short non-unified intervals. For example, during the long span of 640–673, Neustria, Austrasia, and Burgundy were often ruled by different royal branches. This helps explain the later decline of central kingship, often associated with the so-called “do-nothing kings.”

    On the other hand, if only the FRANCIA line is considered, the scan may show “count greater than 1” continuously after 884–887, which would miss an important historical fact. After Charles the Fat was deposed, East Francia passed to his nephew Arnulf, while West Francia was contested by Odo and later by Charles the Simple from a Carolingian branch. Until 987, there was no universally recognized sole ruler of all Francia. The kingdom dimension should therefore be split at least into WEST_FRANCIA and EAST_FRANCIA, and the unified-period rule should be adjusted accordingly.

    3. Multi-Source Conflicts and Confidence

    The same historical figure may appear differently in engravings, chronicles, and later history books. Evidence should therefore be modeled explicitly.

    1. CREATE TABLE evidence (
    2. evidence_id SERIAL PRIMARY KEY,
    3. evidence_type VARCHAR(16) NOT NULL CHECK (evidence_type IN ('portrait','chronicle','modern_work')),
    4. title VARCHAR(128) NOT NULL,
    5. author VARCHAR(64),
    6. produced_year INT,
    7. supports JSONB NOT NULL, -- Claims: [{"entity":"person:fr-006","field":"start_date","value":481,"stance":"support"}]
    8. reliability NUMERIC(3,2) NOT NULL
    9. );
    10. CREATE TABLE person_conflict (
    11. conflict_id SERIAL PRIMARY KEY,
    12. person_id VARCHAR(32) REFERENCES person(person_id),
    13. field_name VARCHAR(32) NOT NULL,
    14. value_a TEXT, value_b TEXT,
    15. stance_a VARCHAR(16), stance_b VARCHAR(16),
    16. resolution VARCHAR(16) NOT NULL, -- pending / adopt_a / adopt_b / keep_both
    17. resolved_by VARCHAR(64), resolved_at TIMESTAMP
    18. );

    Several principles should be applied:

    1. Evidence grading. Contemporary or near-contemporary chronicles are generally more reliable than later retellings. Engravings are useful visual evidence for identity and inscriptions, but they should not be used as the sole basis for dates.
    2. Preserve conflicts. When contradictory information is found, it should not be silently overwritten. It should be recorded in person_conflict and adjudicated by an editor.
    3. Confidence can be calculated. A person’s confidence can initially be initialized as a weighted average of evidence reliability and then overwritten after manual review.
    4. Legendary figures should be separated. Figures such as Faramund should have a person record but no real reign, or they can be represented by a placeholder interval of type legacy and excluded from statistics.

    For boundary questions such as “Who was the first king?”, the model does not need to decide which answer is “correct.” It should record different criteria. For example, Merovech appears in sixth-century sources, but contemporary records about him are largely lost, and it is uncertain whether he held the formal title of “king.” Whether he should be counted as the “first monarch supported by historical evidence” can be marked with a criteria_version field. The same dataset can then support multiple counting conventions.

    4. Image Assets and Dating Risks

    4.1 Portrait asset model

    Each engraving should be treated as an independent asset. A high-resolution digital file, a consistent naming convention, and complete provenance and rights information are all necessary.

    1. {
    2. "portrait_id": "fr-portrait-0006",
    3. "person_id": "fr-006",
    4. "inscription": "Chlodoveus Rex Francorum",
    5. "medium": "engraving",
    6. "object_uri": "oss://historical-portraits/fr/0006.tif",
    7. "sha256": "9f2c...",
    8. "width": 1240, "height": 1860,
    9. "plate_no": 6,
    10. "catalogue_note": "Plate 6 in the catalogue; inscription corresponds to Clovis I",
    11. "rights": "public-domain-scan",
    12. "checksum_verified_at": "2025-01-01T00:00:00Z"
    13. }

    Large engraving collections may require substantial image storage and distribution. Common cloud capabilities can be used:

    • object storage for master files and derived images;
    • a content delivery network to accelerate access;
    • prefix-triggered functions to generate thumbnails and progressive image formats automatically;
    • logging and monitoring services to track access anomalies and storage capacity.

    These capabilities should be evaluated as candidates, and the final design should be tested and costed.

    4.2 Required dating-risk annotations

    The original catalogue dates the engravings to the “16th century” but also states that the collection includes Louis XIV. Louis XIV acceded in 1643 and died in 1715, so no 16th-century engraver could have depicted him. The most reasonable explanation is that information comes from different editions or sources, such as a later re-engraving of an older edition or a later addition. The data model should preserve this contradiction rather than choose one side arbitrarily.

    1. {
    2. "catalogue_id": "fr-engraving-set",
    3. "dating": {
    4. "value": 1600,
    5. "precision": "century",
    6. "confidence": 0.45,
    7. "constraints": [
    8. {"type": "terminus_post_quem", "year": 1643, "reason": "Includes a portrait of Louis XIV"},
    9. {"type": "terminus_ante_quem", "year": 1715, "reason": "Year of death of Louis XIV"}
    10. ],
    11. "status": "conflict_pending",
    12. "note": "The 16th-century dating conflicts with part of the content. It may be a mixture of different editions and needs verification against the source copy."
    13. },
    14. "attribution": {
    15. "creator": "Nicolas de Larmessin",
    16. "confidence": 0.7,
    17. "note": "Several members of the Larmessin engraving family share this name; the exact author requires further research."
    18. }
    19. }

    This leads to a general approach for date annotation:

    • record both precision and confidence for time values;
    • support two constraint types: “not earlier than a certain year” (terminus post quem) and “not later than a certain year” (terminus ante quem);
    • use a status field to indicate whether a conflict exists.

    This method applies equally to uncertain dates such as birth and death years, or reign start and end dates.

    5. API Design and Analysis Scenarios

    5.1 Read APIs

    1. GET /api/v1/persons?dynasty=merovingian&identity_status=attested&page=1&size=20
    2. GET /api/v1/persons/{person_id} # Person details: dates, reigns, kinship, portraits, evidence
    3. GET /api/v1/reigns?kingdom=FRANCIA&overlap_with=0558-0561
    4. GET /api/v1/unified-periods?kingdom=FRANCIA
    5. GET /api/v1/lineage?from=fr-006&depth=3 # Kinship and succession subgraph

    All time parameters should use numeric formats such as YYYY or YYYYMMDD to make parsing, sorting, and validation easier.

    5.2 Typical analyses

    • Reign duration ranking: actual duration can be calculated as the difference between start and end dates plus one day, with excluded co-ruling periods removed. Results can retain both days and years.
    • Division and reunification: expressions such as “the kingdom was divided among sons” and “the kingdom was reunited” can be mapped to splits and merges of unified periods.
    • Father-son succession chains: recursive queries can traverse the kinship table and mark points where the succession chain breaks.
    • Homonym disambiguation: grouping by base_name can reveal where additional aliases or descriptive terms are needed.

    Example queries are shown below.

    1. -- 1) Calculate actual reign days in FRANCIA after clipping overlapping intervals
    2. WITH merged AS (
    3. SELECT person_id,
    4. start_date,
    5. LEAST(end_date, COALESCE(LEAD(start_date) OVER (PARTITION BY person_id ORDER BY start_date), end_date)) AS clipped_end
    6. FROM reign
    7. WHERE kingdom_code = 'FRANCIA'
    8. )
    9. SELECT person_id,
    10. SUM(to_date(clipped_end::text, 'YYYYMMDD')
    11. - to_date(start_date::text, 'YYYYMMDD') + 1) AS actual_days
    12. FROM merged
    13. GROUP BY person_id
    14. ORDER BY actual_days DESC
    15. LIMIT 10;
    16. -- 2) Father-son succession: check whether a son acceded in or before the father's death year
    17. SELECT c.display_name AS son, p.display_name AS father
    18. FROM kinship k
    19. JOIN person c ON c.person_id = k.to_person
    20. JOIN person p ON p.person_id = k.from_person
    21. WHERE k.relation = 'father'
    22. AND EXISTS (
    23. SELECT 1 FROM reign rc
    24. JOIN reign rp ON rp.person_id = p.person_id AND rp.kingdom_code = 'FRANCIA'
    25. WHERE rc.person_id = c.person_id
    26. AND rc.kingdom_code = 'FRANCIA'
    27. AND rc.start_date <= rp.end_date + 101
    28. );

    6. Data Quality Rules and Validation

    Data quality should be treated as a first-class engineering concern. Every rule should be expressed as an executable check.

    ID Rule Severity Description
    Q1 Ordinal uniqueness Error (base_name, ordinal) must not be duplicated
    Q2 Date validity Error Start and end years must be positive integers not later than 1715, and start_date <= end_date
    Q3 Identity consistency Error A person with identity_status = legendary must not have a real reign
    Q4 Lifespan contains reign Warning A reign interval must fall within the person’s lifespan, allowing ±1 year of error
    Q5 Reign overlap Warning Overlapping reigns for the same monarch, kingdom, and period require manual confirmation
    Q6 Orphan kinship Warning Both ends of a kinship relation must reference valid persons
    Q7 Portrait binding Warning Each monarch should be associated with at least one portrait, and the inscription should match the base name plus ordinal
    Q8 Dating conflict Warning Catalogue dating must satisfy all constraints; otherwise, the item should enter a manual verification queue

    Validation should be implemented as repeatable jobs, such as scheduled daily tasks, with results written to a data-quality dashboard. Error-level issues should block publication, while warning-level issues should enter a manual review queue.

    7. Visualization Design

    • Timeline swimlane view: This is the core visualization. The horizontal axis represents years, and each lane represents one monarch. Color blocks distinguish reign type, such as sole rule, co-rule, or sub-king status. Division periods and unified periods can be distinguished with background colors. Chlothar I’s two reign intervals can appear as two blocks in the same lane, while Charles the Fat can span both East Francia and West Francia lanes. It becomes immediately visible who ruled at the same time as whom.
    • Genealogy tree: Father-son relations form the main structure, while sibling and marriage relations are shown with connecting lines. Legendary figures can be distinguished with dashed borders.
    • Relationship graph: Persons are nodes, relationships are edges, and node size can be tied to reign duration or evidence count. This is useful for exploring influence and succession networks.
    • Large image browsing: When the number of engravings is large, tile-based slicing and progressive loading can avoid loading all original images at once.

    Accessibility should also be considered:

    • color should not be the only information carrier; textures or patterns should also distinguish reign types;
    • every color block should provide readable labels and tooltips;
    • chart data should also be available in table form for users of assistive technologies or text-based environments.

    8. Delivery Path

    Phase Goal Key deliverables Acceptance criteria
    Phase 1 Ingestible and searchable Person, reign, and kingdom core tables; import scripts All 35 source entries are imported with no orphan records
    Phase 2 Analyzable Unified-period scan, reign-duration calculation, kinship query APIs Scan results match source annotations, and differences are documented
    Phase 3 Traceable Evidence table, conflict table, date-annotation specification Every conclusion can be traced to an engraving or source
    Phase 4 Extensible Queens, regents, capitals, and map layers New entities can be added without changing existing table structures
    Phase 5 Presentable Timeline, genealogy tree, relationship graph, and accessibility support Usable in major browsers and with screen readers

    Two additional points should be considered during import:

    • Not every monarch in the source has an ordinal number. For example, the entry before Hugh Capet lacks an ordinal. Both the original ordinal and a computed ordinal should be preserved.
    • Layout information such as “left/right” should be recorded as spatial properties of catalogue entries, not mixed into person identity fields.

    Conclusion

    The value of this French monarch engraving collection is not limited to the images themselves. Once the fiction around Faramund, the sparse records of Clodion, the two reunifications associated with Chlothar I, and the brief unification of Charles the Fat are transformed into structured data, many previously ambiguous questions can be answered through queries: who ruled at the same time, when the kingdom divided, and where a genealogical chain breaks.

    This approach can be reused for any historical material that consists of “person lists + time intervals + relationship networks”: decompose lists into entities and relationships, treat intervals as first-class objects, model uncertainty explicitly, and automate quality rules. The value of the data asset comes precisely from these seemingly small modeling decisions.