source: osm/applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java@ 29668

Last change on this file since 29668 was 29668, checked in by pieren, 13 years ago

trac #5654 Handle an NPE (reason not identified, not able to reproduce)

File size: 16.8 KB
Line 
1// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3@gmail.com> and others
2package cadastre_fr;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.marktr;
6
7import java.awt.GridBagLayout;
8import java.awt.event.ActionEvent;
9import java.awt.event.MouseEvent;
10import java.awt.event.MouseListener;
11import java.beans.PropertyChangeEvent;
12import java.beans.PropertyChangeListener;
13import java.util.ArrayList;
14
15import javax.swing.JDialog;
16import javax.swing.JLabel;
17import javax.swing.JOptionPane;
18import javax.swing.JPanel;
19import javax.swing.JTextField;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.actions.JosmAction;
23import org.openstreetmap.josm.data.coor.EastNorth;
24import org.openstreetmap.josm.gui.layer.Layer;
25import org.openstreetmap.josm.tools.GBC;
26
27public class MenuActionGrabPlanImage extends JosmAction implements Runnable, MouseListener {
28
29 /**
30 * Action calling the wms grabber for non georeferenced images called "plan image"
31 */
32 private static final long serialVersionUID = 1L;
33
34 public static String name = marktr("Georeference an image");
35
36 private DownloadWMSPlanImage downloadWMSPlanImage;
37 private WMSLayer wmsLayer;
38 private int countMouseClicked = 0;
39 private int mode = 0;
40 private int cGetCorners = 1;
41 private int cGetLambertCrosspieces = 2;
42 private EastNorth ea1;
43 private EastNorth ea2;
44 private long mouseClickedTime = 0;
45 private EastNorth georefpoint1;
46 private EastNorth georefpoint2;
47 private boolean ignoreMouseClick = false;
48 private boolean clickOnTheMap = false;
49
50 /**
51 * The time which needs to pass between two clicks during georeferencing, in milliseconds
52 */
53 private int initialClickDelay;
54
55 public MenuActionGrabPlanImage() {
56 super(tr(name), "cadastre_small", tr("Grab non-georeferenced image"), null, false, "cadastrefr/grabplanimage", true);
57 }
58
59 public void actionCompleted() {
60 countMouseClicked = 0;
61 mode = 0;
62 mouseClickedTime = System.currentTimeMillis();
63 }
64
65 public void actionInterrupted() {
66 actionCompleted();
67 if (wmsLayer != null) {
68 Main.main.removeLayer(wmsLayer);
69 wmsLayer = null;
70 }
71 }
72
73 @Override
74 protected void updateEnabledState() {
75 if (wmsLayer == null || Main.map == null || Main.map.mapView == null) return;
76 if (countMouseClicked == 0 && mode == 0) return;
77 for (Layer l : Main.map.mapView.getAllLayersAsList())
78 if (l == wmsLayer)
79 return;
80 JOptionPane.showMessageDialog(Main.parent, tr("Georeferencing interrupted"));
81 actionInterrupted();
82 }
83
84 public void actionPerformed(ActionEvent ae) {
85 if (Main.map != null) {
86 if (CadastrePlugin.isCadastreProjection()) {
87 //wmsLayer = WMSDownloadAction.getLayer();
88 wmsLayer = new MenuActionNewLocation().addNewLayer(new ArrayList<WMSLayer>());
89 if (wmsLayer == null) return;
90 downloadWMSPlanImage = new DownloadWMSPlanImage();
91 downloadWMSPlanImage.download(wmsLayer);
92 initialClickDelay = Main.pref.getInteger("cadastrewms.georef-click-delay",200);
93 // download sub-images of the cadastre scan and join them into one single
94 Main.worker.execute(this);
95 } else {
96 CadastrePlugin.askToChangeProjection();
97 }
98 }
99 }
100
101 public void run() {
102 // wait until plan image is fully loaded and joined into one single image
103 boolean loadedFromCache = downloadWMSPlanImage.waitFinished();
104 if (loadedFromCache) {
105 Main.map.repaint();
106 } else if (wmsLayer.getImages().size() == 0) {
107 // action canceled or image loaded from cache (and already georeferenced)
108 actionInterrupted();
109 } else {
110 int reply = JOptionPane.CANCEL_OPTION;
111 if (wmsLayer.isAlreadyGeoreferenced()) {
112 reply = JOptionPane.showConfirmDialog(null,
113 tr("This image contains georeference data.\n"+
114 "Do you want to use them ?"),
115 null,
116 JOptionPane.YES_NO_OPTION);
117 }
118 if (reply == JOptionPane.OK_OPTION) {
119 transformGeoreferencedImg();
120 } else {
121 mouseClickedTime = System.currentTimeMillis();
122 Main.map.mapView.addMouseListener(this);
123 if (Main.pref.getBoolean("cadastrewms.noImageCropping", false) == false)
124 startCropping();
125 else
126 startGeoreferencing();
127 }
128 }
129 }
130
131 public void mouseClicked(MouseEvent e) {
132 if (System.currentTimeMillis() - mouseClickedTime < initialClickDelay) {
133 System.out.println("mouse click bounce detected");
134 return; // mouse click anti-bounce
135 }
136 else
137 mouseClickedTime = System.currentTimeMillis();
138 if (e.getButton() != MouseEvent.BUTTON1)
139 return;
140 if (ignoreMouseClick) return; // In case we are currently just allowing zooming to read lambert coordinates
141 EastNorth ea = Main.getProjection().latlon2eastNorth(Main.map.mapView.getLatLon(e.getX(), e.getY()));
142 System.out.println("clic:"+countMouseClicked+" ,"+ea+", mode:"+mode);
143 if (clickOnTheMap) {
144 clickOnTheMap = false;
145 handleNewCoordinates(ea.east(), ea.north());
146 } else {
147 countMouseClicked++;
148 // ignore clicks outside the image
149 if (ea.east() < wmsLayer.getImage(0).min.east() || ea.east() > wmsLayer.getImage(0).max.east()
150 || ea.north() < wmsLayer.getImage(0).min.north() || ea.north() > wmsLayer.getImage(0).max.north())
151 return;
152 if (mode == cGetCorners) {
153 if (countMouseClicked == 1) {
154 ea1 = ea;
155 continueCropping();
156 }
157 if (countMouseClicked == 2) {
158 wmsLayer.cropImage(ea1, ea);
159 Main.map.mapView.repaint();
160 startGeoreferencing();
161 }
162 } else if (mode == cGetLambertCrosspieces) {
163 if (countMouseClicked == 1) {
164 ea1 = ea;
165 inputLambertPosition(); // This will automatically asks for second point and continue the georeferencing
166 }
167 if (countMouseClicked == 2) {
168 ea2 = ea;
169 inputLambertPosition(); // This will automatically ends the georeferencing
170 }
171 }
172 }
173 }
174
175 /**
176 *
177 * @return false if all operations are canceled
178 */
179 private boolean startCropping() {
180 mode = cGetCorners;
181 countMouseClicked = 0;
182 Object[] options = { "OK", "Cancel" };
183 int ret = JOptionPane.showOptionDialog( null,
184 tr("Click first corner for image cropping\n(two points required)"),
185 tr("Image cropping"),
186 JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
187 null, options, options[0]);
188 if (ret == JOptionPane.OK_OPTION) {
189 mouseClickedTime = System.currentTimeMillis();
190 } else
191 if (canceledOrRestartCurrAction("image cropping"))
192 return startCropping();
193 return true;
194 }
195
196 /**
197 *
198 * @return false if all operations are canceled
199 */
200 private boolean continueCropping() {
201 Object[] options = { "OK", "Cancel" };
202 int ret = JOptionPane.showOptionDialog( null,
203 tr("Click second corner for image cropping"),
204 tr("Image cropping"),
205 JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
206 null, options, options[0]);
207 if (ret != JOptionPane.OK_OPTION) {
208 if (canceledOrRestartCurrAction("image cropping"))
209 return startCropping();
210 }
211 return true;
212 }
213
214 /**
215 *
216 * @return false if all operations are canceled
217 */
218 private boolean startGeoreferencing() {
219 countMouseClicked = 0;
220 mode = cGetLambertCrosspieces;
221 Object[] options = { "OK", "Cancel" };
222 int ret = JOptionPane.showOptionDialog( null,
223 tr("Click first Lambert crosspiece for georeferencing\n(two points required)"),
224 tr("Image georeferencing"),
225 JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
226 null, options, options[0]);
227 if (ret == JOptionPane.OK_OPTION) {
228 mouseClickedTime = System.currentTimeMillis();
229 } else
230 if (canceledOrRestartCurrAction("georeferencing"))
231 return startGeoreferencing();
232 return true;
233 }
234
235 /**
236 *
237 * @return false if all operations are canceled
238 */
239 private boolean continueGeoreferencing() {
240 Object[] options = { "OK", "Cancel" };
241 int ret = JOptionPane.showOptionDialog( null,
242 tr("Click second Lambert crosspiece for georeferencing"),
243 tr("Image georeferencing"),
244 JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
245 null, options, options[0]);
246 if (ret != JOptionPane.OK_OPTION) {
247 if (canceledOrRestartCurrAction("georeferencing"))
248 return startGeoreferencing();
249 }
250 return true;
251 }
252
253 /**
254 * Ends the georeferencing by computing the affine transformation
255 */
256 private void endGeoreferencing() {
257 Main.map.mapView.removeMouseListener(this);
258 affineTransform(ea1, ea2, georefpoint1, georefpoint2);
259 wmsLayer.grabThread.saveNewCache();
260 Main.map.mapView.repaint();
261 actionCompleted();
262 clickOnTheMap = false;
263 ignoreMouseClick = false;
264 }
265
266 /**
267 *
268 * @return false if all operations are canceled
269 */
270 private boolean canceledOrRestartCurrAction(String action) {
271 Object[] options = { "Cancel", "Retry" };
272 int selectedValue = JOptionPane.showOptionDialog( null,
273 tr("Do you want to cancel completely\n"+
274 "or just retry "+action+" ?"), "",
275 JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
276 null, options, options[0]);
277 countMouseClicked = 0;
278 if (selectedValue == 0) { // "Cancel"
279 // remove layer
280 Main.main.removeLayer(wmsLayer);
281 wmsLayer = null;
282 Main.map.mapView.removeMouseListener(this);
283 return false;
284 }
285 return true;
286 }
287
288 private void inputLambertPosition() {
289 JLabel labelEnterPosition = new JLabel(
290 tr("Enter cadastre east,north position"));
291 JLabel labelWarning = new JLabel(
292 tr("(Warning: verify north with arrow !!)"));
293 JPanel p = new JPanel(new GridBagLayout());
294 JLabel labelEast = new JLabel(tr("East"));
295 JLabel labelNorth = new JLabel(tr("North"));
296 final JTextField inputEast = new JTextField();
297 final JTextField inputNorth = new JTextField();
298 p.add(labelEnterPosition, GBC.eol());
299 p.add(labelWarning, GBC.eol());
300 p.add(labelEast, GBC.std().insets(0, 0, 10, 0));
301 p.add(inputEast, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
302 p.add(labelNorth, GBC.std().insets(0, 0, 10, 0));
303 p.add(inputNorth, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
304 final Object[] options = {tr("OK"),
305 tr("Cancel"),
306 tr("I use the mouse")};
307 final JOptionPane pane = new JOptionPane(p,
308 JOptionPane.INFORMATION_MESSAGE, JOptionPane.YES_NO_CANCEL_OPTION,
309 null, options, options[0]);
310 String number;
311 if (countMouseClicked == 1)
312 number = "first";
313 else
314 number = "second";
315 JDialog dialog = pane.createDialog(Main.parent, tr(
316 "Set {0} Lambert coordinates", number));
317 dialog.setModal(false);
318 ignoreMouseClick = true; // To avoid mouseClicked from being called
319 // during coordinates reading
320 dialog.setAlwaysOnTop(true);
321 dialog.setVisible(true);
322 pane.addPropertyChangeListener(new PropertyChangeListener() {
323 public void propertyChange(PropertyChangeEvent evt) {
324 if (JOptionPane.VALUE_PROPERTY.equals(evt.getPropertyName())) {
325 ignoreMouseClick = false;
326 // Cancel
327 if (pane.getValue().equals(options[1])) {
328 if (canceledOrRestartCurrAction("georeferencing"))
329 startGeoreferencing();
330 }
331 // Click on the map
332 if (pane.getValue().equals(options[2])) {
333 clickOnTheMap = true;
334 } else {
335 // OK (coordinates manually entered)
336 clickOnTheMap = false;
337 if (inputEast.getText().length() != 0
338 && inputNorth.getText().length() != 0) {
339 double e, n;
340 try {
341 e = Double.parseDouble(inputEast.getText());
342 n = Double.parseDouble(inputNorth.getText());
343 } catch (NumberFormatException ex) {
344 return;
345 }
346 handleNewCoordinates(e, n);
347 }
348 }
349 }
350 }
351 });
352 }
353
354 private void handleNewCoordinates(double e, double n) {
355 if (countMouseClicked == 1) {
356 georefpoint1 = new EastNorth(e, n);
357 continueGeoreferencing();
358 } else {
359 georefpoint2 = new EastNorth(e, n);
360 endGeoreferencing();
361 }
362 }
363
364 /**
365 * Use point org1 as anchor for scale, then move org1 to dst1, then rotate org2 on dst2
366 * around org1/dst1 anchor
367 * @param org1 first point at original coordinate system (the grabbed image)
368 * @param org2 second point "
369 * @param dst1 first point at final destination coordinate system (the real east/north coordinate system)
370 * @param dst2 second point "
371 */
372 private void affineTransform(EastNorth org1, EastNorth org2, EastNorth dst1, EastNorth dst2) {
373 // handle an NPE case I'm not able to reproduce
374 if(org1==null || org2==null || dst1==null || dst2==null)
375 {
376 JOptionPane.showMessageDialog(Main.parent,
377 tr("Ooops. I failed to catch all coordinates\n"+
378 "correctly. Retry please."));
379 System.out.println("failed to transform: one coordinate missing:"
380 +"org1="+org1+", org2="+org2+", dst1="+dst1+", dst2="+dst2);
381 return;
382 }
383 double angle = dst1.heading(dst2) - org1.heading(org2);
384 double proportion = dst1.distance(dst2)/org1.distance(org2);
385 // move
386 double dx = dst1.getX() - org1.getX();
387 double dy = dst1.getY() - org1.getY();
388 wmsLayer.getImage(0).shear(dx, dy);
389 org1 = org1.add(dx, dy); // org1=dst1 now
390 org2 = org2.add(dx, dy);
391 // rotate : org1(=dst1 now) is anchor for rotation and scale
392 wmsLayer.getImage(0).rotate(dst1, angle);
393 org2 = org2.rotate(dst1, angle);
394 // scale image from anchor org1(=dst1 now)
395 wmsLayer.getImage(0).scale(dst1, proportion);
396 }
397
398 private void transformGeoreferencedImg() {
399 georefpoint1 = new EastNorth(wmsLayer.X0, wmsLayer.Y0);
400 georefpoint2 = new EastNorth(wmsLayer.X0+wmsLayer.fX*wmsLayer.communeBBox.max.getX(),
401 wmsLayer.Y0+wmsLayer.fY*wmsLayer.communeBBox.max.getX());
402 ea1 = new EastNorth(wmsLayer.getImage(0).min.east(), wmsLayer.getImage(0).max.north());
403 EastNorth ea2 = wmsLayer.getImage(0).max;
404 affineTransform(ea1, ea2, georefpoint1, georefpoint2);
405 wmsLayer.grabThread.saveNewCache();
406 Main.map.mapView.repaint();
407 }
408
409 public void mouseEntered(MouseEvent arg0) {
410 }
411
412 public void mouseExited(MouseEvent arg0) {
413 }
414
415 public void mousePressed(MouseEvent arg0) {
416 }
417
418 public void mouseReleased(MouseEvent arg0) {
419 }
420
421}
Note: See TracBrowser for help on using the repository browser.