Skip to content

Commit 59f7b4e

Browse files
Feature/13 coordinate vars (#29)
* Refactor URI responsibility onto Container and Var interfaces * Add NetCDF reference path concept and impl * Add method to alias interface to determine whether a property is a reference * Add variable reference vocab * Add Model context concept combining prefix mappings and aliases Move alias definition class to context package * Refactor attribute URI responsibility onto interface Refactor binary array impl to incorporate model context * Misc changes * Remove unused classes * Add integration test * Restore tests for refactored model context functionality * Add test for var reference functionality on netcdf impl Add binary array test utils * Refactor existing tests to use test utils * Add missing comments * Test and fix circular reference error * Add dimension concept and NetCDF impl * Add dimensions to model output * Fix test, remove obsolete test * Add coordinate range concept and netcdf impl * Add coordinate range to model output * Set array type on dimensional variables * Add single and multi valued variable reference parser * Add multi variable parsing on NetCDF attribute * Add multiple variable references to model output * Add integration test * Fix test * Add BALD reference vocab * Add RDF list verification method * Add coordinate reference to model output * Add coordinate reference to NetCDF dimensions * Add dimension coordinate test * Update integration test to include references * Add single and multi valued variable reference parser * Add multi variable parsing on NetCDF attribute * Add multiple variable references to model output * Add integration test * Fix resource references * Fix tests sorting blank nodes * Doc comments * Order subcontainers deterministically in tests * Set theme jekyll-theme-slate * Create index.md Initialise GH pages * Update property usage on coordinate var references
1 parent 2bc5de0 commit 59f7b4e

File tree

25 files changed

+601
-71
lines changed

25 files changed

+601
-71
lines changed

binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import bald.jsonld.ResourceFileConverter
55
import bald.model.ModelVerifier
66
import bald.netcdf.CdlConverter.writeToNetCdf
77
import net.bald.vocab.BALD
8+
import org.apache.jena.datatypes.xsd.XSDDatatype
89
import org.apache.jena.rdf.model.ModelFactory.createDefaultModel
10+
import org.apache.jena.rdf.model.Resource
911
import org.apache.jena.rdf.model.ResourceFactory.*
1012
import org.apache.jena.riot.RiotException
1113
import org.apache.jena.vocabulary.*
@@ -360,14 +362,11 @@ class BinaryArrayConvertCliTest {
360362
statement(RDF.type, BALD.Container)
361363
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/")) {
362364
statement(TestVocab.orderedVar) {
363-
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/var0"))
364-
statement(RDF.rest) {
365-
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/foo/bar/var2"))
366-
statement(RDF.rest) {
367-
statement(RDF.first, createResource("http://test.binary-array-ld.net/example/baz/var3"))
368-
statement(RDF.rest, RDF.nil)
369-
}
370-
}
365+
list(
366+
createResource("http://test.binary-array-ld.net/example/var0"),
367+
createResource("http://test.binary-array-ld.net/example/foo/bar/var2"),
368+
createResource("http://test.binary-array-ld.net/example/baz/var3")
369+
)
371370
}
372371
statement(TestVocab.rootVar, createResource("http://test.binary-array-ld.net/example/var0"))
373372
statement(TestVocab.unorderedVar, createResource("http://test.binary-array-ld.net/example/foo/bar/var2"))
@@ -407,4 +406,75 @@ class BinaryArrayConvertCliTest {
407406
}
408407
}
409408
}
409+
410+
@Test
411+
fun run_withCoordinateVars_outputsCoordinateRanges() {
412+
val aliasFile = ResourceFileConverter.toFile("/turtle/alias.ttl", "ttl")
413+
val inputFile = writeToNetCdf("/netcdf/coordinate-var.cdl")
414+
val outputFile = createTempFile()
415+
416+
run(
417+
"--uri", "http://test.binary-array-ld.net/example",
418+
"--alias", aliasFile.absolutePath,
419+
inputFile.absolutePath,
420+
outputFile.absolutePath
421+
)
422+
423+
fun sortAnon(res: Resource): String {
424+
return if (res.hasProperty(BALD.target)) {
425+
res.getProperty(BALD.target).`object`.toString()
426+
} else {
427+
res.id.toString()
428+
}
429+
}
430+
431+
val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl")
432+
ModelVerifier(model).apply {
433+
resource("http://test.binary-array-ld.net/example") {
434+
statement(RDF.type, BALD.Container)
435+
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/")) {
436+
statement(RDF.type, BALD.Container)
437+
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/elev"), sortAnon = ::sortAnon) {
438+
statement(RDF.type, BALD.Array)
439+
statement(RDFS.label, createPlainLiteral("height"))
440+
statement(BALD.references) {
441+
statement(RDF.type, BALD.Reference)
442+
statement(BALD.target, createResource("http://test.binary-array-ld.net/example/lat"))
443+
statement(BALD.targetRefShape) {
444+
list(createTypedLiteral(15), createTypedLiteral(1))
445+
}
446+
}
447+
statement(BALD.references) {
448+
statement(RDF.type, BALD.Reference)
449+
statement(BALD.target, createResource("http://test.binary-array-ld.net/example/lon"))
450+
statement(BALD.targetRefShape) {
451+
list(createTypedLiteral(1), createTypedLiteral(10))
452+
}
453+
}
454+
statement(BALD.shape) {
455+
list(createTypedLiteral(15), createTypedLiteral(10))
456+
}
457+
}
458+
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/lat")) {
459+
statement(RDF.type, BALD.Array)
460+
statement(RDFS.label, createPlainLiteral("latitude"))
461+
statement(BALD.arrayFirstValue, createTypedLiteral("6.5", XSDDatatype.XSDfloat))
462+
statement(BALD.arrayLastValue, createTypedLiteral("-6.5", XSDDatatype.XSDfloat))
463+
statement(BALD.shape) {
464+
list(createTypedLiteral(15))
465+
}
466+
}
467+
statement(BALD.contains, createResource("http://test.binary-array-ld.net/example/lon")) {
468+
statement(RDF.type, BALD.Array)
469+
statement(RDFS.label, createPlainLiteral("longitude"))
470+
statement(BALD.arrayFirstValue, createTypedLiteral("0.5", XSDDatatype.XSDfloat))
471+
statement(BALD.arrayLastValue, createTypedLiteral("9.5", XSDDatatype.XSDfloat))
472+
statement(BALD.shape) {
473+
list(createTypedLiteral(10))
474+
}
475+
}
476+
}
477+
}
478+
}
479+
}
410480
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package net.bald
2+
3+
/**
4+
* The range of values that is spanned by a coordinate variable.
5+
*/
6+
interface CoordinateRange {
7+
/**
8+
* The first value in the range, if it has one.
9+
* Otherwise, null.
10+
*/
11+
val first: Any?
12+
13+
/**
14+
* The last value in the range, if it has one.
15+
* Otherwise, null.
16+
*/
17+
val last: Any?
18+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package net.bald
2+
3+
/**
4+
* A dimension that specifies the shape of a variable.
5+
*/
6+
interface Dimension {
7+
/**
8+
* The size of the dimension.
9+
*/
10+
val size: Int
11+
12+
/**
13+
* The coordinate variable that corresponds to the dimension, if one exists.
14+
* Otherwise, null.
15+
*/
16+
val coordinate: Var?
17+
}

binary-array-ld-lib/src/main/kotlin/net/bald/Var.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,14 @@ interface Var: AttributeSource {
88
* The URI of the variable.
99
*/
1010
val uri: String
11+
12+
/**
13+
* The coordinate range spanned by the variable, if it has one.
14+
*/
15+
val range: CoordinateRange?
16+
17+
/**
18+
* The dimensions that specify the shape of the variable.
19+
*/
20+
fun dimensions(): Sequence<Dimension>
1121
}

binary-array-ld-lib/src/main/kotlin/net/bald/alias/ModelAliasDefinition.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,8 @@ class ModelAliasDefinition(
6262
clsUris.contains(cls.uri) -> false
6363
else -> {
6464
val nextClsUris = cls.uri?.let(clsUris::plus) ?: clsUris
65-
cls.listProperties(RDFS.subClassOf).let { parents ->
66-
containsReferenceCls(parents, nextClsUris)
67-
}
65+
val parents = cls.listProperties(RDFS.subClassOf)
66+
containsReferenceCls(parents, nextClsUris)
6867
}
6968
}
7069
}

binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelVarBuilder.kt

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,118 @@
11
package net.bald.model
22

33
import net.bald.AttributeSource
4+
import net.bald.Dimension
45
import net.bald.Var
56
import net.bald.vocab.BALD
7+
import org.apache.jena.rdf.model.Model
8+
import org.apache.jena.rdf.model.RDFList
9+
import org.apache.jena.rdf.model.RDFNode
610
import org.apache.jena.rdf.model.Resource
11+
import org.apache.jena.rdf.model.ResourceFactory.createTypedLiteral
12+
import org.apache.jena.vocabulary.RDF
713

814
open class ModelVarBuilder(
915
private val container: Resource,
1016
private val attrFct: ModelAttributeBuilder.Factory
1117
) {
1218
open fun addVar(v: Var) {
13-
val vRes = container.model.createResource(v.uri, BALD.Resource)
19+
val dimBuilder = dimensionBuilder(v)
20+
val vRes = container.model.createResource(v.uri, dimBuilder.type)
1421
container.addProperty(BALD.contains, vRes)
1522
addAttributes(v, vRes)
23+
addCoordinateRange(v, vRes)
24+
dimBuilder.addDimensions(vRes)
1625
}
1726

1827
private fun addAttributes(source: AttributeSource, resource: Resource) {
1928
val builder = attrFct.forResource(resource)
2029
source.attributes().forEach(builder::addAttribute)
2130
}
2231

32+
private fun addCoordinateRange(v: Var, resource: Resource) {
33+
v.range?.let { range ->
34+
range.first?.let(::createTypedLiteral)?.let { first ->
35+
resource.addProperty(BALD.arrayFirstValue, first)
36+
}
37+
range.last?.let(::createTypedLiteral)?.let { last ->
38+
resource.addProperty(BALD.arrayLastValue, last)
39+
}
40+
}
41+
}
42+
43+
private fun dimensionBuilder(v: Var): VarDimensionBuilder {
44+
val dims = v.dimensions().toList()
45+
return if (dims.isEmpty()) {
46+
VarDimensionBuilder.Base
47+
} else {
48+
VarDimensionBuilder.Dimensional(container.model, dims)
49+
}
50+
}
51+
52+
private interface VarDimensionBuilder {
53+
val type: Resource
54+
fun addDimensions(resource: Resource)
55+
56+
object Base: VarDimensionBuilder {
57+
override val type: Resource get() = BALD.Resource
58+
59+
override fun addDimensions(resource: Resource) {
60+
// do nothing
61+
}
62+
}
63+
64+
class Dimensional(
65+
private val model: Model,
66+
private val dims: List<Dimension>
67+
): VarDimensionBuilder {
68+
override val type: Resource get() = BALD.Array
69+
70+
override fun addDimensions(resource: Resource) {
71+
val model = resource.model
72+
val shape = shape()
73+
resource.addProperty(BALD.shape, shape)
74+
75+
dims.forEachIndexed { idx, dim ->
76+
dim.coordinate?.let { coordinate ->
77+
if (coordinate.uri != resource.uri) {
78+
val target = model.createResource(coordinate.uri)
79+
val targetRefShape = targetRefShape(dim, idx)
80+
81+
val reference = model.createResource()
82+
.addProperty(RDF.type, BALD.Reference)
83+
.addProperty(BALD.targetRefShape, targetRefShape)
84+
.addProperty(BALD.target, target)
85+
resource.addProperty(BALD.references, reference)
86+
}
87+
}
88+
}
89+
}
90+
91+
private fun shape(): RDFList {
92+
val sizeIt = dims.map(::size).iterator()
93+
return model.createList(sizeIt)
94+
}
95+
96+
private fun targetRefShape(dim: Dimension, ordinal: Int): RDFList {
97+
val nodeIt = (dims.indices).map { idx ->
98+
if (idx == ordinal) {
99+
size(dim)
100+
} else unitNode
101+
}.iterator()
102+
103+
return model.createList(nodeIt)
104+
}
105+
106+
private fun size(dim: Dimension): RDFNode {
107+
return createTypedLiteral(dim.size)
108+
}
109+
}
110+
}
111+
112+
companion object {
113+
private val unitNode = createTypedLiteral(1)
114+
}
115+
23116
open class Factory(
24117
private val attrFct: ModelAttributeBuilder.Factory
25118
) {

binary-array-ld-lib/src/main/kotlin/net/bald/vocab/BALD.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ object BALD {
1919
val Container: Resource = createResource("${prefix}Container")
2020
val Resource: Resource = createResource("${prefix}Resource")
2121
val Array: Resource = createResource("${prefix}Array")
22+
val Reference: Resource = createResource("${prefix}Reference")
2223

2324
/**
2425
* Properties
2526
*/
2627
val contains: Property = createProperty("${prefix}contains")
2728
val isPrefixedBy: Property = createProperty("${prefix}isPrefixedBy")
2829
val references: Property = createProperty("${prefix}references")
30+
val shape: Property = createProperty("${prefix}shape")
31+
val targetRefShape: Property = createProperty("${prefix}targetRefShape")
32+
val arrayFirstValue: Property = createProperty("${prefix}arrayFirstValue")
33+
val arrayLastValue: Property = createProperty("${prefix}arrayLastValue")
34+
val target: Property = createProperty("${prefix}target")
2935
}

binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelAttributeBuilderTest.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,11 @@ class ModelAttributeBuilderTest {
8383
builder.addAttribute(attr)
8484
ResourceVerifier(resource).statements {
8585
statement(RDFS.label) {
86-
statement(RDF.first, createResource("http://test.binary-array-ld.net/var0"))
87-
statement(RDF.rest) {
88-
statement(RDF.first, createResource("http://test.binary-array-ld.net/var1"))
89-
statement(RDF.rest) {
90-
statement(RDF.first, createResource("http://test.binary-array-ld.net/var2"))
91-
statement(RDF.rest, RDF.nil)
92-
}
93-
}
86+
list(
87+
createResource("http://test.binary-array-ld.net/var0"),
88+
createResource("http://test.binary-array-ld.net/var1"),
89+
createResource("http://test.binary-array-ld.net/var2")
90+
)
9491
}
9592
}
9693
}

binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayConverterTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class ModelBinaryArrayConverterTest {
2626
private fun newVar(uri: String): Var {
2727
return mock {
2828
on { this.uri } doReturn uri
29+
on { dimensions() } doReturn emptySequence()
2930
}
3031
}
3132

0 commit comments

Comments
 (0)