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

Last change on this file since 32060 was 32060, checked in by donvip, 9 years ago

remove tabs

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