diff --git a/frontend/src/pages/Visualize.tsx b/frontend/src/pages/Visualize.tsx
index 8bd27bd051..cf05a3358e 100644
--- a/frontend/src/pages/Visualize.tsx
+++ b/frontend/src/pages/Visualize.tsx
@@ -490,99 +490,82 @@ export function Visualize() {
? (import.meta.env.VITE_SPARQL_ENDPOINT || 'http://localhost:7878/query')
: '/query'; // Always use relative URL in production
+ // SPARQL query aligned with actual RDF generated by oxigraph_sync.py and oxigraph_person_sync.py
+ // Namespaces match the Python sync scripts:
+ // - nde: for Custodian type
+ // - hc: for ghcid, isil predicates
+ // - hp: for person URIs
const constructQuery = `
- PREFIX crm:
- PREFIX hc:
- PREFIX hcc:
+ PREFIX rdf:
+ PREFIX rdfs:
PREFIX skos:
PREFIX schema:
PREFIX foaf:
- PREFIX dcterms:
PREFIX owl:
- PREFIX prov:
-
+ PREFIX org:
+ PREFIX geo:
+ PREFIX cidoc:
+ PREFIX nde:
+ PREFIX hc:
+ PREFIX hp:
+
CONSTRUCT {
- # Core custodian data
- ?custodian a crm:E39_Actor ;
- skos:prefLabel ?label ;
- skos:altLabel ?altLabel ;
- hc:custodian_type ?type ;
- dcterms:identifier ?dcId .
+ # Custodians - core data
+ ?custodian a nde:Custodian ;
+ rdfs:label ?label ;
+ skos:prefLabel ?prefLabel ;
+ schema:name ?name ;
+ hc:ghcid ?ghcid ;
+ hc:isil ?isil ;
+ schema:url ?website ;
+ foaf:homepage ?homepage ;
+ owl:sameAs ?wikidata .
- # Location with coordinates
- ?custodian crm:P53_has_former_or_current_location ?place .
- ?place a schema:Place ;
- schema:latitude ?lat ;
- schema:longitude ?lon ;
- schema:address ?address .
+ # Location (schema:location, not crm:P53)
+ ?custodian schema:location ?location .
+ ?location geo:lat ?lat ;
+ geo:long ?lon .
- # Identifiers (creates edges: custodian -> identifier)
- ?custodian crm:P48_has_preferred_identifier ?identifier .
- ?identifier a crm:E42_Identifier ;
- skos:inScheme ?scheme ;
- skos:notation ?notation ;
- schema:url ?identUrl .
-
- # Digital platform / website
- ?custodian foaf:homepage ?platform .
- ?platform a hcc:DigitalPlatform ;
- schema:url ?platformUrl .
-
- # Social media profiles (creates edges: custodian -> social)
- ?custodian foaf:account ?social .
- ?social a foaf:OnlineAccount ;
- hc:platform_type ?socialType ;
- foaf:accountName ?accountName ;
- foaf:accountServiceHomepage ?socialUrl .
-
- # Wikidata/VIAF sameAs links (creates edges)
- ?custodian owl:sameAs ?sameAs .
-
- # GeoNames containedInPlace
- ?custodian schema:containedInPlace ?geonames .
+ # Persons linked to custodians
+ ?person a schema:Person ;
+ rdfs:label ?personLabel ;
+ schema:name ?personName ;
+ schema:worksFor ?custodian ;
+ org:memberOf ?custodian ;
+ schema:jobTitle ?jobTitle .
}
WHERE {
- ?custodian a crm:E39_Actor .
- OPTIONAL { ?custodian skos:prefLabel ?label }
- OPTIONAL { ?custodian skos:altLabel ?altLabel }
- OPTIONAL { ?custodian hc:custodian_type ?type }
- OPTIONAL { ?custodian dcterms:identifier ?dcId }
-
- # Location
+ # Get all custodians (nde:Custodian is the primary type)
+ ?custodian a nde:Custodian .
+ OPTIONAL { ?custodian rdfs:label ?label }
+ OPTIONAL { ?custodian skos:prefLabel ?prefLabel }
+ OPTIONAL { ?custodian schema:name ?name }
+ OPTIONAL { ?custodian hc:ghcid ?ghcid }
+ OPTIONAL { ?custodian hc:isil ?isil }
+ OPTIONAL { ?custodian schema:url ?website }
+ OPTIONAL { ?custodian foaf:homepage ?homepage }
OPTIONAL {
- ?custodian crm:P53_has_former_or_current_location ?place .
- OPTIONAL { ?place schema:latitude ?lat }
- OPTIONAL { ?place schema:longitude ?lon }
- OPTIONAL { ?place schema:address ?address }
+ ?custodian owl:sameAs ?wikidata .
+ FILTER(STRSTARTS(STR(?wikidata), "http://www.wikidata.org/"))
}
- # Identifiers
+ # Location using schema:location (as generated by oxigraph_sync.py)
OPTIONAL {
- ?custodian crm:P48_has_preferred_identifier ?identifier .
- OPTIONAL { ?identifier skos:inScheme ?scheme }
- OPTIONAL { ?identifier skos:notation ?notation }
- OPTIONAL { ?identifier schema:url ?identUrl }
+ ?custodian schema:location ?location .
+ OPTIONAL { ?location geo:lat ?lat }
+ OPTIONAL { ?location geo:long ?lon }
}
- # Digital platform
+ # Persons linked to custodians via schema:worksFor
OPTIONAL {
- ?custodian foaf:homepage ?platform .
- OPTIONAL { ?platform schema:url ?platformUrl }
+ ?person a schema:Person ;
+ schema:worksFor ?custodian .
+ OPTIONAL { ?person rdfs:label ?personLabel }
+ OPTIONAL { ?person schema:name ?personName }
+ OPTIONAL { ?person schema:jobTitle ?jobTitle }
+ OPTIONAL { ?person org:memberOf ?custodian }
}
-
- # Social media
- OPTIONAL {
- ?custodian foaf:account ?social .
- OPTIONAL { ?social hc:platform_type ?socialType }
- OPTIONAL { ?social foaf:accountName ?accountName }
- OPTIONAL { ?social foaf:accountServiceHomepage ?socialUrl }
- }
-
- # External links
- OPTIONAL { ?custodian owl:sameAs ?sameAs }
- OPTIONAL { ?custodian schema:containedInPlace ?geonames }
}
- LIMIT 500
`;
let rdfData = '';
@@ -638,25 +621,17 @@ export function Visualize() {
console.log(`Parsed: ${result.nodes.length} nodes, ${result.links.length} links`);
loadGraphData(result);
- // After loading, set filter to show just ONE random node type
- // This prevents overwhelming users with all ~500 nodes
+ // Show ALL node types by default - no random filtering
+ // Users can filter down using the sidebar controls if needed
if (result.nodes.length > 0) {
// Extract unique node types from the result
const uniqueTypes = new Set(result.nodes.map(n => n.type));
const typesArray = Array.from(uniqueTypes);
- if (typesArray.length > 0) {
- // Pick one random type
- const randomIndex = Math.floor(Math.random() * typesArray.length);
- const randomType = typesArray[randomIndex];
-
- // Set filter to only show this one type
- setFilters({ nodeTypes: new Set([randomType]) });
-
- // Count nodes of this type
- const nodesOfType = result.nodes.filter(n => n.type === randomType);
- console.log(`RDF loaded: showing ${nodesOfType.length} nodes of type "${randomType}" (${result.nodes.length} total). Click "All" to see everything.`);
- }
+ // Set filter to show ALL types by default
+ setFilters({ nodeTypes: new Set(typesArray) });
+
+ console.log(`RDF loaded: showing all ${result.nodes.length} nodes across ${typesArray.length} types: ${typesArray.join(', ')}`);
}
// Update cache state