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

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

Encalupse OsmPrimitive.incomplete

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