package org.openlca.app.editors.lcia_methods; import java.awt.Desktop; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ImageHyperlink; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.openlca.app.M; import org.openlca.app.components.FileChooser; import org.openlca.app.editors.parameters.ModelParameterPage; import org.openlca.app.rcp.images.Icon; import org.openlca.app.rcp.images.Images; import org.openlca.app.util.Actions; import org.openlca.app.util.Colors; import org.openlca.app.util.Controls; import org.openlca.app.util.Error; import org.openlca.app.util.Info; import org.openlca.app.util.Question; import org.openlca.app.util.UI; import org.openlca.app.util.tables.Tables; import org.openlca.app.util.viewers.Viewers; import org.openlca.core.model.ImpactMethod; import org.openlca.core.model.ModelType; import org.openlca.core.model.Parameter; import org.openlca.core.model.ParameterScope; import org.openlca.core.model.Uncertainty; import org.openlca.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Shows imported shape-files and parameters from these shape-files that can be * used in a localised LCIA method. */ class ShapeFilePage extends FormPage { private Logger log = LoggerFactory.getLogger(getClass()); private FormToolkit tk; private Composite body; private ShapeFileSection[] sections; private ScrolledForm form; private ImpactMethodEditor editor; public ShapeFilePage(ImpactMethodEditor editor) { super(editor, "ShapeFilePage", "Shape files (beta)"); this.editor = editor; } @Override protected void createFormContent(IManagedForm managedForm) { form = UI.formHeader(managedForm, "Shape file parameters"); tk = managedForm.getToolkit(); body = UI.formBody(form, tk); createFileSection(); List<String> shapeFiles = ShapeFileUtils.getShapeFiles(method()); sections = new ShapeFileSection[shapeFiles.size()]; for (int i = 0; i < shapeFiles.size(); i++) sections[i] = new ShapeFileSection(i, shapeFiles.get(i)); form.reflow(true); } private void createFileSection() { Composite comp = UI.formSection(body, tk, "Files"); createFolderLink(comp); UI.filler(comp, tk); Button importButton = tk.createButton(comp, M.Import, SWT.NONE); importButton.setImage(Icon.IMPORT.get()); Controls.onSelect(importButton, (e) -> { File file = FileChooser.forImport("*.shp"); if (file != null) checkRunImport(file); }); UI.filler(comp, tk); Button evaluateButton = tk.createButton(comp, M.EvaluateLocations, SWT.NONE); evaluateButton.setImage(Icon.EXPRESSION.get()); Controls.onSelect(evaluateButton, (e) -> { try { new ProgressMonitorDialog(UI.shell()).run(true, true, new EvaluateLocationsJob(method())); } catch (Exception ex) { log.error("Failed to evaluate locations", ex); } }); } private void createFolderLink(Composite composite) { UI.formLabel(composite, tk, "Location"); ImageHyperlink link = tk.createImageHyperlink(composite, SWT.TOP); File folder = ShapeFileUtils.getFolder(method()); link.setText(Strings.cut(folder.getAbsolutePath(), 75)); link.setImage(Icon.FOLDER.get()); link.setForeground(Colors.linkBlue()); link.setToolTipText(folder.getAbsolutePath()); Controls.onClick(link, e -> { try { if (folder.exists() && folder.isDirectory()) Desktop.getDesktop().open(folder); } catch (Exception ex) { log.error("failed to open shape-file folder", ex); } }); } private List<ShapeFileParameter> checkRunImport(File file) { if (!ShapeFileUtils.isValid(file)) { org.openlca.app.util.Error.showBox("Invalid file", "The file " + file.getName() + " is not a valid shape file."); return Collections.emptyList(); } if (ShapeFileUtils.alreadyExists(method(), file)) { org.openlca.app.util.Error .showBox("File already exists", "A shape file with the given " + "name already exists for this method."); return Collections.emptyList(); } try { return runImport(file); } catch (Exception e) { log.error("Failed to import shape file " + file, e); return Collections.emptyList(); } } private List<ShapeFileParameter> runImport(File file) throws Exception { String shapeFile = ShapeFileUtils.importFile(method(), file); List<ShapeFileParameter> params = ShapeFileUtils.getParameters(method(), shapeFile); for (ShapeFileParameter parameter : params) { if (!Parameter.isValidName(parameter.getName())) { org.openlca.app.util.Error.showBox("Invalid parameter", "The parameter name '" + parameter.getName() + "' is not supported"); ShapeFileUtils.deleteFile(method(), shapeFile); return Collections.emptyList(); } } ShapeFileSection section = new ShapeFileSection(sections.length, shapeFile); ShapeFileSection[] newSections = new ShapeFileSection[sections.length + 1]; System.arraycopy(sections, 0, newSections, 0, sections.length); newSections[sections.length] = section; this.sections = newSections; section.parameterTable.viewer.setInput(params); form.reflow(true); editor.getParameterSupport().evaluate(); return params; } private ImpactMethod method() { return editor.getModel(); } private class ShapeFileSection { private int index; private String shapeFile; private Section section; private ShapeFileParameterTable parameterTable; ShapeFileSection(int index, String shapeFile) { this.index = index; this.shapeFile = shapeFile; render(); } private void render() { section = UI.section(body, tk, M.Parameters + " - " + shapeFile); Composite composite = UI.sectionClient(section, tk); parameterTable = new ShapeFileParameterTable(shapeFile, composite); Action delete = Actions.onRemove(() -> { if (delete()) { removeExternalSourceReferences(); save(); } }); Action update = new Action(M.Update) { @Override public ImageDescriptor getImageDescriptor() { return Icon.REFRESH.descriptor(); } @Override public void run() { File file = FileChooser.forImport("*.shp"); if (file == null) return; Set<String> previouslyLinked = getReferencedParameters(); delete(true); List<ShapeFileParameter> parameters = checkRunImport(file); Set<String> stillLinked = getReferencedParameters(); Map<String, ShapeFileParameter> nameToParam = new HashMap<>(); for (ShapeFileParameter parameter : parameters) if (previouslyLinked.contains(parameter.getName())) { stillLinked.add(parameter.getName()); nameToParam.put(parameter.getName(), parameter); } updateExternalSourceReferences(stillLinked, nameToParam); save(); } }; ShowMapAction showAction = new ShowMapAction(this); AddParamAction addAction = new AddParamAction(this); Actions.bind(section, showAction, delete, update); Actions.bind(parameterTable.viewer, showAction, addAction); } private void save() { editor.doSave(null); } private boolean delete() { return delete(false); } private boolean delete(boolean force) { boolean del = force || Question.ask(M.DeleteShapeFile, M.ReallyDeleteShapeFile); if (!del) return false; ShapeFileUtils.deleteFile(method(), shapeFile); section.dispose(); ShapeFileSection[] newSections = new ShapeFileSection[sections.length - 1]; System.arraycopy(sections, 0, newSections, 0, index); if ((index + 1) < sections.length) { System.arraycopy(sections, index + 1, newSections, index, newSections.length - index); } sections = newSections; form.reflow(true); return true; } private void removeExternalSourceReferences() { for (Parameter parameter : method().getParameters()) { if (!parameter.isInputParameter()) continue; if (shapeFile.equals(parameter.getExternalSource())) { parameter.setExternalSource(null); } } editor.getParameterSupport().evaluate(); editor.setDirty(true); } /* * stillLinked: names of parameters that were linked to the shape file * before updating and afterwards (only set those) */ private void updateExternalSourceReferences(Set<String> stillLinked, Map<String, ShapeFileParameter> nameToParam) { for (Parameter parameter : method().getParameters()) { if (!parameter.isInputParameter()) continue; if (!shapeFile.equals(parameter.getExternalSource())) continue; if (!stillLinked.contains(parameter.getName())) { parameter.setExternalSource(null); } else { ShapeFileParameter param = nameToParam.get(parameter.getName()); if (param == null) continue; parameter.setValue((param.getMin() + param.getMax()) / 2); parameter.setUncertainty(Uncertainty.uniform( param.getMin(), param.getMax())); } } editor.getParameterSupport().evaluate(); } private Set<String> getReferencedParameters() { Set<String> names = new HashSet<>(); for (Parameter parameter : method().getParameters()) if (parameter.isInputParameter()) if (shapeFile.equals(parameter.getExternalSource())) names.add(parameter.getName()); return names; } } private class ShapeFileParameterTable { private String[] columns = { M.Name, M.Minimum, M.Maximum }; private TableViewer viewer; private List<ShapeFileParameter> params; ShapeFileParameterTable(String shapeFile, Composite parent) { viewer = Tables.createViewer(parent, columns); viewer.setLabelProvider(new ShapeFileParameterLabel()); Tables.bindColumnWidths(viewer, 0.4, 0.3, 0.3); try { params = ShapeFileUtils.getParameters(method(), shapeFile); viewer.setInput(params); } catch (Exception e) { log.error("failed to read parameteres for shape file " + shapeFile, e); } } private class ShapeFileParameterLabel extends LabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object o, int i) { if (i == 0) return Images.get(ModelType.PARAMETER); else return null; } @Override public String getColumnText(Object o, int i) { if (!(o instanceof ShapeFileParameter)) return null; ShapeFileParameter p = (ShapeFileParameter) o; switch (i) { case 0: return p.getName(); case 1: return Double.toString(p.getMin()); case 2: return Double.toString(p.getMax()); default: return null; } } } } private class ShowMapAction extends Action { private ShapeFileSection section; public ShowMapAction(ShapeFileSection section) { this.section = section; setToolTipText(M.ShowInMap); setText(M.ShowInMap); setImageDescriptor(Icon.MAP.descriptor()); } @Override public void run() { if (section == null || section.parameterTable == null) return; ShapeFileParameter param = Viewers .getFirstSelected(section.parameterTable.viewer); if (param == null) ShapeFileUtils.openFileInMap(method(), section.shapeFile); else ShapeFileUtils.openFileInMap(method(), section.shapeFile, param); } } private class AddParamAction extends Action { private ShapeFileSection section; public AddParamAction(ShapeFileSection section) { this.section = section; setToolTipText(M.AddToMethodParameters); setText(M.AddToMethodParameters); setImageDescriptor(Icon.ADD.descriptor()); } @Override public void run() { ShapeFileParameter param = getSelected(); if (param == null) return; if (exists(param)) { Info.showBox(M.ParameterAlreadyAdded, M.SelectedParameterWasAlreadyAdded); return; } if (otherExists(param)) { Error.showBox(M.ParameterWithSameNameExists, M.ParameterWithSameNameExistsInMethod); return; } if (!Parameter.isValidName(param.getName())) { Error.showBox(M.InvalidParameterName, param.getName() + " " + M.IsNotValidParameterName); return; } addParam(param); } private ShapeFileParameter getSelected() { if (section == null || section.parameterTable == null) return null; ShapeFileParameter param = Viewers .getFirstSelected(section.parameterTable.viewer); if (param == null) { Error.showBox(M.NoParameterSelected, M.NoShapefileParameterSelected); return null; } return param; } private boolean exists(ShapeFileParameter param) { for (Parameter realParam : method().getParameters()) { if (Strings.nullOrEqual(param.getName(), realParam.getName()) && Strings.nullOrEqual("SHAPE_FILE", realParam.getSourceType()) && Strings.nullOrEqual(section.shapeFile, realParam.getExternalSource())) return true; } return false; } private boolean otherExists(ShapeFileParameter param) { for (Parameter realParam : method().getParameters()) { if (Strings.nullOrEqual(param.getName(), realParam.getName()) && !Strings.nullOrEqual(section.shapeFile, realParam.getExternalSource())) return true; } return false; } private void addParam(ShapeFileParameter param) { Parameter realParam = new Parameter(); realParam.setRefId(UUID.randomUUID().toString()); realParam.setExternalSource(section.shapeFile); realParam.setInputParameter(true); realParam.setName(param.getName()); realParam.setDescription("from shapefile: " + section.shapeFile); realParam.setValue((param.getMin() + param.getMax()) / 2); realParam.setUncertainty(Uncertainty.uniform(param.getMin(), param.getMax())); realParam.setScope(ParameterScope.IMPACT_METHOD); realParam.setSourceType("SHAPE_FILE"); method().getParameters().add(realParam); editor.setDirty(true); editor.setActivePage(ModelParameterPage.ID); editor.getParameterSupport().evaluate(); } } }