diff --git a/README.md b/README.md index da3d5fa..126a03d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,3 @@ the `ncgen` command line tool must be available on your system. You can use Maven to build this project and each of its modules with `mvn clean package`. After building, the JAR for the command line application is located at `binary-array-ld-cli/target/bald-cli.jar`. - - - diff --git a/binary-array-ld-cli/src/main/kotlin/net/bald/BinaryArrayConvertCli.kt b/binary-array-ld-cli/src/main/kotlin/net/bald/BinaryArrayConvertCli.kt index f43aa97..24d219e 100644 --- a/binary-array-ld-cli/src/main/kotlin/net/bald/BinaryArrayConvertCli.kt +++ b/binary-array-ld-cli/src/main/kotlin/net/bald/BinaryArrayConvertCli.kt @@ -21,6 +21,7 @@ class BinaryArrayConvertCli { addOption("a", "alias", true, "Comma-delimited list of RDF alias files.") addOption("c", "context", true, "Comma-delimited list of JSON-LD context files.") addOption("o", "output", true, "Output format. eg. ttl, json-ld, rdfxml.") + addOption("d", "download", true, "The URL from which the original file can be downloaded.") addOption("h", "help", false, "Show help.") } @@ -42,7 +43,7 @@ class BinaryArrayConvertCli { val context = context(opts.contextLocs) val alias = alias(opts.aliasLocs) val inputLoc = opts.inputLoc ?: throw IllegalArgumentException("First argument is required: NetCDF file to convert.") - val ba = NetCdfBinaryArray.create(inputLoc, opts.uri, context, alias) + val ba = NetCdfBinaryArray.create(inputLoc, opts.uri, context, alias, opts.downloadUrl) val model = ba.use(ModelBinaryArrayConverter::convert) val outputFormat = opts.outputFormat ?: "ttl" diff --git a/binary-array-ld-cli/src/main/kotlin/net/bald/CommandLineOptions.kt b/binary-array-ld-cli/src/main/kotlin/net/bald/CommandLineOptions.kt index bcd1f20..528f79a 100644 --- a/binary-array-ld-cli/src/main/kotlin/net/bald/CommandLineOptions.kt +++ b/binary-array-ld-cli/src/main/kotlin/net/bald/CommandLineOptions.kt @@ -13,5 +13,6 @@ class CommandLineOptions( val aliasLocs: List get() = cmd.getOptionValue("alias")?.split(",") ?: emptyList() val contextLocs: List get() = cmd.getOptionValue("context")?.split(",") ?: emptyList() val outputFormat: String? get() = cmd.getOptionValue("output") + val downloadUrl: String? get() = cmd.getOptionValue("download") val help: Boolean get() = cmd.hasOption("help") } \ No newline at end of file diff --git a/binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt b/binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt index 6f8e40e..853b5d7 100644 --- a/binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt +++ b/binary-array-ld-cli/src/test/kotlin/net/bald/BinaryArrayConvertCliTest.kt @@ -97,6 +97,33 @@ class BinaryArrayConvertCliTest { } } + @Test + fun run_withDownloadUrl_outputsDownloadUrl() { + val inputFile = writeToNetCdf("/netcdf/identity.cdl") + val outputFile = createTempFile() + run( + "--uri", "http://test.binary-array-ld.net/example", + "--download", "http://test.binary-array-ld.net/download/example.nc", + inputFile.absolutePath, + outputFile.absolutePath + ) + + val model = createDefaultModel().read(outputFile.toURI().toString(), "ttl") + ModelVerifier(model).apply { + resource("http://test.binary-array-ld.net/example/") { + format() + statement(RDF.type, BALD.Container) + distribution("http://test.binary-array-ld.net/download/example.nc") + statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var0")) { + statement(RDF.type, BALD.Resource) + } + statement(BALD.contains, model.createResource("http://test.binary-array-ld.net/example/var1")) { + statement(RDF.type, BALD.Resource) + } + } + } + } + @Test fun run_withOutputFormat_outputsToFile() { val inputFile = writeToNetCdf("/netcdf/identity.cdl") @@ -569,9 +596,10 @@ class BinaryArrayConvertCliTest { } } - private fun StatementsVerifier.distribution() { + private fun StatementsVerifier.distribution(downloadUrl: String? = null) { statement(DCAT.distribution) { statement(RDF.type, DCAT.Distribution) + if (downloadUrl != null) statement(DCAT.downloadURL, createResource(downloadUrl)) statement(DCAT.mediaType) { statement(DCTerms.identifier, createStringLiteral("application/x-netcdf")) statement(RDF.type, DCTerms.MediaType) diff --git a/binary-array-ld-demo/src/main/java/net/bald/NetCdfConvertJava.java b/binary-array-ld-demo/src/main/java/net/bald/NetCdfConvertJava.java index 9b9b0fe..17501a5 100644 --- a/binary-array-ld-demo/src/main/java/net/bald/NetCdfConvertJava.java +++ b/binary-array-ld-demo/src/main/java/net/bald/NetCdfConvertJava.java @@ -27,7 +27,7 @@ public static void convert() throws Exception { public static void convertWithExternalPrefixes() throws Exception { PrefixMapping prefix = ModelFactory.createDefaultModel().read("/path/to/context.json", "json-ld"); ModelContext context = ModelContext.create(prefix); - BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", context, null); + BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", context, null, null); Model model = ModelBinaryArrayConverter.convert(ba); try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) { @@ -38,7 +38,7 @@ public static void convertWithExternalPrefixes() throws Exception { public static void convertWithAliases() throws Exception { Model aliasModel = ModelFactory.createDefaultModel().read("/path/to/alias.ttl", "ttl"); AliasDefinition alias = ModelAliasDefinition.create(aliasModel); - BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", null, alias); + BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", null, alias, null); Model model = ModelBinaryArrayConverter.convert(ba); try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) { diff --git a/binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt b/binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt index c60e8a3..d1d827e 100644 --- a/binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt +++ b/binary-array-ld-lib/src/main/kotlin/net/bald/BinaryArray.kt @@ -28,5 +28,4 @@ interface BinaryArray: Closeable { * The distribution of the binary array, if it is available. Otherwise, null. */ val distribution: Distribution? - } \ No newline at end of file diff --git a/binary-array-ld-lib/src/main/kotlin/net/bald/Distribution.kt b/binary-array-ld-lib/src/main/kotlin/net/bald/Distribution.kt index 92b2e0a..59a068c 100644 --- a/binary-array-ld-lib/src/main/kotlin/net/bald/Distribution.kt +++ b/binary-array-ld-lib/src/main/kotlin/net/bald/Distribution.kt @@ -1,5 +1,7 @@ package net.bald +import org.apache.jena.rdf.model.Resource + /** * A distribution of a binary array file. */ @@ -8,4 +10,10 @@ interface Distribution { * The media type of the binary array file. */ val mediaType: String + + /** + * The URL from which this distribution of the file can be downloaded, if it has one. + * Otherwise, null. + */ + val downloadUrl: Resource? } \ No newline at end of file diff --git a/binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelBinaryArrayBuilder.kt b/binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelBinaryArrayBuilder.kt index 99b756f..d0c170f 100644 --- a/binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelBinaryArrayBuilder.kt +++ b/binary-array-ld-lib/src/main/kotlin/net/bald/model/ModelBinaryArrayBuilder.kt @@ -42,6 +42,11 @@ class ModelBinaryArrayBuilder( val distribution = model.createResource() .addProperty(RDF.type, DCAT.Distribution) .addProperty(DCAT.mediaType, mediaType) + .apply { + dist.downloadUrl?.let { downloadUrl -> + addProperty(DCAT.downloadURL, downloadUrl) + } + } root.addProperty(DCAT.distribution, distribution) } } diff --git a/binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayBuilderTest.kt b/binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayBuilderTest.kt index 87c5f9b..db879db 100644 --- a/binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayBuilderTest.kt +++ b/binary-array-ld-lib/src/test/kotlin/net/bald/model/ModelBinaryArrayBuilderTest.kt @@ -36,6 +36,7 @@ class ModelBinaryArrayBuilderTest { } private val distribution = mock { on { mediaType } doReturn "application/x-netcdf" + on { downloadUrl } doReturn createResource("http://test.binary-array-ld.net/download/example.nc") } private val ba = mock { on { this.root } doReturn root @@ -93,7 +94,7 @@ class ModelBinaryArrayBuilderTest { ModelVerifier(model).apply { resource("http://test.binary-array-ld.net/example/") { format() - distribution() + distribution("http://test.binary-array-ld.net/download/example.nc") } } } diff --git a/binary-array-ld-lib/src/test/kotlin/net/bald/model/StatementsVerifierExt.kt b/binary-array-ld-lib/src/test/kotlin/net/bald/model/StatementsVerifierExt.kt index 55bad0e..8061a9f 100644 --- a/binary-array-ld-lib/src/test/kotlin/net/bald/model/StatementsVerifierExt.kt +++ b/binary-array-ld-lib/src/test/kotlin/net/bald/model/StatementsVerifierExt.kt @@ -2,21 +2,22 @@ package net.bald.model import bald.model.StatementsVerifier import org.apache.jena.rdf.model.ResourceFactory +import org.apache.jena.rdf.model.ResourceFactory.createResource import org.apache.jena.vocabulary.DCAT import org.apache.jena.vocabulary.DCTerms import org.apache.jena.vocabulary.RDF fun StatementsVerifier.format() { statement(DCTerms.format) { - statement(DCTerms.identifier, - ResourceFactory.createResource("http://vocab.nerc.ac.uk/collection/M01/current/NC/")) + statement(DCTerms.identifier, createResource("http://vocab.nerc.ac.uk/collection/M01/current/NC/")) statement(RDF.type, DCTerms.MediaType) } } -fun StatementsVerifier.distribution() { +fun StatementsVerifier.distribution(downloadUrl: String? = null) { statement(DCAT.distribution) { statement(RDF.type, DCAT.Distribution) + if (downloadUrl != null) statement(DCAT.downloadURL, createResource(downloadUrl)) statement(DCAT.mediaType) { statement(DCTerms.identifier, ResourceFactory.createStringLiteral("application/x-netcdf")) statement(RDF.type, DCTerms.MediaType) diff --git a/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfBinaryArray.kt b/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfBinaryArray.kt index 3f6f702..fb9cbb4 100644 --- a/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfBinaryArray.kt +++ b/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfBinaryArray.kt @@ -5,12 +5,14 @@ import net.bald.Distribution import net.bald.Format import net.bald.alias.AliasDefinition import net.bald.context.ModelContext +import org.apache.jena.rdf.model.ResourceFactory.createResource import org.apache.jena.shared.PrefixMapping import ucar.nc2.AttributeContainer import ucar.nc2.Group import ucar.nc2.NetcdfFile import ucar.nc2.NetcdfFiles import java.io.File +import java.net.URI /** * NetCDF implementation of [BinaryArray]. @@ -20,7 +22,8 @@ class NetCdfBinaryArray( val uri: String, private val file: NetcdfFile, private val context: ModelContext, - val alias: AliasDefinition + val alias: AliasDefinition, + private val downloadUrl: String? ): BinaryArray { override val root: NetCdfContainer get() = container(file.rootGroup) @@ -32,7 +35,19 @@ class NetCdfBinaryArray( } override val format: Format get() = NetCdfFormat - override val distribution: Distribution get() = NetCdfDistribution() + + override val distribution: Distribution get() { + val downloadUrl = downloadUrl ?: file.location.takeIf(::isHttp) + val res = downloadUrl?.let(::createResource) + return NetCdfDistribution(res) + } + + private fun isHttp(loc: String): Boolean { + return try { + val scheme = URI(loc).scheme + scheme == "http" || scheme == "https" + } catch (e: Exception) { false } + } val prefixSrc: String? get() = prefixSourceName() @@ -75,6 +90,7 @@ class NetCdfBinaryArray( * @param uri The URI which identifies the dataset. * @param context The external context with which to resolve prefix mappings. * @param alias The alias definition with which to resolve resource and property references. + * @param downloadUrl The URL from which the file can be downloaded, if it has one. Otherwise, null. * @return A [BinaryArray] representation of the NetCDF file. */ @JvmStatic @@ -82,11 +98,12 @@ class NetCdfBinaryArray( fileLoc: String, uri: String? = null, context: ModelContext? = null, - alias: AliasDefinition? = null + alias: AliasDefinition? = null, + downloadUrl: String? = null ): NetCdfBinaryArray { val file = NetcdfFiles.open(fileLoc) val requiredUri = uri ?: uri(fileLoc) - return create(file, requiredUri, context, alias) + return create(file, requiredUri, context, alias, downloadUrl) } /** @@ -105,11 +122,12 @@ class NetCdfBinaryArray( file: NetcdfFile, uri: String, context: ModelContext? = null, - alias: AliasDefinition? = null + alias: AliasDefinition? = null, + downloadUrl: String? = null ): NetCdfBinaryArray { val requiredContext = context ?: ModelContext.Empty val requiredAlias = alias ?: AliasDefinition.Empty - return NetCdfBinaryArray(uri, file, requiredContext, requiredAlias) + return NetCdfBinaryArray(uri, file, requiredContext, requiredAlias, downloadUrl) } private fun uri(fileLoc: String): String { diff --git a/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfDistribution.kt b/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfDistribution.kt index ca106c1..e57aac5 100644 --- a/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfDistribution.kt +++ b/binary-array-ld-netcdf/src/main/kotlin/net/bald/netcdf/NetCdfDistribution.kt @@ -1,10 +1,13 @@ package net.bald.netcdf import net.bald.Distribution +import org.apache.jena.rdf.model.Resource /** * NetCDF implementation of [Distribution]. */ -class NetCdfDistribution: Distribution { +class NetCdfDistribution( + override val downloadUrl: Resource? = null +): Distribution { override val mediaType: String get() = "application/x-netcdf" } \ No newline at end of file diff --git a/binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/NetCdfBinaryArrayTest.kt b/binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/NetCdfBinaryArrayTest.kt index 97a8ec5..de229d9 100644 --- a/binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/NetCdfBinaryArrayTest.kt +++ b/binary-array-ld-netcdf/src/test/kotlin/net/bald/netcdf/NetCdfBinaryArrayTest.kt @@ -18,12 +18,19 @@ import org.apache.jena.vocabulary.SKOS import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals +import kotlin.test.assertNull class NetCdfBinaryArrayTest { - private fun fromCdl(cdlLoc: String, uri: String? = null, context: ModelContext? = null, alias: AliasDefinition? = null): BinaryArray { + private fun fromCdl( + cdlLoc: String, + uri: String? = null, + context: ModelContext? = null, + alias: AliasDefinition? = null, + downloadUrl: String? = null + ): BinaryArray { val file = writeToNetCdf(cdlLoc) - return NetCdfBinaryArray.create(file.absolutePath, uri, context, alias) + return NetCdfBinaryArray.create(file.absolutePath, uri, context, alias, downloadUrl) } /** @@ -47,6 +54,24 @@ class NetCdfBinaryArrayTest { assertEquals(expectedUri, ba.uri) } + @Test + fun distribution_withDownloadUrl_returnsDistribution() { + val downloadUrl = "http://test.binary-array-ld.net/download/identity.nc" + val ba = fromCdl("/netcdf/identity.cdl", "http://test.binary-array-ld.net/identity.nc", downloadUrl = downloadUrl) + val distribution = ba.distribution + assertEquals(downloadUrl, distribution?.downloadUrl?.uri) + assertEquals("application/x-netcdf", distribution?.mediaType) + } + + @Test + fun distribution_withoutDownloadUrl_returnsEmptyDistribution() { + val ba = fromCdl("/netcdf/identity.cdl", "http://test.binary-array-ld.net/identity.nc") + val distribution = ba.distribution + assertNull(distribution?.downloadUrl?.uri) + assertEquals("application/x-netcdf", distribution?.mediaType) + } + + /** * Requirements class A-2 */ diff --git a/docs/alias.md b/docs/alias.md index d871944..b85e58a 100644 --- a/docs/alias.md +++ b/docs/alias.md @@ -40,7 +40,7 @@ and an alias definition to create a `BinaryArray` with both. ```java Model aliasModel = ModelFactory.createDefaultModel().read("/path/to/alias.ttl", "ttl"); AliasDefinition alias = ModelAliasDefinition.create(aliasModel); -BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", null, alias); +BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", null, alias, null); Model model = ModelBinaryArrayConverter.convert(ba); try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) { diff --git a/docs/cli.md b/docs/cli.md index c167623..764720a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -46,6 +46,10 @@ You can find the documentation for this feature [here](context.md#cli). The CLI supports [aliases](alias.md). You can find the documentation for this feature [here](alias.md#cli). +### Download URL + +The CLI supports [download URLs](download.md). + ### Quick Reference You can supply command line options in long form with the `--` prefix or short form with `-`, @@ -57,4 +61,5 @@ followed by their value. | --uri | -u | The URI which identifies the dataset. | Input file URI | | --output | -o | Output format, eg. ttl, json-ld, rdfxml. | ttl | | --context | -c | Comma-delimited list of JSON-LD context files. || -| --alias | -a | Comma-delimited list of RDF alias files. || \ No newline at end of file +| --alias | -a | Comma-delimited list of RDF alias files. || +| --download | -d | The URL from which the original file can be downloaded, if available. || \ No newline at end of file diff --git a/docs/context.md b/docs/context.md index 36e777e..defa5ad 100644 --- a/docs/context.md +++ b/docs/context.md @@ -30,7 +30,7 @@ to create a `BinaryArray` with both. ```java PrefixMapping prefix = ModelFactory.createDefaultModel().read("/path/to/context.json", "json-ld"); ModelContext context = ModelContext.create(prefix); -BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", context, null); +BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.nc", "http://test.binary-array-ld.net/example", context, null, null); Model model = ModelBinaryArrayConverter.convert(ba); try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) { diff --git a/docs/download.md b/docs/download.md new file mode 100644 index 0000000..9f6beb8 --- /dev/null +++ b/docs/download.md @@ -0,0 +1,32 @@ +# Download URL + +The BALD CLI and library allow you to specify a download URL for the given NetCDF file, +as described in the [draft specification](http://docs.opengeospatial.org/DRAFTS/19-002.html#_download_url). + +If you don't specify a download URL, but the given location of the NetCDF file is a remote URL (ie. it has `http` or `https` scheme), +then the download URL will be inferred to be the same. + +## CLI + +You can optionally provide the download URL as a command line argument. +Use the `--download` or `-d` option to specify the download URL for the NetCDF file. + +#### Example +``` +java -jar bald-cli.jar --download http://test.binary-array-ld.net/download/netcdf.nc /path/to/netcdf.nc /path/to/graph.ttl +``` + +## Library + +You can optionally provide the download URL as a parameter to the `NetCdfBinaryArray.create` method. +Otherwise, you can simply pass in a null value. + +#### Example +```java +BinaryArray ba = NetCdfBinaryArray.create("/path/to/input.ttl", "http://test.binary-array-ld.net/example", null, null, "http://test.binary-array-ld.net/download/netcdf.nc"); +Model model = ModelBinaryArrayConverter.convert(ba); + +try (OutputStream output = new FileOutputStream("/path/to/output.ttl")) { + model.write(output, "ttl"); +} +``` \ No newline at end of file diff --git a/docs/lib.md b/docs/lib.md index 977ac52..cff9141 100644 --- a/docs/lib.md +++ b/docs/lib.md @@ -64,3 +64,7 @@ You can find the documentation for this feature [here](context.md#library). The library supports [aliases](alias.md). You can find the documentation for this feature [here](alias.md#library). + +### Download URL + +The library supports [download URLs](download.md). diff --git a/docs/usage.md b/docs/usage.md index b8262db..e70cb70 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,4 +6,5 @@ This project can be used as a command line interface or as a library. ### Features * [Context](context.md) -* [Aliases](alias.md) \ No newline at end of file +* [Aliases](alias.md) +* [Download URL](download.md) \ No newline at end of file