package org.esa.snap.rcp.placemark; import com.bc.ceres.glayer.Layer; import com.bc.ceres.swing.selection.SelectionChangeEvent; import com.bc.ceres.swing.selection.SelectionChangeListener; import com.vividsolutions.jts.geom.Coordinate; import org.esa.snap.core.dataio.placemark.PlacemarkIO; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.GeoCoding; import org.esa.snap.core.datamodel.GeoPos; import org.esa.snap.core.datamodel.PinDescriptor; import org.esa.snap.core.datamodel.PixelPos; import org.esa.snap.core.datamodel.Placemark; import org.esa.snap.core.datamodel.PlacemarkDescriptor; import org.esa.snap.core.datamodel.PlacemarkNameFactory; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductManager; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.ProductNodeGroup; import org.esa.snap.core.datamodel.ProductNodeListener; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.datamodel.TiePointGrid; import org.esa.snap.core.util.Guardian; import org.esa.snap.core.util.StringUtils; import org.esa.snap.core.util.SystemUtils; import org.esa.snap.core.util.io.FileUtils; import org.esa.snap.core.util.io.SnapFileFilter; import org.esa.snap.netbeans.docwin.WindowUtilities; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.rcp.util.SelectionSupport; import org.esa.snap.rcp.windows.ProductSceneViewTopComponent; import org.esa.snap.runtime.Config; import org.esa.snap.ui.AbstractDialog; import org.esa.snap.ui.DecimalCellEditor; import org.esa.snap.ui.DecimalTableCellRenderer; import org.esa.snap.ui.ModalDialog; import org.esa.snap.ui.SnapFileChooser; import org.esa.snap.ui.color.ColorTableCellEditor; import org.esa.snap.ui.color.ColorTableCellRenderer; import org.esa.snap.ui.product.AbstractPlacemarkTableModel; import org.esa.snap.ui.product.BandChooser; import org.esa.snap.ui.product.ProductSceneView; import org.esa.snap.ui.product.VectorDataLayer; import org.openide.awt.UndoRedo; import org.openide.util.HelpCtx; import org.openide.windows.TopComponent; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.MouseInputAdapter; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumnModel; import javax.swing.table.TableRowSorter; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.text.DecimalFormat; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.prefs.Preferences; import static org.esa.snap.rcp.SnapApp.SelectionSourceHint.*; /** * @author Tonio Fincke */ public class PlacemarkManagerTopComponent extends TopComponent implements UndoRedo.Provider, HelpCtx.Provider { public static final String PREFERENCE_KEY_PIN_IO_DIR = "pin.io.dir"; private static final String PREFERENCE_KEY_ADJUST_PIN_GEO_POS = Placemark.PREFERENCE_KEY_ADJUST_PIN_GEO_POS; private final PlacemarkDescriptor placemarkDescriptor; private final Preferences preferences; private SnapApp snapApp; private final HashMap<Product, Band[]> productToSelectedBands; private final HashMap<Product, TiePointGrid[]> productToSelectedGrids; private Product product; private JTable placemarkTable; private PlacemarkListener placemarkListener; private Band[] selectedBands; private TiePointGrid[] selectedGrids; private boolean synchronizingPlacemarkSelectedState; private AbstractPlacemarkTableModel placemarkTableModel; private PlacemarkManagerButtons buttonPane; private ProductSceneView currentView; private final SelectionChangeListener selectionChangeHandler; private final List<List<Placemark>> relatedPlacemarks; private final boolean adjustPinGeoPos; public PlacemarkManagerTopComponent(PlacemarkDescriptor placemarkDescriptor, TableModelFactory modelFactory) { this.placemarkDescriptor = placemarkDescriptor; snapApp = SnapApp.getDefault(); preferences = snapApp.getPreferences(); productToSelectedBands = new HashMap<>(50); productToSelectedGrids = new HashMap<>(50); placemarkTableModel = modelFactory.createTableModel(placemarkDescriptor, product, null, null); selectionChangeHandler = new ViewSelectionChangeHandler(); relatedPlacemarks = new ArrayList<>(); adjustPinGeoPos = Config.instance().preferences().getBoolean(PREFERENCE_KEY_ADJUST_PIN_GEO_POS, true); initUI(); setDisplayName(getTitle()); } public void initUI() { setLayout(new BorderLayout()); placemarkTable = new JTable(placemarkTableModel); placemarkTable.setRowSorter(new TableRowSorter<>(placemarkTableModel)); placemarkTable.setName("placemarkTable"); placemarkTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); placemarkTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); placemarkTable.setRowSelectionAllowed(true); // IMPORTANT: We set ReorderingAllowed=false, because we export the // table model AS IS to a flat text file. placemarkTable.getTableHeader().setReorderingAllowed(false); ToolTipSetter toolTipSetter = new ToolTipSetter(); placemarkTable.addMouseMotionListener(toolTipSetter); placemarkTable.addMouseListener(toolTipSetter); placemarkTable.addMouseListener(new PopupListener()); placemarkTable.getSelectionModel().addListSelectionListener(new PlacemarkTableSelectionHandler()); updateTableModel(); final TableColumnModel columnModel = placemarkTable.getColumnModel(); columnModel.addColumnModelListener(new ColumnModelListener()); JScrollPane tableScrollPane = new JScrollPane(placemarkTable); JPanel mainPane = new JPanel(new BorderLayout(4, 4)); mainPane.add(tableScrollPane, BorderLayout.CENTER); buttonPane = new PlacemarkManagerButtons(this); JPanel content = new JPanel(new BorderLayout(4, 4)); content.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); content.add(BorderLayout.CENTER, mainPane); content.add(BorderLayout.EAST, buttonPane); Component southExtension = getSouthExtension(); if (southExtension != null) { content.add(BorderLayout.SOUTH, southExtension); } content.setPreferredSize(new Dimension(420, 200)); setCurrentView(snapApp.getSelectedProductSceneView()); setProduct(snapApp.getSelectedProduct(VIEW)); snapApp.getSelectionSupport(ProductSceneView.class).addHandler(new ProductSceneViewSelectionChangeHandler()); snapApp.getProductManager().addListener(new ProductRemovedListener()); updateUIState(); add(content, BorderLayout.CENTER); } void applyFilteredGrids() { if (product != null) { Band[] allBands = product.getBands(); TiePointGrid[] allGrids = product.getTiePointGrids(); BandChooser bandChooser = new BandChooser(SwingUtilities.getWindowAncestor(this), "Available Bands And Tie Point Grids", getHelpId(), false, allBands, selectedBands, allGrids, selectedGrids, true); if (bandChooser.show() == ModalDialog.ID_OK) { selectedBands = bandChooser.getSelectedBands(); selectedGrids = bandChooser.getSelectedTiePointGrids(); productToSelectedBands.put(product, selectedBands); productToSelectedGrids.put(product, selectedGrids); updateTableModel(); } } } PlacemarkDescriptor getPlacemarkDescriptor() { return placemarkDescriptor; } private void setCurrentView(ProductSceneView sceneView) { if (sceneView != currentView) { if (currentView != null) { currentView.getSelectionContext().removeSelectionChangeListener(selectionChangeHandler); } currentView = sceneView; if (currentView != null) { currentView.getSelectionContext().addSelectionChangeListener(selectionChangeHandler); setProduct(currentView.getProduct()); } else { setProduct(null); } } } protected final Product getProduct() { return product; } public void setProduct(Product product) { if (this.product == product) { return; } Product oldProduct = this.product; if (oldProduct != null) { oldProduct.removeProductNodeListener(placemarkListener); } this.product = product; selectedBands = productToSelectedBands.get(this.product); selectedGrids = productToSelectedGrids.get(this.product); if (this.product != null) { if (placemarkListener == null) { placemarkListener = new PlacemarkListener(); } this.product.addProductNodeListener(placemarkListener); } updateTableModel(); updatePlacemarkTableSelectionFromView(); updateUIState(); } protected String getTitle() { return ""; } protected String getHelpId() { return null; } protected Component getSouthExtension() { return null; } private void updateTableModel() { placemarkTableModel.setProduct(product); placemarkTableModel.setSelectedBands(selectedBands); placemarkTableModel.setSelectedGrids(selectedGrids); addCellRenderer(placemarkTable.getColumnModel()); addCellEditor(placemarkTable.getColumnModel()); } protected void addCellRenderer(TableColumnModel columnModel) { columnModel.getColumn(0).setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("0.000"))); columnModel.getColumn(1).setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("0.000"))); columnModel.getColumn(2).setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("0.000000"))); columnModel.getColumn(3).setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("0.000000"))); columnModel.getColumn(4).setCellRenderer(new ColorTableCellRenderer()); columnModel.getColumn(5).setCellRenderer(new RightAlignmentTableCellRenderer()); } protected void addCellEditor(TableColumnModel columnModel) { final DecimalCellEditor pixelCellEditor = new DecimalCellEditor(); columnModel.getColumn(0).setCellEditor(pixelCellEditor); columnModel.getColumn(1).setCellEditor(pixelCellEditor); columnModel.getColumn(2).setCellEditor(new DecimalCellEditor(-180, 180)); columnModel.getColumn(3).setCellEditor(new DecimalCellEditor(-90, 90)); columnModel.getColumn(4).setCellEditor(new ColorTableCellEditor()); } private ProductSceneView getSceneView() { final ProductSceneView selectedProductSceneView = snapApp.getSelectedProductSceneView(); if (selectedProductSceneView == null && product != null) { final Band[] bands = product.getBands(); for (Band band : bands) { ProductSceneViewTopComponent productSceneViewTopComponent = getProductSceneViewTopComponent(band); if (productSceneViewTopComponent != null) { return productSceneViewTopComponent.getView(); } } final TiePointGrid[] tiePointGrids = product.getTiePointGrids(); for (TiePointGrid tiePointGrid : tiePointGrids) { ProductSceneViewTopComponent productSceneViewTopComponent = getProductSceneViewTopComponent(tiePointGrid); if (productSceneViewTopComponent != null) { return productSceneViewTopComponent.getView(); } } } return selectedProductSceneView; } //copied from TimeSeriesManagerForm private ProductSceneViewTopComponent getProductSceneViewTopComponent(RasterDataNode raster) { return WindowUtilities.getOpened(ProductSceneViewTopComponent.class) .filter(topComponent -> raster == topComponent.getView().getRaster()) .findFirst() .orElse(null); } private Placemark getPlacemarkAt(final int selectedRow) { Placemark placemark = null; if (product != null) { if (selectedRow > -1 && selectedRow < getPlacemarkGroup(product).getNodeCount()) { placemark = getPlacemarkGroup(product).get(selectedRow); } } return placemark; } void newPin() { Guardian.assertNotNull("product", product); String[] uniquePinNameAndLabel = PlacemarkNameFactory.createUniqueNameAndLabel(placemarkDescriptor, product); Placemark newPlacemark = Placemark.createPointPlacemark(placemarkDescriptor, uniquePinNameAndLabel[0], uniquePinNameAndLabel[1], "", new PixelPos(0, 0), null, product.getSceneGeoCoding()); if (PlacemarkDialog.showEditPlacemarkDialog( SwingUtilities.getWindowAncestor(this), product, newPlacemark, placemarkDescriptor)) { makePlacemarkNameUnique(newPlacemark); UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(product); if (undoManager != null) { undoManager.addEdit(UndoablePlacemarkActionFactory.createUndoablePlacemarkInsertion(product, newPlacemark, placemarkDescriptor)); } updateUIState(); } } void copyActivePlacemark() { Guardian.assertNotNull("product", product); Placemark activePlacemark = getSelectedPlacemark(); Guardian.assertNotNull("activePlacemark", activePlacemark); Placemark newPlacemark = Placemark.createPointPlacemark(activePlacemark.getDescriptor(), "copy_of_" + activePlacemark.getName(), activePlacemark.getLabel(), activePlacemark.getDescription(), activePlacemark.getPixelPos(), activePlacemark.getGeoPos(), activePlacemark.getProduct().getSceneGeoCoding()); newPlacemark.setStyleCss(activePlacemark.getStyleCss()); if (PlacemarkDialog.showEditPlacemarkDialog( SwingUtilities.getWindowAncestor(this), product, newPlacemark, placemarkDescriptor)) { makePlacemarkNameUnique(newPlacemark); UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(product); if (undoManager != null) { undoManager.addEdit(UndoablePlacemarkActionFactory.createUndoablePlacemarkCopying(product, newPlacemark, placemarkDescriptor)); } updateUIState(); } } private ProductNodeGroup<Placemark> getPlacemarkGroup(Product product) { return placemarkDescriptor.getPlacemarkGroup(product); } void editActivePin() { Guardian.assertNotNull("product", product); Placemark activePlacemark = getSelectedPlacemark(); Placemark originalPlacemark = Placemark.createPointPlacemark(activePlacemark.getDescriptor(), activePlacemark.getName(), activePlacemark.getLabel(), activePlacemark.getDescription(), activePlacemark.getPixelPos(), activePlacemark.getGeoPos(), activePlacemark.getProduct().getSceneGeoCoding()); Guardian.assertNotNull("activePlacemark", activePlacemark); if (PlacemarkDialog.showEditPlacemarkDialog(SwingUtilities.getWindowAncestor(this), product, activePlacemark, placemarkDescriptor)) { makePlacemarkNameUnique(activePlacemark); UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(product); if (undoManager != null) { undoManager.addEdit(UndoablePlacemarkActionFactory.createUndoablePlacemarkEditing(product, originalPlacemark, activePlacemark, placemarkDescriptor)); } updateUIState(); } } void removeSelectedPins() { final List<Placemark> placemarks = getSelectedPlacemarks(); for (Placemark placemark : placemarks) { getPlacemarkGroup(product).remove(placemark); } int selectedRow = placemarkTable.getSelectedRow(); if (selectedRow >= getPlacemarkGroup(product).getNodeCount()) { selectedRow = getPlacemarkGroup(product).getNodeCount() - 1; } if (selectedRow >= 0) { placemarkTable.getSelectionModel().setSelectionInterval(selectedRow, selectedRow); } UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(product); if (undoManager != null) { undoManager.addEdit(UndoablePlacemarkActionFactory.createUndoablePlacemarkRemoval(product, placemarks, placemarkDescriptor)); } updateUIState(); } private int getNumSelectedPlacemarks() { int[] rowIndexes = placemarkTable.getSelectedRows(); return rowIndexes != null ? rowIndexes.length : 0; } private Placemark getSelectedPlacemark() { int rowIndex = placemarkTable.getSelectedRow(); if (rowIndex >= 0) { final int modelIndex = placemarkTable.convertRowIndexToModel(rowIndex); return placemarkTableModel.getPlacemarkAt(modelIndex); } return null; } private List<Placemark> getSelectedPlacemarks() { List<Placemark> placemarkList = new ArrayList<>(); int[] sortedRowIndexes = placemarkTable.getSelectedRows(); if (sortedRowIndexes != null) { for (int rowIndex : sortedRowIndexes) { int modelRowIndex = placemarkTable.convertRowIndexToModel(rowIndex); placemarkList.add(placemarkTableModel.getPlacemarkAt(modelRowIndex)); } } return placemarkList; } void zoomToActivePin() { Guardian.assertNotNull("product", product); Placemark activePlacemark = getSelectedPlacemark(); Guardian.assertNotNull("activePlacemark", activePlacemark); final ProductSceneView view = getSceneView(); //todo [Multisize_products] use scene raster transform here final Object placemarkGeometry = activePlacemark.getFeature().getDefaultGeometry(); if (placemarkGeometry != null && placemarkGeometry instanceof com.vividsolutions.jts.geom.Point) { final Coordinate coordinate = ((com.vividsolutions.jts.geom.Point) placemarkGeometry).getCoordinate(); final Point2D modelPos = new Point2D.Double(coordinate.x, coordinate.y); view.zoom(modelPos.getX(), modelPos.getY(), view.getZoomFactor()); updateUIState(); } } // }} Actions ///////////////////////////////////////////////////////////////////////////////////////////////// private void makePlacemarkNameUnique(Placemark newPlacemark) { if (makePlacemarkNameUnique0(newPlacemark, product)) { Dialogs.showWarning(MessageFormat.format("{0} has been renamed to ''{1}'',\n" + "because a {2} with the former name already exists.", StringUtils.firstLetterUp(placemarkDescriptor.getRoleLabel()), newPlacemark.getName(), placemarkDescriptor.getRoleLabel())); } } protected void updateUIState() { boolean productSelected = product != null; int numSelectedPins = 0; if (productSelected) { updatePlacemarkTableSelectionFromView(); numSelectedPins = getNumSelectedPlacemarks(); } placemarkTable.setEnabled(productSelected); buttonPane.updateUIState(productSelected, placemarkTable.getRowCount(), numSelectedPins); } private void updatePlacemarkTableSelectionFromView() { if (!synchronizingPlacemarkSelectedState) { try { synchronizingPlacemarkSelectedState = true; if (product != null) { Placemark[] placemarks = placemarkTableModel.getPlacemarks(); for (int i = 0; i < placemarks.length; i++) { if (i < placemarkTable.getRowCount()) { Placemark placemark = placemarks[i]; int sortedRowAt = placemarkTable.convertRowIndexToView(i); boolean selected = isPlacemarkSelectedInView(placemark); if (selected != placemarkTable.isRowSelected(sortedRowAt)) { if (selected) { placemarkTable.getSelectionModel().addSelectionInterval(sortedRowAt, sortedRowAt); } else { placemarkTable.getSelectionModel().removeSelectionInterval(sortedRowAt, sortedRowAt); } } } } } placemarkTable.revalidate(); placemarkTable.repaint(); } finally { synchronizingPlacemarkSelectedState = false; } } } void importPlacemarks(boolean allPlacemarks) { List<Placemark> placemarks; try { placemarks = loadPlacemarksFromFile(); } catch (IOException e) { e.printStackTrace(); Dialogs.showError(MessageFormat.format("I/O error, failed to import {0}s:\n{1}", /*I18N*/ placemarkDescriptor.getRoleLabel(), e.getMessage())); return; } if (placemarks.isEmpty()) { return; } addPlacemarksToProduct(placemarks, product, allPlacemarks); } void addPlacemarksToProduct(List<Placemark> placemarks, Product targetProduct, boolean allPlacemarks) { final GeoCoding geoCoding = targetProduct.getSceneGeoCoding(); final boolean canGetPixelPos = geoCoding != null && geoCoding.canGetPixelPos(); final boolean isPin = placemarkDescriptor instanceof PinDescriptor; int numPinsOutOfBounds = 0; int numPinsRenamed = 0; int numInvalids = 0; for (Placemark placemark : placemarks) { if (makePlacemarkNameUnique0(placemark, targetProduct)) { numPinsRenamed++; placemark = createTransferrablePlacemark(placemark, targetProduct); } final PixelPos pixelPos = placemark.getPixelPos(); boolean productContainsPixelPos = targetProduct.containsPixel(pixelPos); if (!canGetPixelPos && isPin && !productContainsPixelPos) { numInvalids++; continue; } // from here on we only handle GCPs and valid Pins if (canGetPixelPos && adjustPinGeoPos) { placemarkDescriptor.updatePixelPos(geoCoding, placemark.getGeoPos(), pixelPos); } if (!productContainsPixelPos && isPin) { numPinsOutOfBounds++; } else { getPlacemarkGroup(targetProduct).add(placemark); if (adjustPinGeoPos) { placemark.setPixelPos(pixelPos); } else { placemark.setGeoPos(placemark.getGeoPos()); } } if (!allPlacemarks) { break; // import only the first one } } String intoProductMessage = ""; if (targetProduct != product) { intoProductMessage = "into product " + targetProduct.getDisplayName() + "\n"; } if (numInvalids > 0) { Dialogs.showWarning(MessageFormat.format( "One or more {0}s have not been imported,\n{1}because they can not be assigned to a product without a geo-coding.", placemarkDescriptor.getRoleLabel(), intoProductMessage)); } if (numPinsRenamed > 0) { Dialogs.showWarning(MessageFormat.format( "One or more {0}s have been renamed,\n{1}because their former names are already existing.", placemarkDescriptor.getRoleLabel(), intoProductMessage)); } if (numPinsOutOfBounds > 0) { if (numPinsOutOfBounds == placemarks.size()) { Dialogs.showError( MessageFormat.format( "No {0}s have been imported,\n{1}because their pixel positions\nare outside the product''s bounds.", placemarkDescriptor.getRoleLabel(), intoProductMessage) ); } else { Dialogs.showError( MessageFormat.format( "{0} {1}s have not been imported,\n{2}because their pixel positions\nare outside the product''s bounds.", numPinsOutOfBounds, placemarkDescriptor.getRoleLabel(), intoProductMessage) ); } } } private boolean isPlacemarkSelectedInView(Placemark placemark) { boolean selected = false; final ProductSceneView sceneView = getSceneView(); if (sceneView != null) { if (getPlacemarkDescriptor() instanceof PinDescriptor) { selected = sceneView.isPinSelected(placemark); } else { selected = sceneView.isGcpSelected(placemark); } } return selected; } private boolean makePlacemarkNameUnique0(Placemark placemark, Product targetProduct) { ProductNodeGroup<Placemark> placemarkGroup = getPlacemarkGroup(targetProduct); if (placemarkGroup.get(placemark.getName()) == placemark) { return false; } String name0 = placemark.getName(); String name = name0; String label0 = placemark.getLabel(); String label = label0; int id = 1; while (placemarkGroup.contains(name)) { if (placemarkGroup.get(name).getLabel().equals(label)) { label = label0 + "_" + id; } name = name0 + "_" + id; id++; } if (!name0.equals(name)) { placemark.setName(name); if (!label0.equals(label)) { placemark.setLabel(label); } return true; } return false; } private List<Placemark> loadPlacemarksFromFile() throws IOException { final SnapFileChooser fileChooser = new SnapFileChooser(); String roleLabel = StringUtils.firstLetterUp(placemarkDescriptor.getRoleLabel()); fileChooser.setDialogTitle("Import " + roleLabel + "s"); /*I18N*/ setComponentName(fileChooser, "Import"); fileChooser.addChoosableFileFilter(PlacemarkIO.createTextFileFilter()); fileChooser.setFileFilter(PlacemarkIO.createPlacemarkFileFilter()); fileChooser.setCurrentDirectory(getIODir()); int result = fileChooser.showOpenDialog(SwingUtilities.getWindowAncestor(this)); if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file != null) { setIODir(file.getAbsoluteFile().getParentFile()); GeoCoding geoCoding = null; if (product != null) { geoCoding = product.getSceneGeoCoding(); } return PlacemarkIO.readPlacemarks(new FileReader(file), geoCoding, placemarkDescriptor); } } return Collections.emptyList(); } void exportPlacemarks() { final SnapFileChooser fileChooser = new SnapFileChooser(); fileChooser.setDialogTitle(MessageFormat.format("Export {0}(s)", StringUtils.firstLetterUp(placemarkDescriptor.getRoleLabel()))); setComponentName(fileChooser, "Export_Selected"); fileChooser.addChoosableFileFilter(PlacemarkIO.createTextFileFilter()); fileChooser.setFileFilter(PlacemarkIO.createPlacemarkFileFilter()); final File ioDir = getIODir(); fileChooser.setCurrentDirectory(ioDir); fileChooser.setSelectedFile(new File(ioDir, placemarkDescriptor.getRoleName())); int result = fileChooser.showSaveDialog(SwingUtilities.getWindowAncestor(this)); if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file != null) { if (Boolean.TRUE.equals(Dialogs.requestOverwriteDecision(getTitle(), file))) { setIODir(file.getAbsoluteFile().getParentFile()); SnapFileFilter snapFileFilter = fileChooser.getSnapFileFilter(); String fileExtension = FileUtils.getExtension(file); if (fileExtension == null || !StringUtils.contains(snapFileFilter.getExtensions(), fileExtension)) { file = FileUtils.ensureExtension(file, snapFileFilter.getDefaultExtension()); } try { if (snapFileFilter.getFormatName().equals( PlacemarkIO.createPlacemarkFileFilter().getFormatName())) { final List<Placemark> placemarkList = getPlacemarksForExport(); PlacemarkIO.writePlacemarksFile(new FileWriter(file), placemarkList); } else { try (Writer writer = new FileWriter(file)) { writePlacemarkDataTableText(writer); } } } catch (IOException ioe) { Dialogs.showError(String.format("I/O Error.\n Failed to export %ss.\n%s", placemarkDescriptor.getRoleLabel(), ioe.getMessage())); ioe.printStackTrace(); } } } } } private List<Placemark> getPlacemarksForExport() { boolean noPlacemarksSelected = placemarkTable.getSelectionModel().isSelectionEmpty(); final List<Placemark> placemarkList; if (noPlacemarksSelected) { placemarkList = Arrays.asList(placemarkTableModel.getPlacemarks()); } else { placemarkList = getSelectedPlacemarks(); } return placemarkList; } void transferPlacemarks() { // ask for destination product Product thisProduct = getProduct(); Product[] allProducts = snapApp.getProductManager().getProducts(); if (allProducts.length < 2 || thisProduct == null) { return; } Product[] allOtherProducts = new Product[allProducts.length - 1]; int allOtherProductsIndex = 0; for (int i = 0; i < allProducts.length; i++) { Product product = allProducts[i]; if (product != thisProduct) { allOtherProducts[allOtherProductsIndex++] = product; } } ProductChooser productChooser = new ProductChooser( snapApp.getMainFrame(), getTitle(), getHelpId(), allOtherProducts, null); int buttonID = productChooser.show(); System.out.println("buttonID = " + buttonID); if (buttonID == AbstractDialog.ID_OK) { // copy placemarks List<Placemark> placemarks = getPlacemarksForExport(); Product[] selectedProducts = productChooser.getSelectedProducts(); boolean notAlreadyAsked = true; boolean updateExistingPins = true; for (Product selectedProduct : selectedProducts) { List<Placemark> placemarksCopy = new ArrayList<>(placemarks.size()); for (Placemark placemark : placemarks) { Placemark[] existingPlacemarks = getExistingPlacemarks(placemark, selectedProduct); if (existingPlacemarks.length > 0) { if (notAlreadyAsked) { notAlreadyAsked = false; Dialogs.Answer decision = Dialogs.requestDecision("Transfer placemarks", "Do you want to update existing placemarks?", false, null); updateExistingPins = decision == Dialogs.Answer.YES; } if (updateExistingPins) { for (Placemark existingPlacemark : existingPlacemarks) { existingPlacemark.setName(placemark.getName()); existingPlacemark.setLabel(placemark.getLabel()); existingPlacemark.setDescription(placemark.getDescription()); existingPlacemark.setPixelPos(placemark.getPixelPos()); existingPlacemark.setGeoPos(placemark.getGeoPos()); existingPlacemark.setStyleCss(placemark.getStyleCss()); } } else { Placemark placemarkToTransfer = createTransferrablePlacemark(placemark, selectedProduct); placemarksCopy.add(placemarkToTransfer); setRelatedPlacemark(placemark, placemarkToTransfer); } } else { Placemark placemarkToTransfer = createTransferrablePlacemark(placemark, selectedProduct); placemarksCopy.add(placemarkToTransfer); setRelatedPlacemark(placemark, placemarkToTransfer); } } addPlacemarksToProduct(placemarksCopy, selectedProduct, true); } } } private Placemark createTransferrablePlacemark(Placemark placemark, Product product) { Placemark newPlacemark = Placemark.createPointPlacemark(placemark.getDescriptor(), placemark.getName(), placemark.getLabel(), placemark.getDescription(), placemark.getPixelPos(), placemark.getGeoPos(), product.getSceneGeoCoding()); newPlacemark.setStyleCss(placemark.getStyleCss()); return newPlacemark; } private Placemark[] getExistingPlacemarks(Placemark referencePlacemark, Product product) { List<Placemark> associatedPlacemarksList = new ArrayList<>(); for (List<Placemark> relatedPlacemarkList : relatedPlacemarks) { for (Placemark placemark : relatedPlacemarkList) { if (placemark == referencePlacemark) { for (Placemark placemarkCandidate : relatedPlacemarkList) { if (placemarkCandidate.getProduct() == product) { associatedPlacemarksList.add(placemarkCandidate); } } } } } return associatedPlacemarksList.toArray(new Placemark[associatedPlacemarksList.size()]); } private void setRelatedPlacemark(Placemark originalPlacemark, Placemark newPlacemark) { boolean added = false; for (List<Placemark> relatedPlacemarkList : relatedPlacemarks) { for (int j = 0; j < relatedPlacemarkList.size(); j++) { Placemark placemark = relatedPlacemarkList.get(j); if (placemark == originalPlacemark) { added = relatedPlacemarkList.add(newPlacemark); break; } } } if (!added) { ArrayList<Placemark> relatedPlacemarkList = new ArrayList<>(); relatedPlacemarkList.add(originalPlacemark); relatedPlacemarkList.add(newPlacemark); relatedPlacemarks.add(relatedPlacemarkList); } } // private void removePlacemarksFromRemovedProducts(Product removedProduct) { // for (List<Placemark> relatedPlacemarkList : relatedPlacemarks) { // for (Placemark placemark : relatedPlacemarkList) { // if (placemark.getProduct() == removedProduct) { // relatedPlacemarkList.remove(placemark); // if (relatedPlacemarkList.size() == 1) { // relatedPlacemarks.remove(relatedPlacemarkList); // } // } // } // } // } private void removePlacemarksFromRelatedPlacemarks(Placemark placemark) { for (List<Placemark> relatedPlacemarkList : relatedPlacemarks) { for (Placemark relatedPlacemark : relatedPlacemarkList) { if (placemark == relatedPlacemark) { relatedPlacemarkList.remove(placemark); if (relatedPlacemarkList.size() == 1) { relatedPlacemarks.remove(relatedPlacemarkList); } } } } } private void setComponentName(JComponent component, String name) { component.setName(getClass().getName() + "." + name); } void exportPlacemarkDataTable() { final SnapFileChooser fileChooser = new SnapFileChooser(); fileChooser.setDialogTitle(MessageFormat.format("Export {0} Data Table", /*I18N*/ StringUtils.firstLetterUp(placemarkDescriptor.getRoleLabel()))); setComponentName(fileChooser, "Export_Data_Table"); fileChooser.setFileFilter(PlacemarkIO.createTextFileFilter()); final File ioDir = getIODir(); fileChooser.setCurrentDirectory(ioDir); fileChooser.setSelectedFile(new File(ioDir, "Data")); int result = fileChooser.showSaveDialog(SwingUtilities.getWindowAncestor(this)); if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file != null) { if (Boolean.TRUE.equals(Dialogs.requestOverwriteDecision(getTitle(), file))) { setIODir(file.getAbsoluteFile().getParentFile()); file = FileUtils.ensureExtension(file, PlacemarkIO.FILE_EXTENSION_FLAT_TEXT); try { try (Writer writer = new FileWriter(file)) { writePlacemarkDataTableText(writer); } } catch (IOException ignored) { Dialogs.showError(MessageFormat.format("I/O Error.\nFailed to export {0} data table.", /*I18N*/ placemarkDescriptor.getRoleLabel())); } } } } } private void writePlacemarkDataTableText(final Writer writer) { final String[] standardColumnNames = placemarkTableModel.getStandardColumnNames(); final int columnCountMin = standardColumnNames.length; final int columnCount = placemarkTableModel.getColumnCount(); String[] additionalColumnNames = new String[columnCount - columnCountMin]; for (int i = 0; i < additionalColumnNames.length; i++) { additionalColumnNames[i] = placemarkTableModel.getColumnName(columnCountMin + i); } List<Placemark> placemarkList = new ArrayList<>(); List<Object[]> valueList = new ArrayList<>(); for (int sortedRow = 0; sortedRow < placemarkTable.getRowCount(); ++sortedRow) { ListSelectionModel selectionModel = placemarkTable.getSelectionModel(); if (selectionModel.isSelectionEmpty() || selectionModel.isSelectedIndex(sortedRow)) { final int modelRow = placemarkTable.convertRowIndexToModel(sortedRow); placemarkList.add(placemarkTableModel.getPlacemarkAt(modelRow)); Object[] values = new Object[columnCount]; for (int col = 0; col < columnCount; col++) { values[col] = placemarkTableModel.getValueAt(modelRow, col); } valueList.add(values); } } PlacemarkIO.writePlacemarksWithAdditionalData(writer, placemarkDescriptor.getRoleLabel(), product.getName(), placemarkList, valueList, standardColumnNames, additionalColumnNames); } private void setIODir(File dir) { if (preferences != null && dir != null) { preferences.put(PREFERENCE_KEY_PIN_IO_DIR, dir.getPath()); } } private File getIODir() { File dir = SystemUtils.getUserHomeDir(); if (preferences != null) { dir = new File(preferences.get(PREFERENCE_KEY_PIN_IO_DIR, dir.getPath())); } return dir; } @Override public UndoRedo getUndoRedo() { if (product != null) { return snapApp.getUndoManager(getProduct()); } return UndoRedo.NONE; } private class PlacemarkListener implements ProductNodeListener { @Override public void nodeChanged(ProductNodeEvent event) { ProductNode sourceNode = event.getSourceNode(); if (sourceNode instanceof Placemark && sourceNode.getOwner() == placemarkDescriptor.getPlacemarkGroup( product)) { updateUIState(); } } @Override public void nodeDataChanged(ProductNodeEvent event) { ProductNode sourceNode = event.getSourceNode(); if (sourceNode instanceof Placemark && sourceNode.getOwner() == placemarkDescriptor.getPlacemarkGroup( product)) { updateUIState(); } } @Override public void nodeAdded(ProductNodeEvent event) { ProductNode sourceNode = event.getSourceNode(); if (sourceNode instanceof Placemark && sourceNode.getOwner() == placemarkDescriptor.getPlacemarkGroup( product)) { placemarkTableModel.addPlacemark((Placemark) sourceNode); updateUIState(); } } @Override public void nodeRemoved(ProductNodeEvent event) { ProductNode sourceNode = event.getSourceNode(); if (sourceNode instanceof Placemark) { final Placemark placemark = (Placemark) sourceNode; removePlacemarksFromRelatedPlacemarks(placemark); if (sourceNode.getOwner() == placemarkDescriptor.getPlacemarkGroup(product)) { placemarkTableModel.removePlacemark(placemark); int selectedRow = placemarkTable.getSelectedRow(); if (selectedRow >= getPlacemarkGroup(product).getNodeCount()) { selectedRow = getPlacemarkGroup(product).getNodeCount() - 1; } if (selectedRow >= 0) { placemarkTable.getSelectionModel().setSelectionInterval(selectedRow, selectedRow); } updateUIState(); } } } } private class ToolTipSetter extends MouseInputAdapter { private int _rowIndex; private ToolTipSetter() { _rowIndex = -1; } @Override public void mouseExited(MouseEvent e) { _rowIndex = -1; } @Override public void mouseMoved(MouseEvent e) { int rowIndex = placemarkTable.rowAtPoint(e.getPoint()); if (rowIndex != _rowIndex) { _rowIndex = rowIndex; if (_rowIndex >= 0 && _rowIndex < placemarkTable.getRowCount()) { GeoPos geoPos = getPlacemarkAt(placemarkTable.convertRowIndexToModel(_rowIndex)).getGeoPos(); if (geoPos != null) { placemarkTable.setToolTipText(geoPos.getLonString() + " / " + geoPos.getLatString()); } } } } } private static class ColumnModelListener implements TableColumnModelListener { @Override public void columnAdded(TableColumnModelEvent e) { int minWidth; final int index = e.getToIndex(); switch (index) { case 0: case 1: minWidth = 60; break; default: minWidth = 80; } TableColumnModel columnModel = (TableColumnModel) e.getSource(); columnModel.getColumn(index).setPreferredWidth(minWidth); columnModel.getColumn(index).setCellRenderer(new RightAlignmentTableCellRenderer()); } @Override public void columnRemoved(TableColumnModelEvent e) { } @Override public void columnMoved(TableColumnModelEvent e) { } @Override public void columnMarginChanged(ChangeEvent e) { } @Override public void columnSelectionChanged(ListSelectionEvent e) { } } private class PopupListener extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { action(e); } @Override public void mouseReleased(MouseEvent e) { action(e); } @Override public void mouseClicked(MouseEvent e) { action(e); } private void action(MouseEvent e) { if (e.isPopupTrigger()) { if (getNumSelectedPlacemarks() > 0) { final JPopupMenu popupMenu = new JPopupMenu(); final JMenuItem menuItem; menuItem = new JMenuItem("Copy selected data to clipboard"); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { final StringWriter stringWriter = new StringWriter(); writePlacemarkDataTableText(stringWriter); String text = stringWriter.toString(); text = text.replaceAll("\r\n", "\n"); text = text.replaceAll("\r", "\n"); SystemUtils.copyToClipboard(text); } }); popupMenu.add(menuItem); final Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), placemarkTable); popupMenu.show(placemarkTable, point.x, point.y); } } } } private class ProductSceneViewSelectionChangeHandler implements SelectionSupport.Handler<ProductSceneView> { @Override public void selectionChange(ProductSceneView oldValue, ProductSceneView newValue) { if (oldValue == currentView) { setCurrentView(null); } setCurrentView(newValue); } } private class ProductRemovedListener implements ProductManager.Listener { @Override public void productAdded(ProductManager.Event event) { //do nothing } @Override public void productRemoved(ProductManager.Event event) { productToSelectedBands.remove(product); productToSelectedGrids.remove(product); // removePlacemarksFromRemovedProducts(product); } } private class PlacemarkTableSelectionHandler implements ListSelectionListener { @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() || e.getFirstIndex() == -1 || synchronizingPlacemarkSelectedState) { return; } try { synchronizingPlacemarkSelectedState = true; Placemark[] placemarks = placemarkTableModel.getPlacemarks(); ArrayList<Placemark> selectedPlacemarks = new ArrayList<>(); for (int i = 0; i < placemarks.length; i++) { Placemark placemark = placemarks[i]; int sortedIndex = placemarkTable.convertRowIndexToView(i); if (placemarkTable.isRowSelected(sortedIndex)) { selectedPlacemarks.add(placemark); } } ProductSceneView sceneView = getSceneView(); if (sceneView != null) { Placemark[] placemarkArray = selectedPlacemarks.toArray(new Placemark[selectedPlacemarks.size()]); //todo remove code smell - tf 20151118 if (getPlacemarkDescriptor() instanceof PinDescriptor) { sceneView.selectPins(placemarkArray); } else { sceneView.selectGcps(placemarkArray); } } } finally { updateUIState(); synchronizingPlacemarkSelectedState = false; } } } private class ViewSelectionChangeHandler implements SelectionChangeListener { @Override public void selectionChanged(SelectionChangeEvent event) { if (synchronizingPlacemarkSelectedState) { return; } final ProductSceneView sceneView = getSceneView(); if (sceneView != null) { Layer layer = sceneView.getSelectedLayer(); if (layer instanceof VectorDataLayer) { VectorDataLayer vectorDataLayer = (VectorDataLayer) layer; if (vectorDataLayer.getVectorDataNode() == getProduct().getPinGroup().getVectorDataNode() || vectorDataLayer.getVectorDataNode() == getProduct().getGcpGroup().getVectorDataNode()) { updateUIState(); } } } } @Override public void selectionContextChanged(SelectionChangeEvent event) { } } private static class RightAlignmentTableCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); label.setHorizontalAlignment(JLabel.RIGHT); return label; } } }