| /******************************************************************************* |
| * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,2021 |
| * IBM Corporation, BfR, and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation and new features |
| * Bundesinstitut für Risikobewertung - Pajek Graph interface, new Veterinary Models |
| * Bundesinstitut fur Risikobewertung |
| *******************************************************************************/ |
| |
| package org.eclipse.stem.ui.views.graphmap; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.ConcurrentModificationException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| //import java.util.Observable; |
| //import java.util.Observer; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.EMap; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.stem.core.graph.DynamicLabel; |
| import org.eclipse.stem.core.graph.DynamicNodeLabel; |
| import org.eclipse.stem.core.graph.Edge; |
| import org.eclipse.stem.core.graph.EdgeLabel; |
| import org.eclipse.stem.core.graph.Graph; |
| import org.eclipse.stem.core.graph.Node; |
| import org.eclipse.stem.core.graph.NodeLabel; |
| import org.eclipse.stem.core.model.Decorator; |
| import org.eclipse.stem.core.model.STEMTime; |
| import org.eclipse.stem.core.modifier.DoubleModifier; |
| import org.eclipse.stem.core.modifier.FeatureModifier; |
| import org.eclipse.stem.core.modifier.Modifier; |
| import org.eclipse.stem.core.predicate.And; |
| import org.eclipse.stem.core.predicate.ElapsedTimeTest; |
| import org.eclipse.stem.core.predicate.Not; |
| import org.eclipse.stem.core.predicate.Predicate; |
| import org.eclipse.stem.core.predicate.PredicateExpression; |
| import org.eclipse.stem.core.trigger.TriggerList; |
| import org.eclipse.stem.definitions.adapters.spatial.geo.LatLong; |
| import org.eclipse.stem.definitions.adapters.spatial.geo.LatLong.Segment; |
| import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProvider; |
| import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProviderAdapter; |
| import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProviderAdapterFactory; |
| import org.eclipse.stem.definitions.edges.MigrationEdge; |
| import org.eclipse.stem.definitions.edges.MigrationEdgeLabel; |
| import org.eclipse.stem.definitions.edges.PopulationEdge; |
| import org.eclipse.stem.diseasemodels.standard.DiseaseModelLabel; |
| import org.eclipse.stem.geography.centers.GeographicCenters; |
| import org.eclipse.stem.jobs.simulation.ISimulation; |
| import org.eclipse.stem.jobs.simulation.ISimulationListener; |
| import org.eclipse.stem.jobs.simulation.SimulationEvent; |
| import org.eclipse.stem.populationmodels.standard.PopulationModelLabel; |
| import org.eclipse.stem.ui.adapters.color.ColorProviderAdapter; |
| import org.eclipse.stem.ui.adapters.color.ColorProviderAdapterFactory; |
| import org.eclipse.stem.ui.preferences.PreferenceConstants; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.layout.FormAttachment; |
| import org.eclipse.swt.layout.FormData; |
| import org.eclipse.swt.layout.FormLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * This class represents a visualization of the geographic attributes of a |
| * {@link org.eclipse.stem.jobs.simulation.org.eclipse.stem.jobs.simulation.Simulation} |
| * . |
| */ |
| public class GraphMapControl extends Composite implements ISimulationListener, |
| GeoViewOptionsBar.PropertySelectionListener { |
| |
| private ISimulation simulation; |
| private GraphMapCanvas canvas; |
| private String selectedEdge; |
| private Job refreshJob; |
| private boolean refreshPending; |
| private StemPolygonList polygonsToDraw; |
| private ColorProviderAdapter colorProviderAdapter; |
| private GeoViewOptionsBar optionsBar; |
| private Map<Decorator, DecoratorDisplayData> decoratorToDecoratorDisplayDataMap; |
| private Map<LatLong, StemPolygonList> polygonListMap; |
| private GraphMapView view; |
| private boolean showOptionsBar; |
| private FormData optionsBarFormData; |
| private Composite topComposite; |
| |
| public GraphMapControl(Composite parent, int style, GraphMapView view) { |
| super(parent, style); |
| this.view = view; |
| decoratorToDecoratorDisplayDataMap = new HashMap<Decorator, DecoratorDisplayData>(); |
| polygonListMap = new HashMap<LatLong, StemPolygonList>(); |
| |
| canvas = new GraphMapCanvas(this, SWT.NONE, this.view); |
| canvas.setColorProvider(colorProviderAdapter); |
| canvas.addPaneChangeListener(new PaneChangeListener(this)); |
| |
| setLayout(new FormLayout()); |
| |
| topComposite = new Composite(this, SWT.NONE); |
| topComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); |
| canvas.setGainScaleLabel(new Label(topComposite, SWT.RIGHT)); |
| |
| optionsBar = new GeoViewOptionsBar(this, SWT.NONE, view); |
| optionsBar.addPropertySelectionListener(this); |
| |
| FormData topCompositeFormData = new FormData(); |
| topComposite.setLayoutData(topCompositeFormData); |
| topCompositeFormData.top = new FormAttachment(0, 0); |
| topCompositeFormData.left = new FormAttachment(0, 0); |
| topCompositeFormData.right = new FormAttachment(100, 0); |
| |
| FormData geoRendererFormData = new FormData(); |
| canvas.setLayoutData(geoRendererFormData); |
| geoRendererFormData.top = new FormAttachment(topComposite, 0); |
| geoRendererFormData.bottom = new FormAttachment(optionsBar, 0); |
| geoRendererFormData.left = new FormAttachment(0, 0); |
| geoRendererFormData.right = new FormAttachment(100, 0); |
| |
| optionsBarFormData = new FormData(); |
| optionsBar.setLayoutData(optionsBarFormData); |
| optionsBarFormData.bottom = new FormAttachment(100, 0); |
| optionsBarFormData.left = new FormAttachment(0, 0); |
| optionsBarFormData.right = new FormAttachment(100, 0); |
| // XXX for now |
| this.showOptionsBar = true; |
| pack(); |
| } |
| |
| public ISimulation getSimulation() { |
| return simulation; |
| } |
| |
| /** |
| * Setting the {@link org.eclipse.stem.jobs.simulation.Simulation} has the |
| * side-effect of causing the control to remove itself as a listener from |
| * any previously set {@link org.eclipse.stem.jobs.simulation.Simulation} |
| * and adding itself as a listener to the new one. It will also cause the |
| * image to be initialized from the contents of the new |
| * {@link org.eclipse.stem.jobs.simulation.Simulation} as appropriate. |
| * |
| * @param simulation |
| * the {@link org.eclipse.stem.jobs.simulation.Simulation} whose |
| * Lat/Long data will be rendered. |
| */ |
| public void setSimulation(ISimulation simulation) { |
| // Any need to switch? |
| if (this.simulation == simulation) { |
| // No |
| return; |
| } // if no need to switch? |
| |
| // Should we remove ourselves as listeners from the current simulation? |
| if (this.simulation != null) { |
| // Yes |
| this.simulation.removeSimulationListener(this); |
| } |
| |
| this.simulation = simulation; |
| |
| // Was there a new simulation to switch to? |
| if (this.simulation != null) { |
| // Yes |
| this.simulation.addSimulationListener(this); |
| optionsBar.setSimulation(this.simulation); |
| setGeoViewProperties(); |
| } |
| refresh(); |
| } |
| |
| public void showOptionsBar(boolean toggle){ |
| this.showOptionsBar = toggle; |
| if (this.showOptionsBar){ |
| optionsBarFormData.top = null; |
| this.layout(); |
| } else{ |
| optionsBarFormData.top = new FormAttachment(100,0); |
| this.layout(); |
| } |
| redraw(); |
| } |
| /** |
| * Update the contents of {@link #geographicRenderer} from the contents of |
| * the {@link #simulation}. This method should only be called by the UI |
| * thread otherwise it contains a race condition that can lock up the |
| * refreshing of the map canvas. |
| */ |
| public void refresh() { |
| // This is complex and potential confusing code. The basic idea is that |
| // we want to determine what to draw on the mapCanvas in a thread |
| // separate from the UI thread, but we also want to capture a request to |
| // refresh the mapCanvas that comes while a background Job is already |
| // running. This request could have been generated because something |
| // significant like a decorator or a property has changed and |
| // we don't want to lose that switch. |
| // We have two fields that capture the state of the refresh. The first |
| // is the flag refreshPending which if true indicates that a call to |
| // refreshMapCanvas was processed while a background Job was executing. |
| // This flag is set instead of creating another background Job. The |
| // second is the field refreshJob which is set to the background Job if |
| // it is present and running. |
| // |
| // If we enter the method and refreshPending is true then we immediately |
| // exit because a refresh job will be created and there's nothing more |
| // for us to request. If, instead, refreshPending is false, then we |
| // check to see if a refreshJob is already running. If so, we set |
| // refreshPending to true, and exit. If not, we create the background |
| // refresh job and assign it to the field refreshJob. |
| // |
| // In the background refreshing job we process the Lat/Long data and |
| // then when done we add a Runnable on to the UI thread to redraw the |
| // mapCanvas. That Runnable also checks to see if the refresh pending |
| // flag is set and if so, resets the flag and then adds another Runnable |
| // to the UI thread to re-enter the refreshMapCanvas method. |
| // |
| // There is a race condition in the method if called by other than the |
| // UI thread. If a thread different from the UI thread were to call this |
| // method then it could reach position "RC #2" in the code below and |
| // then be preempted. The UI thread could then execute the test at "RC |
| // #1" below and find that there is no refresh pending. It would exit |
| // and then the other thread would resume at "RC #2" and set the |
| // refreshPending flag. This would cause all subsequent method |
| // invocations to immediately exit and the map canvas would never be |
| // refreshed again. |
| |
| if (refreshJob != null) { |
| refreshPending = true; |
| return; |
| } |
| |
| refreshJob = new Job(Messages.getString("IMView.MRefresh")) { |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| polygonsToDraw = createPolygonsToDraw(monitor); |
| |
| if (polygonsToDraw == null || polygonsToDraw.isEmpty()) { |
| return Status.OK_STATUS; |
| } |
| |
| try { |
| if (PlatformUI.isWorkbenchRunning()) { |
| Display display = Display.getDefault(); |
| |
| if (!display.isDisposed()) { |
| display.asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| if (!canvas.isDisposed()) { |
| canvas.render(polygonsToDraw, |
| simulation); |
| refreshJob = null; |
| |
| if (refreshPending) { |
| refreshPending = false; |
| refresh(); |
| } |
| } else { |
| refreshJob = null; |
| } |
| } |
| }); |
| } |
| } |
| } catch (NullPointerException npe) { |
| // See 177966: |
| // We ignore the exception, there's nothing to do |
| } catch (SWTException se) { |
| // Sometimes this happens when shutting down STEM with |
| // simulations |
| // still running. Nothing we can do about it, so just |
| // ignore. |
| } |
| |
| monitor.done(); |
| |
| return Status.OK_STATUS; |
| } |
| }; |
| refreshJob.schedule(); |
| } |
| |
| @Override |
| public void simulationChanged(SimulationEvent event) { |
| switch (event.getSimulationState()) { |
| case COMPLETED_CYCLE: |
| // It could be that the propertySelector is not initialized |
| // yet. This could happen if, when the simulation is first set, its |
| // scenario was not completely initialized, and so didn't have a |
| // canonical graph. If this is the case, we need to try again. |
| |
| if (!optionsBar.isInitialized()) { |
| optionsBar.setSimulation(getSimulation()); |
| } |
| |
| refresh(); |
| break; |
| case RESET: |
| refresh(); |
| break; |
| case RUNNING: |
| case COMPLETED_SEQUENCE: |
| case PAUSED: |
| case STOPPED: |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| optionsBar.removePropertySelectionListener(this); |
| |
| if (simulation != null) { |
| simulation.removeSimulationListener(this); |
| } |
| } |
| |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| canvas.addSelectionChangedListener(listener); |
| } |
| |
| public void removeSelectionChangedListener( |
| ISelectionChangedListener listener) { |
| canvas.removeSelectionChangedListener(listener); |
| } |
| |
| public Image getMapCanvasImage() { |
| return canvas.getCanvasImage(); |
| } |
| |
| @Override |
| public void propertySelected() { |
| setGeoViewProperties(); |
| layout(); |
| refresh(); |
| } |
| |
| private void setGeoViewProperties() { |
| Decorator decorator = optionsBar.getSelectedDecorator(); |
| if (decorator != null) { |
| AdapterFactory adapterFactory = ColorProviderAdapterFactory.INSTANCE |
| .getFactoryForType(optionsBar.getSelectedColorProvider()); |
| |
| Adapter adapter = adapterFactory.adapt(decorator.getGraph(), optionsBar.getSelectedColorProvider()); |
| |
| if (adapter instanceof ColorProviderAdapter) { |
| colorProviderAdapter = (ColorProviderAdapter) adapter; |
| colorProviderAdapter.setSelectedDecorator(optionsBar |
| .getSelectedDecorator()); |
| colorProviderAdapter.setSelectedPopulationIdentifier(optionsBar |
| .getSelectedPopulationIdentifier()); |
| canvas.setColorProvider(colorProviderAdapter); |
| selectedEdge = optionsBar.getSelectedEdge(); |
| |
| if (optionsBar.getSelectedProperty() != null) { |
| colorProviderAdapter.setSelectedProperty(optionsBar |
| .getSelectedProperty()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This runs in the background {@link Job} created in {@link #refresh()} |
| * |
| * @param decorator |
| * the {@link Decorator} for whom that data is being retrieved. |
| * @param monitor |
| * the progress monitor to use to report the progress of |
| * obtaining the data |
| * @return the {@link DecoratorDisplayData} for the {@link Decorator}. |
| */ |
| private DecoratorDisplayData getDecoratorDisplayData(Decorator decorator, |
| IProgressMonitor monitor) { |
| DecoratorDisplayData retValue = decoratorToDecoratorDisplayDataMap |
| .get(decorator); |
| |
| // Is there already an instance for this decorator? |
| if (retValue == null) { |
| retValue = new DecoratorDisplayData(); |
| decoratorToDecoratorDisplayDataMap.put(decorator, retValue); |
| |
| if (decorator instanceof TriggerList) { |
| Graph graph = simulation.getScenario().getCanonicalGraph(); |
| // Node randomNode = null; |
| // |
| for (Node node : graph.getNodes().values()) { |
| retValue.addNode(node); |
| // randomNode = node; |
| } |
| // if (randomNode != null) { |
| // LatLongProviderAdapter latLongProvider = (LatLongProviderAdapter) LatLongProviderAdapterFactory.INSTANCE |
| // .adapt(randomNode, LatLongProvider.class); |
| // |
| // retValue.setLatLongProviderAdapter(latLongProvider); |
| // } |
| } else { |
| if (decorator.getLabelsToUpdate() == null |
| || decorator.getLabelsToUpdate().isEmpty()) { |
| return retValue; |
| } |
| |
| // DynamicNodeLabel firstNodeLabel = (DynamicNodeLabel) decorator |
| // .getLabelsToUpdate().get(0); |
| // if (firstNodeLabel != null) { |
| // Node node = firstNodeLabel.getNode(); |
| // LatLongProviderAdapter latLongProvider = (LatLongProviderAdapter) LatLongProviderAdapterFactory.INSTANCE |
| // .adapt(node, LatLongProvider.class); |
| // |
| // retValue.setLatLongProviderAdapter(latLongProvider); |
| // } |
| |
| for (DynamicLabel label : decorator.getLabelsToUpdate()) { |
| retValue.addNode(((DynamicNodeLabel) label).getNode()); |
| } |
| } |
| } |
| |
| // Does the instance have all of it's lat/long data retrieved yet? |
| if (!retValue.hasAllLatLong()) { |
| monitor.subTask(decorator.getDublinCore().getTitle()); |
| retValue.updateLatLong(monitor); |
| } |
| |
| return retValue; |
| } |
| |
| /** |
| * This runs in the background {@link Job} created in |
| * {@link #refreshMapCanvas()}. |
| * |
| * @param monitor |
| * progress monitor for obtaining the polygons |
| * @return a collection of polygon values to render in the |
| * {@link #mapCanvas} |
| */ |
| private StemPolygonList createPolygonsToDraw(IProgressMonitor monitor) { |
| Decorator selectedDecorator = optionsBar.getSelectedDecorator(); |
| String selectedPopulationIdentifier = optionsBar |
| .getSelectedPopulationIdentifier(); |
| StemPolygonList retValue = new StemPolygonList(); |
| |
| if (selectedDecorator == null) { |
| return null; |
| } |
| |
| DecoratorDisplayData displayData = getDecoratorDisplayData( |
| selectedDecorator, monitor); |
| |
| if (displayData == null) { |
| return null; |
| } |
| |
| monitor.beginTask(selectedDecorator.getDublinCore().getTitle(), |
| displayData.getNodeData().size()); |
| |
| if (selectedDecorator instanceof TriggerList) { |
| Map<URI, Double> nodeValues = getMigrationNodeValues(); |
| |
| for (Map.Entry<Node, LatLong> labelData : displayData.getNodeData() |
| .entrySet()) { |
| // This is the Identifiable that will produce the lat/long data. |
| Node node = labelData.getKey(); |
| LatLong latLong = labelData.getValue(); |
| |
| monitor.subTask(node.getDublinCore().getTitle()); |
| |
| if (latLong != null && latLong.size() > 0) { |
| StemPolygonList stemPolygonsList = polygonListMap |
| .get(latLong); |
| Double value = nodeValues.get(node.getURI()); |
| |
| if (stemPolygonsList == null) { |
| stemPolygonsList = new StemPolygonList(latLong, node); |
| polygonListMap.put(latLong, stemPolygonsList); |
| } |
| |
| if (value != null) { |
| stemPolygonsList.setValueToDisplay(value); |
| } else { |
| stemPolygonsList.setValueToDisplay(0.0); |
| } |
| |
| retValue.addAll(stemPolygonsList); |
| } |
| |
| monitor.worked(1); |
| } |
| } else { |
| for (Map.Entry<Node, LatLong> labelData : displayData.getNodeData() |
| .entrySet()) { |
| // This is the Identifiable that will produce the lat/long data. |
| Node node = labelData.getKey(); |
| LatLong latLong = labelData.getValue(); |
| |
| boolean foundLabel = false; |
| |
| boolean success=true; |
| do { |
| success = true; |
| try { |
| for (NodeLabel lab : labelData.getKey().getLabels()) { |
| if (lab instanceof DiseaseModelLabel |
| && ((DiseaseModelLabel) lab) |
| .getPopulationModelLabel() |
| .getPopulationIdentifier() |
| .equals(selectedPopulationIdentifier)) { |
| foundLabel = true; |
| break; |
| } else if (lab instanceof PopulationModelLabel |
| && ((PopulationModelLabel) lab) |
| .getPopulationIdentifier().equals( |
| selectedPopulationIdentifier)) { |
| foundLabel = true; |
| break; |
| } |
| } |
| } catch(ConcurrentModificationException cme) { // Occational occurence. Retry if it happens |
| success = false; |
| } |
| } while(!success); |
| |
| if (!foundLabel) { |
| continue; |
| } |
| |
| monitor.subTask(node.getDublinCore().getTitle()); |
| |
| if (latLong != null && latLong.size() > 0) { |
| StemPolygonList stemPolygonsList = polygonListMap |
| .get(latLong); |
| |
| if (stemPolygonsList == null) { |
| stemPolygonsList = new StemPolygonList(latLong, node); |
| polygonListMap.put(latLong, stemPolygonsList); |
| } |
| |
| stemPolygonsList.setValueToDisplay(Double.NaN); |
| retValue.addAll(stemPolygonsList); |
| } |
| |
| monitor.worked(1); |
| } |
| } |
| |
| if (selectedEdge == null) { |
| return retValue; |
| } |
| |
| Map<URI, Double> edgeValues = null; |
| String selectedEdgeCopy = selectedEdge; |
| |
| if (selectedEdge.startsWith("triggered")) { |
| selectedEdgeCopy = selectedEdge.replaceFirst("triggered", ""); |
| edgeValues = getMigrationEdgeValues(false); |
| } else if (selectedEdge.startsWith("history")) { |
| selectedEdgeCopy = selectedEdge.replaceFirst("history", ""); |
| edgeValues = getMigrationEdgeValues(true); |
| } |
| |
| EMap<URI, Edge> allEdges = selectedDecorator.getGraph().getEdges(); |
| List<Edge> edges = new ArrayList<Edge>(); |
| |
| for (URI uri : allEdges.keySet()) { |
| if (uri.toString().startsWith(selectedEdgeCopy)) { |
| Edge edge = allEdges.get(uri); |
| |
| if (edge instanceof PopulationEdge) { |
| String popID = ((PopulationEdge) edge) |
| .getPopulationIdentifier(); |
| |
| if (selectedPopulationIdentifier.equals(popID)) { |
| edges.add(edge); |
| } |
| } else { |
| edges.add(edge); |
| } |
| } |
| } |
| |
| if (edges.isEmpty()) { |
| return retValue; |
| } |
| |
| Set<URI> addedEdges = new HashSet<URI>(); |
| |
| for (Edge nextEdge : edges) { |
| Node nodeA = nextEdge.getA(); |
| Node nodeB = nextEdge.getB(); |
| |
| if (nodeA == null || nodeB == null) { |
| // It is possible that edges for air transport has a missing |
| // node if sub model is not included in the scenario. |
| continue; |
| } |
| |
| String nodeAISOKey = nodeA.getURI().lastSegment(); |
| String nodeBISOKey = nodeB.getURI().lastSegment(); |
| double[] centerA = GeographicCenters.getCenter(nodeAISOKey); |
| double[] centerB = GeographicCenters.getCenter(nodeBISOKey); |
| |
| if (centerA == null) { |
| LatLongProviderAdapter latLongProviderA = (LatLongProviderAdapter) LatLongProviderAdapterFactory.INSTANCE |
| .adapt(nodeA, LatLongProvider.class); |
| |
| //latLongProviderA.setTarget(nodeA); |
| centerA = latLongProviderA.getCenter(); |
| } |
| |
| if (centerB == null) { |
| LatLongProviderAdapter latLongProviderB = (LatLongProviderAdapter) LatLongProviderAdapterFactory.INSTANCE |
| .adapt(nodeB, LatLongProvider.class); |
| |
| //latLongProviderB.setTarget(nodeB); |
| centerB = latLongProviderB.getCenter(); |
| } |
| |
| if (centerA == null || centerB == null) { |
| continue; |
| } |
| |
| if (!addedEdges.contains(nextEdge.getURI())) { |
| Segment edgeSegment = new Segment(new double[][] { centerA, |
| centerB }); |
| StemPolygon stemPolygon = new StemPolygon(edgeSegment, nextEdge); |
| |
| if (edgeValues == null) { |
| stemPolygon.setValueToDisplay(Double.NaN); |
| retValue.add(stemPolygon); |
| addedEdges.add(nextEdge.getURI()); |
| } else { |
| Double value = edgeValues.get(nextEdge.getURI()); |
| |
| if (value != null) { |
| stemPolygon.setValueToDisplay(value); |
| retValue.add(stemPolygon); |
| addedEdges.add(nextEdge.getURI()); |
| } |
| } |
| } |
| } |
| |
| return retValue; |
| } |
| |
| private Map<URI, Double> getMigrationNodeValues() { |
| EMap<URI, Edge> edges = simulation.getScenario().getCanonicalGraph() |
| .getEdges(); |
| Map<URI, Double> edgeValues = getMigrationEdgeValues(true); |
| Map<URI, Double> nodeValues = new HashMap<URI, Double>(); |
| |
| for (URI edgeURI : edgeValues.keySet()) { |
| double value = edgeValues.get(edgeURI); |
| MigrationEdge edge = (MigrationEdge) edges.get(edgeURI); |
| URI nodeURI = edge.getNodeBURI(); |
| Double oldValue = nodeValues.get(nodeURI); |
| |
| if (oldValue == null) { |
| nodeValues.put(nodeURI, value); |
| } else { |
| nodeValues.put(nodeURI, oldValue + value); |
| } |
| } |
| |
| return nodeValues; |
| } |
| |
| private Map<URI, Double> getMigrationEdgeValues(boolean history) { |
| EMap<URI, Edge> edges = simulation.getScenario().getCanonicalGraph() |
| .getEdges(); |
| Map<URI, EdgeLabel> edgeLabels = new HashMap<URI, EdgeLabel>(); |
| Map<URI, Double> edgeValues = new HashMap<URI, Double>(); |
| |
| for (Edge edge : edges.values()) { |
| EdgeLabel edgeLabel = edge.getLabel(); |
| |
| edgeLabels.put(edgeLabel.getURI(), edgeLabel); |
| } |
| |
| for (Decorator deco : simulation.getScenario().getScenarioDecorators()) { |
| if (!(deco instanceof TriggerList)) { |
| continue; |
| } |
| |
| TriggerList triggerList = (TriggerList) deco; |
| |
| for (int i = 0; i < triggerList.getPredicateList().size(); i++) { |
| Predicate predicate = triggerList.getPredicateList().get(i); |
| Decorator action = triggerList.getActionList().get(i); |
| Interval interval = getTimeInterval(predicate); |
| long time = simulation.getScenario().getSequencer() |
| .getCurrentTime().getTime().getTime(); |
| |
| if (interval == null) { |
| continue; |
| } |
| |
| if (history && time < interval.startTime) { |
| continue; |
| } else if (!history |
| && (time < interval.startTime || time > interval.endTime)) { |
| continue; |
| } |
| |
| if (!(action instanceof Modifier)) { |
| continue; |
| } |
| |
| Modifier modifier = (Modifier) action; |
| EdgeLabel label = edgeLabels.get(modifier.getTargetURI()); |
| |
| if (label == null || !(label instanceof MigrationEdgeLabel)) { |
| continue; |
| } |
| |
| MigrationEdgeLabel migrationLabel = (MigrationEdgeLabel) label; |
| |
| for (FeatureModifier fm : modifier.getFeatureModifiers()) { |
| if (!fm.getFeatureName().equals("migrationRate")) { |
| continue; |
| } |
| |
| double value = ((DoubleModifier) fm).getValue(); |
| URI edgeURI = migrationLabel |
| .getURIOfIdentifiableToBeLabeled(); |
| Double oldValue = edgeValues.get(edgeURI); |
| |
| if (oldValue == null) { |
| edgeValues.put(edgeURI, value); |
| } else { |
| edgeValues.put(edgeURI, oldValue + value); |
| } |
| } |
| } |
| } |
| |
| return edgeValues; |
| } |
| |
| private Interval getTimeInterval(Predicate predicate) { |
| if (!(predicate instanceof PredicateExpression)) { |
| return null; |
| } |
| |
| Predicate and = ((PredicateExpression) predicate).getPredicate(); |
| |
| if (!(and instanceof And)) { |
| return null; |
| } |
| |
| EList<Predicate> operands = ((And) and).getOperands(); |
| |
| if (operands.size() != 2) { |
| return null; |
| } |
| |
| ElapsedTimeTest timeTest1 = null; |
| Not not = null; |
| |
| if (operands.get(0) instanceof ElapsedTimeTest |
| && operands.get(1) instanceof Not) { |
| timeTest1 = (ElapsedTimeTest) operands.get(0); |
| not = (Not) operands.get(1); |
| } else if (operands.get(1) instanceof ElapsedTimeTest |
| && operands.get(0) instanceof Not) { |
| timeTest1 = (ElapsedTimeTest) operands.get(1); |
| not = (Not) operands.get(0); |
| } else { |
| return null; |
| } |
| |
| if (!(not.getOperand() instanceof ElapsedTimeTest)) { |
| return null; |
| } |
| |
| ElapsedTimeTest timeTest2 = (ElapsedTimeTest) not.getOperand(); |
| long millis1 = timeTest1.getReferenceTime().getTime().getTime() |
| + timeTest1.getNumberofDays() |
| * STEMTime.Units.DAY.getMilliseconds(); |
| long millis2 = timeTest2.getReferenceTime().getTime().getTime() |
| + timeTest2.getNumberofDays() |
| * STEMTime.Units.DAY.getMilliseconds(); |
| |
| return new Interval(millis1, millis2); |
| } |
| |
| private class Interval { |
| |
| public long startTime; |
| public long endTime; |
| |
| public Interval(long startTime, long endTime) { |
| this.startTime = startTime; |
| this.endTime = endTime; |
| } |
| |
| } |
| |
| private class PaneChangeListener implements PropertyChangeListener { |
| GraphMapControl thiss; |
| |
| public PaneChangeListener(GraphMapControl ctrl) { |
| this.thiss = ctrl; |
| } |
| |
| public void propertyChange(PropertyChangeEvent evt) { |
| |
| double[] mapScale = (double[]) evt.getNewValue(); |
| double scaleX = mapScale[0]; |
| double relTransX = mapScale[1]; |
| double relTransY = mapScale[2]; |
| int viewId = GraphMapRegistry.getInstance().getViewId(thiss.view); |
| GraphMapPreferences prefs = GraphMapPreferences.getInstance(); |
| prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_TRANS_X, relTransX); |
| prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_TRANS_Y, relTransY); |
| prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_SCALE_FACTOR,scaleX); |
| |
| } |
| |
| |
| |
| // update method commented out since use of Observable has deprecated in Java 11 |
| /* |
| * @Override public void update(Observable o, Object arg) { PaneChangeEvent e = |
| * (PaneChangeEvent) arg; int viewId = |
| * GraphMapRegistry.getInstance().getViewId(thiss.view); GraphMapPreferences |
| * prefs = GraphMapPreferences.getInstance(); |
| * prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| * PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_TRANS_X, e.getTranformationX()); |
| * prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| * PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_TRANS_Y, e.getTranformationY()); |
| * prefs.updatePreferenceState(viewId, thiss.getSimulation(), |
| * PreferenceConstants.GRAPHMAP_PERSISTANT_VIEW_SCALE_FACTOR, |
| * e.getScaleFactor()); } |
| */ |
| |
| |
| |
| |
| |
| |
| } |
| } |