AJAY AGRAWAL
AJAY AGRAWAL

Reputation: 67

Incorrect result in SHACL validation

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

Answers (1)

Vladimir Alexiev
Vladimir Alexiev

Reputation: 176

There are several remaining problems:

  • You may need to add a type: 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
  • You must be very clear what is $this in the query: it's the target node (bot:Element or a Wall, but definitely not a geometry)
  • Both of your walls look for geo:hasGeometry $this, but it's not possible for 2 walls to have the same geometry, so that fails
  • sh: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 variables

See 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

Related Questions