Repository: mobie/mobie-viewer-fiji (language: python)

Issue:
Loading a mergedGrid does not work in current MoBIE-beta
Loading a dataset that contains a mergedGrid fails with:
```
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Module threw exception
	at net.imagej.legacy.LegacyService.runLegacyCompatibleCommand(LegacyService.java:307)
	at net.imagej.legacy.DefaultLegacyHooks.interceptRunPlugIn(DefaultLegacyHooks.java:166)
	at ij.IJ.runPlugIn(IJ.java)
	at ij.Executer.runCommand(Executer.java:150)
	at ij.Executer.run(Executer.java:68)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Module threw exception
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at net.imagej.legacy.LegacyService.runLegacyCompatibleCommand(LegacyService.java:303)
	... 5 more
Caused by: java.lang.RuntimeException: Module threw exception
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:63)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:225)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	... 1 more
Caused by: java.lang.RuntimeException: Unknown class: mergedGrid
	at org.embl.mobie.viewer.serialize.JsonHelper.createObjectFromJsonValue(JsonHelper.java:45)
	at org.embl.mobie.viewer.serialize.SourceTransformerListAdapter.deserialize(SourceTransformerListAdapter.java:38)
	at org.embl.mobie.viewer.serialize.SourceTransformerListAdapter.deserialize(SourceTransformerListAdapter.java:14)
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
	at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
	at com.google.gson.Gson.fromJson(Gson.java:932)
	at com.google.gson.Gson.fromJson(Gson.java:897)
	at com.google.gson.Gson.fromJson(Gson.java:846)
	at org.embl.mobie.viewer.serialize.DatasetJsonParser.parseDataset(DatasetJsonParser.java:24)
	at org.embl.mobie.viewer.MoBIE.openDataset(MoBIE.java:220)
	at org.embl.mobie.viewer.MoBIE.openDataset(MoBIE.java:160)
	at org.embl.mobie.viewer.MoBIE.<init>(MoBIE.java:96)
	at org.embl.mobie.viewer.command.OpenMoBIEProjectCommand.run(OpenMoBIEProjectCommand.java:25)
	at org.scijava.command.CommandModule.run(CommandModule.java:196)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:163)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:124)
	... 6 more
```

To reproduce: open the project at /g/kreshuk/pape/Work/mobie/covid-if-project (data is not uploaded to s3 yet)


Discussion / hints:
It is because at some point some sources started to contain `grid` instead of `mergedGrid` in metadata.
The current version is using a `grid.`
> It is because at some point some sources started to contain `grid` instead of `mergedGrid` in metadata.
> The current version is using a `grid.`

@tischi and me decided a while ago that the two grids should be called `mergedGrid` and `transformedGrid`. Maybe this has not been updated properly here yet?
I'll fix it in the code now, but some sources will not work.
@KateMoreva please wait before you change too much; maybe @tischi is working on this already.
> I'll fix it in the code now, but some sources will not work.
@constantinpape
Done.
Please try it with the current `develop.`

> I'll fix it in the code now, but some sources will not work.
> @constantinpape
> Done.
> Please try it with the current `develop.`

Thanks. I will wait till this is deployed to MoBIE-beta though in order to properly test if this works in Fiji.
@tischi this issue is still there on MoBIE-beta

and now?

Now there's a different error:
```
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Module threw exception
	at net.imagej.legacy.LegacyService.runLegacyCompatibleCommand(LegacyService.java:307)
	at net.imagej.legacy.DefaultLegacyHooks.interceptRunPlugIn(DefaultLegacyHooks.java:166)
	at ij.IJ.runPlugIn(IJ.java)
	at ij.Executer.runCommand(Executer.java:150)
	at ij.Executer.run(Executer.java:68)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Module threw exception
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at net.imagej.legacy.LegacyService.runLegacyCompatibleCommand(LegacyService.java:303)
	... 5 more
Caused by: java.lang.RuntimeException: Module threw exception
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:63)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:225)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	... 1 more
Caused by: java.lang.NullPointerException
	at org.embl.mobie.viewer.MoBIE.getSource(MoBIE.java:289)
	at org.embl.mobie.viewer.MoBIE.loadImageSegmentsTable(MoBIE.java:366)
	at org.embl.mobie.viewer.MoBIE.loadPrimarySegmentsTables(MoBIE.java:414)
	at org.embl.mobie.viewer.MoBIE.loadPrimarySegmentsTables(MoBIE.java:487)
	at org.embl.mobie.viewer.view.ViewManager.loadTablesAndCreateImageSegments(ViewManager.java:452)
	at org.embl.mobie.viewer.view.ViewManager.showSegmentationDisplay(ViewManager.java:407)
	at org.embl.mobie.viewer.view.ViewManager.showSourceDisplay(ViewManager.java:338)
	at org.embl.mobie.viewer.view.ViewManager.show(ViewManager.java:289)
	at org.embl.mobie.viewer.MoBIE.openDataset(MoBIE.java:215)
	at org.embl.mobie.viewer.MoBIE.openDataset(MoBIE.java:149)
	at org.embl.mobie.viewer.MoBIE.<init>(MoBIE.java:85)
	at org.embl.mobie.viewer.command.OpenMoBIEProjectCommand.run(OpenMoBIEProjectCommand.java:25)
	at org.scijava.command.CommandModule.run(CommandModule.java:196)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:163)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:124)
	... 6 more
```
At some point it tries to load but then cannot find this source: `plate_cell_segmentation`
Does that help to understand what's going on?
Maybe some issue with sources that change their name?

> At some point it tries to load but then cannot find this source: `plate_cell_segmentation`
> Does that help to understand what's going on?
> Maybe some issue with sources that change their name?

Ok, this might be an issue in the json then. I will have a look at this on Sunday or Monday.
OK, Thanks!
I had a look at the code an in principle it should also work for transformed sources with name changes.
Maybe the JSON is referring to a source that does not exist...


> Maybe the JSON is referring to a source that does not exist...

I will check and let you know.
@tischi I have checked this out now and I am pretty sure that the json is correct, but I think I know what's going wrong in the viewer:
- the source `plate_cell_segmentation` is added through `mergedGridSourceName` here: https://gist.github.com/constantinpape/814327089f1a75ef31d366295138dd82#file-view-json-L477 (I copied the view to a gist because the dataset.json is too large to be rendered by github)
-  It's then used here: https://gist.github.com/constantinpape/814327089f1a75ef31d366295138dd82#file-view-json-L57

So I think that you forget to add the new sources resulting from `mergedGridSourceName` to the list of available sources in the viewer. Let me know if you need more information to fix this, or when I can try this again.
I am confused....

It tries to find the `plate_cell_segmentation` in the `sources` field of this data structure:

```
public class Dataset
{
	public boolean is2D = false;
	public int timepoints = 1;
	public Map< String, SourceSupplier> sources;
	public Map< String, View> views;
}
```

There it cannot find it, because the `plate_cell_segmentation` is only created on the fly in one of the views.

Note that the `public Map< String, SourceSupplier> sources` is populated directly from the dataset.json. This is different from the map `private Map< String, SourceAndConverter< ? > > sourceNameToSourceAndConverter` that we populate for each `View` while actually loading and transforming the sources.

In the dataset.sources we have the notion of a `SegmentationSource`, e.g.

```
"cell_segmentation_WellE10_0000": {
      "segmentation": {
        "imageData": {
          "ome.zarr": {
            "relativePath": "images/ome-zarr/cell_segmentation_WellE10_0000.ome.zarr"
          }
        },
        "tableData": {
          "tsv": {
            "relativePath": "tables/cell_segmentation_WellB06_0003"
          }
        }
      }
    },
```

...while in `mobie.sourceNameToSourceAndConverter` I think we do not have a logic of a source being a segmentation source with an associated table but we handle this via the `segmentationDisplay`, which can look like this:

```
"segmentationDisplay": {
            "lut": "glasbey",
            "name": "cell_segmentation",
            "opacity": 0.5,
            "showTable": false,
            "sources": [
              "plate_cell_segmentation"
            ],
            "tables": [
              "default.tsv"
            ],
            "visible": false
          }
```

...thus I am confused now as it seems we have two different ways of specifying that a source is a segmentation source and also what the name of the table is.

In fact, maybe this is OK, but for the `plate_cell_segmentation` we might in fact be missing information like this: 

```
"tableData": {
          "tsv": {
            "relativePath": "tables/plate_cell_segmentation"
          }
```

Maybe we have not thought of this use-case where a derived segmentation source is created? And thus we do not have a way to fully specify where the table data lives?







@tischi in this case, `plate_cell_segmentation` merges some other segmentations into one source. The other segmentations have tables, which should be merged into a single one. Note that the same happens when multiple segmentations are listed in a `transformedGrid`, e.g. [here](https://github.com/mobie/zebrafish-lm-project/blob/main/data/actin/dataset.json#L2331-L2342). This case works as expected already:
![Screenshot from 2021-11-08 11-13-19](https://user-images.githubusercontent.com/4263537/140724276-cb3e3939-efb5-44eb-85a9-5dd1173a6e36.png)


Yes, but [here](https://github.com/mobie/zebrafish-lm-project/blob/eed520982568ab36c73127d0440660dc00ae9ef1/data/actin/dataset.json#L2331-L2342) the individual segmentation sources **exist in dataset.json**.

whereas here they don't:

```
        "segmentationDisplay": {
          "lut": "glasbey",
          "name": "cell_segmentation",
          "opacity": 0.5,
          "showTable": false,
          "sources": [
            "plate_cell_segmentation"
          ],
          "tables": [
            "default.tsv"
          ],
          "visible": false
        }
      },
```

because `plate_cell_segmentation` is not a "SegmentationSource" in the sense that it is specified like this in dataset.json

The individual segmentations (https://gist.github.com/constantinpape/814327089f1a75ef31d366295138dd82#file-view-json-L489-L490) **do** exist in the dataset.json. 
This is the code:
```
public void loadPrimarySegmentsTables( SegmentationSourceDisplay segmentationDisplay )
	{
		segmentationDisplay.tableRows = new ArrayList<>();
		final ArrayList< List< TableRowImageSegment > > primaryTables = loadPrimarySegmentsTables( segmentationDisplay, segmentationDisplay.getTables().get( 0 ) );

		for ( List< TableRowImageSegment > primaryTable : primaryTables )
		{
			segmentationDisplay.tableRows.addAll( primaryTable );
		}
	}
```

and

```
private ArrayList< List< TableRowImageSegment > > loadPrimarySegmentsTables(SegmentationSourceDisplay segmentationDisplay, String table )
	{
		final ArrayList< List< TableRowImageSegment > > primaryTables = new ArrayList<>();

		// TODO: make parallel
		for ( String sourceName : segmentationDisplay.getSources() )
		{
			final List< TableRowImageSegment > primaryTable = loadImageSegmentsTable( sourceName, table );
			primaryTables.add( primaryTable );
		}

		return primaryTables;
	}
```

The issue now is that `loadImageSegmentsTable( sourceName, table );` does not work for `plate_cell_segmentation` because it cannot find the table...

I think what I need to do is amend this code:

```
        private ArrayList< List< TableRowImageSegment > > loadPrimarySegmentsTables(SegmentationSourceDisplay segmentationDisplay, String table )
	{
		final ArrayList< List< TableRowImageSegment > > primaryTables = new ArrayList<>();

		// TODO: make parallel
		for ( String sourceName : segmentationDisplay.getSources() )
		{
                        // TODO: make also work for derived sources
                        //    i.e. find the derived source(s) and their tables
			final List< TableRowImageSegment > primaryTable = loadImageSegmentsTable( sourceName, table );
			primaryTables.add( primaryTable );
		}

		return primaryTables;
	}
```
...see the 2nd TODO in above code.

What do you think? 
> ...see the 2nd TODO in above code.
> 
> What do you think?

Yes, I think that's exactly what needs to be done. More concretely, I think there are 2 cases that currently wouldn't work:
- If we have a new segmentationSource that results from a `mergedGrid`; then you somehow need to merge all the tables of the sources in the grid (as in this example)
- If we have a segmentation that changes its name due to `sourceNameAfterTransform` in the current view.
@tischi I have created two example views as we have discussed now, see https://github.com/mobie/zebrafish-lm-project/issues/10 for details.

Fix patch:
diff --git a/src/main/java/org/embl/mobie/viewer/MoBIE.java b/src/main/java/org/embl/mobie/viewer/MoBIE.java
index cdf4a9254..963de6221 100644
--- a/src/main/java/org/embl/mobie/viewer/MoBIE.java
+++ b/src/main/java/org/embl/mobie/viewer/MoBIE.java
@@ -1,19 +1,17 @@
 package org.embl.mobie.viewer;
 
+import bdv.SpimSource;
 import bdv.tools.transformation.TransformedSource;
-import bdv.util.BdvFunctions;
 import bdv.util.volatiles.SharedQueue;
 import bdv.img.n5.N5ImageLoader;
 import bdv.viewer.Source;
 import bdv.viewer.SourceAndConverter;
 import de.embl.cba.bdv.utils.Logger;
-import net.imglib2.realtransform.AffineTransform3D;
 import org.embl.mobie.io.ome.zarr.loaders.N5OMEZarrImageLoader;
 import org.embl.mobie.viewer.display.SegmentationSourceDisplay;
 import org.embl.mobie.viewer.display.AnnotatedIntervalDisplay;
 import org.embl.mobie.viewer.annotate.AnnotatedIntervalCreator;
 import org.embl.mobie.viewer.annotate.AnnotatedIntervalTableRow;
-import org.embl.mobie.viewer.playground.SourceChanger;
 import org.embl.mobie.viewer.serialize.DatasetJsonParser;
 import org.embl.mobie.viewer.serialize.ProjectJsonParser;
 import org.embl.mobie.viewer.source.ImageDataFormat;
@@ -21,6 +19,7 @@
 import org.embl.mobie.viewer.source.SegmentationSource;
 import org.embl.mobie.viewer.source.SpimDataOpener;
 import org.embl.mobie.viewer.table.TableDataFormat;
+import org.embl.mobie.viewer.transform.MergedGridSource;
 import org.embl.mobie.viewer.ui.UserInterface;
 import org.embl.mobie.viewer.ui.WindowArrangementHelper;
 import org.embl.mobie.viewer.view.View;
@@ -34,10 +33,8 @@
 import mpicbg.spim.data.SpimData;
 import mpicbg.spim.data.sequence.ImgLoader;
 import sc.fiji.bdvpg.PlaygroundPrefs;
-import sc.fiji.bdvpg.scijava.services.SourceAndConverterService;
 import sc.fiji.bdvpg.services.SourceAndConverterServices;
 import sc.fiji.bdvpg.sourceandconverter.importer.SourceAndConverterFromSpimDataCreator;
-import sc.fiji.bdvpg.sourceandconverter.transform.SourceAffineTransformer;
 
 import java.io.IOException;
 import java.util.*;
@@ -45,7 +42,6 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.function.Function;
 
 public class MoBIE
 {
@@ -64,7 +60,6 @@ public class MoBIE
 	private String imageRoot;
 	private String tableRoot;
 	private HashMap< String, ImgLoader > sourceNameToImgLoader;
-	private SourceAndConverterService sacService;
 	private Map< String, SourceAndConverter< ? > > sourceNameToSourceAndConverter;
 
 	public MoBIE( String projectRoot ) throws IOException
@@ -81,7 +76,6 @@ public MoBIE( String projectLocation, MoBIESettings settings ) throws IOExceptio
 		PlaygroundPrefs.setSourceAndConverterUIVisibility( false );
 		project = new ProjectJsonParser().parseProject( FileAndUrlUtils.combinePath( projectRoot,  "project.json" ) );
 		this.settings = setImageDataFormat( projectLocation );
-		sacService = ( SourceAndConverterService ) SourceAndConverterServices.getSourceAndConverterService();
 		openDataset();
 	}
 
@@ -284,18 +278,9 @@ public void close()
 //		userInterface.dispose();
 	}
 
-	public synchronized ImageSource getSource(String sourceName )
+	public synchronized ImageSource getSource( String sourceName )
 	{
-		try
-		{
-			return dataset.sources.get( sourceName ).get();
-		}
-		catch ( Exception e )
-		{
-			System.err.println( "Could not find source " + sourceName + " among the available sources in this dataset.");
-			e.printStackTrace();
-			throw new RuntimeException();
-		}
+		return dataset.sources.get( sourceName ).get();
 	}
 
 	public SourceAndConverter< ? > openSourceAndConverter( String sourceName )
@@ -415,18 +400,61 @@ private List< Map< String, List< String > > > loadAdditionalTables( List<String>
 
 	private ArrayList< List< TableRowImageSegment > > loadPrimarySegmentsTables(SegmentationSourceDisplay segmentationDisplay, String table )
 	{
-		final ArrayList< List< TableRowImageSegment > > primaryTables = new ArrayList<>();
+		final Set< String > rootSourceNames = ConcurrentHashMap.newKeySet();
 
-		// TODO: make parallel
 		for ( String sourceName : segmentationDisplay.getSources() )
 		{
-			final List< TableRowImageSegment > primaryTable = loadImageSegmentsTable( sourceName, table );
-			primaryTables.add( primaryTable );
+			fetchTableRootSources( getSourceAndConverter( sourceName ).getSpimSource(), rootSourceNames );
+		}
+
+		// TODO: make parallel
+		final ArrayList< List< TableRowImageSegment > > primaryTables = new ArrayList<>();
+		for ( String sourceName : rootSourceNames )
+		{
+			addPrimaryTable( table, primaryTables, sourceName );
 		}
 
 		return primaryTables;
 	}
 
+	/**
+	 * Recursively fetch all root sources
+	 * @param source
+	 * @param rootSourceNames
+	 */
+	private void fetchTableRootSources( Source< ? > source, Set< String > rootSourceNames )
+	{
+		if ( source instanceof SpimSource )
+		{
+			rootSourceNames.add( source.getName() );
+		}
+		else if ( source instanceof TransformedSource )
+		{
+			final Source< ? > wrappedSource = ( ( TransformedSource ) source ).getWrappedSource();
+
+			fetchTableRootSources( wrappedSource, rootSourceNames );
+		}
+		else if (  source instanceof MergedGridSource )
+		{
+			final MergedGridSource< ? > mergedGridSource = ( MergedGridSource ) source;
+			final List< ? extends Source< ? > > gridSources = mergedGridSource.getGridSources();
+			for ( Source< ? > gridSource : gridSources )
+			{
+				fetchTableRootSources( gridSource, rootSourceNames );
+			}
+		}
+		else
+		{
+			throw new IllegalArgumentException("Sources of type " + source.getClass().getName() + " are currently not supported in MoBIE.fetchTableRootSources.");
+		}
+	}
+
+	private void addPrimaryTable( String table, ArrayList< List< TableRowImageSegment > > primaryTables, String sourceName )
+	{
+		final List< TableRowImageSegment > primaryTable = loadImageSegmentsTable( sourceName, table );
+		primaryTables.add( primaryTable );
+	}
+
 	private Map< String, List< String > > createColumnsForMerging( List< TableRowImageSegment > segments, Map< String, List< String > > newColumns )
 	{
 		final ArrayList< String > segmentIdColumn = TableColumns.getColumn( segments, TableColumnNames.SEGMENT_LABEL_ID );
diff --git a/src/main/java/org/embl/mobie/viewer/transform/MergedGridSourceTransformer.java b/src/main/java/org/embl/mobie/viewer/transform/MergedGridSourceTransformer.java
index c190709de..683d2c8a5 100644
--- a/src/main/java/org/embl/mobie/viewer/transform/MergedGridSourceTransformer.java
+++ b/src/main/java/org/embl/mobie/viewer/transform/MergedGridSourceTransformer.java
@@ -28,7 +28,6 @@ public class MergedGridSourceTransformer extends AbstractSourceTransformer
 	private transient MergedGridSource< ? > mergedGridSource;
 	private transient double[] translationRealOffset;
 
-
 	@Override
 	public void transform( Map< String, SourceAndConverter< ? > > sourceNameToSourceAndConverter )
 	{
diff --git a/src/main/java/org/embl/mobie/viewer/view/ViewManager.java b/src/main/java/org/embl/mobie/viewer/view/ViewManager.java
index 409355da7..c48a59472 100644
--- a/src/main/java/org/embl/mobie/viewer/view/ViewManager.java
+++ b/src/main/java/org/embl/mobie/viewer/view/ViewManager.java
@@ -4,6 +4,7 @@
 import bdv.util.BdvHandle;
 import bdv.viewer.Source;
 import bdv.viewer.SourceAndConverter;
+import de.embl.cba.tables.Logger;
 import de.embl.cba.tables.color.ColoringModel;
 import de.embl.cba.tables.color.ColumnColoringModelCreator;
 import de.embl.cba.tables.select.DefaultSelectionModel;
@@ -146,8 +147,15 @@ public void showInTableViewer( SegmentationSourceDisplay display  )
 		Map<String, String> sourceNameToTableDir = new HashMap<>();
 		for ( String source: display.getSources() )
 		{
-			sourceNameToTableDir.put( source, moBIE.getTablesDirectoryPath( (SegmentationSource) moBIE.getSource( source ) )
-			);
+			try
+			{
+				sourceNameToTableDir.put( source, moBIE.getTablesDirectoryPath( ( SegmentationSource ) moBIE.getSource( source ) )
+				);
+			} catch ( Exception e )
+			{
+				Logger.info("[WARNING] Could not store table directory for " + source );
+				sourceNameToTableDir.put( source, null );
+			}
 		}
 		display.tableViewer = new TableViewer<>( moBIE, display.tableRows, display.selectionModel, display.coloringModel, display.getName(), sourceNameToTableDir, false ).show();
 		display.selectionModel.listeners().add( display.tableViewer );


Test patch:
diff --git a/src/test/java/projects/OpenLocalCovidPlate.java b/src/test/java/projects/OpenLocalCovidPlate.java
index bd83cfb2b..9ac051103 100644
--- a/src/test/java/projects/OpenLocalCovidPlate.java
+++ b/src/test/java/projects/OpenLocalCovidPlate.java
@@ -16,8 +16,7 @@ public static void main( String[] args )
 		imageJ.ui().showUI();
 
 		try {
-			new MoBIE("/g/kreshuk/pape/Work/mobie/covid-if-project/data",
-					MoBIESettings.settings().imageDataFormat( ImageDataFormat.OmeZarr ) );
+			new MoBIE("/g/kreshuk/pape/Work/mobie/covid-if-project/data", MoBIESettings.settings().imageDataFormat( ImageDataFormat.OmeZarr ) );
 		} catch (IOException e) {
 			e.printStackTrace();
 		}