@@ -234,8 +234,13 @@ export function convertGrapholToTurtle(content: string): string {
234234 turtleLines . push ( "" ) ;
235235 }
236236
237- const entitiesById = new Map < string , GrapholEntity > ( ) ;
237+ // Build entity type map (IRI → kind) from diagram nodes, scoping IDs per-diagram
238+ // to avoid cross-diagram ID collisions (each diagram reuses n1, n2, …).
239+ const entitiesByIri = new Map < string , GrapholEntity > ( ) ;
240+ const emittedEdges = new Set < string > ( ) ;
241+
238242 for ( const diagram of findElements ( content , "diagram" ) ) {
243+ const localMap = new Map < string , GrapholEntity > ( ) ;
239244 for ( const node of findElements ( diagram . body , "node" ) ) {
240245 const id = node . attrs . id ;
241246 const kind = grapholTypeToEntityKind ( node . attrs . type ) ;
@@ -248,12 +253,27 @@ export function convertGrapholToTurtle(content: string): string {
248253
249254 const label = normalizeLabel ( getTagText ( node . body , "label" ) ) ;
250255 const entity : GrapholEntity = label ? { id, iri, kind, label } : { id, iri, kind } ;
251- entitiesById . set ( id , entity ) ;
256+ localMap . set ( id , entity ) ;
257+ if ( ! entitiesByIri . has ( iri ) ) entitiesByIri . set ( iri , entity ) ;
258+ }
259+
260+ // Process edges within the same diagram using its local ID map
261+ for ( const edge of findElements ( diagram . body , "edge" ) ) {
262+ const edgeType = ( edge . attrs . type ?? "" ) . toLowerCase ( ) ;
263+ const source = edge . attrs . source ? localMap . get ( edge . attrs . source ) : undefined ;
264+ const target = edge . attrs . target ? localMap . get ( edge . attrs . target ) : undefined ;
265+ if ( ! source || ! target ) continue ;
266+ const predicate = grapholEdgePredicate ( source , target , edgeType ) ;
267+ if ( ! predicate ) continue ;
268+ const triple = `<${ escapeIri ( source . iri ) } > ${ predicate } <${ escapeIri ( target . iri ) } > .` ;
269+ if ( emittedEdges . has ( triple ) ) continue ;
270+ emittedEdges . add ( triple ) ;
271+ turtleLines . push ( triple ) ;
252272 }
253273 }
254274
255275 const emittedEntities = new Set < string > ( ) ;
256- for ( const entity of entitiesById . values ( ) ) {
276+ for ( const entity of entitiesByIri . values ( ) ) {
257277 if ( emittedEntities . has ( entity . iri ) ) continue ;
258278 emittedEntities . add ( entity . iri ) ;
259279 turtleLines . push ( `<${ escapeIri ( entity . iri ) } > a ${ grapholEntityTypeTriple ( entity . kind ) } .` ) ;
@@ -264,19 +284,36 @@ export function convertGrapholToTurtle(content: string): string {
264284
265285 if ( emittedEntities . size > 0 ) turtleLines . push ( "" ) ;
266286
267- const emittedEdges = new Set < string > ( ) ;
268- for ( const diagram of findElements ( content , "diagram" ) ) {
269- for ( const edge of findElements ( diagram . body , "edge" ) ) {
270- const edgeType = ( edge . attrs . type ?? "" ) . toLowerCase ( ) ;
271- const source = edge . attrs . source ? entitiesById . get ( edge . attrs . source ) : undefined ;
272- const target = edge . attrs . target ? entitiesById . get ( edge . attrs . target ) : undefined ;
273- if ( ! source || ! target ) continue ;
274- const predicate = grapholEdgePredicate ( source , target , edgeType ) ;
275- if ( ! predicate ) continue ;
276- const triple = `<${ escapeIri ( source . iri ) } > ${ predicate } <${ escapeIri ( target . iri ) } > .` ;
277- if ( emittedEdges . has ( triple ) ) continue ;
278- emittedEdges . add ( triple ) ;
279- turtleLines . push ( triple ) ;
287+ // Extract annotations (rdfs:label, rdfs:comment, …) from the <iris> section
288+ const irisSection = findFirstElement ( ontology . body , "iris" ) ;
289+ if ( irisSection ) {
290+ const emittedAnnotations = new Set < string > ( ) ;
291+ for ( const iriEl of findElements ( irisSection . body , "iri" ) ) {
292+ const iriValue = getTagText ( iriEl . body , "value" ) ;
293+ if ( ! iriValue || ! / ^ h t t p s ? : \/ \/ / i. test ( iriValue ) ) continue ;
294+ const annotationsEl = findFirstElement ( iriEl . body , "annotations" ) ;
295+ if ( ! annotationsEl ) continue ;
296+ for ( const ann of findElements ( annotationsEl . body , "annotation" ) ) {
297+ const property = getTagText ( ann . body , "property" ) ;
298+ if ( ! property ) continue ;
299+ const lexicalForm = getTagText ( ann . body , "lexicalForm" ) ;
300+ if ( ! lexicalForm ) continue ;
301+ const language = getTagText ( ann . body , "language" ) ;
302+ const datatype = getTagText ( ann . body , "datatype" ) ;
303+ const escaped = escapeTurtleLiteral ( lexicalForm ) ;
304+ let objectLiteral : string ;
305+ if ( language && language . trim ( ) ) {
306+ objectLiteral = `"${ escaped } "@${ language . trim ( ) } ` ;
307+ } else if ( datatype && datatype !== "None" && datatype !== "http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral" ) {
308+ objectLiteral = `"${ escaped } "^^<${ escapeIri ( datatype ) } >` ;
309+ } else {
310+ objectLiteral = `"${ escaped } "` ;
311+ }
312+ const triple = `<${ escapeIri ( iriValue ) } > <${ escapeIri ( property ) } > ${ objectLiteral } .` ;
313+ if ( emittedAnnotations . has ( triple ) ) continue ;
314+ emittedAnnotations . add ( triple ) ;
315+ turtleLines . push ( triple ) ;
316+ }
280317 }
281318 }
282319
0 commit comments