Currently I am trying to infer a new property maps:mapstoclass
based on the triples below. The idea is that I can use the inferred outcome (together with an rdf file containing alignment of data:classes) to determine the similarity between data0:object100
and its overlapping objects from data1:
, specified in maps:hasOverlap
.
maps:relation_obj1 a maps:OverlapRelations ;
maps:hasOverlap [ a data1:classA ;
maps:mainRelativeArea "80.0"^^xsd:float ;
maps:secRelativeArea "100.0"^^xsd:float ;
maps:secfeature data1:object1 ;
] ;
maps:hasOverlap [ a data1:classX ;
maps:mainRelativeArea "40.0"^^xsd:float ;
maps:secRelativeArea "100.0"^^xsd:float ;
maps:secfeature data1:object2 ;
] ;
maps:mainfeature data0:object100 ;
maps:mainclass data0:classB .
Firstly, I've looked at whether the object properties of maps:hasOverlap
satisfy my qualifiedValueShape
(the shacl shapes/rule are given at the end). In this case only the 'hasOverlap' object with maps:secfeature data1:object1 satisfies the condition. Thus the object of 'maps:mapsto' should be data1:object1. The result I expect is:
maps:relation_obj1 maps:mapstoclass data1:object1.
However, I currently get:
maps:relation_obj1 maps:mapstoclass data1:object1, data1:object2.
What am I doing wrong? Does the sh:condition of the rule need to be applied explicitly within sh:object? I've looked at node expressions, but did not succeed in using it - and I could not find applicable examples in the documentation.
The shapes used:
ex:mainAreaShape
rdf:type sh:NodeShape;
sh:property [
sh:path maps:mainRelativeArea ;
sh:minInclusive 80 ;
].
ex:secAreaShape
rdf:type sh:NodeShape;
sh:property [
sh:path maps:secRelativeArea ;
sh:minInclusive 80 ;
].
ex:OverlapRelations
rdf:type rdfs:Class, sh:NodeShape ;
sh:targetClass maps:OverlapRelations;
rdfs:label "whether overlap between features is enough to generate relation" ;
sh:rule [
rdf:type sh:TripleRule ;
sh:subject sh:this ;
sh:predicate maps:mapstoclass;
sh:object [sh:path (maps:hasOverlap
rdf:type) ;
];
sh:condition ex:OverlapRelations;
sh:condition [
sh:property [
sh:path maps:hasOverlap ;
sh:nodeKind sh:BlankNode ;
sh:minCount 1;
sh:qualifiedValueShape [
sh:and (ex:mainAreaShape ex:secAreaShape);
];
sh:qualifiedMinCount 1;
sh:qualifiedMaxCount 1;
];
];
].
The sh:condition only filters out the focus nodes to which the rule applies, but then has no impact on the evaluation of sh:object. In your case, without verifying, I assume that your (single) focus node maps:relation_obj1 does fulfill the condition because one of its values conforms to the QVS. However, the sh:object expression is still being evaluated for all values of the path maps:hasOverlap/rdf:type, which then delivers the types of both.
One option would be to express what you need in SPARQL and use a SPARQL-based rule.
Another option would be to move the logic currently in sh:condition into the sh:object node expression. I believe sh:filterShape
https://w3c.github.io/shacl/shacl-af/#node-expressions-filter-shape
could be used here. Keep in mind that node expressions are basically pipelines, where nodes go in on one side and other nodes go out on the other side. In your case, I think you want to
Sorry I cannot spend more time on this particular example, but maybe it's something like
sh:object [ # 3.
sh:path rdf:type ;
sh:nodes [ # 2.
sh:filterShape [
sh:nodeKind sh:BlankNode ;
sh:node ex:mainAreaShape, ex:secAreaShape ; # same as your sh:not
] ;
sh:nodes [ # 1.
sh:path maps:hasOverlap ;
]
]
]
You need to "read" node expressions from the inside out, so the innermost hasOverlap path is evaluated first, then the results of that are run through the filter. If there are no resulting nodes, or those don't have an rdf:type then no sh:object is found and thus no triple inferred.
BTW the sh:targetClass is not needed and I think the whole thing could also be expressed using the (newer) sh:values keyword as
ex:OverlapRelations
a rdfs:Class, sh:NodeShape ;
rdfs:label "whether overlap between features is enough to generate relation" ;
sh:property [
sh:path maps:mapstoclass ;
sh:values [ # 1.
sh:path rdf:type ;
sh:nodes [ # 2.
sh:filterShape [
sh:nodeKind sh:BlankNode ;
sh:node ex:mainAreaShape, ex:secAreaShape ;
] ;
sh:nodes [ # 1.
sh:path maps:hasOverlap ;
]
]
]
] .
Again, untested so please apologize any glitches :)