From 70ac4aadc5f1abb018d823446de2efd2bad6f8a7 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 30 Sep 2021 16:01:25 -0400 Subject: [PATCH] feat: add CanonicalMetadataParser * canonical and translated parsers do not need n5 on construction * add tests * add addPathsRecursive to ContainerMetadataNode --- .../janelia/saalfeldlab/n5/ij/N5Importer.java | 16 +- .../canonical/CanonicalMetadataParser.java | 272 ++++++++++++++++++ .../TranslatedTreeMetadataParser.java | 237 +++------------ .../container/ContainerMetadataNode.java | 20 +- .../n5/ui/DatasetSelectorDialog.java | 2 +- .../n5/ui/N5MetadataTranslationPanel.java | 10 +- .../n5/metadata/CanonicalMetadataTests.java | 39 +++ .../n5/metadata/TransformTests.java | 4 +- 8 files changed, 381 insertions(+), 219 deletions(-) create mode 100644 src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/CanonicalMetadataParser.java diff --git a/src/main/java/org/janelia/saalfeldlab/n5/ij/N5Importer.java b/src/main/java/org/janelia/saalfeldlab/n5/ij/N5Importer.java index 34ee88c..3c3b865 100644 --- a/src/main/java/org/janelia/saalfeldlab/n5/ij/N5Importer.java +++ b/src/main/java/org/janelia/saalfeldlab/n5/ij/N5Importer.java @@ -43,7 +43,6 @@ import net.imglib2.img.imageplus.ImagePlusImgFactory; import net.imglib2.loops.LoopBuilder; import net.imglib2.parallel.DefaultTaskExecutor; -import net.imglib2.transform.integer.MixedTransform; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.ARGBType; import net.imglib2.type.numeric.NumericType; @@ -51,10 +50,7 @@ import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.DoubleType; import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.util.Intervals; import net.imglib2.util.Util; -import net.imglib2.view.IntervalView; -import net.imglib2.view.MixedTransformView; import net.imglib2.view.Views; import org.janelia.saalfeldlab.n5.DataType; import org.janelia.saalfeldlab.n5.N5DatasetDiscoverer; @@ -220,11 +216,13 @@ public void run(final String args) { selectionDialog.setLoaderExecutor( exec ); selectionDialog.setTreeRenderer(new N5DatasetTreeCellRenderer(true)); -// selectionDialog.getTranslationPanel().setFilter( -// x -> -// x.getMultiscales() == null && -// x.getSpatialTransform() != null && -// x.getAttributes() != null ); + + // restrict canonical metadata to those with spatial metadata, but without + // multiscale + selectionDialog.getTranslationPanel().setFilter( + x -> x.getMultiscales() == null && + x.getSpatialTransform() != null && + x.getAttributes() != null); selectionDialog.setContainerPathUpdateCallback(x -> { if (x != null) diff --git a/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/CanonicalMetadataParser.java b/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/CanonicalMetadataParser.java new file mode 100644 index 0000000..bc0de96 --- /dev/null +++ b/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/CanonicalMetadataParser.java @@ -0,0 +1,272 @@ +package org.janelia.saalfeldlab.n5.metadata.canonical; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import org.janelia.saalfeldlab.n5.Bzip2Compression; +import org.janelia.saalfeldlab.n5.Compression; +import org.janelia.saalfeldlab.n5.CompressionAdapter; +import org.janelia.saalfeldlab.n5.DataType; +import org.janelia.saalfeldlab.n5.DatasetAttributes; +import org.janelia.saalfeldlab.n5.GsonAttributesParser; +import org.janelia.saalfeldlab.n5.GzipCompression; +import org.janelia.saalfeldlab.n5.Lz4Compression; +import org.janelia.saalfeldlab.n5.N5Reader; +import org.janelia.saalfeldlab.n5.N5TreeNode; +import org.janelia.saalfeldlab.n5.RawCompression; +import org.janelia.saalfeldlab.n5.XzCompression; +import org.janelia.saalfeldlab.n5.metadata.N5MetadataParser; +import org.janelia.saalfeldlab.n5.metadata.container.ContainerMetadataNode; +import org.janelia.saalfeldlab.n5.metadata.transforms.LinearSpatialTransform; +import org.janelia.saalfeldlab.n5.metadata.transforms.SpatialTransform; +import org.janelia.saalfeldlab.n5.metadata.transforms.SpatialTransformAdapter; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; + +import net.thisptr.jackson.jq.BuiltinFunctionLoader; +import net.thisptr.jackson.jq.Expression; +import net.thisptr.jackson.jq.Function; +import net.thisptr.jackson.jq.PathOutput; +import net.thisptr.jackson.jq.Scope; +import net.thisptr.jackson.jq.Version; +import net.thisptr.jackson.jq.Versions; +import net.thisptr.jackson.jq.exception.JsonQueryException; +import net.thisptr.jackson.jq.internal.misc.Strings; +import net.thisptr.jackson.jq.path.Path; + +/** + * A parser for the "canonical" metadata dialect. + * + * @author John Bogovic + */ +public class CanonicalMetadataParser implements N5MetadataParser { + + protected Gson gson; + + protected ContainerMetadataNode root; + + protected Predicate filter; + + public CanonicalMetadataParser() { + this(x -> true); + } + + public CanonicalMetadataParser( final Predicate filter) { + this.filter = filter; + + } + + public static Gson buildGson( final N5Reader n5 ) { + final GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(SpatialTransform.class, new SpatialTransformAdapter( n5 )); +// gsonBuilder.registerTypeAdapter(LinearSpatialTransform.class, new SpatialTransformAdapter( n5 )); + gsonBuilder.registerTypeAdapter(CanonicalMetadata.class, new CanonicalMetadata.CanonicalMetadataAdapter()); + gsonBuilder.registerTypeAdapter(DataType.class, new DataType.JsonAdapter()); + gsonBuilder.registerTypeHierarchyAdapter(Compression.class, CompressionAdapter.getJsonAdapter()); + gsonBuilder.disableHtmlEscaping(); + return gsonBuilder.create(); + } + + public void setFilter(final Predicate filter) { + this.filter = filter; + } + + public CanonicalMetadataParser( final N5Reader n5, final String n5Tree, final String translation) { + + final GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter( SpatialTransform.class, new SpatialTransformAdapter( n5 ) ); + gsonBuilder.registerTypeAdapter( LinearSpatialTransform.class, new SpatialTransformAdapter( n5 ) ); + gsonBuilder.registerTypeAdapter( CanonicalMetadata.class, new CanonicalMetadata.CanonicalMetadataAdapter() ); + gsonBuilder.registerTypeAdapter( DataType.class, new DataType.JsonAdapter()); + gsonBuilder.registerTypeHierarchyAdapter( Compression.class, CompressionAdapter.getJsonAdapter()); + gsonBuilder.disableHtmlEscaping(); + gson = gsonBuilder.create(); + + root = gson.fromJson( n5Tree, ContainerMetadataNode.class ); + } + + public Gson getGson() { + return gson; + } + + public void setGson( Gson gson ) { + this.gson = gson; + } + + protected void setup( final N5Reader n5 ) { + // TODO rebuilding gson and root is the safest thing to do, but possibly inefficient + +// if( gson == null ) + setGson( buildGson( n5 )); + +// if( root == null ) { + root = ContainerMetadataNode.build(n5, gson); + root.addPathsRecursive(); +// } + } + + @Override + public Optional parseMetadata(N5Reader n5, N5TreeNode node) { + setup( n5 ); + return parseMetadata( node, n5.getGroupSeparator()); +// if( root == null ) { +// return Optional.empty(); +// } +// +// return root.getChild( node.getPath(), n5.getGroupSeparator() ) +// .map( ContainerMetadataNode::getAttributes ) +// .map( this::canonicalMetadata ) +// .filter(filter); + } + + @Override + public Optional parseMetadata(final N5Reader n5, final String dataset) { + return parseMetadata( n5, new N5TreeNode( dataset )); + } + + public Optional parseMetadata(final String dataset, final String groupSep ) { + return parseMetadata( new N5TreeNode( dataset ), groupSep ); + } + + public Optional parseMetadata(N5TreeNode node, String groupSep) { + if (root == null) + return Optional.empty(); + + return root.getChild(node.getPath(), groupSep) + .map(ContainerMetadataNode::getAttributes) + .map(this::canonicalMetadata) + .filter(filter); + } + + public CanonicalMetadata canonicalMetadata(final HashMap attrMap) { + return gson.fromJson(gson.toJson(attrMap), CanonicalMetadata.class); + } + + public static Scope buildRootScope() { + // First of all, you have to prepare a Scope which s a container of + // built-in/user-defined functions and variables. + final Scope rootScope = Scope.newEmptyScope(); + + // Use BuiltinFunctionLoader to load built-in functions from the classpath. + BuiltinFunctionLoader.getInstance().loadFunctions(Versions.JQ_1_6, rootScope); + + // You can also define a custom function. E.g. + rootScope.addFunction("repeat", 1, new Function() { + @Override + public void apply(final Scope scope, final List args, final JsonNode in, final Path path, + final PathOutput output, final Version version) throws JsonQueryException { + args.get(0).apply(scope, in, (time) -> { + output.emit(new TextNode(Strings.repeat(in.asText(), time.asInt())), null); + }); + } + }); + return rootScope; + } + + public static Optional datasetAttributes(final Gson gson, + HashMap attributeMap) { + + try { + + final long[] dimensions = GsonAttributesParser.parseAttribute(attributeMap, "dimensions", long[].class, + gson); + if (dimensions == null) + return Optional.empty(); + + final DataType dataType = GsonAttributesParser.parseAttribute(attributeMap, "dataType", DataType.class, + gson); + if (dataType == null) + return Optional.empty(); + + int[] blockSize = GsonAttributesParser.parseAttribute(attributeMap, "blockSize", int[].class, gson); + if (blockSize == null) + blockSize = Arrays.stream(dimensions).mapToInt(a -> (int) a).toArray(); + + Compression compression = GsonAttributesParser.parseAttribute(attributeMap, "compression", + Compression.class, gson); + + /* version 0 */ + if (compression == null) { + switch (GsonAttributesParser.parseAttribute(attributeMap, "compression", String.class, gson)) { + case "raw": + compression = new RawCompression(); + break; + case "gzip": + compression = new GzipCompression(); + break; + case "bzip2": + compression = new Bzip2Compression(); + break; + case "lz4": + compression = new Lz4Compression(); + break; + case "xz": + compression = new XzCompression(); + break; + } + } + + return Optional.of(new DatasetAttributes(dimensions, blockSize, dataType, compression)); + + } catch (Exception e) { + } + + return Optional.empty(); + } + + public static Optional datasetAttributes(final JsonDeserializationContext context, JsonElement elem ) { + + try { + + final long[] dimensions = context.deserialize( elem.getAsJsonObject().get("dimensions"), long[].class); + if (dimensions == null) + return Optional.empty(); + + final DataType dataType = context.deserialize( elem.getAsJsonObject().get("dataType"), DataType.class); + if (dataType == null) + return Optional.empty(); + + int[] blockSize = context.deserialize( elem.getAsJsonObject().get("blockSize"), int[].class ); + if (blockSize == null) + blockSize = Arrays.stream(dimensions).mapToInt(a -> (int)a).toArray(); + + Compression compression = context.deserialize( elem.getAsJsonObject().get("compression"), Compression.class ); + + /* version 0 */ + if (compression == null) { + final String compressionString = context.deserialize( elem.getAsJsonObject().get("compression"), String.class ); + switch ( compressionString ) { + case "raw": + compression = new RawCompression(); + break; + case "gzip": + compression = new GzipCompression(); + break; + case "bzip2": + compression = new Bzip2Compression(); + break; + case "lz4": + compression = new Lz4Compression(); + break; + case "xz": + compression = new XzCompression(); + break; + } + } + + return Optional.of(new DatasetAttributes(dimensions, blockSize, dataType, compression)); + + } catch (Exception e) {} + + return Optional.empty(); + } + +} diff --git a/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/TranslatedTreeMetadataParser.java b/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/TranslatedTreeMetadataParser.java index d07c446..56e695e 100644 --- a/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/TranslatedTreeMetadataParser.java +++ b/src/main/java/org/janelia/saalfeldlab/n5/metadata/canonical/TranslatedTreeMetadataParser.java @@ -1,39 +1,22 @@ package org.janelia.saalfeldlab.n5.metadata.canonical; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import org.janelia.saalfeldlab.n5.Bzip2Compression; -import org.janelia.saalfeldlab.n5.Compression; -import org.janelia.saalfeldlab.n5.CompressionAdapter; -import org.janelia.saalfeldlab.n5.DataType; -import org.janelia.saalfeldlab.n5.DatasetAttributes; -import org.janelia.saalfeldlab.n5.GsonAttributesParser; -import org.janelia.saalfeldlab.n5.GzipCompression; -import org.janelia.saalfeldlab.n5.Lz4Compression; import org.janelia.saalfeldlab.n5.N5Reader; import org.janelia.saalfeldlab.n5.N5TreeNode; -import org.janelia.saalfeldlab.n5.RawCompression; -import org.janelia.saalfeldlab.n5.XzCompression; -import org.janelia.saalfeldlab.n5.metadata.N5MetadataParser; import org.janelia.saalfeldlab.n5.metadata.container.ContainerMetadataNode; -import org.janelia.saalfeldlab.n5.metadata.transforms.LinearSpatialTransform; -import org.janelia.saalfeldlab.n5.metadata.transforms.SpatialTransform; -import org.janelia.saalfeldlab.n5.metadata.transforms.SpatialTransformAdapter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.TextNode; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; +import com.google.gson.JsonSyntaxException; import net.thisptr.jackson.jq.BuiltinFunctionLoader; import net.thisptr.jackson.jq.Expression; @@ -48,59 +31,31 @@ import net.thisptr.jackson.jq.path.Path; /** - * Interface for metadata describing how spatial data are oriented in physical space. + * A parser for metadata that are translated into the "canonical" dialect. * * @author John Bogovic */ -public class TranslatedTreeMetadataParser implements N5MetadataParser { +public class TranslatedTreeMetadataParser extends CanonicalMetadataParser { private final String translation; private final Scope scope; - private final Gson gson; - private final ObjectMapper objMapper; - private final ContainerMetadataNode root; - private ContainerMetadataNode translatedRoot; - private Predicate filter; - - public TranslatedTreeMetadataParser(final N5Reader n5, final String translation) { - this(n5, translation, x -> true); + public TranslatedTreeMetadataParser(final String translation) { + this(translation, x -> true); } - public TranslatedTreeMetadataParser(final N5Reader n5, final String translation, + public TranslatedTreeMetadataParser(final String translation, final Predicate filter) { - + super( filter ); this.translation = resolveImports( translation ); - this.filter = filter; scope = buildRootScope(); objMapper = new ObjectMapper(); - gson = buildGson( n5 ); - - root = ContainerMetadataNode.build(n5, gson); - try { - translatedRoot = gson.fromJson( transform(gson.toJson(root)), ContainerMetadataNode.class); - addPaths(); - } catch (Exception e) { - e.printStackTrace(); - translatedRoot = null; - } - } - - public static Gson buildGson( final N5Reader n5 ) { - final GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(SpatialTransform.class, new SpatialTransformAdapter( n5 )); - gsonBuilder.registerTypeAdapter(LinearSpatialTransform.class, new SpatialTransformAdapter( n5 )); - gsonBuilder.registerTypeAdapter(CanonicalMetadata.class, new CanonicalMetadata.CanonicalMetadataAdapter()); - gsonBuilder.registerTypeAdapter(DataType.class, new DataType.JsonAdapter()); - gsonBuilder.registerTypeHierarchyAdapter(Compression.class, CompressionAdapter.getJsonAdapter()); - gsonBuilder.disableHtmlEscaping(); - return gsonBuilder.create(); } public static String resolveImports(String query) { @@ -113,52 +68,12 @@ public static String resolveImports(String query) { return query; } - public void setFilter(final Predicate filter) { - this.filter = filter; - } - public TranslatedTreeMetadataParser( final N5Reader n5, final String n5Tree, final String translation) { this.translation = resolveImports( translation ); scope = buildRootScope(); objMapper = new ObjectMapper(); - - final GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter( SpatialTransform.class, new SpatialTransformAdapter( n5 ) ); - gsonBuilder.registerTypeAdapter( LinearSpatialTransform.class, new SpatialTransformAdapter( n5 ) ); - gsonBuilder.registerTypeAdapter( CanonicalMetadata.class, new CanonicalMetadata.CanonicalMetadataAdapter() ); - gsonBuilder.registerTypeAdapter( DataType.class, new DataType.JsonAdapter()); - gsonBuilder.registerTypeHierarchyAdapter( Compression.class, CompressionAdapter.getJsonAdapter()); - gsonBuilder.disableHtmlEscaping(); - gson = gsonBuilder.create(); - - root = gson.fromJson( n5Tree, ContainerMetadataNode.class ); - try { - translatedRoot = gson.fromJson( transform(gson.toJson(root)), ContainerMetadataNode.class ); - addPaths(); - } catch (Exception e) { - e.printStackTrace(); - translatedRoot = null; - } - } - - private void addPaths() throws Exception { - JsonNode inJsonNode = objMapper.readTree(gson.toJson(translatedRoot)); - - final List out = new ArrayList<>(); - JsonQuery.compile( FinalTranslations.PATHFUNS + " addPaths", Versions.JQ_1_6).apply(scope, inJsonNode, out::add); - - final StringBuffer stringOutput = new StringBuffer(); - for (final JsonNode n : out) - stringOutput.append(n.toString() + "\n"); - - translatedRoot = gson.fromJson(stringOutput.toString(), ContainerMetadataNode.class); - } - - public Gson getGson() - { - return gson; } public String transform( final String in ) throws JsonMappingException, JsonProcessingException @@ -175,22 +90,36 @@ public String transform( final String in ) throws JsonMappingException, JsonProc return stringOutput.toString(); } + protected void setup( final N5Reader n5 ) { +// if( gson == null ) + setGson( buildGson( n5 )); + +// if( root == null ) { + root = ContainerMetadataNode.build(n5, gson); + root.addPathsRecursive(); + + try { + translatedRoot = gson.fromJson( transform(gson.toJson(root)), ContainerMetadataNode.class); + translatedRoot.addPathsRecursive(); + } catch (JsonSyntaxException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } +// } + } + @Override public Optional parseMetadata(N5Reader n5, N5TreeNode node) { - - if( translatedRoot == null ) { - return Optional.empty(); - } - - return translatedRoot.getChild( node.getPath(), n5.getGroupSeparator() ) - .map( ContainerMetadataNode::getAttributes ) - .map( this::canonicalMetadata ) - .filter(filter); + setup( n5 ); + return parseMetadata( node, n5.getGroupSeparator()); } @Override public Optional parseMetadata(final N5Reader n5, final String dataset) { - return parseMetadata( new N5TreeNode( dataset ), n5.getGroupSeparator() ); + return parseMetadata( n5, new N5TreeNode( dataset ) ); } public Optional parseMetadata(final String dataset, final String groupSep ) { @@ -202,8 +131,10 @@ public Optional parseMetadata(N5TreeNode node, String groupSe if (translatedRoot == null) return Optional.empty(); - return translatedRoot.getChild(node.getPath(), groupSep).map(ContainerMetadataNode::getAttributes) - .map(this::canonicalMetadata); + return translatedRoot.getChild( node.getPath(), groupSep ) + .map( ContainerMetadataNode::getAttributes ) + .map( this::canonicalMetadata ) + .filter(filter); } public CanonicalMetadata canonicalMetadata(final HashMap attrMap) { @@ -241,103 +172,5 @@ public void apply(final Scope scope, final List args, final JsonNode }); return rootScope; } - - public static Optional datasetAttributes(final Gson gson, - HashMap attributeMap) { - - try { - - final long[] dimensions = GsonAttributesParser.parseAttribute(attributeMap, "dimensions", long[].class, - gson); - if (dimensions == null) - return Optional.empty(); - - final DataType dataType = GsonAttributesParser.parseAttribute(attributeMap, "dataType", DataType.class, - gson); - if (dataType == null) - return Optional.empty(); - - int[] blockSize = GsonAttributesParser.parseAttribute(attributeMap, "blockSize", int[].class, gson); - if (blockSize == null) - blockSize = Arrays.stream(dimensions).mapToInt(a -> (int) a).toArray(); - - Compression compression = GsonAttributesParser.parseAttribute(attributeMap, "compression", - Compression.class, gson); - - /* version 0 */ - if (compression == null) { - switch (GsonAttributesParser.parseAttribute(attributeMap, "compression", String.class, gson)) { - case "raw": - compression = new RawCompression(); - break; - case "gzip": - compression = new GzipCompression(); - break; - case "bzip2": - compression = new Bzip2Compression(); - break; - case "lz4": - compression = new Lz4Compression(); - break; - case "xz": - compression = new XzCompression(); - break; - } - } - - return Optional.of(new DatasetAttributes(dimensions, blockSize, dataType, compression)); - - } catch (Exception e) { - } - - return Optional.empty(); - } - - public static Optional datasetAttributes(final JsonDeserializationContext context, JsonElement elem ) { - - try { - - final long[] dimensions = context.deserialize( elem.getAsJsonObject().get("dimensions"), long[].class); - if (dimensions == null) - return Optional.empty(); - - final DataType dataType = context.deserialize( elem.getAsJsonObject().get("dataType"), DataType.class); - if (dataType == null) - return Optional.empty(); - - int[] blockSize = context.deserialize( elem.getAsJsonObject().get("blockSize"), int[].class ); - if (blockSize == null) - blockSize = Arrays.stream(dimensions).mapToInt(a -> (int)a).toArray(); - - Compression compression = context.deserialize( elem.getAsJsonObject().get("compression"), Compression.class ); - - /* version 0 */ - if (compression == null) { - final String compressionString = context.deserialize( elem.getAsJsonObject().get("compression"), String.class ); - switch ( compressionString ) { - case "raw": - compression = new RawCompression(); - break; - case "gzip": - compression = new GzipCompression(); - break; - case "bzip2": - compression = new Bzip2Compression(); - break; - case "lz4": - compression = new Lz4Compression(); - break; - case "xz": - compression = new XzCompression(); - break; - } - } - - return Optional.of(new DatasetAttributes(dimensions, blockSize, dataType, compression)); - - } catch (Exception e) {} - - return Optional.empty(); - } } diff --git a/src/main/java/org/janelia/saalfeldlab/n5/metadata/container/ContainerMetadataNode.java b/src/main/java/org/janelia/saalfeldlab/n5/metadata/container/ContainerMetadataNode.java index 22dfff7..09f219e 100644 --- a/src/main/java/org/janelia/saalfeldlab/n5/metadata/container/ContainerMetadataNode.java +++ b/src/main/java/org/janelia/saalfeldlab/n5/metadata/container/ContainerMetadataNode.java @@ -17,12 +17,12 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; /** * * @author John Bogovic - * */ public class ContainerMetadataNode { @@ -43,6 +43,24 @@ public Map getChildren() { return children; } + /** + * Adds path attributes to this node and recursively to its children. + */ + public void addPathsRecursive() { + addPathsRecursive(""); + } + + /** + * Adds path attributes to this node and recursively to its children. + * + * @param thisPath + */ + public void addPathsRecursive( String thisPath ) { + attributes.put("path", new JsonPrimitive( thisPath )); + for ( String childPath : children.keySet() ) + children.get(childPath).addPathsRecursive( thisPath + "/" + childPath ); + } + public Optional getChild(final String relativePath, final String groupSeparator) { if (relativePath.isEmpty()) return Optional.of(this); diff --git a/src/main/java/org/janelia/saalfeldlab/n5/ui/DatasetSelectorDialog.java b/src/main/java/org/janelia/saalfeldlab/n5/ui/DatasetSelectorDialog.java index 54b0f96..72b63c9 100644 --- a/src/main/java/org/janelia/saalfeldlab/n5/ui/DatasetSelectorDialog.java +++ b/src/main/java/org/janelia/saalfeldlab/n5/ui/DatasetSelectorDialog.java @@ -557,7 +557,7 @@ private void openContainer(final Function n5Fun, final Supplie } // Optional translatedParser = translationPanel.getParserOptional( gson ); - Optional translatedParser = translationPanel.getParserOptional( n5 ); + Optional translatedParser = translationPanel.getParserOptional(); translatedParser.ifPresent( p -> { parserList.clear(); parserList.add(translatedParser.get()); diff --git a/src/main/java/org/janelia/saalfeldlab/n5/ui/N5MetadataTranslationPanel.java b/src/main/java/org/janelia/saalfeldlab/n5/ui/N5MetadataTranslationPanel.java index 841db8c..734923c 100644 --- a/src/main/java/org/janelia/saalfeldlab/n5/ui/N5MetadataTranslationPanel.java +++ b/src/main/java/org/janelia/saalfeldlab/n5/ui/N5MetadataTranslationPanel.java @@ -77,11 +77,11 @@ public void setFilter(Predicate filter) { // return Optional.empty(); // } - public TranslatedTreeMetadataParser getParser(final N5Reader n5) { + public TranslatedTreeMetadataParser getParser() { if (filter == null) - return new TranslatedTreeMetadataParser(n5, textArea.getText()); + return new TranslatedTreeMetadataParser(textArea.getText()); else - return new TranslatedTreeMetadataParser(n5, textArea.getText(), filter); + return new TranslatedTreeMetadataParser(textArea.getText(), filter); } /** @@ -89,10 +89,10 @@ public TranslatedTreeMetadataParser getParser(final N5Reader n5) { * * @return the parser optional */ - public Optional getParserOptional(final N5Reader n5) { + public Optional getParserOptional() { if (isTranslationProvided()) - return Optional.of(getParser(n5)); + return Optional.of(getParser()); else return Optional.empty(); } diff --git a/src/test/java/org/janelia/saalfeldlab/n5/metadata/CanonicalMetadataTests.java b/src/test/java/org/janelia/saalfeldlab/n5/metadata/CanonicalMetadataTests.java index f04e8d3..9702fea 100644 --- a/src/test/java/org/janelia/saalfeldlab/n5/metadata/CanonicalMetadataTests.java +++ b/src/test/java/org/janelia/saalfeldlab/n5/metadata/CanonicalMetadataTests.java @@ -3,16 +3,23 @@ import org.janelia.saalfeldlab.n5.DataType; import org.janelia.saalfeldlab.n5.DatasetAttributes; import org.janelia.saalfeldlab.n5.GzipCompression; +import org.janelia.saalfeldlab.n5.N5FSReader; import org.janelia.saalfeldlab.n5.metadata.canonical.Axis; import org.janelia.saalfeldlab.n5.metadata.canonical.CanonicalDatasetMetadata; import org.janelia.saalfeldlab.n5.metadata.canonical.SpatialMetadataCanonical; +import org.janelia.saalfeldlab.n5.metadata.container.ContainerMetadataNode; import org.janelia.saalfeldlab.n5.metadata.imagej.CanonicalMetadataToImagePlus; import org.janelia.saalfeldlab.n5.metadata.transforms.AffineSpatialTransform; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; + + import static org.junit.Assert.*; +import java.io.File; import java.io.IOException; +import java.util.Optional; import ij.ImagePlus; import ij.ImageStack; @@ -27,8 +34,22 @@ public class CanonicalMetadataTests { private int[] blkSz3d, blkSz4d, blkSz5d; + File n5rootF; + + private N5FSReader n5; + @Before public void before() { + + final String n5Root = "src/test/resources/test.n5"; + n5rootF = new File(n5Root); + + try { + n5 = new N5FSReader( n5rootF.getCanonicalPath() ); + }catch( IOException e ) { + e.printStackTrace(); + } + imp3d = new ImagePlus("imp3d", ImageStack.create(1, 1, 24, 8)); imp4d = new ImagePlus("imp4d", ImageStack.create(1, 1, 24, 8)); imp5d = new ImagePlus("imp5d", ImageStack.create(1, 1, 24, 8)); @@ -118,6 +139,24 @@ public void testAxesToImagePlus() throws IOException { assertEquals("xyczt nt", 4, imp5d.getNFrames()); } + @Test + public void testMetadataTree() + { + ContainerMetadataNode rootNode = ContainerMetadataNode.build(n5, n5.getGson()); + Assert.assertNotNull("rootNode not null", rootNode); + Assert.assertEquals( "num children of root", 9, rootNode.getChildren().keySet().size()); + + Optional cosemMsNodeOpt = rootNode.getChild("cosem_ms", "/"); + Assert.assertTrue("has cosem_ms", cosemMsNodeOpt.isPresent()); + ContainerMetadataNode cosemMsNode = cosemMsNodeOpt.get(); + Assert.assertEquals( "num children of cosem_ms", 3, cosemMsNode.getChildren().keySet().size()); + + Optional s0Opt = cosemMsNode.getChild("s0", "/"); + Assert.assertTrue("has s0", s0Opt.isPresent()); + ContainerMetadataNode s0 = s0Opt.get(); + Assert.assertTrue("s0 has transform attribute", s0.getAttributes().keySet().contains("transform")); + } + private void printDims(ImagePlus imp) { System.out.println(imp.getNChannels()); System.out.println(imp.getNSlices()); diff --git a/src/test/java/org/janelia/saalfeldlab/n5/metadata/TransformTests.java b/src/test/java/org/janelia/saalfeldlab/n5/metadata/TransformTests.java index c51fe79..527a46d 100644 --- a/src/test/java/org/janelia/saalfeldlab/n5/metadata/TransformTests.java +++ b/src/test/java/org/janelia/saalfeldlab/n5/metadata/TransformTests.java @@ -11,6 +11,7 @@ import org.janelia.saalfeldlab.n5.RunImportExportTest; import org.janelia.saalfeldlab.n5.imglib2.N5Utils; import org.janelia.saalfeldlab.n5.metadata.canonical.CanonicalMetadata; +import org.janelia.saalfeldlab.n5.metadata.canonical.CanonicalMetadataParser; import org.janelia.saalfeldlab.n5.metadata.canonical.SpatialMetadataCanonical; import org.janelia.saalfeldlab.n5.metadata.canonical.TranslatedTreeMetadataParser; import org.janelia.saalfeldlab.n5.metadata.transforms.AffineSpatialTransform; @@ -131,7 +132,8 @@ public void testTransforms() throws IOException { private void testParsedTransformSeq( String dataset ) { // canonical parser - final TranslatedTreeMetadataParser parser = new TranslatedTreeMetadataParser( n5, "." ); +// final TranslatedTreeMetadataParser parser = new TranslatedTreeMetadataParser( n5, "." ); + final CanonicalMetadataParser parser = new CanonicalMetadataParser(); Optional metaOpt = parser.parseMetadata(n5, dataset); CanonicalMetadata meta = metaOpt.get(); SpatialMetadataCanonical parsedXfm = meta.getSpatialTransform();