Reputation: 67
I am trying to validate a data graph against the shape graph using pyshacl. Basically, the data graph represents a geometric representation of two walls in a building, whose distance from each other is around 12.5 meters. The shapes graph checks if the distance between the walls is less than 10 meters. The code I am using is as follows:
from pyshacl import validate
from rdflib import Graph
Datagraph = '''
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix bot: <https://w3id.org/bot#> .
@prefix ifc: <https://standards.buildingsmart.org/IFC/DEV/IFC2x3/TC1/OWL#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix lbd: <https://linkebuildingdata.org/LBD#> .
@prefix props: <http://lbd.arch.rwth-aachen.de/props#> .
@prefix geo: <http://www.opengis.net/ont/geosparql#> .
@prefix unit: <http://qudt.org/vocab/unit/> .
@prefix IFC4-PSD: <https://www.linkedbuildingdata.net/IFC4-PSD#> .
@prefix smls: <https://w3id.org/def/smls-owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix inst: <https://www.ugent.be/myAwesomeFirstBIMProject#> .
@prefix fog: <https://w3id.org/fog#> .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353
props:objectTypeIfcObject inst:objectTypeIfcObject_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:reference inst:reference_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:extendToStructure inst:extendToStructure_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:loadBearing inst:loadBearing_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:isExternal inst:isExternal_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:category inst:category_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
rdf:type bot:Element ;
props:globalIdIfcRoot inst:globalIdIfcRoot_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:nameIfcRoot inst:nameIfcRoot_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
props:batid inst:batid_1eec9390-5f68-4a64-aaa9-34a883f3c353 ;
geo:hasGeometry inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353_geometry .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353_geometry
fog:asObj_v3.0-obj "diAzODcyLjM3ODM0NTUyNjY4OSAtNDY2LjM1NTc3MjU3ODAzNjA3IDAuMAp2IDM4NzIuMzc4MzQ1NTI2Njg5IC00NjYuMzU1NzcyNTc4MDM2MDcgNzk5OS45OTk5OTk5OTk5OTkKdiAzODcyLjM3ODM0NTUyNjY4OSAtNDA2Ni4zNTU3NzI1NzgwMzY1IDAuMAp2IDM4NzIuMzc4MzQ1NTI2Njg5IC00MDY2LjM1NTc3MjU3ODAzNjUgNzk5OS45OTk5OTk5OTk5OTkKdiA0MTYyLjM3ODM0NTUyNjY4OSAtNDY2LjM1NTc3MjU3ODAzNjA3IDAuMAp2IDQxNjIuMzc4MzQ1NTI2Njg5IC00NjYuMzU1NzcyNTc4MDM2MDcgNzk5OS45OTk5OTk5OTk5OTkKdiAzODcyLjM3ODM0NTUyNjY4OSAtN"^^<https://www.w3.org/TR/xmlschema-2/#base64Binary> ;
lbd:hasBoundingBox inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353_geometry_bb .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353_geometry_bb
lbd:z-max "7999.999999999999"^^xsd:double ;
lbd:z-min "0.0"^^xsd:double ;
lbd:y-max "-466.35577257803607"^^xsd:double ;
lbd:y-min "-4066.3557725780365"^^xsd:double ;
lbd:x-max "4162.378345526689"^^xsd:double ;
lbd:x-min "3872.378345526689"^^xsd:double .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd
props:nameIfcRoot inst:nameIfcRoot_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:reference inst:reference_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:loadBearing inst:loadBearing_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:objectTypeIfcObject inst:objectTypeIfcObject_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:globalIdIfcRoot inst:globalIdIfcRoot_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:category inst:category_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:isExternal inst:isExternal_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
geo:hasGeometry inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd_geometry ;
lbd:containsInBoundingBox inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c31b ;
props:batid inst:batid_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
props:extendToStructure inst:extendToStructure_1eec9390-5f68-4a64-aaa9-34a883f3c0dd ;
rdf:type bot:Element .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd_geometry
fog:asObj_v3.0-obj "diAtODYzNy42MjE2NTQ0NzMyOTIgLTQzNTYuMzU1NzcyNTc3OTk2IDAuMAp2IC04NjM3LjYyMTY1NDQ3MzI5MiAtNDM1Ni4zNTU3NzI1Nzc5OTYgNzk5OS45OTk5OTk5OTk5OTkKdiAtODYzNy42MjE2NTQ0NzMyOTIgODI0My42NDQyMjc0MjIwMSAwLjAKdiAtODYzNy42MjE2NTQ0NzMyOTIgODI0My42NDQyMjc0MjIwMSA3OTk5Ljk5OTk5OTk5OTk5OQp2IC04OTI3LjYyMTY1NDQ3MzI5MiAtNDM1Ni4zNTU3NzI1Nzc5OTYgMC4wCnYgLTg5MjcuNjIxNjU0NDczMjkyIC00MzU2LjM1MCAxMTcgMTE4CmYgMTIyIDEyNCAxMjMKZiAxMjIgMTIxIDEyNApmIDEyNyAxMjggMTI1CmYgMTI4IDEyNiAxMjUKZiAxMjkgMTMwIDEzMQpmIDEzMiAxMjkgMTMxCg=="^^<https://www.w3.org/TR/xmlschema-2/#base64Binary> ;
lbd:hasBoundingBox inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd_geometry_bb .
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd_geometry_bb
lbd:z-max "7999.999999999999"^^xsd:double ;
lbd:z-min "0.0"^^xsd:double ;
lbd:y-max "8243.64422742201"^^xsd:double ;
lbd:y-min "-4356.355772577996"^^xsd:double ;
lbd:x-max "-8637.621654473292"^^xsd:double ;
lbd:x-min "-8927.621654473292"^^xsd:double .
'''
Shapegraph = '''
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix bot: <https://w3id.org/bot#> .
@prefix ifc: <https://standards.buildingsmart.org/IFC/DEV/IFC2x3/TC1/OWL#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix lbd: <https://linkebuildingdata.org/LBD#> .
@prefix props: <http://lbd.arch.rwth-aachen.de/props#> .
@prefix geo: <http://www.opengis.net/ont/geosparql#> .
@prefix unit: <http://qudt.org/vocab/unit/> .
@prefix IFC4-PSD: <https://www.linkedbuildingdata.net/IFC4-PSD#> .
@prefix smls: <https://w3id.org/def/smls-owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix inst: <https://www.ugent.be/myAwesomeFirstBIMProject#> .
@prefix fog: <https://w3id.org/fog#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
ifc:geometricconstraintshape a sh:NodeShape ;
sh:targetClass bot:Element;
sh:sparql[
sh:message "The geometric constraint is violated" ;
sh:select """
SELECT $this
WHERE{
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353 geo:hasGeometry $this .
$this lbd:hasBoundingBox ?wall1bb.
?wall1bb lbd:x-min ?x_min.
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd geo:hasGeometry $this .
$this lbd:hasBoundingBox ?wall2bb.
?wall2bb lbd:x-max ?x_max.
FILTER (ifc:computedistance(?x_min,?x_max)<=10.0) .
} """ ;
] .
ifc:computedistance a sh:SPARQLFunction;
rdfs:comment "Finds the distance between the two elements";
sh:parameter[
sh:path lbd:x_min ;
sh:datatype xsd:double;
sh:description "The inner x coordinate of the wall on the right side";
];
sh:parameter[
sh:path lbd:x_max ;
sh:datatype xsd:double;
sh:description "The outer x coordinate of the wall on the left side";
];
sh:returnType xsd:double;
sh:select"""
SELECT (BIND(($x_min - $x_max)/1000) AS ?result)
WHERE {
}
""".
'''
r = validate(data_graph = Datagraph, shacl_graph = Shapegraph, data_graph_format="ttl", shacl_graph_format="ttl", inference = "rdfs", debug = True, abort_on_error = False, meta_shacl = False, serialize_report_graph = "ttl")
conforms, results_graph, results_text = r
Since the actual distance between the walls is around 12510mm (I have divided the calculated distance by 1000 in the shape to convert it into meters), the datagraph should not confirm to the shape graph. However, in my case, the result I am getting is "Datagraph conforms to constraints".
Not sure where I am making a mistake in developing the shape. Could anyone please help to identify the mistake and suggest how to correct it? Thanks in advance.
I also tried a simpler query to avoid function call as follows:
ifc:geometricconstraintshape a sh:NodeShape ;
sh:targetClass bot:Element;
sh:sparql[
sh:message "The geometric constraint is violated" ;
sh:select """
SELECT $this
WHERE{
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353 geo:hasGeometry $this .
$this lbd:hasBoundingBox ?wall1bb.
?wall1bb lbd:x-min ?x_min.
inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd geo:hasGeometry $this .
$this lbd:hasBoundingBox ?wall2bb.
?wall2bb lbd:x-max ?x_max.
BIND ((?x_min - ?x_max)/1000 AS ?result) .
FILTER (?result<=10.0) .
} """ ;
] .
Still, I am not getting the correct results.
To avoid any issue due to incorrect target nodes, I wrote the query in another form as shown below:
ifc:computedistance a sh:SPARQLFunction;
rdfs:comment "Finds the distance between the two elements";
sh:parameter[
sh:path ifc:op1 ;
sh:datatype xsd:double;
sh:description "The inner x coordinate of the wall on the right side";
];
sh:parameter[
sh:path ifc:op2 ;
sh:datatype xsd:double;
sh:description "The outer x coordinate of the wall on the left side";
];
sh:returnType xsd:double;
sh:select"""
SELECT ?result
WHERE {
BIND ((($op1 - $op2)/1000) AS ?result).
}
""".
ifc:lessThan a sh:SPARQLFunction;
rdfs:comment "Returns if op1<op2.";
sh:parameter[
sh:path ifc:op1;
sh:datatype xsd:double;
sh:description "The difference between the two coordinates";
] ;
sh:parameter[
sh:path ifc:op2;
sh:datatype xsd:double;
sh:description "The constraint value to be checked against";
] ;
sh:returnType xsd:boolean ;
sh:select """
SELECT ?result
WHERE {
BIND (IF(?op1<?op2, true, false) AS ?result).
}
""".
ifc:geometricconstraintshape a sh:NodeShape ;
sh:targetClass bot:Element;
sh:expression[
sh:message "The geometric constraint is violated" ;
ifc:lessThan(
[
ifc:computedistance(
[sh:path (inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c353_geometry_bb lbd:x-min)]
[sh:path (inst:wall_1eec9390-5f68-4a64-aaa9-34a883f3c0dd_geometry_bb lbd:x-max)]
)
]
10.0
);
].
'''
However, still, the datagraph is confirming to all values of constraint in the shape graph.
Upvotes: 0
Views: 165
Reputation: 176
There are several remaining problems:
sh:sparql [a sh:SPARQLConstraint; ...
sh:targetClass bot:Element
targets all nodes of that class. You want to limit this to Walls, and NOT use 2 fixed wall instances in the constraint$this
in the query: it's the target node (bot:Element
or a Wall
, but definitely not a geometry)geo:hasGeometry $this
, but it's not possible for 2 walls to have the same geometry, so that failssh:sparql
is run on each target node. And since each query must compare $this
against all other walls, the complexity becomes very high. It's better to find the violations in sh:target [a sh:SPARQLTarget; sh:select """...
and then use sh:sparql [a sh:SPARQLConstraint
only to fill the ViolationReport variablesSee examples around https://transparency.ontotext.com/spec/#validation-rules. Note: "SPARQL check" is NOT a SHACL check, it's a simple SPARQL query that finds problems.
Upvotes: 0