/* * Copyright (C) 2013 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.ui.product; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.glayer.Layer; import com.bc.ceres.glayer.LayerContext; import com.bc.ceres.glayer.LayerTypeRegistry; import com.bc.ceres.grender.Rendering; import com.bc.ceres.swing.figure.Figure; import com.bc.ceres.swing.figure.FigureChangeEvent; import com.bc.ceres.swing.figure.FigureChangeListener; import com.bc.ceres.swing.figure.FigureCollection; import com.bc.ceres.swing.figure.FigureStyle; import com.bc.ceres.swing.figure.support.DefaultFigureCollection; import com.bc.ceres.swing.figure.support.DefaultFigureStyle; import org.esa.snap.core.datamodel.Placemark; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.ProductNodeListenerAdapter; import org.esa.snap.core.datamodel.SceneTransformProvider; import org.esa.snap.core.datamodel.VectorDataNode; import org.esa.snap.core.util.Debug; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * A layer for vector data nodes. * * @author Norman Fomferra */ public class VectorDataLayer extends Layer { private static final VectorDataLayerType TYPE = LayerTypeRegistry.getLayerType(VectorDataLayerType.class); private VectorDataNode vectorDataNode; private final SimpleFeatureFigureFactory figureFactory; private FigureCollection figureCollection; private VectorDataChangeHandler vectorDataChangeHandler; private boolean reactingAgainstFigureChange; private static int id; public VectorDataLayer(LayerContext ctx, VectorDataNode vectorDataNode, SceneTransformProvider provider) { this(TYPE, vectorDataNode, provider, TYPE.createLayerConfig(ctx)); getConfiguration().setValue(VectorDataLayerType.PROPERTY_NAME_VECTOR_DATA, vectorDataNode.getName()); } protected VectorDataLayer(VectorDataLayerType vectorDataLayerType, VectorDataNode vectorDataNode, SceneTransformProvider provider, PropertySet configuration) { super(vectorDataLayerType, configuration); setUniqueId(); this.vectorDataNode = vectorDataNode; setName(vectorDataNode.getName()); figureFactory = new SimpleFeatureFigureFactory(vectorDataNode.getFeatureType(), provider); figureCollection = new DefaultFigureCollection(); updateFigureCollection(); vectorDataChangeHandler = new VectorDataChangeHandler(); vectorDataNode.getProduct().addProductNodeListener(vectorDataChangeHandler); figureCollection.addChangeListener(new FigureChangeHandler()); } private void setUniqueId() { setId(VectorDataLayerType.VECTOR_DATA_LAYER_ID_PREFIX + (++id)); } public VectorDataNode getVectorDataNode() { return vectorDataNode; } @Override protected void disposeLayer() { Product product = vectorDataNode.getProduct(); if (product != null) { product.removeProductNodeListener(vectorDataChangeHandler); } vectorDataNode = null; super.disposeLayer(); } private void updateFigureCollection() { FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = vectorDataNode.getFeatureCollection(); Figure[] figures = figureCollection.getFigures(); Map<SimpleFeature, SimpleFeatureFigure> figureMap = new HashMap<>(); for (Figure figure : figures) { if (figure instanceof SimpleFeatureFigure) { SimpleFeatureFigure simpleFeatureFigure = (SimpleFeatureFigure) figure; figureMap.put(simpleFeatureFigure.getSimpleFeature(), simpleFeatureFigure); } } FeatureIterator<SimpleFeature> featureIterator = featureCollection.features(); while (featureIterator.hasNext()) { SimpleFeature simpleFeature = featureIterator.next(); SimpleFeatureFigure featureFigure = figureMap.get(simpleFeature); if (featureFigure != null) { figureMap.remove(simpleFeature); final String css = SimpleFeatureFigureFactory.getStyleCss(simpleFeature, vectorDataNode.getDefaultStyleCss()); final FigureStyle normalStyle = DefaultFigureStyle.createFromCss(css); final FigureStyle selectedStyle = getFigureFactory().deriveSelectedStyle(normalStyle); featureFigure.setNormalStyle(normalStyle); featureFigure.setSelectedStyle(selectedStyle); } else { featureFigure = getFigureFactory().createSimpleFeatureFigure(simpleFeature, vectorDataNode.getDefaultStyleCss()); figureCollection.addFigure(featureFigure); } featureFigure.forceRegeneration(); } Collection<SimpleFeatureFigure> remainingFigures = figureMap.values(); figureCollection.removeFigures(remainingFigures.toArray(new Figure[remainingFigures.size()])); } private void setLayerStyle(String styleCss) { // todo - implement me (nf) // this method is called if no figure is selected, but the layer editor is showing and users can modify style settings Debug.trace("VectorDataLayer.setLayerStyle: styleCss = " + styleCss); } public SimpleFeatureFigureFactory getFigureFactory() { return figureFactory; } public FigureCollection getFigureCollection() { return figureCollection; } @Override protected Rectangle2D getLayerModelBounds() { if (figureCollection.getFigureCount() == 0) { return null; } else { return figureCollection.getBounds(); } } @Override protected void renderLayer(Rendering rendering) { figureCollection.draw(rendering); } private class VectorDataChangeHandler extends ProductNodeListenerAdapter { @Override public void nodeChanged(ProductNodeEvent event) { if (event.getSourceNode() == getVectorDataNode()) { Debug.trace("VectorDataLayer$VectorDataChangeHandler.nodeChanged: event = " + event); if (ProductNode.PROPERTY_NAME_NAME.equals(event.getPropertyName())) { setName(getVectorDataNode().getName()); } else if (VectorDataNode.PROPERTY_NAME_STYLE_CSS.equals(event.getPropertyName())) { if (event.getNewValue() != null) { setLayerStyle(event.getNewValue().toString()); } } else if (VectorDataNode.PROPERTY_NAME_FEATURE_COLLECTION.equals(event.getPropertyName())) { if (!reactingAgainstFigureChange) { updateFigureCollection(); // checkme - we could do better by computing changed modelRegion instead of passing null (nf) fireLayerDataChanged(null); } } } else if (event.getSourceNode() instanceof Placemark) { final Placemark sourceNode = (Placemark) event.getSourceNode(); if (getVectorDataNode().getPlacemarkGroup().contains(sourceNode)) if (event.getPropertyName().equals(Placemark.PROPERTY_NAME_STYLE_CSS)) { updateFigureCollection(); } else if (event.getPropertyName().equals("geometry")) { updateFigureCollection(); } else if (event.getPropertyName().equals(Placemark.PROPERTY_NAME_GEOPOS)) { updateFigureCollection(); } else if (event.getPropertyName().equals(Placemark.PROPERTY_NAME_PIXELPOS)) { updateFigureCollection(); } } } } private class FigureChangeHandler implements FigureChangeListener { @Override public void figureChanged(FigureChangeEvent event) { final Figure sourceFigure = event.getSourceFigure(); if (sourceFigure instanceof SimpleFeatureFigure) { SimpleFeatureFigure featureFigure = (SimpleFeatureFigure) sourceFigure; try { final VectorDataNode vectorDataNode = getVectorDataNode(); final SimpleFeature simpleFeature = featureFigure.getSimpleFeature(); Debug.trace("VectorDataLayer$FigureChangeHandler: vectorDataNode=" + vectorDataNode.getName() + ", featureType=" + simpleFeature.getFeatureType().getTypeName()); reactingAgainstFigureChange = true; vectorDataNode.fireFeaturesChanged(simpleFeature); // checkme - we could do better by computing changed modelRegion instead of passing null (nf) fireLayerDataChanged(null); } finally { reactingAgainstFigureChange = false; } } } } }