// License: GPL. For details, see LICENSE file.
package cadastre_fr;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.tools.GBC;
public class RasterImageGeoreferencer implements MouseListener {
private int countMouseClicked = 0;
private int mode = 0;
private int cGetCorners = 1;
private int cGetLambertCrosspieces = 2;
private EastNorth ea1;
private EastNorth ea2;
private long mouseClickedTime = 0;
private WMSLayer wmsLayer;
private EastNorth georefpoint1;
private EastNorth georefpoint2;
private boolean ignoreMouseClick = false;
private boolean clickOnTheMap = false;
/**
* The time which needs to pass between two clicks during georeferencing, in milliseconds
*/
private int initialClickDelay;
public void addListener() {
Main.map.mapView.addMouseListener(this);
}
/**
*
* @return false if all operations are canceled
*/
public boolean startCropping(WMSLayer wmsLayer) {
this.wmsLayer = wmsLayer;
mode = cGetCorners;
countMouseClicked = 0;
initialClickDelay = Main.pref.getInteger("cadastrewms.georef-click-delay", 200);
mouseClickedTime = System.currentTimeMillis();
Object[] options = {"OK", "Cancel"};
int ret = JOptionPane.showOptionDialog(null,
tr("Click first corner for image cropping\n(two points required)"),
tr("Image cropping"),
JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, options, options[0]);
if (ret == JOptionPane.OK_OPTION) {
mouseClickedTime = System.currentTimeMillis();
} else
if (canceledOrRestartCurrAction("image cropping"))
return startCropping(wmsLayer);
return true;
}
/**
*
* @return false if all operations are canceled
*/
public boolean startGeoreferencing(WMSLayer wmsLayer) {
this.wmsLayer = wmsLayer;
countMouseClicked = 0;
mode = cGetLambertCrosspieces;
initialClickDelay = Main.pref.getInteger("cadastrewms.georef-click-delay", 200);
mouseClickedTime = System.currentTimeMillis();
Object[] options = {"OK", "Cancel"};
int ret = JOptionPane.showOptionDialog(null,
tr("Click first Lambert crosspiece for georeferencing\n(two points required)"),
tr("Image georeferencing"),
JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, options, options[0]);
if (ret == JOptionPane.OK_OPTION) {
mouseClickedTime = System.currentTimeMillis();
} else
if (canceledOrRestartCurrAction("georeferencing"))
return startGeoreferencing(wmsLayer);
return true;
}
public boolean isRunning() {
return (countMouseClicked != 0 || mode != 0);
}
@Override
public void mouseClicked(MouseEvent e) {
if (System.currentTimeMillis() - mouseClickedTime < initialClickDelay) {
Main.info("mouse click bounce detected");
return; // mouse click anti-bounce
} else
mouseClickedTime = System.currentTimeMillis();
if (e.getButton() != MouseEvent.BUTTON1)
return;
if (ignoreMouseClick) return; // In case we are currently just allowing zooming to read lambert coordinates
EastNorth ea = Main.getProjection().latlon2eastNorth(Main.map.mapView.getLatLon(e.getX(), e.getY()));
Main.info("click:"+countMouseClicked+" ,"+ea+", mode:"+mode);
if (clickOnTheMap) {
clickOnTheMap = false;
handleNewCoordinates(ea.east(), ea.north());
} else {
// ignore clicks outside the image
if (ea.east() < wmsLayer.getImage(0).min.east() || ea.east() > wmsLayer.getImage(0).max.east()
|| ea.north() < wmsLayer.getImage(0).min.north() || ea.north() > wmsLayer.getImage(0).max.north()) {
Main.info("ignore click outside the image");
return;
}
countMouseClicked++;
if (mode == cGetCorners) {
if (countMouseClicked == 1) {
ea1 = ea;
continueCropping();
}
if (countMouseClicked == 2) {
wmsLayer.cropImage(ea1, ea);
Main.map.mapView.repaint();
startGeoreferencing(wmsLayer);
}
} else if (mode == cGetLambertCrosspieces) {
if (countMouseClicked == 1) {
ea1 = ea;
inputLambertPosition(); // This will automatically asks for second point and continue the georeferencing
}
if (countMouseClicked == 2) {
ea2 = ea;
inputLambertPosition(); // This will automatically ends the georeferencing
}
}
}
}
/**
*
* @return false if all operations are canceled
*/
private boolean canceledOrRestartCurrAction(String action) {
Object[] options = {"Cancel", "Retry"};
int selectedValue = JOptionPane.showOptionDialog(null,
tr("Do you want to cancel completely\n"+
"or just retry "+action+" ?"), "",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[0]);
countMouseClicked = 0;
if (selectedValue == 0) { // "Cancel"
// remove layer
Main.getLayerManager().removeLayer(wmsLayer);
wmsLayer = null;
Main.map.mapView.removeMouseListener(this);
return false;
}
return true;
}
/**
* Use point org1 as anchor for scale, then move org1 to dst1, then rotate org2 on dst2
* around org1/dst1 anchor
* @param org1 first point at original coordinate system (the grabbed image)
* @param org2 second point
* @param dst1 first point at final destination coordinate system (the real east/north coordinate system)
* @param dst2 second point
*/
private void affineTransform(EastNorth org1, EastNorth org2, EastNorth dst1, EastNorth dst2) {
// handle an NPE case I'm not able to reproduce
if (org1 == null || org2 == null || dst1 == null || dst2 == null) {
JOptionPane.showMessageDialog(Main.parent,
tr("Ooops. I failed to catch all coordinates\n"+
"correctly. Retry please."));
Main.warn("failed to transform: one coordinate missing:"
+"org1="+org1+", org2="+org2+", dst1="+dst1+", dst2="+dst2);
return;
}
double angle = dst1.heading(dst2) - org1.heading(org2);
double proportion = dst1.distance(dst2)/org1.distance(org2);
// move
double dx = dst1.getX() - org1.getX();
double dy = dst1.getY() - org1.getY();
wmsLayer.getImage(0).shear(dx, dy);
// rotate : dst1 is anchor for rotation and scale
wmsLayer.getImage(0).rotate(dst1, angle);
// scale image from anchor dst1
wmsLayer.getImage(0).scale(dst1, proportion);
}
/**
* Ends the georeferencing by computing the affine transformation
*/
private void endGeoreferencing() {
Main.map.mapView.removeMouseListener(this);
affineTransform(ea1, ea2, georefpoint1, georefpoint2);
wmsLayer.grabThread.saveNewCache();
Main.map.mapView.repaint();
actionCompleted();
clickOnTheMap = false;
ignoreMouseClick = false;
}
private void inputLambertPosition() {
JLabel labelEnterPosition = new JLabel(
tr("Enter cadastre east,north position"));
JLabel labelWarning = new JLabel(
tr("(Warning: verify north with arrow !!)"));
JPanel p = new JPanel(new GridBagLayout());
JLabel labelEast = new JLabel(tr("East"));
JLabel labelNorth = new JLabel(tr("North"));
final JTextField inputEast = new JTextField();
final JTextField inputNorth = new JTextField();
p.add(labelEnterPosition, GBC.eol());
p.add(labelWarning, GBC.eol());
p.add(labelEast, GBC.std().insets(0, 0, 10, 0));
p.add(inputEast, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
p.add(labelNorth, GBC.std().insets(0, 0, 10, 0));
p.add(inputNorth, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
final Object[] options = {tr("OK"),
tr("Cancel"),
tr("I use the mouse")};
final JOptionPane pane = new JOptionPane(p,
JOptionPane.INFORMATION_MESSAGE, JOptionPane.YES_NO_CANCEL_OPTION,
null, options, options[0]);
String number;
if (countMouseClicked == 1)
number = "first";
else
number = "second";
JDialog dialog = pane.createDialog(Main.parent, tr(
"Set {0} Lambert coordinates", number));
dialog.setModal(false);
ignoreMouseClick = true; // To avoid mouseClicked from being called
// during coordinates reading
dialog.setAlwaysOnTop(true);
dialog.setVisible(true);
pane.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (JOptionPane.VALUE_PROPERTY.equals(evt.getPropertyName())) {
ignoreMouseClick = false;
// Cancel
if (pane.getValue().equals(options[1])) {
if (canceledOrRestartCurrAction("georeferencing"))
startGeoreferencing(wmsLayer);
}
// Click on the map
if (pane.getValue().equals(options[2])) {
clickOnTheMap = true;
} else {
// OK (coordinates manually entered)
clickOnTheMap = false;
if (inputEast.getText().length() != 0
&& inputNorth.getText().length() != 0) {
double e, n;
try {
e = Double.parseDouble(inputEast.getText());
n = Double.parseDouble(inputNorth.getText());
} catch (NumberFormatException ex) {
return;
}
handleNewCoordinates(e, n);
}
}
}
}
});
}
/**
*
* @return false if all operations are canceled
*/
private boolean continueCropping() {
Object[] options = {"OK", "Cancel"};
int ret = JOptionPane.showOptionDialog(null,
tr("Click second corner for image cropping"),
tr("Image cropping"),
JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, options, options[0]);
if (ret != JOptionPane.OK_OPTION) {
if (canceledOrRestartCurrAction("image cropping"))
return startCropping(wmsLayer);
}
return true;
}
public void transformGeoreferencedImg() {
georefpoint1 = new EastNorth(wmsLayer.X0, wmsLayer.Y0);
georefpoint2 = new EastNorth(wmsLayer.X0+wmsLayer.fX*wmsLayer.communeBBox.max.getX(),
wmsLayer.Y0+wmsLayer.fY*wmsLayer.communeBBox.max.getX());
ea1 = new EastNorth(wmsLayer.getImage(0).min.east(), wmsLayer.getImage(0).max.north());
EastNorth ea2 = wmsLayer.getImage(0).max;
affineTransform(ea1, ea2, georefpoint1, georefpoint2);
wmsLayer.grabThread.saveNewCache();
Main.map.mapView.repaint();
}
/**
*
* @return false if all operations are canceled
*/
private boolean continueGeoreferencing() {
Object[] options = {"OK", "Cancel"};
int ret = JOptionPane.showOptionDialog(null,
tr("Click second Lambert crosspiece for georeferencing"),
tr("Image georeferencing"),
JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, options, options[0]);
if (ret != JOptionPane.OK_OPTION) {
if (canceledOrRestartCurrAction("georeferencing"))
return startGeoreferencing(wmsLayer);
}
return true;
}
private void handleNewCoordinates(double e, double n) {
if (countMouseClicked == 1) {
georefpoint1 = new EastNorth(e, n);
continueGeoreferencing();
} else {
georefpoint2 = new EastNorth(e, n);
endGeoreferencing();
}
}
private void actionCompleted() {
countMouseClicked = 0;
mode = 0;
mouseClickedTime = System.currentTimeMillis();
}
public void actionInterrupted() {
actionCompleted();
if (wmsLayer != null) {
Main.getLayerManager().removeLayer(wmsLayer);
wmsLayer = null;
}
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent arg0) {
}
}