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

Last change on this file since 30737 was 30737, checked in by donvip, 10 years ago

[josm_plugins] fix Java 7 / unused code warnings

File size: 23.0 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<>();
134 List<Way> mouseOnExistingBuildingWays = new ArrayList<>();
135 mouseOnExistingWays = new ArrayList<>();
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<>();
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<>();
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<>();
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<>();
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<>();
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<>();
314 insertPoints.put(ws.way, is);
315 }
316
317 is.add(ws.lowerIndex);
318 }
319 Set<Pair<Node,Node>> segSet = new HashSet<>();
320 ArrayList<Way> replacedWays = new ArrayList<>();
321 ArrayList<Way> reuseWays = new ArrayList<>();
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<>(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<>();
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<>(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.