diff --git a/src/main/java/qupath/ext/biop/abba/ABBAExtension.java b/src/main/java/qupath/ext/biop/abba/ABBAExtension.java
index f185dc6..fb4929b 100644
--- a/src/main/java/qupath/ext/biop/abba/ABBAExtension.java
+++ b/src/main/java/qupath/ext/biop/abba/ABBAExtension.java
@@ -3,29 +3,39 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qupath.lib.common.Version;
+import qupath.lib.gui.ActionTools;
import qupath.lib.gui.QuPathGUI;
import qupath.lib.gui.extensions.GitHubProject;
import qupath.lib.gui.extensions.QuPathExtension;
+import qupath.lib.gui.tools.MenuTools;
/**
- * Install Warpy as an extension.
+ * Install ABBA extension as an extension.
*
- * Installs Warpy into QuPath, adding some metadata and adds the necessary global variables to QuPath's Preferences
+ * Installs ABBA extension into QuPath, adding some metadata and adds the necessary global variables to QuPath's Preferences
*
* @author Nicolas Chiaruttini
*/
public class ABBAExtension implements QuPathExtension, GitHubProject {
private final static Logger logger = LoggerFactory.getLogger(ABBAExtension.class);
-
@Override
public GitHubRepo getRepository() {
return GitHubRepo.create("QuPath ABBA Extension", "biop", "qupath-extension-abba");
}
+ private static boolean alreadyInstalled = false;
+
@Override
public void installExtension(QuPathGUI qupath) {
-
+ if (alreadyInstalled)
+ return;
+ alreadyInstalled = true;
+ var actionLoadAtlasRois = ActionTools.createAction(new LoadAtlasRoisToQuPathCommand(qupath), "Load Atlas Annotations into Open Image");
+
+ MenuTools.addMenuItems(qupath.getMenu("Extensions", false),
+ MenuTools.createMenu("ABBA",actionLoadAtlasRois)
+ );
}
@Override
diff --git a/src/main/java/qupath/ext/biop/abba/AtlasTools.java b/src/main/java/qupath/ext/biop/abba/AtlasTools.java
new file mode 100644
index 0000000..f762f0d
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/AtlasTools.java
@@ -0,0 +1,236 @@
+package qupath.ext.biop.abba;
+
+import ij.gui.Roi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import qupath.ext.biop.abba.struct.AtlasHelper;
+import qupath.ext.biop.abba.struct.AtlasNode;
+import qupath.ext.biop.abba.struct.AtlasOntology;
+import qupath.imagej.tools.IJTools;
+import qupath.lib.common.ColorTools;
+import qupath.lib.gui.QuPathGUI;
+import qupath.lib.images.ImageData;
+import qupath.lib.images.servers.ImageServer;
+import qupath.lib.images.servers.RotatedImageServer;
+import qupath.lib.measurements.MeasurementList;
+import qupath.lib.measurements.MeasurementListFactory;
+import qupath.lib.objects.PathObject;
+import qupath.lib.objects.PathObjectTools;
+import qupath.lib.objects.PathObjects;
+import qupath.lib.projects.Project;
+import qupath.lib.projects.ProjectImageEntry;
+import qupath.lib.projects.Projects;
+import qupath.lib.roi.RoiTools;
+import qupath.lib.roi.interfaces.ROI;
+import qupath.lib.scripting.QP;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+public class AtlasTools {
+
+ final static Logger logger = LoggerFactory.getLogger(AtlasTools.class);
+
+ private static QuPathGUI qupath = QuPathGUI.getInstance();
+
+ private static String title = "Load ABBA RoiSets from current QuPath project";
+
+ static private PathObject createAnnotationHierarchy(List annotations) {
+
+ // Map the ID of the annotation to ease finding parents
+ Map mappedAnnotations =
+ annotations
+ .stream()
+ .collect(
+ Collectors.toMap(e -> (int) (e.getMeasurementList().getMeasurementValue("ID")), e -> e)
+ );
+
+ AtomicReference rootReference = new AtomicReference<>();
+
+ mappedAnnotations.forEach((id, annotation) -> {
+ PathObject parent = mappedAnnotations.get((int) annotation.getMeasurementList().getMeasurementValue("Parent ID"));
+ if (parent != null) {
+ parent.addPathObject(annotation);
+ } else {
+ // Found the root Path Object
+ rootReference.set(annotation);
+ }
+ });
+
+ // Return just the root annotation from the atlas
+ return rootReference.get();
+ }
+
+ static PathObject getWarpedAtlasRegions(ImageData imageData, String atlasName, boolean splitLeftRight) {
+
+ List annotations = getFlattenedWarpedAtlasRegions(imageData, atlasName, splitLeftRight); // TODO
+ if (splitLeftRight) {
+ List annotationsLeft = annotations
+ .stream()
+ .filter(po -> po.getPathClass().isDerivedFrom(QP.getPathClass("Left")))
+ .collect(Collectors.toList());
+
+ List annotationsRight = annotations
+ .stream()
+ .filter(po -> po.getPathClass().isDerivedFrom(QP.getPathClass("Right")))
+ .collect(Collectors.toList());
+
+ PathObject rootLeft = createAnnotationHierarchy(annotationsLeft);
+ PathObject rootRight = createAnnotationHierarchy(annotationsRight);
+ ROI rootFused = RoiTools.combineROIs(rootLeft.getROI(), rootRight.getROI(), RoiTools.CombineOp.ADD);
+ PathObject rootObject = PathObjects.createAnnotationObject(rootFused);
+ rootObject.setName("Root");
+ rootObject.addPathObject(rootLeft);
+ rootObject.addPathObject(rootRight);
+ return rootObject; // TODO
+ } else {
+ return createAnnotationHierarchy(annotations);
+ }
+
+ }
+
+ public static List getFlattenedWarpedAtlasRegions(ImageData imageData, String atlasName, boolean splitLeftRight) {
+ Project project = qupath.getProject();
+
+ // Get the project folder and get the ontology
+ Path ontologyPath = Paths.get(Projects.getBaseDirectory(project).getAbsolutePath(), atlasName+"-Ontology.json");
+ AtlasOntology ontology = AtlasHelper.openOntologyFromJsonFile(ontologyPath.toString());
+
+ // Loop through each ImageEntry
+ ProjectImageEntry entry = project.getEntry(imageData);
+
+ Path roisetPath = Paths.get(entry.getEntryPath().toString(), "ABBA-Roiset-"+atlasName+".zip");
+ if (!Files.exists(roisetPath)) {
+ logger.info("No RoiSets found in {}", roisetPath);
+ return null;
+ }
+
+ // Get all the ROIs and add them as PathAnnotations
+ List rois = RoiSetLoader.openRoiSet(roisetPath.toAbsolutePath().toFile());
+ logger.info("Loading {} Atlas Regions for {}", rois.size(), entry.getImageName());
+
+ Roi left = rois.get(rois.size() - 2);
+ Roi right = rois.get(rois.size() - 1);
+
+ rois.remove(left);
+ rois.remove(right);
+
+ // Rotation for rotated servers
+ ImageServer> server = imageData.getServer();
+
+ AffineTransform transform = null;
+
+ if (server instanceof RotatedImageServer) {
+ // The roi will need to be transformed before being imported
+ // First : get the rotation
+ RotatedImageServer ris = (RotatedImageServer) server;
+ switch (ris.getRotation()) {
+ case ROTATE_NONE: // No rotation.
+ break;
+ case ROTATE_90: // Rotate 90 degrees clockwise.
+ transform = AffineTransform.getRotateInstance(Math.PI/2.0);
+ transform.translate(0, -server.getWidth());
+ break;
+ case ROTATE_180: // Rotate 180 degrees.
+ transform = AffineTransform.getRotateInstance(Math.PI);
+ transform.translate(-server.getWidth(), -server.getHeight());
+ break;
+ case ROTATE_270: // Rotate 270 degrees
+ transform = AffineTransform.getRotateInstance(Math.PI*3.0/2.0);
+ transform.translate(-server.getHeight(), 0);
+ break;
+ default:
+ System.err.println("Unknow rotation for rotated image server: "+ris.getRotation());
+ }
+ }
+
+ AffineTransform finalTransform = transform;
+
+ List annotations = rois.stream().map(roi -> {
+ // Create the PathObject
+
+ //PathObject object = IJTools.convertToAnnotation( imp, imageData.getServer(), roi, 1, null );
+ PathObject object = PathObjects.createAnnotationObject(IJTools.convertToROI(roi, 0, 0, 1, null));
+
+ // Handles rotated image server
+ if (finalTransform !=null) {
+ object = PathObjectTools.transformObject(object, finalTransform, true);
+ }
+
+ // Add metadata to object as acquired from the Ontology
+ int object_id = Integer.parseInt(roi.getName());
+ // Get associated information
+ AtlasNode node = ontology.getNodeFromId(object_id);
+ String name = node.data().get(ontology.getNamingProperty());
+ System.out.println("node:"+node.getId()+":"+name);
+ object.setName(name);
+ object.getMeasurementList().putMeasurement("ID", node.getId());
+ if (node.parent()!=null) {
+ object.getMeasurementList().putMeasurement("Parent ID", node.parent().getId());
+ }
+ object.getMeasurementList().putMeasurement("Side", 0);
+ object.setPathClass(QP.getPathClass(name));
+ object.setLocked(true);
+ int[] rgba = node.getColor();
+ int color = ColorTools.packRGB(rgba[0], rgba[1], rgba[2]);
+ object.setColorRGB(color);
+ return object;
+ }).collect(Collectors.toList());
+
+ if (splitLeftRight) {
+ ROI leftROI = IJTools.convertToROI(left, 0, 0, 1, null);
+ ROI rightROI = IJTools.convertToROI(right, 0, 0, 1, null);
+ List splitObjects = new ArrayList<>();
+ for (PathObject annotation : annotations) {
+ ROI shapeLeft = RoiTools.combineROIs(leftROI, annotation.getROI(), RoiTools.CombineOp.INTERSECT);
+ if (!shapeLeft.isEmpty()) {
+ PathObject objectLeft = PathObjects.createAnnotationObject(shapeLeft, annotation.getPathClass(), duplicateMeasurements(annotation.getMeasurementList()));
+ objectLeft.setName(annotation.getName());
+ objectLeft.setPathClass(QP.getDerivedPathClass(QP.getPathClass("Left"), annotation.getPathClass().getName()));
+ objectLeft.setColorRGB(annotation.getColorRGB());
+ objectLeft.setLocked(true);
+ splitObjects.add(objectLeft);
+ }
+
+ ROI shapeRight = RoiTools.combineROIs(rightROI, annotation.getROI(), RoiTools.CombineOp.INTERSECT);
+ if (!shapeRight.isEmpty()) {
+ PathObject objectRight = PathObjects.createAnnotationObject(shapeRight, annotation.getPathClass(), duplicateMeasurements(annotation.getMeasurementList()));
+ objectRight.setName(annotation.getName());
+ objectRight.setPathClass(QP.getDerivedPathClass(QP.getPathClass("Right"), annotation.getPathClass().getName()));
+ objectRight.setColorRGB(annotation.getColorRGB());
+ objectRight.setLocked(true);
+ splitObjects.add(objectRight);
+ }
+
+ }
+ return splitObjects;
+ } else {
+ return annotations;
+ }
+ }
+
+ public static void loadWarpedAtlasAnnotations(ImageData imageData, String atlasName, boolean splitLeftRight) {
+ imageData.getHierarchy().addPathObject(getWarpedAtlasRegions(imageData, atlasName, splitLeftRight));
+ imageData.getHierarchy().fireHierarchyChangedEvent(AtlasTools.class);
+ }
+
+ private static MeasurementList duplicateMeasurements(MeasurementList measurements) {
+ MeasurementList list = MeasurementListFactory.createMeasurementList(measurements.size(), MeasurementList.MeasurementListType.GENERAL);
+
+ for (int i = 0; i < measurements.size(); i++) {
+ String name = measurements.getMeasurementName(i);
+ double value = measurements.getMeasurementValue(i);
+ list.addMeasurement(name, value);
+ }
+ return list;
+ }
+
+}
diff --git a/src/main/java/qupath/ext/biop/abba/Dummy.java b/src/main/java/qupath/ext/biop/abba/Dummy.java
deleted file mode 100644
index 5a85afd..0000000
--- a/src/main/java/qupath/ext/biop/abba/Dummy.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package qupath.ext.biop.abba;
-
-public class Dummy {
-
- public void test() {
-
- }
-}
diff --git a/src/main/java/qupath/ext/biop/abba/LoadAtlasRoisToQuPathCommand.java b/src/main/java/qupath/ext/biop/abba/LoadAtlasRoisToQuPathCommand.java
new file mode 100644
index 0000000..33defe2
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/LoadAtlasRoisToQuPathCommand.java
@@ -0,0 +1,49 @@
+package qupath.ext.biop.abba;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import qupath.lib.display.ImageDisplay;
+import qupath.lib.gui.QuPathGUI;
+import qupath.lib.gui.dialogs.Dialogs;
+import qupath.lib.images.ImageData;
+
+public class LoadAtlasRoisToQuPathCommand implements Runnable {
+
+ private static QuPathGUI qupath;
+
+ private boolean splitLeftRight;
+ private boolean doRun;
+
+ public LoadAtlasRoisToQuPathCommand( final QuPathGUI qupath) {
+ this.qupath = qupath;
+ }
+
+ public void run() {
+
+ String splitMode =
+ Dialogs.showChoiceDialog("Load Brain RoiSets into Image",
+ "This will load any RoiSets Exported using the ABBA tool onto the current image.\nContinue?", new String[]{"Split Left and Right Regions", "Do not split"}, "Do not split");
+
+ switch (splitMode) {
+ case "Do not split" :
+ splitLeftRight = false;
+ doRun = true;
+ break;
+ case "Split Left and Right Regions" :
+ splitLeftRight = true;
+ doRun = true;
+ break;
+ default:
+ // null returned -> cancelled
+ doRun = false;
+ return;
+ }
+ if (doRun) {
+ ImageData imageData = qupath.getImageData();
+ // TODO : Find atlas name
+ AtlasTools.loadWarpedAtlasAnnotations(imageData, "Adult Mouse Brain - Allen Brain Atlas V3", splitLeftRight);
+ System.out.println("Import DONE");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/qupath/ext/biop/abba/RoiSetLoader.java b/src/main/java/qupath/ext/biop/abba/RoiSetLoader.java
new file mode 100644
index 0000000..f1c0d3e
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/RoiSetLoader.java
@@ -0,0 +1,69 @@
+package qupath.ext.biop.abba;
+
+import ij.gui.Roi;
+import ij.io.RoiDecoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class RoiSetLoader {
+ final static Logger logger = LoggerFactory.getLogger( RoiSetLoader.class);
+
+ // Taken directly from the RoiManager, so as to be able to run it concurrently
+ // since the RoiManager only allows for one instance of itself to exist...
+ public static ArrayList openRoiSet( File path ) {
+ ZipInputStream in = null;
+ ByteArrayOutputStream out = null;
+ int nRois = 0;
+ ArrayList rois = new ArrayList<>();
+ try {
+ in = new ZipInputStream(new FileInputStream(path));
+ byte[] buf = new byte[1024];
+ int len;
+ ZipEntry entry = in.getNextEntry();
+ while (entry != null) {
+ String name = entry.getName();
+ if (name.endsWith(".roi")) {
+ out = new ByteArrayOutputStream();
+ while ((len = in.read(buf)) > 0)
+ out.write(buf, 0, len);
+ out.close();
+ byte[] bytes = out.toByteArray();
+ RoiDecoder rd = new RoiDecoder(bytes, name);
+ Roi roi = rd.getRoi();
+ if (roi != null) {
+ name = name.substring(0, name.length() - 4);
+ rois.add(roi);
+ nRois++;
+ }
+ }
+ entry = in.getNextEntry();
+ }
+ in.close();
+ } catch ( IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (in != null)
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ if (out != null)
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ if (nRois == 0) {
+ logger.error("This ZIP archive does not contain '.roi' files: {}", path);
+ }
+ return rois;
+ }
+}
diff --git a/src/main/java/qupath/ext/biop/abba/struct/AtlasHelper.java b/src/main/java/qupath/ext/biop/abba/struct/AtlasHelper.java
new file mode 100644
index 0000000..5085519
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/struct/AtlasHelper.java
@@ -0,0 +1,186 @@
+/*-
+ * #%L
+ * Repo containing a standard API for Atlases and some example ones
+ * %%
+ * Copyright (C) 2021 EPFL
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package qupath.ext.biop.abba.struct;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.*;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Static atlas ontology helper functions
+ */
+public class AtlasHelper {
+
+ public static Map buildIdToAtlasNodeMap(AtlasNode root) {
+ Map result = new HashMap<>();
+ return appendToIdToAtlasNodeMap(result, root);
+ }
+
+ private static Map appendToIdToAtlasNodeMap(Map map, AtlasNode node) {
+ map.put(node.getId(), node);
+ node.children().forEach(child -> {
+ appendToIdToAtlasNodeMap(map, child);
+ });
+ return map;
+ }
+
+ public static AtlasOntology openOntologyFromJsonFile(String path) {
+ File ontologyFile = new File(path);
+ if (ontologyFile.exists()) {
+ Gson gson = new Gson();
+ try {
+ FileReader fr = new FileReader(ontologyFile.getAbsoluteFile());
+ SerializableOntology ontology = gson.fromJson(new FileReader(ontologyFile.getAbsoluteFile()), SerializableOntology.class);
+ ontology.initialize();
+ fr.close();
+ return ontology;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ } else return null;
+ }
+
+ public static class SerializableOntology implements AtlasOntology{
+ String name;
+ String namingProperty;
+ SerializableAtlasNode root;
+ transient Map idToAtlasNodeMap;
+
+ public SerializableOntology(AtlasOntology ontology) {
+ this.name = ontology.getName();
+ this.root = new SerializableAtlasNode(ontology.getRoot(), null);
+ this.namingProperty = ontology.getNamingProperty();
+ }
+
+ static void setAllParents(SerializableAtlasNode node) {
+ node.children.forEach(child -> {
+ child.setParent(node);
+ setAllParents(child);
+ }
+ );
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void initialize() throws Exception {
+ setAllParents(root);
+ idToAtlasNodeMap = AtlasHelper.buildIdToAtlasNodeMap(root);
+ }
+
+ @Override
+ public void setDataSource(URL dataSource) {
+
+ }
+
+ @Override
+ public URL getDataSource() {
+ return null;
+ }
+
+ @Override
+ public AtlasNode getRoot() {
+ return root;
+ }
+
+ @Override
+ public AtlasNode getNodeFromId(int id) {
+ return idToAtlasNodeMap.get(id);
+ }
+
+ @Override
+ public String getNamingProperty() {
+ return namingProperty;
+ }
+
+ @Override
+ public void setNamingProperty(String namingProperty) {
+ this.namingProperty = namingProperty;
+ }
+ }
+
+ public static class SerializableAtlasNode implements AtlasNode {
+
+ final public int id;
+ final public int[] color;
+ final public Map data;
+ final public List children;
+ transient public SerializableAtlasNode parent;
+
+ public SerializableAtlasNode(AtlasNode node, SerializableAtlasNode parent) {
+ this.id = node.getId();
+ this.data = node.data();
+ this.parent = parent;
+ this.color = node.getColor();
+ children = new ArrayList<>();
+ node.children().forEach(n -> {
+ children.add(new SerializableAtlasNode(n, SerializableAtlasNode.this));
+ });
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public int[] getColor() {
+ return color;
+ }
+
+ @Override
+ public Map data() {
+ return data;
+ }
+
+ @Override
+ public AtlasNode parent() {
+ return parent;
+ }
+
+ public void setParent(SerializableAtlasNode parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public List extends AtlasNode> children() {
+ return children;
+ }
+ }
+
+}
diff --git a/src/main/java/qupath/ext/biop/abba/struct/AtlasNode.java b/src/main/java/qupath/ext/biop/abba/struct/AtlasNode.java
new file mode 100644
index 0000000..7410057
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/struct/AtlasNode.java
@@ -0,0 +1,46 @@
+/*-
+ * #%L
+ * Repo containing a standard API for Atlases and some example ones
+ * %%
+ * Copyright (C) 2021 EPFL
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package qupath.ext.biop.abba.struct;
+
+import java.util.List;
+import java.util.Map;
+
+public interface AtlasNode {
+ Integer getId();
+
+ int[] getColor();
+
+ /** Gets the data associated with the node. */
+ Map data();
+
+ /** Gets the parent of this node. */
+ AtlasNode parent();
+
+ /**
+ * Gets the node's children. If this list is mutated, the children will be
+ * affected accordingly. It is the responsibility of the caller to ensure
+ * continued integrity, particularly of parent linkages.
+ */
+ List extends AtlasNode> children();
+
+
+}
diff --git a/src/main/java/qupath/ext/biop/abba/struct/AtlasOntology.java b/src/main/java/qupath/ext/biop/abba/struct/AtlasOntology.java
new file mode 100644
index 0000000..8931d6f
--- /dev/null
+++ b/src/main/java/qupath/ext/biop/abba/struct/AtlasOntology.java
@@ -0,0 +1,44 @@
+/*-
+ * #%L
+ * Repo containing a standard API for Atlases and some example ones
+ * %%
+ * Copyright (C) 2021 EPFL
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package qupath.ext.biop.abba.struct;
+
+import java.net.URL;
+
+public interface AtlasOntology {
+
+ String getName();
+
+ void initialize() throws Exception;
+
+ void setDataSource(URL dataSource);
+
+ URL getDataSource();
+
+ AtlasNode getRoot();
+
+ AtlasNode getNodeFromId(int id);
+
+ String getNamingProperty();
+
+ void setNamingProperty(String namingProperty);
+
+}