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

Last change on this file since 2711 was 2711, checked in by stoecker, 14 years ago

fix bad line endings

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