Browse Source

Merge branch 'main' of github.com:arrys/digital-twin-design

Arkadiusz Ryś 2 years ago
parent
commit
d5f83a9e05

+ 2 - 2
examples/oml/car_example/.fuseki.ttl

@@ -8,8 +8,8 @@
 [] rdf:type fuseki:Server .
 
 <#service> rdf:type fuseki:Service ;
-    rdfs:label          "Car Example" ;												# Human readable label for dataset
-    fuseki:name         "car_example" ;												# Name of the dataset in the endpoint url
+    rdfs:label          "Car" ;												# Human readable label for dataset
+    fuseki:name         "car" ;												# Name of the dataset in the endpoint url
     fuseki:serviceReadWriteGraphStore "data" ;											# SPARQL Graph store protocol (read and write)
     fuseki:endpoint 	[ fuseki:operation fuseki:query ;	fuseki:name "sparql"  ] ;	# SPARQL query service
     fuseki:endpoint 	[ fuseki:operation fuseki:shacl ;	fuseki:name "shacl" ] ;		# SHACL query service

+ 71 - 11
examples/oml/car_example/build.gradle

@@ -3,7 +3,7 @@
  */
 ext.title = 'Car Example'
 description = 'This is a car example'
-group = 'com.example'
+group = 'one.rys.ontology'
 version = '1.0.0'
 
 /* 
@@ -20,23 +20,32 @@ buildscript {
 		mavenCentral()
 	}
 	dependencies {
-        classpath 'io.opencaesar.owl:owl-fuseki-gradle:+'
+		classpath 'io.opencaesar.owl:owl-fuseki-gradle:+'
+        classpath 'io.opencaesar.owl:owl-shacl-fuseki-gradle:+'
         classpath 'io.opencaesar.owl:owl-query-gradle:+'
         classpath 'io.opencaesar.owl:owl-load-gradle:+'
         classpath 'io.opencaesar.owl:owl-reason-gradle:+'
-        classpath 'io.opencaesar.oml:oml-merge-gradle:+'
-        classpath 'io.opencaesar.adapters:oml2owl-gradle:+'
+        classpath 'io.opencaesar.oml:oml-bikeshed-gradle:1.+'
+        classpath 'io.opencaesar.oml:oml-merge-gradle:1.+'
+        classpath 'io.opencaesar.adapters:oml2owl-gradle:1.+'
 	}
 }
 
+/*
+ * Bikeshed folder
+ */
+ext {
+	bikeshed = 'build/bikeshed'
+}
+
 /*
  * Dataset-specific variables
  */
 ext.dataset = [
     // Name of dataset (matches one used in .fuseki.ttl file)
-    name: 'car_example',
+    name: 'car',
     // Root ontology IRI of the dataset
-    rootOntologyIri: 'http://example.com/project/bundle',
+    rootOntologyIri: 'http://ontology.rys.one/project/vocabulary/bundle',
 ]
 
 /*
@@ -68,6 +77,14 @@ dependencies {
     oml "io.opencaesar.ontologies:core-vocabularies:$coreVersion"
 }
 
+/*
+ * A task to extract and merge the OML dependencies
+ */
+task downloadDependencies(type:io.opencaesar.oml.merge.OmlMergeTask) {
+    inputZipPaths = configurations.oml.files
+    outputCatalogFolder = file('build/oml')
+}
+
 /*
  * A task to extract and merge the OML dependencies
  */
@@ -76,6 +93,40 @@ task omlDependencies(type:io.opencaesar.oml.merge.OmlMergeTask, group:"oml") {
     outputCatalogFolder = file('build/oml')
 }
 
+/*
+ * A task to generate Bikeshed specification for the OML catalog
+ */
+task omlToBikeshed(type: io.opencaesar.oml.bikeshed.Oml2BikeshedTask, dependsOn: downloadDependencies) {
+    // OML catalog
+    inputCatalogPath = file('catalog.xml')
+    // OML catalog title
+    inputCatalogTitle = project.title
+    // OML catalog version
+    inputCatalogVersion = project.version
+    // Input Ontology Iri
+    rootOntologyIri = 'http://ontology.rys.one/project/vocabulary/bundle'
+    // OWL folder
+    outputFolderPath = file("$bikeshed")
+    // Publish URL
+    publishUrl = 'https://git.rys.one/dtdesign/car-example'
+}
+
+/*
+ * A task to generate the model documentation in HTML
+ */
+import org.gradle.internal.os.OperatingSystem
+task generateDocs(dependsOn: omlToBikeshed) {
+	inputs.files(fileTree("$bikeshed").include('**/*.bs'))
+	outputs.files(fileTree("$bikeshed").include('**/*.html'))
+    doLast {
+        if (OperatingSystem.current().isWindows()) {
+            exec { commandLine "$bikeshed/publish.bat" }
+        } else {
+            exec { commandLine "$bikeshed/publish.sh" }
+        }
+    }
+}
+
 /*
  * A task to convert the OML catalog to OWL catalog
  */
@@ -143,6 +194,15 @@ task owlQuery(type:io.opencaesar.owl.query.OwlQueryTask, group:"oml", dependsOn:
     format = 'json'
 }
 
+/*
+ * A task to run a set of SHACL validation rules on a Fuseki dataset endpoint
+ */
+task owlShacl(type:io.opencaesar.owl.shacl.fuseki.OwlShaclFusekiTask, group:"oml", dependsOn: owlLoad) {
+    endpointURL = "http://localhost:3030/$dataset.name".toString()
+    queryPath = file('src/shacl')
+    resultPath = file('build/reports')
+}
+
 /*
  * A task to build the project, which executes several tasks together
  */
@@ -178,13 +238,13 @@ def pomConfig = {
     }
     developers {
         developer {
-            id "melaasar"
-            name "Maged Elaasar"
-            email "melaasar@gmail.com"
+            id "arrys"
+            name "Arkadiusz Michał Ryś"
+            email "Arkadiusz.Michal.Rys@gmail.com"
         }
     }
     scm {
-        url 'https://github.com/opencaesar/'+rootProject.name
+        url 'https://git.rys.one/dtdesign/car-example'
     }
 }
 
@@ -214,7 +274,7 @@ publishing {
                     }
                     root.appendNode('name', project.ext.title)
                     root.appendNode('description', project.description)
-                    root.appendNode('url', 'https://github.com/opencaesar/'+rootProject.name)
+                    root.appendNode('url', 'https://git.rys.one/dtdesign/car-example')
                     root.children().last() + pomConfig
                 }
             }

+ 1 - 1
examples/oml/car_example/catalog.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
 <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="public">
-	<rewriteURI uriStartString="http://example.com/project/" rewritePrefix="src/oml/example.com/project/"/>
+	<rewriteURI uriStartString="http://ontology.rys.one/project/" rewritePrefix="src/oml/ontology.rys.one/project/"/>
 	<rewriteURI uriStartString="http://" rewritePrefix="build/oml/"/>
 </catalog>

+ 0 - 2
examples/oml/car_example/src/oml/example.com/project/bundle.oml

@@ -1,2 +0,0 @@
-description bundle <http://example.com/project/bundle#> as ^bundle {
-}

+ 0 - 3
examples/oml/car_example/src/oml/example.com/project/description/bundle.oml

@@ -1,3 +0,0 @@
-description bundle <http://example.com/project/description/bundle#> as ^bundle {
-	
-}

+ 0 - 4
examples/oml/car_example/src/oml/example.com/project/description/cars.oml

@@ -1,4 +0,0 @@
-description <http://example.com/project/description/cars#> as cars {
-	ci car1 ce:Car
-	
-}

+ 0 - 4
examples/oml/car_example/src/oml/example.com/project/vocabulary/car-bundle.oml

@@ -1,4 +0,0 @@
-vocabulary bundle <http://example.com/project/vocabulary/car-bundle#> as car-bundle {
-    includes <http://purl.org/dc/elements/1.1/> as dc
-    includes <http://example.com/project/vocabulary/car#>
-}

+ 0 - 57
examples/oml/car_example/src/oml/example.com/project/vocabulary/car.oml

@@ -1,57 +0,0 @@
-vocabulary <http://example.com/project/vocabulary/car#> as car {
-	extends <http://www.w3.org/2001/XMLSchema#> as xsd
-    extends <http://purl.org/dc/elements/1.1/> as dc
-    extends <http://www.w3.org/2000/01/rdf-schema#> as rdfs
-    
-    
-    aspect Vehicle [
-    	key hasVin
-    ]
-    
-    scalar property hasVin [
-    	domain Vehicle
-    	range xsd:string
-    	functional
-    ]
-    
-    scalar property hasModel [
-    	domain Vehicle
-    	range xsd:string
-    ]
-    
-    scalar property hasManufacturer [
-    	domain Vehicle
-    	range xsd:string
-    ]
-    
-    concept Car :> Vehicle [
-    	
-    ]
-    
-    relation entity HasModel [
-    	from Car
-    	to Wheel
-    	forward hasWheel
-    	reverse isOnCar
-    ]
-    
-    aspect VPart [
-    	
-    ]
-    
-    concept Wheel :> VPart [
-    	
-    ]
-    
-    scalar property hasMass [
-    	domain Wheel
-    	range xsd:double
-    ]
-    
-    relation entity HasWheel [
-    	from Car
-    	to Wheel
-    	forward hasWheel
-    	reverse isOnCar
-    ]
-}

+ 5 - 0
examples/oml/car_example/src/oml/ontology.rys.one/project/description/bundle.oml

@@ -0,0 +1,5 @@
+description bundle <http://ontology.rys.one/project/description/bundle#> as description-bundle {
+	//uses <http://purl.org/dc/elements/1.1/> as dc	
+	uses <http://ontology.rys.one/project/vocabulary/bundle#>
+	includes <http://ontology.rys.one/project/description/cars#>
+}

+ 26 - 0
examples/oml/car_example/src/oml/ontology.rys.one/project/description/cars.oml

@@ -0,0 +1,26 @@
+description <http://ontology.rys.one/project/description/cars#> as cars {
+	//uses <http://purl.org/dc/elements/1.1/> as dc
+	uses <http://ontology.rys.one/project/vocabulary/car#> as car
+	
+	ci tire1  : car:WinterTire []
+	ci wheel1 : car:Wheel [car:hasMass 1.0 car:hasTire tire1]
+	ci wheel2 : car:Wheel [car:hasMass 1.0]
+	ci wheel3 : car:Wheel [car:hasMass 1.0]
+	ci wheel4 : car:Wheel [car:hasMass 1.0]
+	ci model1 : car:Model [car:hasModelName "CAR"]
+	ci car1 : car:Car [
+		car:hasVin "1"
+		car:hasModel model1
+		car:hasWheel wheel1
+		car:hasWheel wheel2
+		car:hasWheel wheel3
+		car:hasWheel wheel4
+	]
+	
+	//ri hasWheel1 : car:HasWheel [from car1 to wheel1]
+	//ri hasWheel2 : car:HasWheel [from car1 to wheel2]
+	//ri hasWheel3 : car:HasWheel [from car1 to wheel3]
+	//ri hasWheel4 : car:HasWheel [from car1 to wheel4]
+	//ri hasModel1 : car:HasModel [from car1 to model1]
+	//ri hasTire1 : car:HasTire [from wheel1 to tire1]
+}

+ 7 - 0
examples/oml/car_example/src/oml/ontology.rys.one/project/vocabulary/bundle.oml

@@ -0,0 +1,7 @@
+//@dc:title "Simple car ontology"
+//@dc:creator "Arkadiusz Michał Ryś"
+//@dc:rights "Copyright 2022"
+vocabulary bundle <http://ontology.rys.one/project/vocabulary/bundle#> as vocabulary-bundle {
+    //includes <http://purl.org/dc/elements/1.1/> as dc
+    includes <http://ontology.rys.one/project/vocabulary/car#>
+}

+ 73 - 0
examples/oml/car_example/src/oml/ontology.rys.one/project/vocabulary/car.oml

@@ -0,0 +1,73 @@
+vocabulary <http://ontology.rys.one/project/vocabulary/car#> as car {
+	extends <http://www.w3.org/2001/XMLSchema#> as xsd
+    //extends <http://purl.org/dc/elements/1.1/> as dc
+    extends <http://www.w3.org/2000/01/rdf-schema#> as rdfs
+    
+    @rdfs:comment "The class of vehicles that are uniquely identified by a vehicle identification number."
+    aspect Vehicle [
+    	key hasVin
+    ]
+    aspect VPart []
+    concept Car :> Vehicle []
+    concept Model []
+    concept Wheel :> VPart []
+    concept Tire :> VPart []
+	
+    scalar property hasVin [
+    	domain Vehicle
+    	range xsd:string
+    	functional
+    ]
+    
+    scalar property hasModelName [
+    	domain Model
+    	range xsd:string
+    ]
+    
+    //scalar property hasManufacturer [
+    //	domain Vehicle
+    //	range xsd:string
+    //]
+    
+    scalar property hasMass [
+    	domain Wheel
+    	range xsd:decimal
+    ]
+	
+	enumerated scalar TireType [
+        "Winter",
+        "Summer",
+        "All-Season"
+    ]
+	
+	scalar property hasTireType [
+	    domain Tire
+		range TireType
+		functional
+	]
+	
+	concept WinterTire :> Tire [
+	    restricts scalar property hasTireType to "Winter"
+	]
+	    
+    relation entity HasModel [
+    	from Car
+    	to Model
+    	forward hasModel
+    	reverse isModelOfCar
+    ]
+
+    relation entity HasWheel [
+    	from Car
+    	to Wheel
+    	forward hasWheel
+    	reverse isWheelOnCar
+    ]
+	
+	relation entity HasTire [
+    	from Wheel
+    	to Tire
+    	forward hasTire
+    	reverse isTireOnWheel
+    ]
+}

+ 124 - 0
examples/oml/car_example/src/shacl/example.ttl

@@ -0,0 +1,124 @@
+@prefix sh: <http://www.w3.org/ns/shacl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix metrology: <http://iupac.org/metrology#> .
+
+# The namespace for metrology shacl shapes.
+@prefix metrology-shapes: <http://iupac.org/metrology/metrology-shapes#> .
+
+# The following defines `metrology-shapes` as the ontology for sh:declare prefixes to be added to each SPARQL query.
+metrology-shapes:
+	a owl:Ontology ;
+	owl:imports sh: ;
+	sh:declare [
+		sh:prefix "metrology" ;
+		sh:namespace "http://iupac.org/metrology#"^^xsd:anyURI ;
+	] ;
+	sh:declare [
+		sh:prefix "owl" ;
+		sh:namespace "http://www.w3.org/2002/07/owl#"^^xsd:anyURI ;
+	] ;
+	sh:declare [
+		sh:prefix "rdf" ;
+		sh:namespace "http://www.w3.org/1999/02/22-rdf-syntax-ns#"^^xsd:anyURI ;
+	] .
+	
+metrology-shapes:Every-Quantity-characterizes-some-Object
+  a sh:NodeShape ;
+  sh:targetClass metrology:Quantity ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a metrology:Quantity, must be a quantity for some metrology:Object." ;
+    sh:prefixes metrology-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this
+      WHERE {
+        $this rdf:type metrology:Quantity .
+        FILTER NOT EXISTS {
+          $this metrology:isQuantityOf ?o .
+          ?o rdf:type metrology:Object .
+        }
+      }
+    """ ;
+  ] ;
+  sh:closed false .
+
+metrology-shapes:Every-QuantityValue-has-Number
+  a sh:NodeShape ;
+  sh:targetClass metrology:QuantityValue ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a metrology:QuantityValue, must have a number via metrology:hasDoubleNumber." ;
+    sh:prefixes metrology-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this
+      WHERE {
+        $this rdf:type metrology:QuantityValue .
+        FILTER NOT EXISTS {
+          $this metrology:hasDoubleNumber ?value .
+        }
+      }
+    """ ;
+  ] ;
+  sh:closed false .
+
+metrology-shapes:Every-QuantityValue-has-Reference
+  a sh:NodeShape ;
+  sh:targetClass metrology:QuantityValue ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a metrology:QuantityValue, must have a number via metrology:hasReference." ;
+    sh:prefixes metrology-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this
+      WHERE {
+        $this rdf:type metrology:QuantityValue .
+        FILTER NOT EXISTS {
+          $this metrology:hasReference ?ref .
+        }
+      }
+    """ ;
+  ] ;
+  sh:closed false .
+
+metrology-shapes:Every-UnitaryQuantity-has-UnitaryKindOfQuantity
+  a sh:NodeShape ;
+  sh:targetClass metrology:UnitaryQuantity ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a metrology:UnitaryQuantity, must have a metrology:UnitaryKindOfQuantity." ;
+    sh:prefixes metrology-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this
+      WHERE {
+        $this rdf:type metrology:UnitaryQuantity .
+        FILTER NOT EXISTS {
+          $this metrology:hasKindOfQuantity ?qk .
+          ?qk rdf:type metrology:UnitaryKindOfQuantity .
+        }
+      }
+    """ ;
+  ] ;
+  sh:closed false .
+
+metrology-shapes:Every-UnitaryQuantity-hasMagnitude-UnitaryQuantityValue
+  a sh:NodeShape ;
+  sh:targetClass metrology:UnitaryQuantity ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a metrology:UnitaryQuantity, must have a magnitue of a metrology:UnitaryQuantityValue." ;
+    sh:prefixes metrology-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this
+      WHERE {
+        $this rdf:type metrology:UnitaryQuantity .
+        FILTER NOT EXISTS {
+          $this metrology:hasMagnitude ?uqv .
+          ?uqv rdf:type metrology:UnitaryQuantityValue .
+        }
+      }
+    """ ;
+  ] ;
+  sh:closed false .

+ 162 - 0
examples/oml/car_example/src/shacl/validation.ttl

@@ -0,0 +1,162 @@
+@prefix sh:   <http://www.w3.org/ns/shacl#> .
+@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .
+@prefix owl:  <http://www.w3.org/2002/07/owl#> .
+
+@prefix car:        <http://ontology.rys.one/project/vocabulary/car#> .
+@prefix car-shapes: <http://ontology.rys.one/project/vocabulary/car-shapes#> .
+
+
+# SHACL-SPARQL supports writing SPARQL by separating two parts:
+# 1) the SPARQL prologue corresponding to instances of sh:declare individuals available in the sh:prefixes ontology.
+#    See: https://www.w3.org/TR/sparql11-query/#rPrologue 
+# 2) the SPARQL query statement: select or ask. 
+#    See: https://www.w3.org/TR/sparql11-query/#rSelectQuery
+#    See: https://www.w3.org/TR/sparql11-query/#rAskQuery
+
+# The following defines `car-shapes` as the ontology for sh:declare prefixes to be added to each SPARQL query.
+car-shapes:
+  a owl:Ontology ;
+  owl:imports sh: ;
+  sh:declare [
+    sh:prefix "car" ;
+    sh:namespace "http://ontology.rys.one/project/vocabulary/car#"^^xsd:anyURI ;
+  ] ;
+  sh:declare [
+    sh:prefix "rdf" ;
+    sh:namespace "http://www.w3.org/1999/02/22-rdf-syntax-ns#"^^xsd:anyURI ;
+  ] .
+  
+# -------------------------------------------------------------
+# Constraint 1 (SHACL-CORE): A car:Car must car:hasModel some (1) car:Model.
+# -------------------------------------------------------------
+
+car-shapes:Car_HasModel_some_Model_shacl
+  a sh:NodeShape ;
+  sh:targetClass car:Car ;
+  sh:property [
+    sh:path car:hasModel ;
+    sh:class car:Model ;
+    #sh:nodeKind sh:IRI ;
+    sh:minCount 1 ;
+	sh:maxCount 1 ;
+  ] ;
+  sh:closed false .
+  
+car-shapes:Wheel_has_mass_shacl
+  a sh:NodeShape ;
+  sh:targetClass car:Wheel ;
+  sh:property [
+    sh:path car:hasMass ;
+    #sh:class xsd:decimal ;
+    #sh:nodeKind sh:IRI ;
+    sh:minCount 1 ;
+	sh:maxCount 1 ;
+  ] ;
+  sh:closed false .
+  
+# -------------------------------------------------------------
+# Constraint 1 (SHACL-SPARQL): A car:Car must car:hasModel some car:Model.
+# -------------------------------------------------------------
+
+car-shapes:Car_HasModel_some_Model_sparql
+  a sh:NodeShape ;
+  sh:targetClass car:Car ;
+  sh:sparql [
+    a sh:SPARQLConstraint ;
+    sh:message "{$this}, a car:Car, must have a model of some car:Model." ;
+    sh:prefixes car-shapes: ;
+    sh:select """
+      SELECT DISTINCT $this ?model
+      WHERE {
+        $this rdf:type car:Car .
+        FILTER NOT EXISTS { 
+          $this car:hasModel ?model .
+          ?model rdf:type car:Model .
+        }
+      }
+      """ ;
+  ] ;
+  sh:closed false .
+  
+  
+# -------------------------------------------------------------
+# Constraint 2 (SHACL-CORE): A car:Wheel must be car:isWheelOnCar exactly 1 car:Car.
+# -------------------------------------------------------------
+
+#car-shapes:Wheel_is_wheel_on_car_by_one_Car_shacl
+#  a sh:NodeShape ;
+#  sh:targetClass car:Wheel ;
+#  sh:property [
+#    sh:path car:isWheelOnCar ;
+#    sh:class car:Car ;
+#    sh:nodeKind sh:IRI ;
+#    sh:minCount 1 ;
+#    sh:maxCount 1 ;
+#  ] ;
+#  sh:closed false .
+
+
+car-shapes:Test_shacl
+  a sh:NodeShape ;
+  sh:targetClass car:Car ;
+  sh:property [
+    sh:path car:hasModel ;
+    sh:class car:Model ;
+    #sh:nodeKind sh:IRI ;
+    sh:minCount 1 ;
+	sh:maxCount 1 ;
+  ] ;
+  sh:closed false .
+  
+
+# -------------------------------------------------------------
+# Constraint 2 (SHACL-SPARQL): A car:Wheel must be car:isWheelOnCar exactly 1 car:Car.
+# -------------------------------------------------------------
+  
+#car-shapes:Wheel_is_wheel_on_car_by_one_Car_sparql
+#  a sh:NodeShape ;
+#  sh:targetClass car:Wheel ;
+#  sh:sparql [
+#    a sh:SPARQLConstraint ;
+#    sh:message "{$this}, an car:Wheel, must be on exactly 1 car:Car instead of {?c}." ;
+#    sh:prefixes car-shapes: ;
+#    sh:select """
+#      SELECT DISTINCT $this (COUNT(?woc) AS ?c)
+#      WHERE {
+#        $this rdf:type car:Wheel .
+#        OPTIONAL {
+#          $this car:isWheelOnCar ?woc .
+#        }
+#      }
+#      GROUP BY $this
+#      HAVING (COUNT(?woc) != 1)
+#      """ ;
+#  ] ;
+#  sh:closed false .
+
+# -------------------------------------------------------------
+# Constraint 3 (SHACL-SPARQL): A car:Car must have n car:Wheel with a total mass equal to 4.0.
+# -------------------------------------------------------------
+
+car-shapes:Wheels_have_4.0_mass 
+  a sh:NodeShape ;
+  sh:targetClass car:Car ;
+  sh:property [
+    sh:path ( car:hasWheel car:hasMass ) ;
+    sh:severity sh:Warning ;
+    sh:sparql [
+      a sh:SPARQLConstraint ;
+      sh:message "Total car:Wheel mass should sum to 4.0." ;
+      sh:prefixes car: ;
+      sh:select """
+        SELECT $this (sum(?mass) as ?value) {
+          $this $PATH ?mass .
+        }
+        GROUP BY $this
+        HAVING (sum(?mass) != 4.0)
+        """
+    ]
+  ] ;
+  sh:closed false .

+ 7 - 0
examples/oml/car_example/src/sparql/cars.sparql

@@ -0,0 +1,7 @@
+PREFIX car:  <http://ontology.rys.one/project/vocabulary/car#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT ?car
+WHERE {
+	?car a car:Car
+}

+ 7 - 0
examples/oml/car_example/src/sparql/compositions.sparql

@@ -0,0 +1,7 @@
+PREFIX car:  <http://ontology.rys.one/project/vocabulary/car#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT ?car ?wheel
+WHERE {
+	?car car:hasWheel ?wheel .
+}

+ 7 - 0
examples/oml/car_example/src/sparql/whichwinter.sparql

@@ -0,0 +1,7 @@
+PREFIX car:  <http://ontology.rys.one/project/vocabulary/car#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT DISTINCT ?wheel
+WHERE {
+    ?wheel car:hasTire [car:hasTireType "Winter"]
+}