Structured Values and Value References

When modeling product features, we sometimes encounter composite values, like

  • "1024 x 768 pixels resolution",
  • "8 litre of gasoline per 100 km/h", or
  • "a pressure of 101.325 kPa at 20 °C" .

There are several ways of handling those cases.

Case 1: Independent Properties

In many cases, the parameters are independent from each other and can simply represented by multiple properties, which will be sub-properties of gr:quantitativeProductOrServiceProperty.

For example, the vertical and horizontal resolution of a smartphone can be modeled using two properties foo:resolutionVertical and foo:resolutionHorizontal:

foo:resolutionVertical a owl:ObjectProperty;
rdfs:subPropertyOf gr:quantitativeProductOrServiceProperty;
rdfs:label "vertical resolution"@en;
rdfs:comment "The vertical resolution of the device"@en;
rdfs:domain gr:ProductOrService;
rdfs:range gr:QuantitativeValue .
 
foo:resolutionHorizontal a owl:ObjectProperty;
rdfs:subPropertyOf gr:quantitativeProductOrServiceProperty;
rdfs:label "horizontal resolution"@en;
rdfs:comment "The horizontal resolution of the device"@en;
rdfs:domain gr:ProductOrService;
rdfs:range gr:QuantitativeValue .
 
foo:Smartphone a owl:Class;
rdfs:subClassOf gr:ProductOrService;
rdfs:label "Smartphone"@en;
rdfs:comment "A Smartphone"@en .

Then, we can define those two properties for a given product:

foo:mySmartphone a foo:Smartphone, gr:Individual;
gr:name "ACME Smartphone"@en;
gr:description "The ACME Smartphone has a screen resolution of 1024x768 pixel"@en;
foo:resolutionHorizontal
[ a gr:QuantitativeValue;
gr:hasValue 1024;
gr:hasUnitOfMeasurement "E37" ]; # UN/CEFACT Common Code for "pixel"
foo:resolutionVertical
[ a gr:QuantitativeValue;
gr:hasValue 768;
gr:hasUnitOfMeasurement "E37" ] . # UN/CEFACT Common Code for "pixel"

This approach is the simplest and can be understood by most clients. The downside is that it cannot be used to model multiple alternative resolutions (e.g. "supports 800x600 and 1024x768 pixel") and it does not represent that one model depends on the other.

Case 2: Value References and Dependencies

Also frequent are cases in which one main value is given with reference to one or more secondary values that influence the measurement or provide context. For example,

  • the pressure of a given gas depends on the temperature when measuring it or
  • the fuel consumption in litres is given for a distance of 100 km at an average speed of 80 km/h or for the driving pattern "urban".

In all of these cases, the basic pattern is as follows:

  1. You define an owl:ObjectProperty which is a rdfs:subPropertyOf of either gr:quantitativeProductOrServiceProperty or gr:qualitativeProductOrServiceProperty. This is for representing the respective product feature, e.g. "fuel consumption" or "pressure".
  2. You define an owl:ObjectProperty which is a rdfs:subPropertyOf of gr:valueReference. This is for representing the reference from the main value to the secondary value providing context or calibration, e.g. "reference speed" or "reference temperature". You could also have multiple such properties.
  3. You attach the main value as an instance of gr:QuantitativeValue (or gr:QualitativeValue) to the product via the first property.
  4. You attach the secondary value or values to the primary value (!) via the respective sub-property of gr:valueReference.

Here is a practical example from the automotive industry, as actually used by Volkswagen.

We use the following two prefixes:
@prefix vvo: <http://purl.org/vvo/ns#> . # Volkswagen Vehicles Ontology
@prefix vso: <http://purl.org/vso/ns#> . # Vehicle Sales Ontology

First we define a property for the main product feature, in this case the fuel consumption:

vso:fuelConsumption a owl:ObjectProperty ;
rdfs:subPropertyOf gr:quantitativeProductOrServiceProperty ;
rdfs:label "fuelConsumption "@en ;
rdfs:domain [ a owl:Class ;
owl:unionOf (vso:MotorizedRoadVehicle vso:Boat vso:Ship) ] ;
rdfs:range gr:QuantitativeValue .

Then we define two properties for linking from the fuel consumption in litres (the main value) to the reference distance (typically 100 km) and the reference speed (e.g. an average of 80 km/h):

vso:referenceDistance a owl:ObjectProperty ;
rdfs:subPropertyOf gr:valueReference ;
rdfs:label "referenceDistance"@en ;
rdfs:comment "The distance to which a vso:fuelConsumption value refers"@en ;
rdfs:domain gr:QuantitativeValue ;
rdfs:range gr:QuantitativeValue .
 
vso:referenceSpeed a owl:ObjectProperty ;
rdfs:subPropertyOf gr:valueReference ;
rdfs:label "referenceSpeed"@en ;
rdfs:comment "The speed to which a vso:fuelConsumption value refers"@en ;
rdfs:domain gr:QuantitativeValue ;
rdfs:range gr:QuantitativeValue .

This allows us to model that a particular car make and model has a fuel consumption of 7.3 litres per 100 km at a speed of 80 km/h:

foo:myCar a vso:VWGolf, gr:ProductOrServiceModel ;
vso:fuelConsumption [ a gr:QuantitativeValueFloat ;
gr:hasValueFloat "7.3"^^xsd:float ;
gr:hasUnitOfMeasurement "LTR"^^xsd:string ; # litres
vso:referenceDistance [
a gr:QuantitativeValueFloat ;
gr:hasValueFloat "100"^^xsd:float ;
gr:hasUnitOfMeasurement "KMT"^^xsd:string ] ;
vso:referenceSpeed [
a gr:QuantitativeValueFloat ;
gr:hasValueFloat "80"^^xsd:float ;
gr:hasUnitOfMeasurement "KMH"^^xsd:string ]
] .
One can also use this with qualitative values (gr:QualitativeValue instances), e.g. if we want to refer to a driving pattern "urban", which represents typical car usage inside a city. In this case, we define a class for grouping such values as a subclass of gr:QualitativeValue, in this case for traffic patterns:
vvo:TrafficPattern a owl:Class ;
rdfs:subClassOf gr:QualitativeValue ;
rdfs:label "Traffic pattern value (the class of predefined values)"@en ;
rdfs:comment """A value indicating a traffic pattern, to be used as a reference for
vso:fuelConsumption or vso:fuelEconomy"""@en .
Then we define one or multiple traffic patterns as instances of this class:
vvo:Urban a vvo:TrafficPattern ;
rdfs:label "Urban (vvo:TrafficPattern)"@en ;
rdfs:comment "Urban usage as a traffic pattern"@en ;
rdfs:isDefinedBy <http://purl.org/vvo/ns#> .

Then we define an owl:ObjectProperty which is a rdfs:subPropertyOf of gr:valueReference. This is for representing the reference from the main value to the secondary value providing context or calibration, in this case the traffic pattern:

vvo:refersToTrafficPattern a owl:ObjectProperty ;
rdfs:subPropertyOf gr:valueReference ;
rdfs:label "refersToTrafficPattern"@en ;
rdfs:comment """Indicates the traffic pattern to which the vso:fuelEconomy or
vso:fuelConsumption value refers"""@en ;

rdfs:domain gr:QuantitativeValue ;
rdfs:range vvo:TrafficPattern.

Now we can say that the car has a fuel consumption of 7.3 litres per 100 km when used according to the traffic pattern "urban".

foo:myCar a vso:VWGolf, gr:ProductOrServiceModel ;
vso:fuelConsumption [ a gr:QuantitativeValueFloat ;
gr:hasValueFloat "7.3"^^xsd:float ;
gr:hasUnitOfMeasurement "LTR"^^xsd:string ; # litres
vso:referenceDistance [
a gr:QuantitativeValueFloat ;
gr:hasValueFloat "100"^^xsd:float ;
gr:hasUnitOfMeasurement "KMT"^^xsd:string ] ;
vvo:refersToTrafficPattern vvo:Urban ]
] .

Case 3: Structured Values

The third class of cases has multiple product features that belong together, e.g. tuples. Practical examples are supported screen resolutions (800x600 and 1024x768).

Here is how this goes in GoodRelations:

First, we define a class for screen resolutions as a subclass of gr:QualitativeValue:

foo:ScreenResolution a owl:Class ;
rdfs:subClassOf gr:QualitativeValue .
Then we define a property for the supported resolutions of a device:
foo:supportedResolution a owl:ObjectProperty ;
rdfs:subPropertyOf gr:qualitativeProductOrServiceProperty ;
rdfs:domain gr:ProductOrService ;
rdfs:range foo:ScreenResolution .

Then we define two properties for linking from the resolution value to its horizontal and vertical part. Note that those are sub-properties of gr:valueReference, not gr:quantitativeProductOrServiceProperty, because they link values to values, not values to products.

foo:resolutionVertical a owl:ObjectProperty ;
rdfs:subPropertyOf gr:valueReference ;
rdfs:label "vertical resolution"@en ;
rdfs:comment "The vertical resolution of this screen resolution"@en ;
rdfs:domain foo:ScreenResolution ;
rdfs:range gr:QuantitativeValue .
 
foo:resolutionHorizontal a owl:ObjectProperty ;
rdfs:subPropertyOf gr:valueReference ;
rdfs:label "horizontal resolution"@en ;
rdfs:comment "The horizontal resolution of this screen resolution"@en ;
rdfs:domain foo:ScreenResolution ;
rdfs:range gr:QuantitativeValue .

Now we can use this to model that a certain projector supports the two resolutions of 1024x768 and 800x600:

foo:myProjector a foo:Projector, gr:Individual ;
gr:name "ACME Projector"@en ;
gr:description """The ACME Projector supports the two screen resolutions of 1024x768
and 800x600 pixel"""@en ;

foo:supportedResolution [
a foo:ScreenResolution ;
foo:resolutionHorizontal
[ a gr:QuantitativeValue ;
gr:hasValue 1024 ;
gr:hasUnitOfMeasurement "E37"^^xsd:string ] ;
# UN/CEFACT Common Code for "pixel"
foo:resolutionVertical
[ a gr:QuantitativeValue ;
gr:hasValue 768 ;
gr:hasUnitOfMeasurement "E37"^^xsd:string ]
# UN/CEFACT Common Code for "pixel"
] ;
foo:supportedResolution [
a foo:ScreenResolution ;
foo:resolutionHorizontal
[ a gr:QuantitativeValue ;
gr:hasValue 800 ;
gr:hasUnitOfMeasurement "E37"^^xsd:string ] ;
# UN/CEFACT Common Code for "pixel"
foo:resolutionVertical
[ a gr:QuantitativeValue ;
gr:hasValue 600 ;
gr:hasUnitOfMeasurement "E37"^^xsd:string ]
# UN/CEFACT Common Code for "pixel"
] .

You are all set ;-)

by Martin Hepp