The standalone bundle

rdf/IPSDatedSites-bundle.ttl holds the data, the whole vocabulary and a materialised CIDOC CRM crosswalk in a single file, so that it can be loaded into a triplestore on its own — the NFDI4Objects knowledge graph in particular.

Why the crosswalk is materialised

Subclass axioms alone are not enough. Most triplestores perform no RDFS entailment by default, and a shared knowledge graph should not be assumed to. With the axioms present but nothing inferred, this is the result:

SELECT (COUNT(DISTINCT ?x) AS ?n) WHERE { ?x a crm:E53_Place }
0

Every findspot is a place by the axioms, yet a consumer querying in CIDOC CRM — which is the entire purpose of the crosswalk — sees nothing. The builder therefore computes the transitive closure over rdfs:subClassOf and writes the inferred rdf:type triples out explicitly. The axioms are kept alongside them, so a store that does reason derives nothing new and nothing conflicts: the materialised triples are exactly what a reasoner would produce.

Only rdf:type over rdfs:subClassOf is inferred. No domain or range inference, no property closure, no owl:sameAs propagation — those would add bulk and embed assumptions the source data do not support.

The closure is computed from the axioms found in the graph rather than from a hard-coded table, so adding a superclass in the export propagates into the bundle without a second edit.

flowchart LR
    I["samian:fs_1003978_969c47<br/>one findspot"]
    A["lado:Findspot"]
    I -->|rdf:type, asserted| A
    crm_E53_Place["crm:E53_Place"]
    I -.->|rdf:type, materialised| crm_E53_Place
    lado_Location["lado:Location"]
    I -.->|rdf:type, materialised| lado_Location
    class I io
    class A local
    class crm_E53_Place crm
    class lado_Location local

    classDef local fill:#e8eef7,stroke:#4a6b96,stroke-width:1px,color:#12181f
    classDef crm fill:#efe7f5,stroke:#7a5a96,stroke-width:1px,color:#12181f
    classDef time fill:#e3f2ec,stroke:#3f8a70,stroke-width:1px,color:#12181f
    classDef ext fill:#f3f1ec,stroke:#8a857a,stroke-width:1px,color:#12181f
    classDef io fill:#faf3e3,stroke:#a8872e,stroke-width:1px,color:#12181f

Solid edge asserted, dashed edges written out by the builder. Without them a CIDOC CRM query returns nothing.

JPG · SVG · Mermaid source — generated, do not edit.

The same node in the finished bundle, drawn from the triples:

Every type one findspot carries

SVG · GraphViz source — cut from the graph by CONSTRUCT, generated, do not edit.

flowchart TB
    SRC["py/ips_rdf_export.py<br/><i>the model</i>"]
    GEN["py/make_webjs.py<br/><i>injects prefixes, measure lists,<br/>figure constants, subclass closure,<br/>the vocabulary prelude</i>"]
    BODY["py/templates/ips_rdf_body.js<br/><i>graph shape, hand-written</i>"]
    JS["webjs/ips_rdf.js"]

    CSV["one CSV"]
    GPY["graph, built in Python"]
    GJS["graph, built in the browser"]
    GATE{"sorted N-Triples<br/>SHA-256 compared"}
    OUT["identical, or the build fails"]

    SRC --> GEN --> JS
    BODY --> JS
    CSV --> GPY
    CSV --> GJS
    SRC --> GPY
    JS --> GJS
    GPY --> GATE
    GJS --> GATE
    GATE --> OUT

    class SRC,GEN,BODY local
    class JS,CSV,GPY,GJS ext
    class GATE,OUT time

    classDef local fill:#e8eef7,stroke:#4a6b96,stroke-width:1px,color:#12181f
    classDef crm fill:#efe7f5,stroke:#7a5a96,stroke-width:1px,color:#12181f
    classDef time fill:#e3f2ec,stroke:#3f8a70,stroke-width:1px,color:#12181f
    classDef ext fill:#f3f1ec,stroke:#8a857a,stroke-width:1px,color:#12181f
    classDef io fill:#faf3e3,stroke:#a8872e,stroke-width:1px,color:#12181f

The browser emitter is generated where the model is tabular and hand-written where it is a graph shape. What holds the two together is the hash comparison, not the generation.

JPG · SVG · Mermaid source — generated, do not edit.

What the bundle counts

Each of these is reported as a void:classPartition in the file’s own self-description:

Class Counted as
lado:DiscoverySite sites
lado:Findspot findspots
lado:FindspotDating datings
lado:PlotRow plot rows
lado:DatingActivity activities
crm:E7_Activity CIDOC CRM activities (after materialisation)
crm:E53_Place CIDOC CRM places (after materialisation)
crm:E52_Time-Span CIDOC CRM time-spans (after materialisation)
time:ProperInterval OWL-Time intervals (after materialisation)

Discovery sites are typed here

The incremental export makes no rdf:type statement about the published samian:loc_ds_* nodes; it only references them. That is correct for an export intended to sit alongside loc_discoverysite_1.ttl.

A standalone bundle is a different case. Without the published file present, crm:P89_falls_within would point at an untyped node and a query for places would miss the sites entirely. The builder therefore asserts lado:DiscoverySite on them, which materialises through lado:Location to crm:E53_Place.

This is a restatement rather than a new claim: the published file already types those nodes the same way. Merging the two files was checked directly, and the bundle adds only what the axioms entail — crm:E53_Place on each site — with no contradiction. Use --no-type-sites to suppress it where the bundle is only ever loaded together with the published data.

One exception surfaced during that check and is worth recording: of the 32 sites referenced here, samian:loc_ds_1004709 (Pecio del Guadiaro, a shipwreck) has no triples at all in the 2019 release of loc_discoverysite_1.ttl. The source database knows the identifier but that file does not contain the node — the site postdates the release. In a merged graph it would be a reference into nothing; in the bundle it is at least typed and labelled. A regenerated discovery-site export would close the gap, and nothing here would need to change, because the export only ever references those nodes.

Loading it

The file is self-contained Turtle. No imports, no external ontology needs to be resolved at load time — the CIDOC CRM and OWL-Time terms are used by URI and their own definitions can be loaded separately if wanted, but nothing here depends on their presence.

The file describes itself as a void:Dataset and a dcat:Dataset, carrying title, issue date, licence and source, and stating in an rdfs:comment how many of its rdf:type triples are inferred rather than asserted.