]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java
First version of district visualisations
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / visualisations / DynamicVisualisationsUI.java
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java
new file mode 100644 (file)
index 0000000..4fe3d59
--- /dev/null
@@ -0,0 +1,778 @@
+package org.simantics.district.network.ui.visualisations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IEditorPart;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.procedure.Listener;
+import org.simantics.district.network.DistrictNetworkUtil;
+import org.simantics.district.network.profile.DynamicVisualisationsRequest;
+import org.simantics.district.network.visualisations.DynamicVisualisationsContributions;
+import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicColoringObject;
+import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicSizingObject;
+import org.simantics.district.network.visualisations.model.ColorBarOptions;
+import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation;
+import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize;
+import org.simantics.district.network.visualisations.model.DynamicColorContribution;
+import org.simantics.district.network.visualisations.model.DynamicColorMap;
+import org.simantics.district.network.visualisations.model.DynamicSizeContribution;
+import org.simantics.district.network.visualisations.model.DynamicSizeMap;
+import org.simantics.district.network.visualisations.model.DynamicVisualisation;
+import org.simantics.district.network.visualisations.model.SizeBarOptions;
+import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation;
+import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize;
+import org.simantics.ui.workbench.IResourceEditorPart;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.ui.workbench.WorkbenchUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DynamicVisualisationsUI extends Composite {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsUI.class);
+
+    private Resource diagramResource;
+    private VisualisationListener listener;
+    private DynamicVisualisation visualisation;
+
+    private Button showSizeButton;
+    private Button sizeTicksButton;
+    private Combo sizeLocationCombo;
+    private Combo sizeSizeCombo;
+    private Button showColorButton;
+    private Button colorTicksButton;
+    private Combo colorLocationCombo;
+    private Combo colorSizeCombo;
+
+    public DynamicVisualisationsUI(Composite parent, int style) {
+        super(parent, style);
+
+        defaultInitializeUI();
+    }
+
+    private void defaultInitializeUI() {
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
+        GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(this);
+        
+        Composite coloringObjectsComposite = new Composite(this, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(coloringObjectsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(coloringObjectsComposite);
+        initializeColoringObjects(coloringObjectsComposite);
+        
+        Composite colorBarsComposite = new Composite(this, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(colorBarsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(colorBarsComposite);
+        initializeColorBars(colorBarsComposite);
+        
+        Composite objectSizesComposite = new Composite(this, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(objectSizesComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(objectSizesComposite);
+        initializeObjectSizes(objectSizesComposite);
+        
+        Composite sizeBarsComposite = new Composite(this, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(sizeBarsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(sizeBarsComposite);
+        initializeSizeBars(sizeBarsComposite);
+        
+    }
+
+    private void initializeColoringObjects(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Coloring Objects");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
+        
+        {
+            createColoringObjectHeaderRow(group);
+        }
+        List<Supplier<Pair<String, DynamicColorContribution>>> suppliers = new ArrayList<>();
+        {
+            try {
+                Collection<DynamicColoringObject> result = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicColoringObject>>() {
+
+                    @Override
+                    public Collection<DynamicColoringObject> perform(ReadGraph graph) throws DatabaseException {
+                        return DynamicVisualisationsContributions.dynamicColoringObjects(graph);
+                    }
+                });
+                
+                for (DynamicColoringObject object : result) {
+                    suppliers.add(createColoringObjectRow(group, object));
+                }
+
+            } catch (DatabaseException e) {
+                e.printStackTrace();
+            }
+        }
+        {
+            Button applyButton = new Button(group, SWT.NONE);
+            applyButton.setText("Apply");
+            applyButton.addSelectionListener(new SelectionAdapter() {
+                
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    List<Pair<String, DynamicColorContribution>> collect = suppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
+                    Simantics.getSession().asyncRequest(new WriteRequest() {
+                        
+                        @Override
+                        public void perform(WriteGraph graph) throws DatabaseException {
+                            DistrictNetworkUtil.setColorContributions(graph, DynamicVisualisationsUI.this.diagramResource, collect);
+                        }
+                    });
+                }
+            });
+        }
+    }
+    
+    private void createColoringObjectHeaderRow(Composite parent) {
+
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Label");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Used");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Variable");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Min");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Max");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Unit");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("ColorMap");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Default");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+    }
+
+    private Map<String, ColoringObjectRow> coloringRows = new HashMap<>();
+    private Map<String, SizingObjectRow> sizingRows = new HashMap<>();
+
+    private static class ColoringObjectRow {
+        
+        private final Label label;
+        private final Button usedButton;
+        private final Combo variableCombo;
+        private final Text minText;
+        private final Text maxText;
+        private final Label unit;
+        private final Combo colorMapCombo;
+        private final Button defaultButton;
+        
+        public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit,
+                Combo colorMapCombo, Button defaultButton) {
+            super();
+            this.label = label;
+            this.usedButton = usedButton;
+            this.variableCombo = variableCombo;
+            this.minText = minText;
+            this.maxText = maxText;
+            this.unit = unit;
+            this.colorMapCombo = colorMapCombo;
+            this.defaultButton = defaultButton;
+        }
+
+        public void update(DynamicColorContribution colorContribution) {
+            String[] items = variableCombo.getItems();
+            for (int i = 0; i < items.length; i++) {
+                if (colorContribution.getLabel().equals(items[i])) {
+                    variableCombo.select(i);
+                    break;
+                }
+            }
+            minText.setText(Double.toString(colorContribution.getDefaultMin()));
+            maxText.setText(Double.toString(colorContribution.getDefaultMax()));
+            unit.setText(colorContribution.getUnit());
+            
+            // color map only supports single for now
+            colorMapCombo.setItems(colorContribution.getDefaultColorMap().getLabel());
+            colorMapCombo.select(0);
+//            String[] colorItems = colorMapCombo.getItems();
+//            for (int i = 0; i < colorItems.length; i++) {
+//                
+//                if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
+//                    colorMapCombo.select(i);
+//                    break;
+//                }
+//            }
+        }
+    }
+
+    private static class SizingObjectRow {
+        
+        private final Label label;
+        private final Button usedButton;
+        private final Combo variableCombo;
+        private final Text minText;
+        private final Text maxText;
+        private final Label unit;
+        private final Combo sizeMapCombo;
+        private final Button defaultButton;
+        
+        public SizingObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit,
+                Combo sizeMapCombo, Button defaultButton) {
+            super();
+            this.label = label;
+            this.usedButton = usedButton;
+            this.variableCombo = variableCombo;
+            this.minText = minText;
+            this.maxText = maxText;
+            this.unit = unit;
+            this.sizeMapCombo = sizeMapCombo;
+            this.defaultButton = defaultButton;
+        }
+
+        public void update(DynamicSizeContribution sizeContribution) {
+            String[] items = variableCombo.getItems();
+            for (int i = 0; i < items.length; i++) {
+                if (sizeContribution.getLabel().equals(items[i])) {
+                    variableCombo.select(i);
+                    break;
+                }
+            }
+            minText.setText(Double.toString(sizeContribution.getDefaultMin()));
+            maxText.setText(Double.toString(sizeContribution.getDefaultMax()));
+            unit.setText(sizeContribution.getUnit());
+            
+            // color map only supports single for now
+            sizeMapCombo.setItems(sizeContribution.getDefaultSizeMap().getLabel());
+            sizeMapCombo.select(0);
+//            String[] colorItems = colorMapCombo.getItems();
+//            for (int i = 0; i < colorItems.length; i++) {
+//                
+//                if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
+//                    colorMapCombo.select(i);
+//                    break;
+//                }
+//            }
+        }
+    }
+
+    private Supplier<Pair<String, DynamicColorContribution>> createColoringObjectRow(Composite parent, DynamicColoringObject object) {
+        Label label = new Label(parent, SWT.NONE);
+        label.setText(object.getColoringObject().getName());
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        Map<String, DynamicColorContribution> colorContributions = object.getColorContributions();
+        
+        Button usedButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
+        
+        Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
+        variableCombo.setItems(colorContributions.keySet().toArray(new String[colorContributions.size()]));
+        
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
+        
+        Text minText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText);
+        
+        Text maxText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
+        
+        Label unit = new Label(parent, SWT.NONE);
+        unit.setText("");
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit);
+        
+        Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo);
+        
+        Button defaultButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
+        
+        variableCombo.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // handle update for others
+                
+                DynamicColorContribution cont = colorContributions.get(variableCombo.getText());
+                
+                if (minText.getText().isEmpty()) {
+                    minText.setText(Double.toString(cont.getDefaultMin()));
+                }
+                if (maxText.getText().isEmpty()) {
+                    maxText.setText(Double.toString(cont.getDefaultMax()));
+                }
+                unit.setText(cont.getUnit());
+                
+                colorMapCombo.setItems(cont.getDefaultColorMap().getLabel());
+                colorMapCombo.select(0);
+                
+                defaultButton.setSelection(true);
+            }
+        });
+
+        coloringRows.put(object.getColoringObject().getName(), new ColoringObjectRow(label, usedButton, variableCombo, minText, maxText, unit, colorMapCombo, defaultButton));
+
+        return new Supplier<Pair<String, DynamicColorContribution>>() {
+
+            @Override
+            public Pair<String, DynamicColorContribution> get() {
+                DynamicColorContribution cont = colorContributions.get(variableCombo.getText());
+                if (cont != null) {
+                    String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
+                    try {
+                        Map<String, DynamicColorMap> colorMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicColorMap>>() {
+    
+                            @Override
+                            public Map<String, DynamicColorMap> perform(ReadGraph graph) throws DatabaseException {
+                                return DynamicVisualisationsContributions.dynamicColorMaps(graph);
+                            }
+                        });
+                        DynamicColorMap dColorMap = colorMaps.get(colorMap);
+                        String label = variableCombo.getItem(variableCombo.getSelectionIndex());
+                        
+                        DynamicColorContribution dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
+                        dcc.setUsed(usedButton.getSelection());
+                        dcc.setUseDefault(defaultButton.getSelection());
+                        
+                        return Pair.make(object.getColoringObject().getName(), dcc);
+                    } catch (DatabaseException e) {
+                        LOGGER.error("Could not get DynamicColorContribution", e);
+                    }
+                }
+                return null;
+            }
+        };
+    }
+    
+    private void createSizingObjectHeaderRow(Composite parent) {
+
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Label");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Used");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Variable");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Min");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Max");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Unit");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("SizeMap");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Default");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+    }
+
+    private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object) {
+        Label label = new Label(parent, SWT.NONE);
+        label.setText(object.getSizingObject().getName());
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        Map<String, DynamicSizeContribution> sizeContributions = object.getSizeContributions();
+        
+        Button usedButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
+        
+        Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
+        variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()]));
+        
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
+        
+        Text minText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText);
+        
+        Text maxText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
+        
+        Label unit = new Label(parent, SWT.NONE);
+        unit.setText("");
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit);
+        
+        Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo);
+        
+        Button defaultButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
+        
+        variableCombo.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // handle update for others
+                
+                DynamicSizeContribution cont = sizeContributions.get(variableCombo.getText());
+                
+                if (minText.getText().isEmpty()) {
+                    minText.setText(Double.toString(cont.getDefaultMin()));
+                }
+                if (maxText.getText().isEmpty()) {
+                    maxText.setText(Double.toString(cont.getDefaultMax()));
+                }
+                unit.setText(cont.getUnit());
+                
+                sizeMapCombo.setItems(cont.getDefaultSizeMap().getLabel());
+                sizeMapCombo.select(0);
+                
+                defaultButton.setSelection(true);
+            }
+        });
+        
+        sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton));
+        
+        return new Supplier<Pair<String, DynamicSizeContribution>>() {
+
+            @Override
+            public Pair<String, DynamicSizeContribution> get() {
+                DynamicSizeContribution cont = sizeContributions.get(variableCombo.getText());
+                if (cont != null) {
+                    String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
+                    try {
+                        Map<String, DynamicSizeMap> sizeMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicSizeMap>>() {
+    
+                            @Override
+                            public Map<String, DynamicSizeMap> perform(ReadGraph graph) throws DatabaseException {
+                                return DynamicVisualisationsContributions.dynamicSizeMaps(graph);
+                            }
+                        });
+                        DynamicSizeMap dColorMap = sizeMaps.get(sizeMap);
+                        String label = variableCombo.getItem(variableCombo.getSelectionIndex());
+                        
+                        DynamicSizeContribution dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
+                        dsc.setUsed(usedButton.getSelection());
+                        dsc.setUseDefault(defaultButton.getSelection());
+                        
+                        return Pair.make(object.getSizingObject().getName(), dsc);
+                    } catch (DatabaseException e) {
+                        LOGGER.error("Could not get DynamicColorContribution", e);
+                    }
+                }
+                return null;
+            }
+        };
+    }
+    
+    private void initializeColorBars(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Color Bars");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group);
+        
+        createColorBars(group);
+    }
+    
+    private void createColorBars(Composite parent) {
+        
+        showColorButton = new Button(parent, SWT.CHECK);
+        showColorButton.setText("Show");
+        
+        colorTicksButton = new Button(parent, SWT.CHECK);
+        colorTicksButton.setText("Ticks");
+        
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Location");
+        colorLocationCombo = new Combo(parent, SWT.READ_ONLY);
+        colorLocationCombo.setItems(Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new));
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Size");
+        colorSizeCombo = new Combo(parent, SWT.READ_ONLY);
+        colorSizeCombo.setItems(Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new));
+        
+        Button applyButton = new Button(parent, SWT.NONE);
+        applyButton.setText("Apply");
+        
+        applyButton.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // persist changes
+                IEditorPart activeEditor = WorkbenchUtils.getActiveEditor();
+                if (activeEditor instanceof IResourceEditorPart) {
+                    Resource diagram = ((IResourceEditorPart) activeEditor).getResourceInput().getResource();
+                    ColorBarOptions options = new ColorBarOptions()
+                            .showColorBars(showColorButton.getSelection())
+                            .showColorBarsTicks(colorTicksButton.getSelection())
+                            .withLocation(ColorBarsLocation.valueOf(colorLocationCombo.getText().toUpperCase()))
+                            .withSize(ColorBarsSize.valueOf(colorSizeCombo.getText().toUpperCase()));
+
+                    Simantics.getSession().asyncRequest(new WriteRequest() {
+                        
+                        @Override
+                        public void perform(WriteGraph graph) throws DatabaseException {
+                            DistrictNetworkUtil.setColorBarOptions(graph, diagram, options);
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    private void initializeObjectSizes(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Object Sizes");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
+        
+        {
+            createSizingObjectHeaderRow(group);
+            createObjectSizes(group);
+        }
+    }
+    
+    private void createObjectSizes(Composite parent) {
+        
+        List<Supplier<Pair<String, DynamicSizeContribution>>> suppliers = new ArrayList<>(); 
+        try {
+            Collection<DynamicSizingObject> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicSizingObject>>() {
+
+                @Override
+                public Collection<DynamicSizingObject> perform(ReadGraph graph) throws DatabaseException {
+                    return DynamicVisualisationsContributions.dynamicSizingObjects(graph);
+                }
+            });
+            
+            for (DynamicSizingObject object : resultSizing) {
+                suppliers.add(createSizingObjectRow(parent, object));
+            }
+        } catch (DatabaseException e) {
+            e.printStackTrace();
+        }
+        
+        {
+            Button applyButton = new Button(parent, SWT.NONE);
+            applyButton.setText("Apply");
+            applyButton.addSelectionListener(new SelectionAdapter() {
+                
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    List<Pair<String, DynamicSizeContribution>> collect = suppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
+                    Simantics.getSession().asyncRequest(new WriteRequest() {
+                        
+                        @Override
+                        public void perform(WriteGraph graph) throws DatabaseException {
+                            DistrictNetworkUtil.setSizeContributions(graph, DynamicVisualisationsUI.this.diagramResource, collect);
+                        }
+                    });
+                }
+            });
+        }
+    }
+
+    private void initializeSizeBars(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Size Bars");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group);
+        
+        createSizeBars(group);
+    }
+
+    private void createSizeBars(Composite parent) {
+        showSizeButton = new Button(parent, SWT.CHECK);
+        showSizeButton.setText("Show");
+        
+        sizeTicksButton = new Button(parent, SWT.CHECK);
+        sizeTicksButton.setText("Ticks");
+        
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Location");
+        sizeLocationCombo = new Combo(parent, SWT.READ_ONLY);
+        sizeLocationCombo.setItems(Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new));
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Size");
+        sizeSizeCombo = new Combo(parent, SWT.NONE);
+        sizeSizeCombo.setItems(Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new));
+        
+        Button applyButton = new Button(parent, SWT.READ_ONLY);
+        applyButton.setText("Apply");
+        
+        applyButton.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // persist changes
+                IEditorPart activeEditor = WorkbenchUtils.getActiveEditor();
+                if (activeEditor instanceof IResourceEditorPart) {
+                    Resource diagram = ((IResourceEditorPart) activeEditor).getResourceInput().getResource();
+                    SizeBarOptions options = new SizeBarOptions()
+                            .showSizeBars(showSizeButton.getSelection())
+                            .showSizeBarsTicks(sizeTicksButton.getSelection())
+                            .withLocation(SizeBarsLocation.valueOf(sizeLocationCombo.getText().toUpperCase()))
+                            .withSize(SizeBarsSize.valueOf(sizeSizeCombo.getText().toUpperCase()));
+
+                    Simantics.getSession().asyncRequest(new WriteRequest() {
+                        
+                        @Override
+                        public void perform(WriteGraph graph) throws DatabaseException {
+                            DistrictNetworkUtil.setSizeBarOptions(graph, diagram, options);
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    public void setDiagramResource(Resource diagramResource) {
+        if (this.diagramResource != diagramResource) {
+            this.diagramResource = diagramResource;
+            updateListening();
+        }
+    }
+
+    private void updateListening() {
+        if (listener != null) {
+            listener.dispose();
+        }
+        listener = new VisualisationListener(this);
+        Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(diagramResource), listener);
+    }
+
+    private static class VisualisationListener implements Listener<DynamicVisualisation> {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationListener.class);
+
+        private boolean disposed;
+        private DynamicVisualisationsUI ui;
+        
+        public VisualisationListener(DynamicVisualisationsUI ui) {
+            this.ui = ui;
+        }
+
+        @Override
+        public void execute(DynamicVisualisation result) {
+            ui.updateVisualisation(result);
+        }
+
+        @Override
+        public void exception(Throwable t) {
+            LOGGER.error("Could not listen visualisation", t);
+        }
+
+        @Override
+        public boolean isDisposed() {
+            return disposed || ui.isDisposed();
+        }
+
+        public void dispose() {
+            this.disposed = true;
+        }
+    }
+
+    public void updateVisualisation(DynamicVisualisation result) {
+        this.visualisation = result;
+        Display.getDefault().asyncExec(() -> {
+            if (getParent().isDisposed())
+                return;
+            
+            Map<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
+            for (Entry<String, DynamicColorContribution> entry : colorContributions.entrySet()) {
+                
+                ColoringObjectRow coloringObjectRow = coloringRows.get(entry.getKey());
+                if (coloringObjectRow != null) {
+                    
+                    coloringObjectRow.update(entry.getValue());
+                    
+                } else {
+                    LOGGER.info("No coloring object visualisation row for key {}", entry.getKey());
+                }
+            }
+            ColorBarOptions colorOptions = visualisation.getColorBarOptions();
+            showColorButton.setSelection(colorOptions.isShowColorBars());
+            colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks());
+            for (int i = 0; i < colorLocationCombo.getItems().length; i++) {
+                String item = colorLocationCombo.getItem(i);
+                if (item.equals(colorOptions.getLocation().toString())) {
+                    colorLocationCombo.select(i);
+                    break;
+                }
+            }
+            for (int i = 0; i < colorSizeCombo.getItems().length; i++) {
+                String item = colorSizeCombo.getItem(i);
+                if (item.equals(colorOptions.getSize().toString())) {
+                    colorSizeCombo.select(i);
+                    break;
+                }
+            }
+            
+            Map<String, DynamicSizeContribution> sizeContributions = visualisation.getSizeContributions();
+            for (Entry<String, DynamicSizeContribution> entry : sizeContributions.entrySet()) {
+                
+                SizingObjectRow sizingObjectRow = sizingRows.get(entry.getKey());
+                if (sizingObjectRow != null) {
+                    
+                    sizingObjectRow.update(entry.getValue());
+                    
+                } else {
+                    LOGGER.info("No sizing object visualisation row for key {}", entry.getKey());
+                }
+            }
+            SizeBarOptions sizeOptions = visualisation.getSizeBarOptions();
+            showSizeButton.setSelection(sizeOptions.isShowSizeBars());
+            sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks());
+            for (int i = 0; i < sizeLocationCombo.getItems().length; i++) {
+                String item = sizeLocationCombo.getItem(i);
+                if (item.equals(sizeOptions.getLocation().toString())) {
+                    sizeLocationCombo.select(i);
+                    break;
+                }
+            }
+            for (int i = 0; i < sizeSizeCombo.getItems().length; i++) {
+                String item = sizeSizeCombo.getItem(i);
+                if (item.equals(sizeOptions.getSize().toString())) {
+                    sizeSizeCombo.select(i);
+                    break;
+                }
+            }
+        });
+    }
+}