Ticket #3771: relation-dialog_loops.patch
File relation-dialog_loops.patch, 19.6 KB (added by , 14 years ago) |
---|
-
src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
14 14 15 15 import javax.swing.DefaultListSelectionModel; 16 16 import javax.swing.ListSelectionModel; 17 import javax.swing.table.AbstractTableModel; 18 17 import javax.swing.table.AbstractTableModel; 18 import javax.swing.event.TableModelListener; 19 import javax.swing.event.TableModelEvent; 20 19 21 import org.openstreetmap.josm.Main; 20 22 import org.openstreetmap.josm.data.osm.Node; 21 23 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 24 26 import org.openstreetmap.josm.data.osm.Way; 25 27 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 26 28 27 public class MemberTableModel extends AbstractTableModel { 28 29 private ArrayList<RelationMember> members; 29 public class MemberTableModel extends AbstractTableModel implements TableModelListener { 30 31 /** 32 * data of the table model: The list of members and the cached WayConnectionType of each member. 33 **/ 34 private ArrayList<RelationMember> members; 35 private ArrayList<WayConnectionType> connectionType = null; 36 30 37 private DefaultListSelectionModel listSelectionModel; 31 38 private CopyOnWriteArrayList<IMemberModelListener> listeners; 32 39 private OsmDataLayer layer; … … 37 44 public MemberTableModel(OsmDataLayer layer) { 38 45 members = new ArrayList<RelationMember>(); 39 46 listeners = new CopyOnWriteArrayList<IMemberModelListener>(); 40 this.layer = layer; 47 this.layer = layer; 48 addTableModelListener(this); 41 49 } 42 50 43 51 public void addMemberModelListener(IMemberModelListener listener) { … … 100 108 return columnIndex == 0; 101 109 } 102 110 103 @Override104 public void setValueAt(Object value, int rowIndex, int columnIndex) {105 RelationMember member = members.get(rowIndex);106 RelationMember newMember = new RelationMember(value.toString(), member.getMember());107 members.remove(rowIndex);108 members.add(rowIndex, newMember);109 }110 111 111 public OsmPrimitive getReferredPrimitive(int idx) { 112 112 return members.get(idx).getMember(); 113 113 } … … 658 658 fireTableDataChanged(); 659 659 } 660 660 } 661 662 /** 663 * Determines the direction of way k with reference to the way ref_i. 664 * The direction of way ref_i is ref_direction. 665 * 666 * ref_i is usually the predecessor of k. 667 * 668 * direction: 669 * Let the relation be a route of oneway streets, and someone travels them in the given order. 670 * Direction is 1 for if it is legel and -1 if it is illegal to do so for the given way. 671 * 672 * If the two ways are not properly linked the return value is 0. 673 **/ 674 private int determineDirection(int ref_i,int ref_direction, int k) { 675 if (ref_i < 0 || k < 0 || ref_i >= members.size() || k >= members.size()) { 676 return 0; 677 } 678 if (ref_direction == 0) { 679 return 0; 680 } 681 682 RelationMember m_ref = members.get(ref_i); 683 RelationMember m = members.get(k); 684 Way way_ref = null; 685 Way way = null; 661 686 662 // simple version of code that was removed from GenericReleationEditor 663 // no recursion and no forward/backward support 664 // TODO: add back the number of linked elements 665 // Returns +1 if member i and (i+1) are ways and could be combined without changing 666 // the direction of one of them. If they are linked "head to head" or "tail to tail" 667 // -1 is returned. 668 // In all other cases the result is null. 669 private Integer linked(int i) { 670 // this method is aimed at finding out whether the 671 // relation member is "linked" with the next, i.e. whether 672 // (if both are ways) these ways are connected. 673 674 Integer link = null; 675 RelationMember m1 = members.get(i); 676 RelationMember m2 = members.get((i + 1) % members.size()); 677 Way way1 = null; 678 Way way2 = null; 679 680 if (m1.isWay()) { 681 way1 = m1.getWay(); 682 } 683 if (m2.isWay()) { 684 way2 = m2.getWay(); 685 } 686 if ((way1 != null) && (way2 != null)) { 687 Node way1first = way1.firstNode(); 688 Node way1last = way1.lastNode(); 689 Node way2first = way2.firstNode(); 690 Node way2last = way2.lastNode(); 691 if (way1first != null && way2first != null && (way1first == way2first)) { 692 link = -1; 693 } else if (way1first != null && way2last != null && (way1first == way2last)) { 694 link = 1; 695 } else if (way1last != null && way2first != null && (way1last == way2first)) { 696 link = 1; 697 } else if (way1last != null && way2last != null && (way1last == way2last)) { 698 link = -1; 699 } 687 if (m_ref.isWay()) { 688 way_ref = m_ref.getWay(); 700 689 } 701 702 return link; 690 if (m.isWay()) { 691 way = m.getWay(); 692 } 693 694 if (way_ref == null || way == null) { 695 return 0; 696 } 697 698 Node nRef = ref_direction > 0 ? way_ref.lastNode() : way_ref.firstNode(); 699 if (nRef == null) { 700 return 0; 701 } 702 703 if (nRef == way.firstNode()) { 704 return 1; 705 } 706 if (nRef == way.lastNode()) { 707 return -1; 708 } 709 return 0; 703 710 } 704 711 705 712 private WayConnectionType wayConnection(int i) { 706 RelationMember m = members.get(i); 707 if (! m.isWay()) 708 return new WayConnectionType(); 709 Way w = m.getWay(); 710 if (w == null || w.incomplete) 711 return new WayConnectionType(); 712 713 int ip = (i - 1 + members.size()) % members.size(); 714 Integer link_p = linked(ip); 715 Integer link_n = linked(i); 716 Integer dir = 1; 717 // FIXME: It is somewhat stupid to loop here, but 718 // there shouldn't be a performance problem in practice. 719 for (int k = i - 1; k >= 0; --k) { 720 Integer link = linked(k); 721 if (link != null) { 722 dir *= link; 723 } else { 724 break; 713 if (connectionType == null) { 714 updateLinks(); 715 } 716 return connectionType.get(i); 717 } 718 719 public void tableChanged(TableModelEvent e) { 720 connectionType = null; 721 } 722 723 public void updateLinks() { 724 connectionType = null; 725 ArrayList<WayConnectionType> con = new ArrayList<WayConnectionType>(); 726 727 for (int i=0; i<members.size(); ++i) con.add(null); 728 729 int firstGroupIdx=0; 730 boolean resetFirstGoupIdx=false; 731 732 for (int i=0; i<members.size(); ++i) { 733 if (resetFirstGoupIdx) { 734 firstGroupIdx = i; 735 resetFirstGoupIdx = false; 736 } 737 738 RelationMember m = members.get(i); 739 if (! m.isWay()) { 740 con.set(i, new WayConnectionType()); 741 resetFirstGoupIdx = true; 742 continue; 743 } 744 745 Way w = m.getWay(); 746 if (w == null || w.incomplete) { 747 con.set(i, new WayConnectionType()); 748 resetFirstGoupIdx = true; 749 continue; 750 } 751 752 boolean linkPrev = (i != firstGroupIdx); 753 boolean linkNext; 754 int dir; 755 if (linkPrev) { 756 dir = determineDirection(i-1, con.get(i-1).direction, i); 757 linkNext = (determineDirection(i, dir, i+1) != 0); 758 } 759 else { 760 dir = determineDirection(i, +1, i+1) != 0 ? +1 : 0; 761 if (dir == 0) { 762 dir = determineDirection(i, -1, i+1) != 0 ? -1 : 0; 763 } 764 linkNext = (dir != 0); 765 } 766 767 con.set(i, new WayConnectionType(linkPrev, linkNext, dir)); 768 769 if (! linkNext) { 770 boolean loop; 771 if (i == firstGroupIdx) { 772 loop = determineDirection(i, 1, i) == 1; 773 } else { 774 loop = determineDirection(i, dir, firstGroupIdx) == con.get(firstGroupIdx).direction; 775 } 776 if (loop) { 777 for (int j=firstGroupIdx; j <= i; ++j) { 778 con.get(j).isLoop = true; 779 } 780 } 781 resetFirstGoupIdx = true; 725 782 } 726 783 } 727 return new WayConnectionType(link_p != null, link_n != null, dir);728 } 729 } 784 connectionType = con; 785 } 786 } -
src/org/openstreetmap/josm/gui/dialogs/relation/RelationNodeMap.java
1 1 package org.openstreetmap.josm.gui.dialogs.relation; 2 2 3 3 import java.util.ArrayList; 4 import java.util.TreeSet; 4 5 5 6 import org.openstreetmap.josm.data.osm.Node; 6 7 import org.openstreetmap.josm.data.osm.RelationMember; … … 12 13 * @author Christiaan Welvaart <cjw@time4t.net> 13 14 * 14 15 */ 15 public class RelationNodeMap { 16 private java.util.HashMap<Node, java.util.TreeSet<Integer>> points; 16 public class RelationNodeMap { 17 /** 18 * For each way endpoint, list all ways that share this node 19 */ 20 private java.util.HashMap<Node, TreeSet<Integer>> points; 21 /** 22 * Singleton nodes 23 */ 17 24 private java.util.HashMap<Node, Integer> nodes; 18 private java.util.Vector<Integer> remaining; 19 private ArrayList<RelationMember> members; 25 private java.util.Vector<Integer> remaining; 26 /** 27 * read only list 28 */ 29 private final ArrayList<RelationMember> members; 20 30 21 31 RelationNodeMap(ArrayList<RelationMember> members) { 22 32 int i; 23 33 24 34 this.members = members; 25 points = new java.util.HashMap<Node, java.util.TreeSet<Integer>>();35 points = new java.util.HashMap<Node, TreeSet<Integer>>(); 26 36 nodes = new java.util.HashMap<Node, Integer>(); 27 37 remaining = new java.util.Vector<Integer>(); 28 38 … … 62 72 Way w = m.getWay(); 63 73 if (w.lastNode() == w.firstNode()) 64 74 { 65 nodes.put(w.firstNode(), Integer.valueOf(n));75 nodes.put(w.firstNode(), n); 66 76 } 67 77 else 68 78 { 69 79 if (!points.containsKey(w.firstNode())) { 70 points.put(w.firstNode(), new java.util.TreeSet<Integer>());80 points.put(w.firstNode(), new TreeSet<Integer>()); 71 81 } 72 points.get(w.firstNode()).add( Integer.valueOf(n));82 points.get(w.firstNode()).add(n); 73 83 74 84 if (!points.containsKey(w.lastNode())) { 75 points.put(w.lastNode(), new java.util.TreeSet<Integer>());85 points.put(w.lastNode(), new TreeSet<Integer>()); 76 86 } 77 points.get(w.lastNode()).add( Integer.valueOf(n));87 points.get(w.lastNode()).add(n); 78 88 } 79 89 } else if (m.isNode()) { 80 90 Node node = m.getNode(); 81 nodes.put(node, Integer.valueOf(n));91 nodes.put(node, n); 82 92 } else { 83 remaining.add( Integer.valueOf(n));93 remaining.add(n); 84 94 } 85 95 } 86 96 … … 103 113 return result; 104 114 } 105 115 106 void move(int from, int to) {107 if (from != to) {108 RelationMember b = members.get(from);109 RelationMember a = members.get(to);110 111 remove(to, b);112 add(to, a);113 }114 }115 116 116 // no node-mapped entries left 117 117 boolean isEmpty() { 118 118 return points.isEmpty() && nodes.isEmpty(); … … 131 131 result = nodes.get(node); 132 132 nodes.remove(node); 133 133 } else if (!points.isEmpty()) { 134 for ( java.util.TreeSet<Integer> set : points.values()) {134 for (TreeSet<Integer> set : points.values()) { 135 135 if (!set.isEmpty()) { 136 136 result = set.first(); 137 137 Way w = members.get(result).getWay(); … … 144 144 145 145 return result; 146 146 } 147 } 147 } -
src/org/openstreetmap/josm/gui/dialogs/relation/WayConnectionType.java
5 5 6 6 public class WayConnectionType { 7 7 8 public final boolean connectedToPrevious; 9 public final boolean connectedToNext; 10 /** Is 1 if way has the same direction as the first way in the set of connected ways. Else it is (-1) */ 8 /** True, if the corresponding primitive is not a way or the way is incomplete */ 9 private final boolean invalid; 10 11 /** True, if linked to the previous / next member. */ 12 public final boolean linkPrev; 13 public final boolean linkNext; 14 15 /** 16 * direction is +1 if the first node of this way is connected to the previous way 17 * and / or the last node of this way is connected to the next way. 18 * direction is -1 if it is the other way around. 19 * If this way is neither connected to the previous nor to the next way, then 20 * direction has the value 0. 21 */ 11 22 public final int direction; 12 /** The WayConnectionType is invalid, if the corresponding primitive is not a way or the way is incomplete */ 13 public final boolean invalid; 23 24 /** True, if the element is part of a closed loop of ways. */ 25 public boolean isLoop; 14 26 15 public WayConnectionType(boolean connectedToPrevious, boolean connectedToNext, int direction) { 16 this.connectedToPrevious = connectedToPrevious; 17 this.connectedToNext = connectedToNext; 27 public WayConnectionType(boolean linkPrev, boolean linkNext, int direction) { 28 this.linkPrev = linkPrev; 29 this.linkNext = linkNext; 30 this.isLoop = false; 18 31 this.direction = direction; 19 32 invalid = false; 20 33 } 21 34 35 /** construct invalid instance */ 22 36 public WayConnectionType() { 23 connectedToPrevious = false; 24 connectedToNext = false; 25 direction = 1; 37 this.linkPrev = false; 38 this.linkNext = false; 39 this.isLoop = false; 40 this.direction = 0; 26 41 invalid = true; 42 } 43 44 public boolean isValid() { 45 return !invalid; 27 46 } 28 47 29 //@Override30 //public String toString() {31 // return ... 32 //}48 @Override 49 public String toString() { 50 return "[P "+linkPrev+" ;N "+linkNext+" ;D "+direction+" ;L "+isLoop+"]"; 51 } 33 52 34 53 public String getToolTip() { 35 if ( invalid) {54 if (!isValid()) { 36 55 return ""; 37 56 } 38 else if ( connectedToPrevious && connectedToNext) {57 else if (linkPrev && linkNext) { 39 58 return tr("way is connected"); 40 59 } 41 else if ( connectedToPrevious) {60 else if (linkPrev) { 42 61 return tr("way is connected to previous relation member"); 43 62 } 44 else if ( connectedToNext) {63 else if (linkNext) { 45 64 return tr("way is connected to next relation member"); 46 65 } 47 66 else { 48 67 return tr("way is not connected to previous or next relation member"); 49 } 68 }//FIXME: isLoop & direction 50 69 } 51 70 } -
src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableLinkedCellRenderer.java
16 16 17 17 final static Image arrowUp = ImageProvider.get("dialogs", "arrowup").getImage(); 18 18 final static Image arrowDown = ImageProvider.get("dialogs", "arrowdown").getImage(); 19 final static Image corners = ImageProvider.get("dialogs", "roundedcorners").getImage(); 19 20 private WayConnectionType value = new WayConnectionType(); 20 21 21 22 @Override … … 33 34 @Override 34 35 public void paintComponent(Graphics g) { 35 36 super.paintComponent(g); 36 if (value == null || value.invalid) {37 if (value == null || !value.isValid()) { 37 38 return; 38 39 } 39 40 40 Image image= null;41 Image arrow = null; 41 42 switch (value.direction) { 42 43 case 1: 43 image= arrowDown;44 arrow = arrowDown; 44 45 break; 45 46 case -1: 46 image= arrowUp;47 arrow = arrowUp; 47 48 break; 48 49 } 49 50 50 51 int ymax=this.getSize().height - 1; 52 int xloop = 8; 51 53 int xoff = this.getSize().width / 2; 54 if (value.isLoop) { 55 xoff -= xloop / 2 - 1; 56 } 52 57 int w = 2; 53 58 int p = 2 + w + 1; 54 59 int y1 = 0; 55 60 int y2 = 0; 56 61 57 if (image != null && (value.connectedToPrevious || value.connectedToNext)) {58 g.drawImage(image, xoff-3, ymax / 2 - 2, null);59 }60 62 61 if (value. connectedToPrevious) {63 if (value.linkPrev) { 62 64 g.setColor(Color.black); 63 g.fillRect(xoff - 2, 0, 5, 2);65 g.fillRect(xoff - 1, 0, 3, 1); 64 66 y1 = 0; 65 67 } else { 66 g.setColor(Color.red); 67 g.drawRect(xoff-1, p - 1 - w, w, w); 68 y1 = p; 68 if (value.isLoop) { 69 g.setColor(Color.black); 70 y1 = 5; 71 g.drawImage(corners,xoff,y1-3,xoff+3,y1, 0,0,3,3, new Color(0,0,0,0), null); 72 g.drawImage(corners,xoff+xloop-2,y1-3,xoff+xloop+1,y1, 2,0,5,3, new Color(0,0,0,0), null); 73 g.drawLine(xoff+3,y1-3,xoff+xloop-3,y1-3); 74 } 75 else { 76 g.setColor(Color.red); 77 g.drawRect(xoff-1, p - 1 - w, w, w); 78 y1 = p; 79 } 69 80 } 70 81 71 if (value. connectedToNext) {82 if (value.linkNext) { 72 83 g.setColor(Color.black); 73 g.fillRect(xoff - 2, ymax - 1, 5, 2);84 g.fillRect(xoff - 1, ymax, 3, 1); 74 85 y2 = ymax; 75 86 } else { 76 g.setColor(Color.red); 77 g.drawRect(xoff-1, ymax - p + 1, w, w); 78 y2 = ymax - p; 87 if (value.isLoop) { 88 g.setColor(Color.black); 89 y2 = ymax - 5; 90 g.fillRect(xoff-1, y2+2, 3, 3); 91 g.drawLine(xoff, y2, xoff, y2+2); 92 g.drawImage(corners,xoff+xloop-2,y2+1,xoff+xloop+1,y2+4, 2,2,5,5, new Color(0,0,0,0), null); 93 g.drawLine(xoff+3-1,y2+3,xoff+xloop-3,y2+3); 94 } 95 else { 96 g.setColor(Color.red); 97 g.drawRect(xoff-1, ymax - p + 1, w, w); 98 y2 = ymax - p; 99 } 100 } 101 102 if ((arrow != null) && (value.linkPrev || value.linkNext)) { 103 g.drawImage(arrow, xoff-3, (y1 + y2) / 2 - 2, null); 79 104 } 105 80 106 g.setColor(Color.black); 81 107 g.drawLine(xoff, y1, xoff, y2); 108 if (value.isLoop) { 109 g.drawLine(xoff+xloop, y1, xoff+xloop, y2); 110 } 111 82 112 } 83 113 }