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

Last change on this file since 2622 was 2622, checked in by jttt, 15 years ago

Dataset changed events refactored - DatasetEventManager now allows to register listeners to current dataset (no need to reregister when activeLayer is changed, events from current dataset are always returned), possibility to automatically receive events in EDT

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