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

Last change on this file since 29829 was 29801, checked in by pieren, 11 years ago

Fix transparency issue on new raster images. Temporary disable georeferences parsing not working on new cadastre WMS. Workaround on address help tool when switching to full screen.

File size: 23.1 KB
Line 
1package cadastre_fr;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Cursor;
6import java.awt.GridBagLayout;
7import java.awt.Point;
8import java.awt.Rectangle;
9import java.awt.Toolkit;
10import java.awt.event.ActionEvent;
11import java.awt.event.ActionListener;
12import java.awt.event.ComponentAdapter;
13import java.awt.event.ComponentEvent;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseEvent;
16import java.awt.event.MouseListener;
17import java.awt.event.MouseMotionListener;
18import java.awt.event.WindowEvent;
19import java.awt.event.WindowListener;
20import java.util.ArrayList;
21import java.util.Collection;
22import java.util.Collections;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30
31import javax.swing.ButtonGroup;
32import javax.swing.ImageIcon;
33import javax.swing.JButton;
34import javax.swing.JCheckBox;
35import javax.swing.JDialog;
36import javax.swing.JLabel;
37import javax.swing.JOptionPane;
38import javax.swing.JPanel;
39import javax.swing.JRadioButton;
40import javax.swing.JTextField;
41import javax.swing.event.ChangeEvent;
42import javax.swing.event.ChangeListener;
43
44import org.openstreetmap.josm.Main;
45import org.openstreetmap.josm.command.AddCommand;
46import org.openstreetmap.josm.command.ChangeCommand;
47import org.openstreetmap.josm.command.ChangePropertyCommand;
48import org.openstreetmap.josm.command.Command;
49import org.openstreetmap.josm.command.SequenceCommand;
50import org.openstreetmap.josm.actions.mapmode.MapMode;
51import org.openstreetmap.josm.data.coor.EastNorth;
52import org.openstreetmap.josm.data.osm.Node;
53import org.openstreetmap.josm.data.osm.OsmPrimitive;
54import org.openstreetmap.josm.data.osm.Relation;
55import org.openstreetmap.josm.data.osm.RelationMember;
56import org.openstreetmap.josm.data.osm.Way;
57import org.openstreetmap.josm.data.osm.WaySegment;
58import org.openstreetmap.josm.gui.MapFrame;
59import org.openstreetmap.josm.gui.MapView;
60import org.openstreetmap.josm.tools.GBC;
61import org.openstreetmap.josm.tools.ImageProvider;
62import org.openstreetmap.josm.tools.Pair;
63import org.openstreetmap.josm.tools.Shortcut;
64
65public class Address extends MapMode implements MouseListener, MouseMotionListener, ActionListener {
66 private static final long serialVersionUID = 1L;
67
68 // perhaps make all these tags configurable in the future
69 private String tagHighway = "highway";
70 private String tagHighwayName = "name";
71 private String tagHouseNumber = "addr:housenumber";
72 private String tagHouseStreet = "addr:street";
73 private String tagBuilding = "building";
74 private String relationAddrType = "associatedStreet";
75 private String relationAddrName = "name";
76 private String relationAddrStreetRole = "street";
77 private String relationMemberHouse = "house";
78
79 private JRadioButton plus_one = new JRadioButton("+1", false);
80 private JRadioButton plus_two = new JRadioButton("+2", true); // enable this by default
81 private JRadioButton minus_one = new JRadioButton("-1", false);
82 private JRadioButton minus_two = new JRadioButton("-2", false);
83 final JCheckBox tagPolygon = new JCheckBox(tr("on polygon"));
84
85 JDialog dialog = null;
86 JButton clearButton = null;
87 final JTextField inputNumber = new JTextField();
88 final JTextField inputStreet = new JTextField();
89 JLabel link = new JLabel();
90 private Way selectedWay;
91 private boolean shift;
92 private boolean ctrl;
93
94 public Address(MapFrame mapFrame) {
95 super(tr("Add address"), "buildings",
96 tr("Helping tool for tag address"),
97 Shortcut.registerShortcut("mapmode:cadastre-fr-buildings", tr("Mode: {0}", tr("CadastreFR - Buildings")), KeyEvent.VK_E, Shortcut.DIRECT),
98 mapFrame, getCursor());
99 }
100
101 @Override public void enterMode() {
102 super.enterMode();
103 if (dialog == null) {
104 createDialog();
105 }
106 dialog.setVisible(true);
107 Main.map.mapView.addMouseListener(this);
108 }
109
110 @Override public void exitMode() {
111 if (Main.map.mapView != null) {
112 super.exitMode();
113 Main.map.mapView.removeMouseListener(this);
114 }
115// dialog.setVisible(false);
116 // kill the window completely to fix an issue on some linux distro and
117 // full screen mode.
118 if(dialog != null)
119 {
120 dialog.dispose();
121 dialog = null;
122 }
123 }
124
125 @Override
126 public void mousePressed(MouseEvent e) {
127 if (e.getButton() != MouseEvent.BUTTON1)
128 return;
129 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
130 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
131 MapView mv = Main.map.mapView;
132 Point mousePos = e.getPoint();
133 List<Way> mouseOnExistingWays = new ArrayList<Way>();
134 List<Way> mouseOnExistingBuildingWays = new ArrayList<Way>();
135 mouseOnExistingWays = new ArrayList<Way>();
136 Node currentMouseNode = mv.getNearestNode(mousePos, OsmPrimitive.isSelectablePredicate);
137 if (currentMouseNode != null) {
138 // click on existing node
139 setNewSelection(currentMouseNode);
140 String num = currentMouseNode.get(tagHouseNumber);
141 if (num != null
142 && currentMouseNode.get(tagHouseStreet) == null
143 && findWayInRelationAddr(currentMouseNode) == null
144 && !inputStreet.getText().equals("")) {
145 // house number already present but not linked to a street
146 Collection<Command> cmds = new LinkedList<Command>();
147 addStreetNameOrRelation(currentMouseNode, cmds);
148 Command c = new SequenceCommand("Add node address", cmds);
149 Main.main.undoRedo.add(c);
150 setNewSelection(currentMouseNode);
151 } else {
152 if (num != null) {
153 try {
154 // add new address
155 Integer.parseInt(num);
156 inputNumber.setText(num);
157 applyInputNumberChange();
158 } catch (NumberFormatException en) {
159 System.out.println("Unable to parse house number \"" + num + "\"");
160 }
161 }
162 if (currentMouseNode.get(tagHouseStreet) != null) {
163 if(Main.pref.getBoolean("cadastrewms.addr.dontUseRelation", false)) {
164 inputStreet.setText(currentMouseNode.get(tagHouseStreet));
165 if (ctrl) {
166 Collection<Command> cmds = new LinkedList<Command>();
167 addAddrToPrimitive(currentMouseNode, cmds);
168 if (num == null)
169 applyInputNumberChange();
170 }
171 setSelectedWay((Way)null);
172 }
173 } else {
174 // check if the node belongs to an associatedStreet relation
175 Way wayInRelationAddr = findWayInRelationAddr(currentMouseNode);
176 if (wayInRelationAddr == null) {
177 // node exists but doesn't carry address information : add tags like a new node
178 if (ctrl) {
179 applyInputNumberChange();
180 }
181 Collection<Command> cmds = new LinkedList<Command>();
182 addAddrToPrimitive(currentMouseNode, cmds);
183 } else {
184 inputStreet.setText(wayInRelationAddr.get(tagHighwayName));
185 setSelectedWay(wayInRelationAddr);
186 }
187 }
188 }
189 } else {
190 List<WaySegment> wss = mv.getNearestWaySegments(mousePos, OsmPrimitive.isSelectablePredicate);
191 for(WaySegment ws : wss) {
192 if (ws.way.get(tagHighway) != null && ws.way.get(tagHighwayName) != null)
193 mouseOnExistingWays.add(ws.way);
194 else if (ws.way.get(tagBuilding) != null && ws.way.get(tagHouseNumber) == null)
195 mouseOnExistingBuildingWays.add(ws.way);
196 }
197 if (mouseOnExistingWays.size() == 1) {
198 // clicked on existing highway => set new street name
199 inputStreet.setText(mouseOnExistingWays.get(0).get(tagHighwayName));
200 setSelectedWay(mouseOnExistingWays.get(0));
201 inputNumber.setText("");
202 setNewSelection(mouseOnExistingWays.get(0));
203 } else if (mouseOnExistingWays.size() == 0) {
204 // clicked a non highway and not a node => add the new address
205 if (inputStreet.getText().equals("") || inputNumber.getText().equals("")) {
206 Toolkit.getDefaultToolkit().beep();
207 } else {
208 Collection<Command> cmds = new LinkedList<Command>();
209 if (ctrl) {
210 applyInputNumberChange();
211 }
212 if (tagPolygon.isSelected()) {
213 addAddrToPolygon(mouseOnExistingBuildingWays, cmds);
214 } else {
215 Node n = createNewNode(e, cmds);
216 addAddrToPrimitive(n, cmds);
217 }
218 }
219 }
220 }
221
222 }
223
224 private Way findWayInRelationAddr(Node n) {
225 List<OsmPrimitive> l = n.getReferrers();
226 for (OsmPrimitive osm : l) {
227 if (osm instanceof Relation && osm.hasKey("type") && osm.get("type").equals(relationAddrType)) {
228 for (RelationMember rm : ((Relation)osm).getMembers()) {
229 if (rm.getRole().equals(relationAddrStreetRole)) {
230 OsmPrimitive osp = rm.getMember();
231 if (osp instanceof Way && osp.hasKey(tagHighwayName)) {
232 return (Way)osp;
233 }
234 }
235 }
236 }
237 }
238 return null;
239 }
240
241 private void addAddrToPolygon(List<Way> mouseOnExistingBuildingWays, Collection<Command> cmds) {
242 for (Way w:mouseOnExistingBuildingWays) {
243 addAddrToPrimitive(w, cmds);
244 }
245 }
246
247 private void addAddrToPrimitive(OsmPrimitive osm, Collection<Command> cmds) {
248 // add the current tag addr:housenumber in node and member in relation (if so configured)
249 if (shift) {
250 try {
251 revertInputNumberChange();
252 } catch (NumberFormatException en) {
253 System.out.println("Unable to parse house number \"" + inputNumber.getText() + "\"");
254 }
255
256 }
257 cmds.add(new ChangePropertyCommand(osm, tagHouseNumber, inputNumber.getText()));
258 addStreetNameOrRelation(osm, cmds);
259 try {
260 applyInputNumberChange();
261 Command c = new SequenceCommand("Add node address", cmds);
262 Main.main.undoRedo.add(c);
263 setNewSelection(osm);
264 } catch (NumberFormatException en) {
265 System.out.println("Unable to parse house number \"" + inputNumber.getText() + "\"");
266 }
267 }
268
269 private Relation findRelationAddr(Way w) {
270 List<OsmPrimitive> l = w.getReferrers();
271 for (OsmPrimitive osm : l) {
272 if (osm instanceof Relation && osm.hasKey("type") && osm.get("type").equals(relationAddrType)) {
273 return (Relation)osm;
274 }
275 }
276 return null;
277 }
278
279 private void addStreetNameOrRelation(OsmPrimitive osm, Collection<Command> cmds) {
280 if (Main.pref.getBoolean("cadastrewms.addr.dontUseRelation", false)) {
281 cmds.add(new ChangePropertyCommand(osm, tagHouseStreet, inputStreet.getText()));
282 } else if (selectedWay != null) {
283 Relation selectedRelation = findRelationAddr(selectedWay);
284 // add the node to its relation
285 if (selectedRelation != null) {
286 RelationMember rm = new RelationMember(relationMemberHouse, osm);
287 Relation newRel = new Relation(selectedRelation);
288 newRel.addMember(rm);
289 cmds.add(new ChangeCommand(selectedRelation, newRel));
290 } else {
291 // create new relation
292 Relation newRel = new Relation();
293 newRel.put("type", relationAddrType);
294 newRel.put(relationAddrName, selectedWay.get(tagHighwayName));
295 newRel.addMember(new RelationMember(relationAddrStreetRole, selectedWay));
296 newRel.addMember(new RelationMember(relationMemberHouse, osm));
297 cmds.add(new AddCommand(newRel));
298 }
299 }
300 }
301
302 private Node createNewNode(MouseEvent e, Collection<Command> cmds) {
303 // DrawAction.mouseReleased() but without key modifiers
304 Node n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
305 cmds.add(new AddCommand(n));
306 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint(), OsmPrimitive.isSelectablePredicate);
307 Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
308 for (WaySegment ws : wss) {
309 List<Integer> is;
310 if (insertPoints.containsKey(ws.way)) {
311 is = insertPoints.get(ws.way);
312 } else {
313 is = new ArrayList<Integer>();
314 insertPoints.put(ws.way, is);
315 }
316
317 is.add(ws.lowerIndex);
318 }
319 Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>();
320 ArrayList<Way> replacedWays = new ArrayList<Way>();
321 ArrayList<Way> reuseWays = new ArrayList<Way>();
322 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
323 Way w = insertPoint.getKey();
324 List<Integer> is = insertPoint.getValue();
325 Way wnew = new Way(w);
326 pruneSuccsAndReverse(is);
327 for (int i : is) {
328 segSet.add(Pair.sort(new Pair<Node,Node>(w.getNode(i), w.getNode(i+1))));
329 }
330 for (int i : is) {
331 wnew.addNode(i + 1, n);
332 }
333 cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
334 replacedWays.add(insertPoint.getKey());
335 reuseWays.add(wnew);
336 }
337 adjustNode(segSet, n);
338
339 return n;
340 }
341
342 private static void adjustNode(Collection<Pair<Node,Node>> segs, Node n) {
343
344 switch (segs.size()) {
345 case 0:
346 return;
347 case 2:
348 // This computes the intersection between
349 // the two segments and adjusts the node position.
350 Iterator<Pair<Node,Node>> i = segs.iterator();
351 Pair<Node,Node> seg = i.next();
352 EastNorth A = seg.a.getEastNorth();
353 EastNorth B = seg.b.getEastNorth();
354 seg = i.next();
355 EastNorth C = seg.a.getEastNorth();
356 EastNorth D = seg.b.getEastNorth();
357
358 double u=det(B.east() - A.east(), B.north() - A.north(), C.east() - D.east(), C.north() - D.north());
359
360 // Check for parallel segments and do nothing if they are
361 // In practice this will probably only happen when a way has been duplicated
362
363 if (u == 0) return;
364
365 // q is a number between 0 and 1
366 // It is the point in the segment where the intersection occurs
367 // if the segment is scaled to lenght 1
368
369 double q = det(B.north() - C.north(), B.east() - C.east(), D.north() - C.north(), D.east() - C.east()) / u;
370 EastNorth intersection = new EastNorth(
371 B.east() + q * (A.east() - B.east()),
372 B.north() + q * (A.north() - B.north()));
373
374 int snapToIntersectionThreshold
375 = Main.pref.getInteger("edit.snap-intersection-threshold",10);
376
377 // only adjust to intersection if within snapToIntersectionThreshold pixel of mouse click; otherwise
378 // fall through to default action.
379 // (for semi-parallel lines, intersection might be miles away!)
380 if (Main.map.mapView.getPoint(n).distance(Main.map.mapView.getPoint(intersection)) < snapToIntersectionThreshold) {
381 n.setEastNorth(intersection);
382 return;
383 }
384
385 default:
386 EastNorth P = n.getEastNorth();
387 seg = segs.iterator().next();
388 A = seg.a.getEastNorth();
389 B = seg.b.getEastNorth();
390 double a = P.distanceSq(B);
391 double b = P.distanceSq(A);
392 double c = A.distanceSq(B);
393 q = (a - b + c) / (2*c);
394 n.setEastNorth(new EastNorth(B.east() + q * (A.east() - B.east()), B.north() + q * (A.north() - B.north())));
395 }
396 }
397
398 static double det(double a, double b, double c, double d) {
399 return a * d - b * c;
400 }
401
402 private static void pruneSuccsAndReverse(List<Integer> is) {
403 //if (is.size() < 2) return;
404
405 HashSet<Integer> is2 = new HashSet<Integer>();
406 for (int i : is) {
407 if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
408 is2.add(i);
409 }
410 }
411 is.clear();
412 is.addAll(is2);
413 Collections.sort(is);
414 Collections.reverse(is);
415 }
416
417 private static Cursor getCursor() {
418 try {
419 return ImageProvider.getCursor("crosshair", null);
420 } catch (Exception e) {
421 }
422 return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
423 }
424
425 private void applyInputNumberChange() {
426 Integer num = Integer.parseInt(inputNumber.getText());
427 if (plus_one.isSelected())
428 num = num + 1;
429 if (plus_two.isSelected())
430 num = num + 2;
431 if (minus_one.isSelected() && num > 1)
432 num = num - 1;
433 if (minus_two.isSelected() && num > 2)
434 num = num - 2;
435 inputNumber.setText(num.toString());
436 }
437
438 private void revertInputNumberChange() {
439 Integer num = Integer.parseInt(inputNumber.getText());
440 if (plus_one.isSelected())
441 num = num - 1;
442 if (plus_two.isSelected())
443 num = num - 2;
444 if (minus_one.isSelected() && num > 1)
445 num = num + 1;
446 if (minus_two.isSelected() && num > 2)
447 num = num + 2;
448 inputNumber.setText(num.toString());
449 }
450
451 private void createDialog() {
452 ImageIcon iconLink = ImageProvider.get(null, "Mf_relation.png");
453 link.setIcon(iconLink);
454 link.setEnabled(false);
455 JPanel p = new JPanel(new GridBagLayout());
456 JLabel number = new JLabel(tr("Next no"));
457 JLabel street = new JLabel(tr("Street"));
458 p.add(number, GBC.std().insets(0, 0, 0, 0));
459 p.add(inputNumber, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
460 p.add(street, GBC.std().insets(0, 0, 0, 0));
461 JPanel p2 = new JPanel(new GridBagLayout());
462 inputStreet.setEditable(false);
463 p2.add(inputStreet, GBC.std().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0));
464 p2.add(link, GBC.eol().insets(10, 0, 0, 0));
465 p.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
466 clearButton = new JButton("Clear");
467 clearButton.addActionListener(new ActionListener() {
468 public void actionPerformed(ActionEvent e) {
469 inputNumber.setText("");
470 inputStreet.setText("");
471 setSelectedWay((Way)null);
472 }
473 });
474 ButtonGroup bgIncremental = new ButtonGroup();
475 bgIncremental.add(plus_one);
476 bgIncremental.add(plus_two);
477 bgIncremental.add(minus_one);
478 bgIncremental.add(minus_two);
479 p.add(minus_one, GBC.std().insets(10, 0, 10, 0));
480// p.add(plus_one, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
481 p.add(plus_one, GBC.std().insets(0, 0, 10, 0));
482 tagPolygon.setSelected(Main.pref.getBoolean("cadastrewms.addr.onBuilding", false));
483 tagPolygon.addChangeListener(new ChangeListener() {
484 @Override
485 public void stateChanged(ChangeEvent arg0) {
486 Main.pref.put("cadastrewms.addr.onBuilding", tagPolygon.isSelected());
487 }
488 });
489 p.add(tagPolygon, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 0));
490 p.add(minus_two, GBC.std().insets(10, 0, 10, 0));
491 p.add(plus_two, GBC.std().insets(0, 0, 10, 0));
492 p.add(clearButton, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 0));
493
494 final Object[] options = {};
495 final JOptionPane pane = new JOptionPane(p,
496 JOptionPane.PLAIN_MESSAGE, JOptionPane.YES_NO_CANCEL_OPTION,
497 null, options, null);
498 dialog = pane.createDialog(Main.parent, tr("Enter addresses"));
499 dialog.setModal(false);
500 dialog.setAlwaysOnTop(true);
501 dialog.addComponentListener(new ComponentAdapter() {
502 protected void rememberGeometry() {
503 Main.pref.put("cadastrewms.addr.bounds", dialog.getX()+","+dialog.getY()+","+dialog.getWidth()+","+dialog.getHeight());
504 }
505 @Override public void componentMoved(ComponentEvent e) {
506 rememberGeometry();
507 }
508 @Override public void componentResized(ComponentEvent e) {
509 rememberGeometry();
510 }
511 });
512 dialog.addWindowListener(new WindowListener() {
513 @Override
514 public void windowClosing(WindowEvent arg0) {
515 exitMode();
516 Main.map.selectMapMode((MapMode)Main.map.getDefaultButtonAction());
517 }
518 public void windowClosed(WindowEvent e) {}
519 public void windowActivated(WindowEvent arg0) {}
520 public void windowDeactivated(WindowEvent arg0) {}
521 public void windowDeiconified(WindowEvent arg0) {}
522 public void windowIconified(WindowEvent arg0) {}
523 public void windowOpened(WindowEvent arg0) {}
524 });
525 String bounds = Main.pref.get("cadastrewms.addr.bounds",null);
526 if (bounds != null) {
527 String[] b = bounds.split(",");
528 dialog.setBounds(new Rectangle(
529 Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3])));
530 }
531}
532
533 private void setSelectedWay(Way w) {
534 this.selectedWay = w;
535 if (w == null) {
536 link.setEnabled(false);
537 } else
538 link.setEnabled(true);
539 link.repaint();
540 }
541
542 private void setNewSelection(OsmPrimitive osm) {
543 Collection<OsmPrimitive> newSelection = new LinkedList<OsmPrimitive>(Main.main.getCurrentDataSet().getSelected());
544 newSelection.clear();
545 newSelection.add(osm);
546 getCurrentDataSet().setSelected(osm);
547 }
548
549}
Note: See TracBrowser for help on using the repository browser.