/*
* Copyright (C) 2012 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.rcp.placemark;
import com.bc.ceres.binding.Property;
import com.bc.ceres.binding.PropertySet;
import com.bc.ceres.swing.binding.BindingContext;
import com.bc.ceres.swing.binding.PropertyPane;
import org.esa.snap.core.datamodel.CrsGeoCoding;
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.Product;
import org.esa.snap.core.datamodel.ProductNode;
import org.esa.snap.core.util.Guardian;
import org.esa.snap.core.util.StringUtils;
import org.esa.snap.ui.ModalDialog;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Window;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* A dialog used to create new placemarks or edit existing placemarks.
*/
public class PlacemarkDialog extends ModalDialog {
private final static String PROPERTY_NAME_NAME = "name";
private final static String PROPERTY_NAME_LABEL = "label";
private final static String PROPERTY_NAME_DESCRIPTION = "description";
private final static String PROPERTY_NAME_STYLE_CSS = "styleCss";
private final static String PROPERTY_NAME_LAT = "lat";
private final static String PROPERTY_NAME_LON = "lon";
private final static String PROPERTY_NAME_PIXEL_X = "pixelX";
private final static String PROPERTY_NAME_PIXEL_Y = "pixelY";
private final static String PROPERTY_NAME_USE_PIXEL_POS = "usePixelPos";
private final Product product;
private final boolean canGetPixelPos;
private final boolean canGetGeoPos;
private final PlacemarkDescriptor placemarkDescriptor;
private final BindingContext bindingContext;
private boolean adjusting;
public PlacemarkDialog(final Window parent, final Product product, final PlacemarkDescriptor placemarkDescriptor,
boolean switchGeoAndPixelPositionsEditable) {
super(parent, "New " + placemarkDescriptor.getRoleLabel(), ModalDialog.ID_OK_CANCEL, null); /*I18N*/
Guardian.assertNotNull("product", product);
this.product = product;
this.placemarkDescriptor = placemarkDescriptor;
bindingContext = new BindingContext();
final GeoCoding geoCoding = this.product.getSceneGeoCoding();
final boolean hasGeoCoding = geoCoding != null;
canGetPixelPos = hasGeoCoding && geoCoding.canGetPixelPos();
canGetGeoPos = hasGeoCoding && geoCoding.canGetGeoPos();
boolean usePixelPos = !hasGeoCoding && switchGeoAndPixelPositionsEditable;
PropertySet propertySet = bindingContext.getPropertySet();
propertySet.addProperties(Property.create(PROPERTY_NAME_NAME, ""),
Property.create(PROPERTY_NAME_LABEL, ""),
Property.create(PROPERTY_NAME_DESCRIPTION, ""),
Property.create(PROPERTY_NAME_STYLE_CSS, ""),
Property.create(PROPERTY_NAME_LAT, 0.0),
Property.create(PROPERTY_NAME_LON, 0.0),
Property.create(PROPERTY_NAME_PIXEL_X, 0.0),
Property.create(PROPERTY_NAME_PIXEL_Y, 0.0),
Property.create(PROPERTY_NAME_USE_PIXEL_POS, usePixelPos)
);
propertySet.getProperty(PROPERTY_NAME_USE_PIXEL_POS).getDescriptor().setAttribute("enabled", hasGeoCoding && switchGeoAndPixelPositionsEditable);
propertySet.getProperty(PROPERTY_NAME_LAT).getDescriptor().setDisplayName("Latitude");
propertySet.getProperty(PROPERTY_NAME_LAT).getDescriptor().setUnit("deg");
propertySet.getProperty(PROPERTY_NAME_LON).getDescriptor().setDisplayName("Longitude");
propertySet.getProperty(PROPERTY_NAME_LON).getDescriptor().setUnit("deg");
propertySet.getProperty(PROPERTY_NAME_PIXEL_X).getDescriptor().setDisplayName("Pixel X");
propertySet.getProperty(PROPERTY_NAME_PIXEL_X).getDescriptor().setUnit("pixels");
propertySet.getProperty(PROPERTY_NAME_PIXEL_Y).getDescriptor().setDisplayName("Pixel Y");
propertySet.getProperty(PROPERTY_NAME_PIXEL_Y).getDescriptor().setUnit("pixels");
PropertyChangeListener geoChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updatePixelPos();
}
};
propertySet.getProperty(PROPERTY_NAME_LAT).addPropertyChangeListener(geoChangeListener);
propertySet.getProperty(PROPERTY_NAME_LON).addPropertyChangeListener(geoChangeListener);
PropertyChangeListener pixelChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateGeoPos();
}
};
propertySet.getProperty(PROPERTY_NAME_PIXEL_X).addPropertyChangeListener(pixelChangeListener);
propertySet.getProperty(PROPERTY_NAME_PIXEL_Y).addPropertyChangeListener(pixelChangeListener);
/*
symbolLabel = new JLabel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
final PixelPos refPoint = symbol.getRefPoint();
Rectangle2D bounds = symbol.getBounds();
double tx = refPoint.getX() - bounds.getX() / 2;
double ty = refPoint.getY() - bounds.getY() / 2;
g2d.translate(tx, ty);
symbol.draw(g2d);
g2d.translate(-tx, -ty);
}
}
};
symbolLabel.setPreferredSize(new Dimension(40, 40));
*/
final JPanel panel = new PropertyPane(bindingContext).createPanel();
final JPanel content = new JPanel(new BorderLayout());
content.add(panel, BorderLayout.CENTER);
content.add(new JLabel("Pixel position is given in product raster coordinates"), BorderLayout.SOUTH);
setContent(content);
if (switchGeoAndPixelPositionsEditable) {
bindingContext.bindEnabledState(PROPERTY_NAME_LAT, false, PROPERTY_NAME_USE_PIXEL_POS, true);
bindingContext.bindEnabledState(PROPERTY_NAME_LON, false, PROPERTY_NAME_USE_PIXEL_POS, true);
bindingContext.bindEnabledState(PROPERTY_NAME_PIXEL_X, true, PROPERTY_NAME_USE_PIXEL_POS, true);
bindingContext.bindEnabledState(PROPERTY_NAME_PIXEL_Y, true, PROPERTY_NAME_USE_PIXEL_POS, true);
}
}
public Product getProduct() {
return product;
}
@Override
protected void onOK() {
if (ProductNode.isValidNodeName(getName())) {
super.onOK();
} else {
showInformationDialog("'" + getName() + "' is not a valid " + placemarkDescriptor.getRoleLabel() + " name."); /*I18N*/
}
}
public String getName() {
return bindingContext.getPropertySet().getValue(PROPERTY_NAME_NAME);
}
public void setName(String name) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_NAME, name);
}
public String getLabel() {
return bindingContext.getPropertySet().getValue(PROPERTY_NAME_LABEL);
}
public void setLabel(String label) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_LABEL, label);
}
public String getDescription() {
return bindingContext.getPropertySet().getValue(PROPERTY_NAME_DESCRIPTION);
}
public void setDescription(String description) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_DESCRIPTION, description);
}
public String getStyleCss() {
return bindingContext.getPropertySet().getValue(PROPERTY_NAME_STYLE_CSS);
}
private void setStyleCss(String styleCss) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_STYLE_CSS, styleCss);
}
public double getPixelX() {
return (Double) bindingContext.getPropertySet().getValue(PROPERTY_NAME_PIXEL_X);
}
public void setPixelX(double pixelX) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_PIXEL_X, pixelX);
}
public double getPixelY() {
return (Double) bindingContext.getPropertySet().getValue(PROPERTY_NAME_PIXEL_Y);
}
public void setPixelY(double pixelY) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_PIXEL_Y, pixelY);
}
public double getLat() {
return (Double) bindingContext.getPropertySet().getValue(PROPERTY_NAME_LAT);
}
public void setLat(double lat) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_LAT, lat);
}
public double getLon() {
return (Double) bindingContext.getPropertySet().getValue(PROPERTY_NAME_LON);
}
public void setLon(double lon) {
bindingContext.getPropertySet().setValue(PROPERTY_NAME_LON, lon);
}
public GeoPos getGeoPos() {
return new GeoPos(getLat(), getLon());
}
public void setGeoPos(GeoPos geoPos) {
if (geoPos != null) {
setLat(geoPos.lat);
setLon(geoPos.lon);
} else {
setLat(0.0F);
setLon(0.0F);
}
}
public PixelPos getPixelPos() {
return new PixelPos(getPixelX(), getPixelY());
}
public void setPixelPos(PixelPos pixelPos) {
if (pixelPos != null) {
setPixelX(pixelPos.x);
setPixelY(pixelPos.y);
} else {
setPixelX(0.0F);
setPixelY(0.0F);
}
}
private void updatePixelPos() {
if (canGetPixelPos && !adjusting) {
adjusting = true;
PixelPos pixelPos = placemarkDescriptor.updatePixelPos(product.getSceneGeoCoding(), getGeoPos(), getPixelPos());
setPixelPos(pixelPos);
adjusting = false;
}
}
private void updateGeoPos() {
if (canGetGeoPos && !adjusting) {
adjusting = true;
GeoPos geoPos = placemarkDescriptor.updateGeoPos(product.getSceneGeoCoding(), getPixelPos(), getGeoPos());
setGeoPos(geoPos);
adjusting = false;
}
}
/**
* Shows a dialog to edit the properties of an placemark.
* If the placemark does not belong to a product it will be added after editing.
*
* @param parent the parent window fo the dialog
* @param product the product where the placemark is already contained or where it will be added
* @param placemark the placemark to edit
* @param placemarkDescriptor the descriptor of the placemark
* @return <code>true</code> if editing was successful, otherwise <code>false</code>.
*/
public static boolean showEditPlacemarkDialog(Window parent, Product product, Placemark placemark,
PlacemarkDescriptor placemarkDescriptor) {
final PlacemarkDialog dialog = new PlacemarkDialog(parent, product, placemarkDescriptor,
placemarkDescriptor instanceof PinDescriptor);
boolean belongsToProduct = placemark.getProduct() != null;
String titlePrefix = belongsToProduct ? "Edit" : "New";
String roleLabel = StringUtils.firstLetterUp(placemarkDescriptor.getRoleLabel());
dialog.getJDialog().setTitle(titlePrefix + " " + roleLabel);
dialog.getJDialog().setName(titlePrefix + "_" + roleLabel);
dialog.setName(placemark.getName());
dialog.setLabel(placemark.getLabel());
dialog.setDescription(placemark.getDescription() != null ? placemark.getDescription() : "");
// prevent that geoPos change updates pixelPos and vice versa during dialog creation
dialog.adjusting = true;
dialog.setPixelPos(placemark.getPixelPos());
GeoPos geoPos = placemark.getGeoPos();
dialog.setGeoPos(geoPos != null ? geoPos : new GeoPos(Float.NaN, Float.NaN));
dialog.adjusting = false;
dialog.setStyleCss(placemark.getStyleCss());
boolean ok = (dialog.show() == ID_OK);
if (ok) {
if (!belongsToProduct) {
// must add to product, otherwise setting the pixel position wil fail
placemarkDescriptor.getPlacemarkGroup(product).add(placemark);
}
placemark.setName(dialog.getName());
placemark.setLabel(dialog.getLabel());
placemark.setDescription(dialog.getDescription());
placemark.setGeoPos(dialog.getGeoPos());
placemark.setStyleCss(dialog.getStyleCss());
}
return ok;
}
public static void main(String[] args) throws TransformException, FactoryException {
Product product1 = new Product("A", "B", 360, 180);
product1.setSceneGeoCoding(new CrsGeoCoding(DefaultGeographicCRS.WGS84, 360, 180, -180.0, 90.0, 1.0, 1.0, 0.0, 0.0));
PinDescriptor descriptor = PinDescriptor.getInstance();
Placemark pin1 = Placemark.createPointPlacemark(descriptor, "pin_1", "Pin 1", "Schnatter!", new PixelPos(0, 0), new GeoPos(), product1.getSceneGeoCoding());
product1.getPinGroup().add(pin1);
showEditPlacemarkDialog(null, product1, pin1, descriptor);
}
}