Skip to content

Commit

Permalink
added deep link on client side
Browse files Browse the repository at this point in the history
  • Loading branch information
porterbot committed Aug 20, 2024
1 parent db0e0af commit d5d2b03
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.janelia.horta.loader.*;
import org.janelia.workstation.common.actions.CopyToClipboardAction;
import org.janelia.workstation.controller.dialog.NeuronColorDialog;
import org.janelia.workstation.controller.dialog.SaveOrOpenDeepLinkDialog;
import org.janelia.workstation.controller.listener.ColorModelListener;
import org.janelia.workstation.controller.listener.UnmixingListener;
import org.janelia.workstation.controller.listener.NeuronVertexCreationListener;
Expand Down Expand Up @@ -1270,6 +1271,14 @@ public void actionPerformed(ActionEvent e) {

snapMenu.add(snapPanel);

topMenu.add(new AbstractAction("Create Deep Link") {
@Override
public void actionPerformed(ActionEvent e) {
SaveOrOpenDeepLinkDialog deepLinkDialog = new SaveOrOpenDeepLinkDialog();
deepLinkDialog.showDialog();
}
});

JMenu viewMenu = new JMenu("View");
topMenu.add(viewMenu);

Expand Down
4 changes: 4 additions & 0 deletions modules/ViewerController/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@
<groupId>org.janelia.workstation</groupId>
<artifactId>viewer3d</artifactId>
</dependency>
<dependency>
<groupId>org.janelia.workstation</groupId>
<artifactId>geometry3d</artifactId>
</dependency>


</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,5 +321,8 @@ public void loadComplete(LoadProjectEvent event) {

// re-enable updates once the model is fully loaded
RefreshHandler.getInstance().ifPresent(rh -> rh.setReceiveUpdates(true));

PostSampleLoadEvent postLoadEvent = new PostSampleLoadEvent(currProject, currProject instanceof TmSample);
ViewerEventBus.postEvent(postLoadEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.janelia.workstation.controller.action;

import com.google.common.eventbus.Subscribe;
import org.janelia.geometry3d.Rotation;
import org.janelia.geometry3d.Vantage;
import org.janelia.model.domain.DomainObject;
import org.janelia.model.domain.Reference;
import org.janelia.model.domain.tiledMicroscope.TmMappedNeuron;
import org.janelia.model.domain.tiledMicroscope.TmSample;
import org.janelia.model.domain.tiledMicroscope.TmWorkspace;
import org.janelia.workstation.common.actions.BaseContextualNodeAction;
import org.janelia.workstation.controller.TmViewerManager;
import org.janelia.workstation.controller.ViewerEventBus;
import org.janelia.workstation.controller.eventbus.PostSampleLoadEvent;
import org.janelia.workstation.controller.eventbus.UnloadProjectEvent;
import org.janelia.workstation.controller.eventbus.ViewEvent;
import org.janelia.workstation.controller.model.DeepLink;
import org.janelia.workstation.controller.model.TmModelManager;
import org.janelia.workstation.controller.model.TmViewState;
import org.janelia.workstation.core.api.DomainMgr;
import org.janelia.workstation.core.workers.SimpleWorker;
import org.janelia.workstation.integration.util.FrameworkAccess;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle;
import org.openide.util.actions.SystemAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Launches the Data Viewer from a context-menu.
*/
@ActionID(
category = "actions",
id = "OpenDeepLinkAction"
)
@ActionRegistration(
displayName = "#CTL_OpenDeepLinkAction",
lazy = false
)
@ActionReferences({
@ActionReference(path = "Menu/Actions/Horta", position = 1510, separatorBefore = 1499)
})
@NbBundle.Messages("CTL_OpenDeepLinkAction=Open Deep Link In Horta")
public class OpenDeepLinkAction extends BaseContextualNodeAction {

private static final Logger log = LoggerFactory.getLogger(OpenDeepLinkAction.class);
private DeepLink deepLink;

public static OpenDeepLinkAction get() {
return SystemAction.get(OpenDeepLinkAction.class);
}

public void setDeepLink(DeepLink link) {
deepLink = link;
}

@Subscribe
public void finishDeepLinkOpen(PostSampleLoadEvent event) {
TmViewState view = deepLink.getViewpoint();
TmModelManager.getInstance().getCurrentView().setCameraFocusX(view.getCameraFocusX());
TmModelManager.getInstance().getCurrentView().setCameraFocusY(view.getCameraFocusY());
TmModelManager.getInstance().getCurrentView().setCameraFocusZ(view.getCameraFocusZ());
float[] rot = view.getCameraRotation();
TmModelManager.getInstance().getCurrentView().setZoomLevel(view.getZoomLevel());
ViewEvent viewEvent = new ViewEvent(this,(double)view.getCameraFocusX(),
(double)view.getCameraFocusY(),
(double)view.getCameraFocusZ(),
view.getZoomLevel(),
rot,
false);
ViewerEventBus.postEvent(viewEvent);
}

@Override
public void performAction() {
DomainObject sample = (deepLink.getSample()==null)? deepLink.getWorkspace() : deepLink.getSample();
if (sample==null) {
log.warn("Action performed with null domain object");
return;
}

ViewerEventBus.registerForEvents(this);
openProject(sample);
}

private void
openProject(DomainObject sample) {
TmViewerManager.getInstance().loadProject(sample);
FrameworkAccess.getBrowsingController().updateRecentlyOpenedHistory(Reference.createFor(sample));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package org.janelia.workstation.controller.dialog;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.janelia.model.domain.Reference;
import org.janelia.model.domain.tiledMicroscope.TmSample;
import org.janelia.model.domain.tiledMicroscope.TmWorkspace;
import org.janelia.workstation.common.gui.dialogs.ModalDialog;
import org.janelia.workstation.controller.access.TiledMicroscopeDomainMgr;
import org.janelia.workstation.controller.action.OpenDeepLinkAction;
import org.janelia.workstation.controller.model.DeepLink;
import org.janelia.workstation.controller.model.TmModelManager;
import org.janelia.workstation.controller.model.TmViewState;
import org.janelia.workstation.integration.util.FrameworkAccess;

import javax.swing.*;
import java.awt.*;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class SaveOrOpenDeepLinkDialog extends ModalDialog {
private JTextArea deepLinkResults = new JTextArea(2,40);
private JTextArea loadDeepLink = new JTextArea(2,40);
private boolean success = false;

public SaveOrOpenDeepLinkDialog() {
super(FrameworkAccess.getMainFrame());

setTitle("Save or Open Deep Link");
setLayout(new GridBagLayout());

// Enable line wrapping
deepLinkResults.setLineWrap(true);
deepLinkResults.setWrapStyleWord(true);
deepLinkResults.setEditable(true);
loadDeepLink.setLineWrap(true);
loadDeepLink.setWrapStyleWord(true);
loadDeepLink.setEditable(true);

GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(10, 10, 0, 10);

constraints.gridx = 0;
constraints.gridy = 0;
add(new JLabel("Generate DeepLink For Current Location:"), constraints);

JButton generateButton = new JButton("Generate");
generateButton.setToolTipText("Generate Deep Link");
generateButton.addActionListener(e -> generateLink());

constraints.gridx = 1;
constraints.gridy = 0;
add(generateButton,constraints);

constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 2;
constraints.insets = new Insets(0, 5, 30, 5);
add(deepLinkResults,constraints);

constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 1;
add(new JLabel("Load DeepLink:"), constraints);

constraints.gridx = 1;
constraints.gridy = 2;
add(loadDeepLink,constraints);

constraints.gridx = 2;
constraints.gridy = 2;
JButton loadButton = new JButton("Load");
loadButton.setToolTipText("Load Deep Link");
loadButton.addActionListener(e -> loadLink());
add(loadButton, constraints);

JButton closeButton = new JButton("Close");
closeButton.setToolTipText("Close");
closeButton.addActionListener(e -> onCancel());
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
add(closeButton, constraints);

getRootPane().setDefaultButton(closeButton);
}

public void showDialog() {
packAndShow();
}

private String encodeValue(String value) throws UnsupportedEncodingException {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}

private void onCancel() {
success = false;
dispose();
}

private void generateLink() {
DeepLink deepLink = new DeepLink();

Map<String, Object> deepLinkMap = new HashMap<>();
TmWorkspace workspace = TmModelManager.getInstance().getCurrentWorkspace();
if (workspace==null) {
TmSample sample = TmModelManager.getInstance().getCurrentSample();
if (sample == null) {
deepLinkResults.setText("There is no workspace or sample currently open to generate a deep link");
return;
}
deepLink.setSample(sample);
deepLinkMap.put("sample", sample.getId());
} else {
deepLink.setWorkspace(workspace);
deepLinkMap.put("workspace", workspace.getId());
}

TmViewState view = TmModelManager.getInstance().getCurrentView();
deepLink.setViewpoint(view);
deepLinkMap.put("viewFocusX", view.getCameraFocusX());
deepLinkMap.put("viewFocusY", view.getCameraFocusY());
deepLinkMap.put("viewFocusZ", view.getCameraFocusZ());
deepLinkMap.put("viewZoom", view.getZoomLevel());

ObjectMapper mapper = new ObjectMapper();
String encodedURL = deepLinkMap.keySet().stream()
.map(key -> {
try {
String jsonVal = mapper.writeValueAsString(deepLinkMap.get(key));
return key + "=" + encodeValue(jsonVal);
} catch (UnsupportedEncodingException | JsonProcessingException e) {
e.printStackTrace();
return "";
}
})
.collect(Collectors.joining("&", "deeplink:", ""));
deepLinkResults.setText(encodedURL);
repaint();
revalidate();
}

private void loadLink() {
String encodedParams = loadDeepLink.getText().replaceAll("deeplink:","");
String[] pairs = encodedParams.split("&");

HashMap<String,String> params = new HashMap<>();
try {
for (String pair : pairs) {
int idx = pair.indexOf("=");
String key = null;
key = URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.toString());
String value = URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.toString());
params.put(key, value);
}

DeepLink parsedDeepLink = new DeepLink();
ObjectMapper mapper = new ObjectMapper();
if (params.containsKey("workspace")) {
TmWorkspace workspace = TiledMicroscopeDomainMgr.getDomainMgr().getWorkspace(Long.parseLong(params.get("workspace")));
parsedDeepLink.setWorkspace(workspace);
} else if (params.containsKey("sample")) {
TmSample sample = TiledMicroscopeDomainMgr.getDomainMgr().getSample(Long.parseLong(params.get("sample")));
parsedDeepLink.setSample(sample);
}

Double focusX = mapper.readValue(params.get("viewFocusX"), Double.class);
Double focusY = mapper.readValue(params.get("viewFocusY"), Double.class);
Double focusZ = mapper.readValue(params.get("viewFocusZ"), Double.class);
Double zoomLevel = mapper.readValue(params.get("viewZoom"), Double.class);
TmViewState view = new TmViewState();
view.setCameraFocusX(focusX);
view.setCameraFocusY(focusY);
view.setCameraFocusZ(focusZ);
view.setZoomLevel(zoomLevel);
parsedDeepLink.setViewpoint(view);

OpenDeepLinkAction action = new OpenDeepLinkAction();
action.setDeepLink(parsedDeepLink);
action.performAction();
} catch (Exception e) {
FrameworkAccess.handleException(e);
}
}


public boolean isSuccess() {
return success;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.janelia.workstation.controller.eventbus;

public class PostSampleLoadEvent extends ViewerEvent {
private boolean sample = false;
private Object project;

public PostSampleLoadEvent(Object project,
boolean isSample) {
super(project);
this.project = project;
this.sample = isSample;
}

public boolean isSample() {
return sample;
}
public Object getProject() {
return project;
}

}
Loading

0 comments on commit d5d2b03

Please sign in to comment.