source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java@ 2985

Last change on this file since 2985 was 2985, checked in by jttt, 14 years ago

Make DataChangedListener deprecated (replaced by DatasetListener), remove remaining listeners

  • Property svn:eol-style set to native
File size: 34.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation;
3
4import static org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction.BACKWARD;
5import static org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction.FORWARD;
6import static org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction.NONE;
7import static org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction.ROUNDABOUT_LEFT;
8import static org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction.ROUNDABOUT_RIGHT;
9
10import java.util.ArrayList;
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.Iterator;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Set;
19import java.util.concurrent.CopyOnWriteArrayList;
20
21import javax.swing.DefaultListSelectionModel;
22import javax.swing.ListSelectionModel;
23import javax.swing.event.TableModelEvent;
24import javax.swing.event.TableModelListener;
25import javax.swing.table.AbstractTableModel;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.data.SelectionChangedListener;
29import org.openstreetmap.josm.data.coor.EastNorth;
30import org.openstreetmap.josm.data.osm.Node;
31import org.openstreetmap.josm.data.osm.OsmPrimitive;
32import org.openstreetmap.josm.data.osm.Relation;
33import org.openstreetmap.josm.data.osm.RelationMember;
34import org.openstreetmap.josm.data.osm.Way;
35import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
36import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
37import org.openstreetmap.josm.data.osm.event.DataSetListener;
38import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
39import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
40import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
41import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
42import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
43import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
44import org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction;
45import org.openstreetmap.josm.gui.layer.OsmDataLayer;
46
47public class MemberTableModel extends AbstractTableModel implements TableModelListener, SelectionChangedListener, DataSetListener {
48
49 /**
50 * data of the table model: The list of members and the cached WayConnectionType of each member.
51 **/
52 private ArrayList<RelationMember> members;
53 private ArrayList<WayConnectionType> connectionType = null;
54
55 private DefaultListSelectionModel listSelectionModel;
56 private CopyOnWriteArrayList<IMemberModelListener> listeners;
57 private OsmDataLayer layer;
58
59 /**
60 * constructor
61 */
62 public MemberTableModel(OsmDataLayer layer) {
63 members = new ArrayList<RelationMember>();
64 listeners = new CopyOnWriteArrayList<IMemberModelListener>();
65 this.layer = layer;
66 addTableModelListener(this);
67 }
68
69 public OsmDataLayer getLayer() {
70 return layer;
71 }
72
73 /* --------------------------------------------------------------------------- */
74 /* Interface SelectionChangedListener */
75 /* --------------------------------------------------------------------------- */
76 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
77 if (Main.main.getEditLayer() != this.layer) return;
78 // just trigger a repaint
79 Collection<RelationMember> sel = getSelectedMembers();
80 fireTableDataChanged();
81 setSelectedMembers(sel);
82 }
83
84 /* --------------------------------------------------------------------------- */
85 /* Interface DataSetListener */
86 /* --------------------------------------------------------------------------- */
87 public void dataChanged(DataChangedEvent event) {
88 // just trigger a repaint - the display name of the relation members may
89 // have changed
90 Collection<RelationMember> sel = getSelectedMembers();
91 fireTableDataChanged();
92 setSelectedMembers(sel);
93 }
94
95 public void nodeMoved(NodeMovedEvent event) {/* ignore */}
96 public void primtivesAdded(PrimitivesAddedEvent event) {/* ignore */}
97
98 public void primtivesRemoved(PrimitivesRemovedEvent event) {
99 // ignore - the relation in the editor might become out of sync with the relation
100 // in the dataset. We will deal with it when the relation editor is closed or
101 // when the changes in the editor are applied.
102 }
103
104 public void relationMembersChanged(RelationMembersChangedEvent event) {
105 // ignore - the relation in the editor might become out of sync with the relation
106 // in the dataset. We will deal with it when the relation editor is closed or
107 // when the changes in the editor are applied.
108 }
109
110 public void tagsChanged(TagsChangedEvent event) {
111 // just refresh the respective table cells
112 //
113 Collection<RelationMember> sel = getSelectedMembers();
114 for (int i=0; i < members.size();i++) {
115 if (members.get(i).getMember() == event.getPrimitive()) {
116 fireTableCellUpdated(i, 1 /* the column with the primitive name */);
117 }
118 }
119 setSelectedMembers(sel);
120 }
121
122 public void wayNodesChanged(WayNodesChangedEvent event) {/* ignore */}
123
124 public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignore */}
125 /* --------------------------------------------------------------------------- */
126
127 public void addMemberModelListener(IMemberModelListener listener) {
128 if (listener != null) {
129 listeners.addIfAbsent(listener);
130 }
131 }
132
133 public void removeMemberModelListener(IMemberModelListener listener) {
134 listeners.remove(listener);
135 }
136
137 protected void fireMakeMemberVisible(int index) {
138 for (IMemberModelListener listener : listeners) {
139 listener.makeMemberVisible(index);
140 }
141 }
142
143 public void populate(Relation relation) {
144 members.clear();
145 if (relation != null) {
146 // make sure we work with clones of the relation members
147 // in the model.
148 members.addAll(new Relation(relation).getMembers());
149 }
150 fireTableDataChanged();
151 }
152
153 public int getColumnCount() {
154 return 3;
155 }
156
157 public int getRowCount() {
158 return members.size();
159 }
160
161 public Object getValueAt(int rowIndex, int columnIndex) {
162 switch (columnIndex) {
163 case 0:
164 return members.get(rowIndex).getRole();
165 case 1:
166 return members.get(rowIndex).getMember();
167 case 2:
168 return wayConnection(rowIndex);
169 }
170 // should not happen
171 return null;
172 }
173
174 @Override
175 public boolean isCellEditable(int rowIndex, int columnIndex) {
176 return columnIndex == 0;
177 }
178
179 @Override
180 public void setValueAt(Object value, int rowIndex, int columnIndex) {
181 RelationMember member = members.get(rowIndex);
182 RelationMember newMember = new RelationMember(value.toString(), member.getMember());
183 members.remove(rowIndex);
184 members.add(rowIndex, newMember);
185 }
186
187 public OsmPrimitive getReferredPrimitive(int idx) {
188 return members.get(idx).getMember();
189 }
190
191 public void moveUp(int[] selectedRows) {
192 if (!canMoveUp(selectedRows))
193 return;
194
195 for (int row : selectedRows) {
196 RelationMember member1 = members.get(row);
197 RelationMember member2 = members.get(row - 1);
198 members.set(row, member2);
199 members.set(row - 1, member1);
200 }
201 fireTableDataChanged();
202 getSelectionModel().setValueIsAdjusting(true);
203 getSelectionModel().clearSelection();
204 for (int row : selectedRows) {
205 row--;
206 getSelectionModel().addSelectionInterval(row, row);
207 }
208 getSelectionModel().setValueIsAdjusting(false);
209 fireMakeMemberVisible(selectedRows[0] - 1);
210 }
211
212 public void moveDown(int[] selectedRows) {
213 if (!canMoveDown(selectedRows))
214 return;
215
216 for (int i = selectedRows.length - 1; i >= 0; i--) {
217 int row = selectedRows[i];
218 RelationMember member1 = members.get(row);
219 RelationMember member2 = members.get(row + 1);
220 members.set(row, member2);
221 members.set(row + 1, member1);
222 }
223 fireTableDataChanged();
224 getSelectionModel();
225 getSelectionModel().setValueIsAdjusting(true);
226 getSelectionModel().clearSelection();
227 for (int row : selectedRows) {
228 row++;
229 getSelectionModel().addSelectionInterval(row, row);
230 }
231 getSelectionModel().setValueIsAdjusting(false);
232 fireMakeMemberVisible(selectedRows[0] + 1);
233 }
234
235 public void remove(int[] selectedRows) {
236 if (!canRemove(selectedRows))
237 return;
238 int offset = 0;
239 for (int row : selectedRows) {
240 row -= offset;
241 members.remove(row);
242 offset++;
243 }
244 fireTableDataChanged();
245 }
246
247 public boolean canMoveUp(int[] rows) {
248 if (rows == null || rows.length == 0)
249 return false;
250 Arrays.sort(rows);
251 return rows[0] > 0 && members.size() > 0;
252 }
253
254 public boolean canMoveDown(int[] rows) {
255 if (rows == null || rows.length == 0)
256 return false;
257 Arrays.sort(rows);
258 return members.size() > 0 && rows[rows.length - 1] < members.size() - 1;
259 }
260
261 public boolean canRemove(int[] rows) {
262 if (rows == null || rows.length == 0)
263 return false;
264 return true;
265 }
266
267 public DefaultListSelectionModel getSelectionModel() {
268 if (listSelectionModel == null) {
269 listSelectionModel = new DefaultListSelectionModel();
270 listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
271 }
272 return listSelectionModel;
273 }
274
275 public void removeMembersReferringTo(List<? extends OsmPrimitive> primitives) {
276 if (primitives == null)
277 return;
278 Iterator<RelationMember> it = members.iterator();
279 while (it.hasNext()) {
280 RelationMember member = it.next();
281 if (primitives.contains(member.getMember())) {
282 it.remove();
283 }
284 }
285 fireTableDataChanged();
286 }
287
288 public void applyToRelation(Relation relation) {
289 relation.setMembers(members);
290 }
291
292 public boolean hasSameMembersAs(Relation relation) {
293 if (relation == null)
294 return false;
295 if (relation.getMembersCount() != members.size())
296 return false;
297 for (int i = 0; i < relation.getMembersCount(); i++) {
298 if (!relation.getMember(i).equals(members.get(i)))
299 return false;
300 }
301 return true;
302 }
303
304 /**
305 * Replies the set of incomplete primitives
306 *
307 * @return the set of incomplete primitives
308 */
309 public Set<OsmPrimitive> getIncompleteMemberPrimitives() {
310 Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
311 for (RelationMember member : members) {
312 if (member.getMember().isIncomplete()) {
313 ret.add(member.getMember());
314 }
315 }
316 return ret;
317 }
318
319 /**
320 * Replies the set of selected incomplete primitives
321 *
322 * @return the set of selected incomplete primitives
323 */
324 public Set<OsmPrimitive> getSelectedIncompleteMemberPrimitives() {
325 Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
326 for (RelationMember member : getSelectedMembers()) {
327 if (member.getMember().isIncomplete()) {
328 ret.add(member.getMember());
329 }
330 }
331 return ret;
332 }
333
334 /**
335 * Replies true if at least one the relation members is incomplete
336 *
337 * @return true if at least one the relation members is incomplete
338 */
339 public boolean hasIncompleteMembers() {
340 for (RelationMember member : members) {
341 if (member.getMember().isIncomplete())
342 return true;
343 }
344 return false;
345 }
346
347 /**
348 * Replies true if at least one of the selected members is incomplete
349 *
350 * @return true if at least one of the selected members is incomplete
351 */
352 public boolean hasIncompleteSelectedMembers() {
353 for (RelationMember member : getSelectedMembers()) {
354 if (member.getMember().isIncomplete())
355 return true;
356 }
357 return false;
358 }
359
360 protected List<Integer> getSelectedIndices() {
361 ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
362 for (int i = 0; i < members.size(); i++) {
363 if (getSelectionModel().isSelectedIndex(i)) {
364 selectedIndices.add(i);
365 }
366 }
367 return selectedIndices;
368 }
369
370 public void addMembersAtBeginning(List<OsmPrimitive> primitives) {
371 if (primitives == null)
372 return;
373 for (OsmPrimitive primitive : primitives) {
374 RelationMember member = new RelationMember("", primitive);
375 members.add(0, member);
376 }
377 fireTableDataChanged();
378 getSelectionModel().clearSelection();
379 for (int i = 0; i < primitives.size(); i++) {
380 getSelectionModel().addSelectionInterval(i, i);
381 }
382 fireMakeMemberVisible(0);
383 }
384
385 public void addMembersAtEnd(List<? extends OsmPrimitive> primitives) {
386 if (primitives == null)
387 return;
388
389 for (OsmPrimitive primitive : primitives) {
390 RelationMember member = new RelationMember("", primitive);
391 members.add(member);
392 }
393 fireTableDataChanged();
394 getSelectionModel().clearSelection();
395 for (int i = 0; i < primitives.size(); i++) {
396 getSelectionModel().addSelectionInterval(members.size() - 1 - i, members.size() - 1 - i);
397 }
398 fireMakeMemberVisible(members.size() - 1);
399 }
400
401 public void addMembersBeforeIdx(List<? extends OsmPrimitive> primitives, int idx) {
402 if (primitives == null)
403 return;
404
405 for (OsmPrimitive primitive : primitives) {
406 RelationMember member = new RelationMember("", primitive);
407 members.add(idx, member);
408 }
409 fireTableDataChanged();
410 getSelectionModel().setValueIsAdjusting(true);
411 getSelectionModel().clearSelection();
412 for (int i = 0; i < primitives.size(); i++) {
413 getSelectionModel().addSelectionInterval(idx + i, idx + i);
414 }
415 getSelectionModel().setValueIsAdjusting(false);
416 fireMakeMemberVisible(idx);
417 }
418
419 public void addMembersAfterIdx(List<? extends OsmPrimitive> primitives, int idx) {
420 if (primitives == null)
421 return;
422 int j = 1;
423 for (OsmPrimitive primitive : primitives) {
424 RelationMember member = new RelationMember("", primitive);
425 members.add(idx + j, member);
426 j++;
427 }
428 fireTableDataChanged();
429 getSelectionModel().setValueIsAdjusting(true);
430 getSelectionModel().clearSelection();
431 for (int i = 0; i < primitives.size(); i++) {
432 getSelectionModel().addSelectionInterval(idx + 1 + i, idx + 1 + i);
433 }
434 getSelectionModel().setValueIsAdjusting(false);
435 fireMakeMemberVisible(idx + 1);
436 }
437
438 /**
439 * Replies the number of members which refer to a particular primitive
440 *
441 * @param primitive the primitive
442 * @return the number of members which refer to a particular primitive
443 */
444 public int getNumMembersWithPrimitive(OsmPrimitive primitive) {
445 int count = 0;
446 for (RelationMember member : members) {
447 if (member.getMember().equals(primitive)) {
448 count++;
449 }
450 }
451 return count;
452 }
453
454 /**
455 * updates the role of the members given by the indices in <code>idx</code>
456 *
457 * @param idx the array of indices
458 * @param role the new role
459 */
460 public void updateRole(int[] idx, String role) {
461 if (idx == null || idx.length == 0)
462 return;
463 for (int row : idx) {
464 RelationMember oldMember = members.get(row);
465 RelationMember newMember = new RelationMember(role, oldMember.getMember());
466 members.remove(row);
467 members.add(row, newMember);
468 }
469 fireTableDataChanged();
470 for (int row : idx) {
471 getSelectionModel().addSelectionInterval(row, row);
472 }
473 }
474
475 /**
476 * Get the currently selected relation members
477 *
478 * @return a collection with the currently selected relation members
479 */
480 public Collection<RelationMember> getSelectedMembers() {
481 ArrayList<RelationMember> selectedMembers = new ArrayList<RelationMember>();
482 for (int i : getSelectedIndices()) {
483 selectedMembers.add(members.get(i));
484 }
485 return selectedMembers;
486 }
487
488 /**
489 * Replies the set of selected referers. Never null, but may be empty.
490 *
491 * @return the set of selected referers
492 */
493 public Set<OsmPrimitive> getSelectedChildPrimitives() {
494 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
495 for (RelationMember m: getSelectedMembers()) {
496 ret.add(m.getMember());
497 }
498 return ret;
499 }
500
501 /**
502 * Replies the set of selected referers. Never null, but may be empty.
503 *
504 * @return the set of selected referers
505 */
506 public Set<OsmPrimitive> getChildPrimitives(Collection<? extends OsmPrimitive> referenceSet) {
507 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
508 if (referenceSet == null) return null;
509 for (RelationMember m: members) {
510 if (referenceSet.contains(m.getMember())) {
511 ret.add(m.getMember());
512 }
513 }
514 return ret;
515 }
516
517 /**
518 * Selects the members in the collection selectedMembers
519 *
520 * @param selectedMembers the collection of selected members
521 */
522 public void setSelectedMembers(Collection<RelationMember> selectedMembers) {
523 if (selectedMembers == null || selectedMembers.isEmpty()) {
524 getSelectionModel().clearSelection();
525 return;
526 }
527
528 // lookup the indices for the respective members
529 //
530 Set<Integer> selectedIndices = new HashSet<Integer>();
531 for (RelationMember member : selectedMembers) {
532 int idx = members.indexOf(member);
533 if ( idx >= 0) {
534 selectedIndices.add(idx);
535 }
536 }
537 setSelectedMembersIdx(selectedIndices);
538 }
539
540 /**
541 * Selects the members in the collection selectedIndices
542 *
543 * @param selectedIndices the collection of selected member indices
544 */
545 public void setSelectedMembersIdx(Collection<Integer> selectedIndices) {
546 if (selectedIndices == null || selectedIndices.isEmpty()) {
547 getSelectionModel().clearSelection();
548 return;
549 }
550 // select the members
551 //
552 getSelectionModel().setValueIsAdjusting(true);
553 getSelectionModel().clearSelection();
554 for (int row : selectedIndices) {
555 getSelectionModel().addSelectionInterval(row, row);
556 }
557 getSelectionModel().setValueIsAdjusting(false);
558 // make the first selected member visible
559 //
560 if (selectedIndices.size() > 0) {
561 fireMakeMemberVisible(Collections.min(selectedIndices));
562 }
563 }
564
565 /**
566 * Replies true if the index-th relation members referrs
567 * to an editable relation, i.e. a relation which is not
568 * incomplete.
569 *
570 * @param index the index
571 * @return true, if the index-th relation members referrs
572 * to an editable relation, i.e. a relation which is not
573 * incomplete
574 */
575 public boolean isEditableRelation(int index) {
576 if (index < 0 || index >= members.size())
577 return false;
578 RelationMember member = members.get(index);
579 if (!member.isRelation())
580 return false;
581 Relation r = member.getRelation();
582 return !r.isIncomplete();
583 }
584
585 /**
586 * Replies true if there is at least one relation member in this model
587 * which refers to at least on the primitives in <code>primitives</code>.
588 *
589 * @param primitives the collection of primitives
590 * @return true if there is at least one relation member in this model
591 * which refers to at least on the primitives in <code>primitives</code>; false
592 * otherwise
593 */
594 public boolean hasMembersReferringTo(Collection<OsmPrimitive> primitives) {
595 if (primitives == null || primitives.isEmpty())
596 return false;
597 HashSet<OsmPrimitive> referrers = new HashSet<OsmPrimitive>();
598 for(RelationMember member : members) {
599 referrers.add(member.getMember());
600 }
601 Iterator<OsmPrimitive> it = primitives.iterator();
602 while(it.hasNext()) {
603 OsmPrimitive referred = it.next();
604 if (referrers.contains(referred))
605 return true;
606 }
607 return false;
608 }
609
610 /**
611 * Selects all mebers which refer to {@see OsmPrimitive}s in the collections
612 * <code>primitmives</code>. Does nothing is primitives is null.
613 *
614 * @param primitives the collection of primitives
615 */
616 public void selectMembersReferringTo(Collection<? extends OsmPrimitive> primitives) {
617 if (primitives == null) return;
618 getSelectionModel().setValueIsAdjusting(true);
619 getSelectionModel().clearSelection();
620 for (int i=0; i< members.size();i++) {
621 RelationMember m = members.get(i);
622 if (primitives.contains(m.getMember())) {
623 this.getSelectionModel().addSelectionInterval(i,i);
624 }
625 }
626 getSelectionModel().setValueIsAdjusting(false);
627 if (getSelectedIndices().size() > 0) {
628 fireMakeMemberVisible(getSelectedIndices().get(0));
629 }
630 }
631
632 /**
633 * Replies true if <code>primitive</code> is currently selected in the layer this
634 * model is attached to
635 *
636 * @param primitive the primitive
637 * @return true if <code>primitive</code> is currently selected in the layer this
638 * model is attached to, false otherwise
639 */
640 public boolean isInJosmSelection(OsmPrimitive primitive) {
641 return layer.data.isSelected(primitive);
642 }
643
644 /**
645 * Replies true if the layer this model belongs to is equal to the active
646 * layer
647 *
648 * @return true if the layer this model belongs to is equal to the active
649 * layer
650 */
651 protected boolean isActiveLayer() {
652 if (Main.map == null || Main.map.mapView == null) return false;
653 return Main.map.mapView.getActiveLayer() == layer;
654 }
655
656 /**
657 * get a node we can link against when sorting members
658 * @param element the element we want to link against
659 * @param linked_element already linked against element
660 * @return the unlinked node if element is a way, the node itself if element is a node, null otherwise
661 */
662 private static Node getUnusedNode(RelationMember element, RelationMember linked_element)
663 {
664 Node result = null;
665
666 if (element.isWay()) {
667 Way w = element.getWay();
668 if (linked_element.isWay()) {
669 Way x = linked_element.getWay();
670 if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode())) {
671 result = w.lastNode();
672 } else {
673 result = w.firstNode();
674 }
675 } else if (linked_element.isNode()) {
676 Node m = linked_element.getNode();
677 if (w.firstNode() == m) {
678 result = w.lastNode();
679 } else {
680 result = w.firstNode();
681 }
682 }
683 } else if (element.isNode()) {
684 Node n = element.getNode();
685 result = n;
686 }
687
688 return result;
689 }
690
691 /*
692 * Sort a collection of relation members by the way they are linked.
693 *
694 * @param relationMembers collection of relation members
695 * @return sorted collection of relation members
696 */
697 private ArrayList<RelationMember> sortMembers(ArrayList<RelationMember> relationMembers) {
698 RelationNodeMap map = new RelationNodeMap(relationMembers);
699 // List of groups of linked members
700 //
701 ArrayList<LinkedList<Integer>> allGroups = new ArrayList<LinkedList<Integer>>();
702
703 // current group of members that are linked among each other
704 // Two successive members are always linked i.e. have a common node.
705 //
706 LinkedList<Integer> group;
707
708 Integer first;
709 while ((first = map.pop()) != null) {
710 group = new LinkedList<Integer>();
711 group.add(first);
712
713 allGroups.add(group);
714
715 Integer next = first;
716 while ((next = map.popAdjacent(next)) != null) {
717 group.addLast(next);
718 }
719
720 // The first element need not be in front of the list.
721 // So the search goes in both directions
722 //
723 next = first;
724 while ((next = map.popAdjacent(next)) != null) {
725 group.addFirst(next);
726 }
727 }
728
729 group = new LinkedList<Integer>();
730 group.addAll(map.getNotSortableMembers());
731 allGroups.add(group);
732
733 ArrayList<RelationMember> newMembers = new ArrayList<RelationMember>();
734 for (LinkedList<Integer> tmpGroup : allGroups) {
735 for (Integer p : tmpGroup) {
736 newMembers.add(relationMembers.get(p));
737 }
738 }
739 return newMembers;
740 }
741
742 /**
743 * Sort the selected relation members by the way they are linked.
744 */
745 void sort() {
746 ArrayList<RelationMember> selectedMembers = new ArrayList<RelationMember>(getSelectedMembers());
747 ArrayList<RelationMember> sortedMembers = null;
748 ArrayList<RelationMember> newMembers;
749 if (selectedMembers.size() <= 1) {
750 newMembers = sortMembers(members);
751 sortedMembers = newMembers;
752 } else {
753 sortedMembers = sortMembers(selectedMembers);
754 List<Integer> selectedIndices = getSelectedIndices();
755 newMembers = new ArrayList<RelationMember>();
756 boolean inserted = false;
757 for (int i=0; i < members.size(); i++) {
758 if (selectedIndices.contains(i)) {
759 if (!inserted) {
760 newMembers.addAll(sortedMembers);
761 inserted = true;
762 }
763 } else {
764 newMembers.add(members.get(i));
765 }
766 }
767 }
768
769 if (members.size() != newMembers.size()) throw new AssertionError();
770
771 members.clear();
772 members.addAll(newMembers);
773 fireTableDataChanged();
774 setSelectedMembers(sortedMembers);
775 }
776
777 /**
778 * Determines the direction of way k with respect to the way ref_i.
779 * The way ref_i is assumed to have the direction ref_direction and
780 * to be the predecessor of k.
781 *
782 * If both ways are not linked in any way, NONE is returned.
783 *
784 * Else the direction is given as follows:
785 * Let the relation be a route of oneway streets, and someone travels them in the given order.
786 * Direction is FORWARD if it is legel and BACKWARD if it is illegal to do so for the given way.
787 *
788 **/
789 private Direction determineDirection(int ref_i,Direction ref_direction, int k) {
790 if (ref_i < 0 || k < 0 || ref_i >= members.size() || k >= members.size())
791 return NONE;
792 if (ref_direction == NONE)
793 return NONE;
794
795 RelationMember m_ref = members.get(ref_i);
796 RelationMember m = members.get(k);
797 Way way_ref = null;
798 Way way = null;
799
800 if (m_ref.isWay()) {
801 way_ref = m_ref.getWay();
802 }
803 if (m.isWay()) {
804 way = m.getWay();
805 }
806
807 if (way_ref == null || way == null)
808 return NONE;
809
810 /** the list of nodes the way k can dock to */
811 List<Node> refNodes= new ArrayList<Node>();
812
813 switch (ref_direction) {
814 case FORWARD:
815 refNodes.add(way_ref.lastNode());
816 break;
817 case BACKWARD:
818 refNodes.add(way_ref.firstNode());
819 break;
820 case ROUNDABOUT_LEFT:
821 case ROUNDABOUT_RIGHT:
822 refNodes = way_ref.getNodes();
823 break;
824 }
825
826 if (refNodes == null)
827 return NONE;
828
829 for (Node n : refNodes) {
830 if (n == null) {
831 continue;
832 }
833 if (roundaboutType(k) != NONE) {
834 for (Node nn : way.getNodes()) {
835 if (n == nn)
836 return roundaboutType(k);
837 }
838 } else {
839 if (n == way.firstNode())
840 return FORWARD;
841 if (n == way.lastNode())
842 return BACKWARD;
843 }
844 }
845 return NONE;
846 }
847
848 /**
849 * determine, if the way i is a roundabout and if yes, what type of roundabout
850 */
851 private Direction roundaboutType(int i) {
852 RelationMember m = members.get(i);
853 if (m == null || !m.isWay()) return NONE;
854 Way w = m.getWay();
855 return roundaboutType(w);
856 }
857 static Direction roundaboutType(Way w) {
858 if (w != null &&
859 "roundabout".equals(w.get("junction")) &&
860 w.getNodesCount() < 200 &&
861 w.getNodesCount() > 2 &&
862 w.getNode(0) != null &&
863 w.getNode(1) != null &&
864 w.getNode(2) != null &&
865 w.firstNode() == w.lastNode()) {
866 /** do some simple determinant / cross pruduct test on the first 3 nodes
867 to see, if the roundabout goes clock wise or ccw */
868 EastNorth en1 = w.getNode(0).getEastNorth();
869 EastNorth en2 = w.getNode(1).getEastNorth();
870 EastNorth en3 = w.getNode(2).getEastNorth();
871 en1 = en1.sub(en2);
872 en2 = en2.sub(en3);
873 return en1.north() * en2.east() - en2.north() * en1.east() > 0 ? ROUNDABOUT_LEFT : ROUNDABOUT_RIGHT;
874 } else
875 return NONE;
876 }
877
878 private WayConnectionType wayConnection(int i) {
879 if (connectionType == null) {
880 updateLinks();
881 }
882 return connectionType.get(i);
883 }
884
885 public void tableChanged(TableModelEvent e) {
886 connectionType = null;
887 }
888
889 /**
890 * Reverse the relation members.
891 */
892 void reverse() {
893 List<Integer> selectedIndices = getSelectedIndices();
894 List<Integer> selectedIndicesReversed = getSelectedIndices();
895
896 if (selectedIndices.size() <= 1) {
897 Collections.reverse(members);
898 fireTableDataChanged();
899 setSelectedMembers(members);
900 } else {
901 Collections.reverse(selectedIndicesReversed);
902
903 ArrayList<RelationMember> newMembers = new ArrayList<RelationMember>(members);
904
905 for (int i=0; i < selectedIndices.size(); i++) {
906 newMembers.set(selectedIndices.get(i), members.get(selectedIndicesReversed.get(i)));
907 }
908
909 if (members.size() != newMembers.size()) throw new AssertionError();
910 members.clear();
911 members.addAll(newMembers);
912 fireTableDataChanged();
913 setSelectedMembersIdx(selectedIndices);
914 }
915 }
916
917 /**
918 * refresh the cache of member WayConnectionTypes
919 */
920 public void updateLinks() {
921 connectionType = null;
922 ArrayList<WayConnectionType> con = new ArrayList<WayConnectionType>();
923
924 for (int i=0; i<members.size(); ++i) {
925 con.add(null);
926 }
927
928 int firstGroupIdx=0;
929 boolean resetFirstGoupIdx=false;
930
931 for (int i=0; i<members.size(); ++i) {
932 if (resetFirstGoupIdx) {
933 firstGroupIdx = i;
934 resetFirstGoupIdx = false;
935 }
936
937 RelationMember m = members.get(i);
938 if (! m.isWay()) {
939 con.set(i, new WayConnectionType());
940 resetFirstGoupIdx = true;
941 continue;
942 }
943
944 Way w = m.getWay();
945 if (w == null || w.isIncomplete()) {
946 con.set(i, new WayConnectionType());
947 resetFirstGoupIdx = true;
948 continue;
949 }
950
951 boolean linkPrev = (i != firstGroupIdx);
952 boolean linkNext;
953 Direction dir;
954 if (linkPrev) {
955 dir = determineDirection(i-1, con.get(i-1).direction, i);
956 linkNext = (determineDirection(i, dir, i+1) != NONE);
957 }
958 else {
959 if (roundaboutType(i) != NONE) {
960 dir = determineDirection(i, roundaboutType(i), i+1) != NONE ? roundaboutType(i) : NONE;
961 } else { /** guess the direction and see if it fits with the next member */
962 dir = determineDirection(i, FORWARD, i+1) != NONE ? FORWARD : NONE;
963 if (dir == NONE) {
964 dir = determineDirection(i, BACKWARD, i+1) != NONE ? BACKWARD : NONE;
965 }
966 }
967 linkNext = (dir != NONE);
968 if (dir == NONE) {
969 if (roundaboutType(i) != NONE) {
970 dir = roundaboutType(i);
971 }
972 }
973
974 }
975
976 con.set(i, new WayConnectionType(linkPrev, linkNext, dir));
977
978 if (! linkNext) {
979 boolean loop;
980 if (i == firstGroupIdx) {
981 loop = determineDirection(i, FORWARD, i) == FORWARD;
982 } else {
983 loop = determineDirection(i, dir, firstGroupIdx) == con.get(firstGroupIdx).direction;
984 }
985 if (loop) {
986 for (int j=firstGroupIdx; j <= i; ++j) {
987 con.get(j).isLoop = true;
988 }
989 }
990 resetFirstGoupIdx = true;
991 }
992 }
993 connectionType = con;
994 // for (int i=0; i<con.size(); ++i) {
995 // System.err.println(con.get(i));
996 // }
997 }
998}
Note: See TracBrowser for help on using the repository browser.