Skip to content

Commit

Permalink
fix: ngff multiscales and relative group path handling on windows
Browse files Browse the repository at this point in the history
* test: build ngff multiscales v0.4 metadata
* fix: use MetadataUtils.relative paths where applicable
* fix: MetadataUtils relativePath for windows
* style: formatting
* doc: MetadataUtils relativePath
  • Loading branch information
bogovicj authored Apr 19, 2024
1 parent bf0fa90 commit 4e617f8
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ public class MetadataUtils {

public static double[] mul(final double[] a, final double[] b) {

final double[] out = new double[ a.length ];
for( int i = 0; i < a.length; i++ )
final double[] out = new double[a.length];
for (int i = 0; i < a.length; i++)
out[i] = a[i] * b[i];

return out;
}

public static double[] mul(final double[] a, final long[] b) {

final double[] out = new double[ a.length ];
for( int i = 0; i < a.length; i++ )
final double[] out = new double[a.length];
for (int i = 0; i < a.length; i++)
out[i] = a[i] * b[i];

return out;
Expand Down Expand Up @@ -91,21 +91,22 @@ public static long[] updateDownsamplingFactors(final long factor, final long[] b
return factors;
}

public static CoordinateTransformation<?>[] buildScaleTranslationTransformList( final double[] scale, final double[] translation ) {
public static CoordinateTransformation<?>[] buildScaleTranslationTransformList(final double[] scale, final double[] translation) {

int nTforms = 0;
if( scale != null )
if (scale != null)
nTforms++;

if( translation != null )
if (translation != null)
nTforms++;

final CoordinateTransformation<?>[] coordinateTransformations = new CoordinateTransformation<?>[nTforms];

int i = 0;
if( scale != null )
if (scale != null)
coordinateTransformations[i++] = new ScaleCoordinateTransformation(scale);

if( translation != null )
if (translation != null)
coordinateTransformations[i++] = new TranslationCoordinateTransformation(translation);

return coordinateTransformations;
Expand All @@ -120,64 +121,64 @@ public static CoordinateTransformation<?>[] buildScaleTranslationTransformList(
* @param datasetMetadata dataset metadata
* @return the single scale metadata
*/
public static N5SingleScaleMetadata setDatasetAttributes( final N5SingleScaleMetadata baseMetadata, final N5DatasetMetadata datasetMetadata )
{
if( baseMetadata.getPath().equals( datasetMetadata.getPath() ))
return new N5SingleScaleMetadata( baseMetadata.getPath(), baseMetadata.spatialTransform3d(),
public static N5SingleScaleMetadata setDatasetAttributes(final N5SingleScaleMetadata baseMetadata, final N5DatasetMetadata datasetMetadata) {

if (baseMetadata.getPath().equals(datasetMetadata.getPath()))
return new N5SingleScaleMetadata(baseMetadata.getPath(), baseMetadata.spatialTransform3d(),
baseMetadata.getDownsamplingFactors(), baseMetadata.getPixelResolution(), baseMetadata.getOffset(),
baseMetadata.unit(), datasetMetadata.getAttributes() );
baseMetadata.unit(), datasetMetadata.getAttributes());
else
return null;
}

public static N5SingleScaleMetadata[] updateChildrenDatasetAttributes( final N5SingleScaleMetadata[] baseMetadata, final N5DatasetMetadata[] datasetMetadata )
{
final HashMap<String,N5SingleScaleMetadata> bases = new HashMap<>();
Arrays.stream( baseMetadata ).forEach( x -> { bases.put( x.getPath(), x ); } );
public static N5SingleScaleMetadata[] updateChildrenDatasetAttributes(final N5SingleScaleMetadata[] baseMetadata,
final N5DatasetMetadata[] datasetMetadata) {

final HashMap<String, N5SingleScaleMetadata> bases = new HashMap<>();
Arrays.stream(baseMetadata).forEach(x -> {
bases.put(x.getPath(), x);
});

return ( N5SingleScaleMetadata[] ) Arrays.stream( datasetMetadata ).map( x -> {
final N5SingleScaleMetadata b = bases.get( x.getPath() );
if( b == null )
return (N5SingleScaleMetadata[])Arrays.stream(datasetMetadata).map(x -> {
final N5SingleScaleMetadata b = bases.get(x.getPath());
if (b == null)
return null;
else
return setDatasetAttributes( b, x );
} ).filter( x -> x != null ).toArray();
return setDatasetAttributes(b, x);
}).filter(x -> x != null).toArray();
}

public static void updateChildrenMetadata( final N5TreeNode parent, final N5Metadata[] childrenMetadata,
final boolean relative )
{
final HashMap<String,N5Metadata> children = new HashMap<>();
Arrays.stream( childrenMetadata ).forEach( x -> {
public static void updateChildrenMetadata(final N5TreeNode parent, final N5Metadata[] childrenMetadata,
final boolean relative) {

final HashMap<String, N5Metadata> children = new HashMap<>();
Arrays.stream(childrenMetadata).forEach(x -> {
final String absolutePath;
if( relative )
{
if (relative) {
absolutePath = normalizeGroupPath(parent.getPath() + "/" + x.getPath());
} else {
absolutePath = x.getPath();
}
children.put( absolutePath, x );
children.put(absolutePath, x);
});
parent.childrenList().forEach( c -> {
parent.childrenList().forEach(c -> {
final N5Metadata m = children.get(MetadataUtils.normalizeGroupPath(c.getPath()));
if( m != null )
c.setMetadata( m );
if (m != null)
c.setMetadata(m);
});
}

public static String canonicalPath( final N5TreeNode parent, final String child )
{
return canonicalPath( parent.getPath(), child );
public static String canonicalPath(final N5TreeNode parent, final String child) {

return canonicalPath(parent.getPath(), child);
}

public static String canonicalPath( final String parent, final String child )
{
try
{
final N5URI url = new N5URI( "?/" + parent + "/" + child );
public static String canonicalPath(final String parent, final String child) {

try {
final N5URI url = new N5URI("?/" + parent + "/" + child);
return url.normalizeGroupPath();
}
catch ( final URISyntaxException e ) { }
} catch (final URISyntaxException e) {}
return null;
}

Expand All @@ -191,21 +192,24 @@ public static String normalizeGroupPath(final String path) {
return path;
}

public static String relativePath( final String parent, final String child )
{
try
{
final N5URI purl = new N5URI( "?" + parent );
final N5URI curl = new N5URI( "?" + child );
final Path ppath = Paths.get( purl.normalizeGroupPath());
final Path cpath = Paths.get( curl.normalizeGroupPath());
return ppath.relativize(cpath).toString();
}
catch ( final URISyntaxException e )
{
e.printStackTrace();
}
return null;
/**
* Returns a relative group path from the child absolute path group path child
* the parent absolute group path.
*
* If the child path is not a descendent of parent, child will be returned.
*
* @param parent an absolute path
* @param child an absolute path
* @return relative path from child to parent, if it exists.
*/
public static String relativePath(final String parent, final String child) {

try {
final String purl = new N5URI("?" + parent).normalizeGroupPath();
final String curl = new N5URI("?" + child).normalizeGroupPath();
return new N5URI("?" + curl.replaceFirst("^"+purl, "")).normalizeGroupPath();
} catch (final URISyntaxException e) {}
return child;
}

/**
Expand All @@ -215,13 +219,13 @@ public static String relativePath( final String parent, final String child )
* @param d exponent
* @return result
*/
public static double[] pow( final double[] x, final int d )
{
final double[] y = new double[ x.length ];
Arrays.fill( y, 1 );
for ( int i = 0; i < d; i++ )
for ( int j = 0; j < x.length; j++ )
y[ j ] *= x[ j ];
public static double[] pow(final double[] x, final int d) {

final double[] y = new double[x.length];
Arrays.fill(y, 1);
for (int i = 0; i < d; i++)
for (int j = 0; j < x.length; j++)
y[j] *= x[j];

return y;
}
Expand All @@ -234,6 +238,7 @@ public static double[] pow( final double[] x, final int d )
* @return a string
*/
public static String getStringNullable(final JsonElement element) {

if (element == null || element.isJsonNull())
return null;
else
Expand All @@ -252,10 +257,9 @@ public static AffineGet scaleTranslationTransforms(final double[] scale, final d

if (translation != null) {

if( scale != null ) {
if (scale != null) {
return new ScaleAndTranslation(scale, translation);
}
else {
} else {
// scale null, translation not null
if (translation.length == 2)
return new Translation2D(translation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.DoubleStream;

Expand Down Expand Up @@ -139,11 +137,9 @@ private static OmeNgffDataset[] buildDatasets( final String path, final NgffSing
for( int i = 0; i < children.length; i++ )
{
datasets[i] = new OmeNgffDataset();
// final Path p = Paths.get(path);
// final Path c = Paths.get(children[i].getPath());
// datasets[i].path = p.relativize(c).toString();
datasets[i].path = Paths.get(path).relativize(Paths.get(children[i].getPath())).toString();
datasets[i].path = MetadataUtils.relativePath(path, children[i].getPath());
datasets[i].coordinateTransformations = children[i].getCoordinateTransformations();

}
return datasets;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import org.janelia.saalfeldlab.n5.DatasetAttributes;
import org.janelia.saalfeldlab.n5.N5URI;
import org.janelia.saalfeldlab.n5.universe.metadata.MetadataUtils;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.Axis;

Expand Down Expand Up @@ -55,8 +57,7 @@ public void addChild(final int idx, final NgffSingleScaleAxesMetadata child) {

final OmeNgffDataset dset = new OmeNgffDataset();
// paths are relative to this object
dset.path = Paths.get(getPath()).relativize(Paths.get(child.getPath())).toString();

dset.path = MetadataUtils.relativePath(getPath(), child.getPath());
dset.coordinateTransformations = child.getCoordinateTransformations();
if (idx < 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import static org.junit.Assert.assertEquals;

import org.janelia.saalfeldlab.n5.N5URI;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.Axis;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.AxisUtils;
import org.junit.Test;

public class BuildMultiscaleTest {

@Test
public void buildNgffMultiscale() {

testHelper("", new String[]{"s0", "s1", "s2", "s3"});

testHelper("a", new String[]{"s0", "s1", "s2", "s3"});
testHelper("a/b", new String[]{"s0", "s1", "s2", "s3"});
testHelper("a/b/c", new String[]{"s0", "s1", "s2", "s3"});

testHelper("a", new String[]{"0/s0", "0/s1", "0/s2", "0/s3"});
}

private static void testHelper(final String path, final String[] childPaths) {

final String downsampleMethod = "sampling";
final Axis[] axes = AxisUtils.buildAxes("x", "y", "z");
final OmeNgffMultiScaleMetadataMutable ms = new OmeNgffMultiScaleMetadataMutable(path);

for (int i = 0; i < childPaths.length; i++) {
final double s = Math.pow(2, i);
ms.addChild(buildScaleLevelMetadata(childPaths[i], new double[]{s, s, s}, axes));
}

final OmeNgffMultiScaleMetadata meta = new OmeNgffMultiScaleMetadata(ms.getAxes().length,
path, path, downsampleMethod, "0.4",
ms.getAxes(),
ms.getDatasets(), null,
ms.coordinateTransformations, ms.metadata, true);

for (int i = 0; i < childPaths.length; i++) {
assertEquals(
String.format("multiscale path incorrect for root: %s, child: %s", path, childPaths[i]),
childPaths[i], meta.getDatasets()[i].path);
}

// test building children from multiscales
// these metadata's path variables must be relative to the root
final NgffSingleScaleAxesMetadata[] children = OmeNgffMultiScaleMetadata.buildMetadata(3, path, null, meta);
for (int i = 0; i < childPaths.length; i++) {
// ensure the paths are equal up to normalization
assertEquals(
String.format("single scale path incorrect for root: %s, child: %s", path, childPaths[i]),
N5URI.normalizeGroupPath(path + "/" + childPaths[i]),
N5URI.normalizeGroupPath(children[i].getPath()));
}
}

private static NgffSingleScaleAxesMetadata buildScaleLevelMetadata(final String path, final double[] res,
final Axis[] axes) {

return new NgffSingleScaleAxesMetadata(path, res, null, axes, null);
}

}

0 comments on commit 4e617f8

Please sign in to comment.