Ignore:
Timestamp:
2011-10-09T21:06:39+02:00 (13 years ago)
Author:
zverik
Message:

refactoring: extract TheRing from CreateMultipolygonAction, remove old code

Location:
applications/editors/josm/plugins/reltoolbox/src/relcontext/actions
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/CreateMultipolygonAction.java

    r26815 r26816  
    1414import org.openstreetmap.josm.data.coor.EastNorth;
    1515import org.openstreetmap.josm.data.osm.*;
    16 import org.openstreetmap.josm.data.osm.MultipolygonCreate.JoinedPolygon;
    1716import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1817import org.openstreetmap.josm.tools.GBC;
     
    423422            boolean found = false;
    424423            for( Way ring : rings ) {
    425                 System.out.println("segment " + segment.getId() + ", ring " + ring.getId());
    426                 System.out.println("ring.containsNode(segment.firstNode()) = " + ring.containsNode(segment.firstNode()));
    427                 System.out.println("ring.containsNode(segment.lastNode() = " + ring.containsNode(segment.lastNode()));
    428                 System.out.println("segmentInsidePolygon(segment.getNode(0), segment.getNode(1), ring.getNodes()) = " + segmentInsidePolygon(segment.getNode(0), segment.getNode(1), ring.getNodes()));
    429424                if( ring.containsNode(segment.firstNode()) && ring.containsNode(segment.lastNode())
    430425                        && !segmentInsidePolygon(segment.getNode(0), segment.getNode(1), ring.getNodes()) )
     
    469464        return relations;
    470465    }
    471    
    472     /**
    473      * Creates ALOT of Multipolygons and pets him gently.
    474      * @return list of new relations.
    475      */
    476     private List<Relation> makeManySimpleMultipolygonsOld( Collection<Way> selection ) {
    477         List<Command> commands = new ArrayList<Command>();
    478         List<Way> ways = new ArrayList<Way>(selection.size());
    479         Map<Way, Way> wayDiff = new HashMap<Way, Way>(selection.size());
    480         List<Relation> relations = new ArrayList<Relation>(ways.size());
    481         Collection<String> linearTags = Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS);
    482         for( Way w : selection ) {
    483             Way newWay = new Way(w);
    484             wayDiff.put(w, newWay);
    485             commands.add(new ChangeCommand(w, newWay));
    486             ways.add(newWay);
    487             Relation r = new Relation();
    488             r.put("type", "multipolygon");
    489             r.addMember(new RelationMember("outer", w));
    490             // move tags to relations
    491             for( String key : newWay.keySet() ) {
    492                 if( !linearTags.contains(key) ) {
    493                     r.put(key, newWay.get(key));
    494                     newWay.remove(key);
    495                 }
    496             }
    497             if( !w.isClosed() ) {
    498                 Way ring = null;
    499                 for( Way tring : selection )
    500                     if( tring.containsNode(newWay.firstNode()) && tring.containsNode(newWay.lastNode())
    501                             && !segmentInsidePolygon(newWay.getNode(0), newWay.getNode(1), tring.getNodes()) )
    502                         ring = tring;
    503                 Way intersection = makeIntersectionLine(newWay, ring);
    504                 commands.add(new AddCommand(intersection));
    505                 r.addMember(new RelationMember("outer", intersection));
    506             }
    507             relations.add(r);
    508         }
    509 
    510         for( int i = 0; i < relations.size() - 1; i++ )
    511             for( int j = i + 1; j < relations.size(); j++ )
    512                 collideMultipolygons(relations.get(i), relations.get(j), commands, wayDiff);
    513 
    514         for( Relation r : relations )
    515             commands.add(new AddCommand(r));
    516         Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygons from rings"), commands));
    517         return relations;
    518     }
    519 
    520     /**
    521      * Copies segment from {@code ring} to close a multipolygon containing {@code segment}.
    522      * @param segment Unclosed segment.
    523      * @param ring Closed ring.
    524      * @return Missing way.
    525      */
    526     private Way makeIntersectionLine( Way segment, Way ring ) {
    527         List<Node> nodes = new ArrayList<Node>(ring.getNodes());
    528         nodes.remove(nodes.size() - 1);
    529         int index1 = nodes.indexOf(segment.firstNode());
    530         int index2 = nodes.indexOf(segment.lastNode());
    531         if( index1 == index2 || index1 < 0 || index2 < 0 )
    532             return null;
    533 
    534         // split ring
    535         List<List<Node>> chunks = new ArrayList<List<Node>>(2);
    536         chunks.add(new ArrayList<Node>());
    537         chunks.add(new ArrayList<Node>());
    538         int chunk = 0, i = index1;
    539         boolean madeCircle = false;
    540         while( i != index1 || !madeCircle ) {
    541             chunks.get(chunk).add(nodes.get(i));
    542             if( i == index2 ) {
    543                 chunk = 1 - chunk;
    544                 chunks.get(chunk).add(nodes.get(i));
    545             }
    546             if( ++i >= nodes.size() )
    547                 i = 0;
    548             madeCircle = true;
    549         }
    550         chunks.get(chunk).add(nodes.get(i));
    551 
    552         // check which segment to add
    553         List<Node> testRing = new ArrayList<Node>(segment.getNodes());
    554         closePolygon(testRing, chunks.get(0));
    555         chunk = segmentInsidePolygon(chunks.get(1).get(0), chunks.get(1).get(1), testRing) ? 1 : 0;
    556 
    557         // create way
    558         Way w = new Way();
    559         w.setKeys(segment.getKeys());
    560         w.setNodes(chunks.get(chunk));
    561         return w;
    562     }
    563466
    564467    /**
     
    583486        Node testNode = new Node(new EastNorth((en1.east() + en2.east()) / 2.0, (en1.north() + en2.north()) / 2.0));
    584487        return Geometry.nodeInsidePolygon(testNode, polygon);
    585     }
    586 
    587     /**
    588      * Removes any intersections between multipolygons.
    589      * @param r1 First multipolygon.
    590      * @param r2 Second multipolygon.
    591      * @param commands List of commands. Only add way commands go there, also see wayDiff.
    592      * @param wayDiff The mapping old way to new Way: if there is no entry in this map, it is created, and
    593      * a ChangeCommand is issued.
    594      */
    595     private static void collideMultipolygons( Relation r1, Relation r2, List<Command> commands, Map<Way, Way> wayDiff ) {
    596     }
    597    
    598     /**
    599      * merges two ways from two multipolygons. The result is several new ways and changed old ones.
    600      * But it can also remove one old way — it is returned then.
    601      * @param w1
    602      * @param w2
    603      * @param commands
    604      */
    605     private static Way collideWays( Way w1, Way w2, List<Command> commands ) {
    606         return null;
    607488    }
    608489
     
    771652
    772653    /**
    773      * Find a multipolygon at the tips of a segment, try to close the way.
    774      *
    775      * Note: this method is abandoned because of it's skyrocketing complexity. The only thing is
    776      * left to write is splitting and ordering ways (see below). But I doubt there is a point to it.
    777      */
    778     private Relation tryToCloseOneWayOld( Way segment ) {
    779         if( segment.isClosed() || segment.isIncomplete() )
    780             return null;
    781 
    782         // find relations that have ways from both arrays
    783         Set<Relation> relations1 = new HashSet<Relation>();
    784         for( Way way : OsmPrimitive.getFilteredList(segment.firstNode().getReferrers(), Way.class) )
    785             relations1.addAll(OsmPrimitive.getFilteredList(way.getReferrers(), Relation.class));
    786         Set<Relation> relations2 = new HashSet<Relation>();
    787         for( Way way : OsmPrimitive.getFilteredList(segment.lastNode().getReferrers(), Way.class) )
    788             relations2.addAll(OsmPrimitive.getFilteredList(way.getReferrers(), Relation.class));
    789         List<Relation> relations = intersection(relations1, relations2);
    790         for( Iterator<Relation> iter = relations.iterator(); iter.hasNext(); ) {
    791             Relation candidate = iter.next();
    792             if( !candidate.isMultipolygon() || candidate.isDeleted() || candidate.isIncomplete() )
    793                 iter.remove();
    794         }
    795         if( relations.isEmpty() )
    796             return null;
    797         int i = 0;
    798         String error = "";
    799         MultipolygonCreate mpc = new MultipolygonCreate();
    800         JoinedPolygon poly = null;
    801         while( error != null && i < relations.size() ) {
    802             error = mpc.makeFromWays(OsmPrimitive.getFilteredSet(relations.get(i).getMemberPrimitives(), Way.class));
    803             if( error != null ) {
    804                 for( JoinedPolygon p : mpc.outerWays ) {
    805                     if( p.nodes.contains(segment.firstNode()) && p.nodes.contains(segment.lastNode()) ) {
    806                         poly = p;
    807                         break;
    808                     }
    809                 }
    810             }
    811             i++;
    812         }
    813         if( poly == null )
    814             return null; // no correct multipolygons with outer contour that segment touches
    815         Relation multipolygon = relations.get(i - 1);
    816 
    817         // time to create a new multipolygon relation and a command stack
    818         List<Command> commands = new ArrayList<Command>();
    819         Relation newRelation = new Relation();
    820         newRelation.put("type", "multipolygon");
    821         newRelation.addMember(new RelationMember("outer", segment));
    822         Collection<String> linearTags = Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS);
    823         Way segmentCopy = new Way(segment);
    824         boolean changed = false;
    825         for( String key : segmentCopy.keySet() ) {
    826             if( !linearTags.contains(key) ) {
    827                 newRelation.put(key, segmentCopy.get(key));
    828                 segmentCopy.remove(key);
    829                 changed = true;
    830             }
    831         }
    832         if( changed )
    833             commands.add(new ChangeCommand(segment, segmentCopy));
    834 
    835         // find a path from one point to another via found outer contour
    836         // but first, determine in which order to traverse nodes
    837         int index1 = poly.nodes.indexOf(segment.firstNode());
    838         int index2 = poly.nodes.indexOf(segment.lastNode());
    839 
    840         List<List<Node>> chunks = new ArrayList<List<Node>>(2);
    841         chunks.add(new ArrayList<Node>());
    842         chunks.add(new ArrayList<Node>());
    843         int chunk = 0;
    844         i = index1;
    845         boolean madeCircle = false;
    846         while( i != index1 || !madeCircle ) {
    847             chunks.get(chunk).add(poly.nodes.get(i));
    848             if( i == index2 ) {
    849                 chunk = 1 - chunk;
    850                 chunks.get(chunk).add(poly.nodes.get(i));
    851             }
    852             if( ++i >= poly.nodes.size() )
    853                 i = 0;
    854             madeCircle = true;
    855         }
    856         chunks.get(chunk).add(poly.nodes.get(i));
    857 
    858         // check which segment to add
    859         List<Node> testRing = new ArrayList<Node>(segment.getNodes());
    860         closePolygon(testRing, chunks.get(0));
    861 //      Node startNode = segmentInsidePolygon(chunks.get(1).get(0), chunks.get(1).get(1), testRing) ? segment.lastNode() : segment.firstNode();
    862 //      Node endNode = startNode.equals(segment.firstNode()) ? segment.lastNode() : segment.firstNode();
    863         int startIndex = segmentInsidePolygon(chunks.get(1).get(0), chunks.get(1).get(1), testRing) ? index2 : index1;
    864         int endIndex = startIndex == index2 ? index1 : index2;
    865         Node startNode = poly.nodes.get(startIndex);
    866         Node endNode = poly.nodes.get(endIndex);
    867 
    868         // add ways containing nodes from startNode to endNode
    869         // note: they are in order!
    870         i = 0;
    871         while( i < poly.ways.size() && !poly.ways.get(i).containsNode(startNode) )
    872             i++;
    873         int startNodeIndex = poly.ways.get(i).getNodes().indexOf(startNode);
    874         if( startNodeIndex == 0 || startNodeIndex == poly.ways.get(i).getNodesCount() - 1 )
    875             i++; // if it's the last node, take next way
    876 
    877         if( poly.ways.get(i).containsNode(endNode) ) {
    878             // ok, both nodes are in the same way
    879             // split it, return the new part
    880             List<Way> newWays = splitWay(poly.ways.get(i), startNode, endNode, commands);
    881             // find which of the parts we need (in case of closed way)
    882             Node testNode = poly.nodes.get((index1 + 1) % poly.nodes.size());
    883         } else {
    884             // so, let's take ways one by one
    885             // todo: split way 1 and add relevant part
    886             List<Way> newWays = splitWay(poly.ways.get(i), startNode, endNode, commands);
    887             i++;
    888             while( !poly.ways.get(i).containsNode(endNode) ) {
    889                 newRelation.addMember(new RelationMember("outer", poly.ways.get(i)));
    890                 i++;
    891             }
    892             // todo: split way 2 and add relevant part
    893             newWays = splitWay(poly.ways.get(i), startNode, endNode, commands);
    894         }
    895 
    896         commands.add(new AddCommand(newRelation));
    897         Main.main.undoRedo.add(new SequenceCommand(tr("Complete multipolygon for way {0}",
    898                 DefaultNameFormatter.getInstance().format(segment)), commands));
    899         return newRelation;
    900     }
    901 
    902     /**
    903654     * Returns all elements from {@code list1} that are in {@code list2}.
    904655     */
     
    910661        return result;
    911662    }
    912    
    913     public static class TheRing {
    914         private Way source;
    915         private List<RingSegment> segments;
    916         private Relation relation = null;
    917 
    918         public TheRing( Way source ) {
    919             this.source = source;
    920             segments = new ArrayList<RingSegment>(1);
    921             segments.add(new RingSegment(source));
    922         }
    923        
    924         public void collide( TheRing other ) {
    925             List<Node> intersectionNodes = new ArrayList<Node>();
    926             List<RingSegment> segmentsList1 = new ArrayList<RingSegment>(segments);
    927             List<RingSegment> segmentsList2 = new ArrayList<RingSegment>(other.segments);
    928             boolean collideNoted = false;
    929             for( int i = 0; i < segmentsList1.size(); i++ ) {
    930                 if( !segmentsList1.get(i).isReference() )
    931                 for( int j = 0; j < segmentsList2.size(); j++ ) {
    932                     // not colliding referencing nodes: they've already collided, and
    933                     // there should be no more than two ways passing through two points.
    934                     if( !segmentsList1.get(i).isReference() ) {
    935                         intersectionNodes.clear();
    936                         boolean colliding = false;
    937                         List<Node> nodes1 = segmentsList1.get(i).getNodes();
    938                         List<Node> nodes2 = segmentsList2.get(j).getNodes();
    939                         for( int ni = 0; ni < nodes2.size(); ni++ ) {
    940                             if( nodes1.contains(nodes2.get(ni)) != colliding ) {
    941                                 intersectionNodes.add(nodes2.get(colliding ? ni-1 : ni));
    942                                 colliding = !colliding;
    943                             }
    944                         }
    945                         if( colliding )
    946                             intersectionNodes.add(nodes2.get(nodes2.size()-1));
    947                         // when an intersection of two rings spans a ring's beginning
    948                         if( segmentsList1.get(i).isRing() && segmentsList2.get(j).isRing() && intersectionNodes.contains(nodes2.get(0)) && intersectionNodes.contains(nodes2.get(nodes2.size()-1)) ) {
    949                             intersectionNodes.remove(0);
    950                             intersectionNodes.remove(intersectionNodes.size()-1);
    951                             intersectionNodes.add(intersectionNodes.get(0));
    952                             intersectionNodes.remove(0);
    953                         }
    954                         if( !collideNoted && !intersectionNodes.isEmpty() ) {
    955                             System.out.println("Rings " + this + " and " + other + " collide.");
    956                             collideNoted = true;
    957                         }
    958                         System.out.print("Intersection nodes for segments " + segmentsList1.get(i) + " and " + segmentsList2.get(j) + ": ");
    959                         for( Node inode : intersectionNodes )
    960                             System.out.print(inode.getUniqueId() + ",");
    961                         System.out.println();
    962                         // unclosed ways produce duplicate nodes
    963                         int ni = 1;
    964                         while( ni < intersectionNodes.size() ) {
    965                             if( intersectionNodes.get(ni-1).equals(intersectionNodes.get(ni)) )
    966                                 intersectionNodes.remove(ni-1);
    967                             else
    968                                 ni++;
    969                         }
    970 //                      boolean thisWayIsReversed = !intersectionNodes.isEmpty() && nodes1.indexOf(intersectionNodes.get(0)) > nodes1.indexOf(intersectionNodes.get(1));
    971                         // now split both ways at control points and remove duplicate parts
    972                         ni = 0;
    973                         while( ni+1 < intersectionNodes.size() ) {
    974                             if( !segmentsList1.get(i).isReferencingEqual(segmentsList2.get(j)) ) {
    975                                 boolean[] isarc = new boolean[] {
    976                                     segments.size() == 1 && !segments.get(0).isRing(),
    977                                     other.segments.size() == 1 && !other.segments.get(0).isRing()
    978                                 };
    979                                 RingSegment segment = splitRingAt(i, intersectionNodes.get(ni), intersectionNodes.get(ni+1));
    980                                 RingSegment otherSegment = other.splitRingAt(j, intersectionNodes.get(ni), intersectionNodes.get(ni+1));
    981                                 if( !isarc[0] && !isarc[1] ) {
    982                                     if( segments.size() > 2 )
    983                                         segment.makeReference(otherSegment);
    984                                     else {
    985                                         // this ring was a ring, and we're not sure "segment" is a correct segment
    986                                         if( segments.get(0).getNodes().size() == otherSegment.getNodes().size() &&
    987                                                 (segments.get(0).getNodes().get(1).equals(otherSegment.getNodes().get(1))) ||
    988                                                 (segments.get(0).getNodes().get(segments.get(0).getNodes().size()-2).equals(otherSegment.getNodes().get(1))))
    989                                             segments.get(0).makeReference(otherSegment);
    990                                         else
    991                                             segments.get(1).makeReference(otherSegment);
    992                                     }
    993                                 } else {
    994                                     // 1. A ring is an arc. It should have only 2 segments after this
    995                                     // 2. But it has one, so add otherSegment as the second.
    996                                     // 3. determine which segment!
    997                                     if( isarc[0] ) {
    998                                         if( other.segments.size() > 2 )
    999                                             segments.add(new RingSegment(otherSegment));
    1000                                         else {
    1001                                             // choose between 2 segments
    1002                                             List<Node> testRing = new ArrayList<Node>(segments.get(0).getNodes());
    1003                                             closePolygon(testRing, other.segments.get(0).getNodes());
    1004                                             int segmentToAdd = segmentInsidePolygon(other.segments.get(1).getNodes().get(0),
    1005                                                     other.segments.get(1).getNodes().get(1), testRing) ? 1 : 0;
    1006                                             segments.add(new RingSegment(other.segments.get(segmentToAdd)));
    1007                                         }
    1008                                     } else
    1009                                         if( segments.size() > 2 )
    1010                                             other.segments.add(new RingSegment(segment));
    1011                                         else {
    1012                                             // choose between 2 segments
    1013                                             List<Node> testRing = new ArrayList<Node>(other.segments.get(0).getNodes());
    1014                                             closePolygon(testRing, segments.get(0).getNodes());
    1015                                             int segmentToAdd = segmentInsidePolygon(segments.get(1).getNodes().get(0),
    1016                                                     segments.get(1).getNodes().get(1), testRing) ? 1 : 0;
    1017                                             other.segments.add(new RingSegment(segments.get(segmentToAdd)));
    1018                                         }
    1019                                 }
    1020                             }
    1021                             ni += 2;
    1022                         }
    1023                     }
    1024                 }
    1025             }
    1026         }
    1027        
    1028         /**
    1029          * Split the segment in this ring at those nodes.
    1030          * @return The segment between nodes.
    1031          */
    1032         private RingSegment splitRingAt( int segmentIndex, Node n1, Node n2 ) {
    1033             if( n1.equals(n2) )
    1034                 throw new IllegalArgumentException("Both nodes are equal, id=" + n1.getUniqueId());
    1035             RingSegment segment = segments.get(segmentIndex);
    1036             boolean isRing = segment.isRing();
    1037             System.out.println("Split segment " + segment + " at nodes " + n1.getUniqueId() + " and " + n2.getUniqueId());
    1038             boolean reversed = segment.getNodes().indexOf(n2) < segment.getNodes().indexOf(n1);
    1039             if( reversed && !isRing ) {
    1040                 // order nodes
    1041                 Node tmp = n1;
    1042                 n1 = n2;
    1043                 n2 = tmp;
    1044             }
    1045             RingSegment secondPart = isRing ? segment.split(n1, n2) : segment.split(n1);
    1046             // if secondPart == null, then n1 == firstNode
    1047             RingSegment thirdPart = isRing ? null : secondPart == null ? segment.split(n2) : secondPart.split(n2);
    1048             // if secondPart == null, then thirdPart is between n1 and n2
    1049             // otherwise, thirdPart is between n2 and lastNode
    1050             // if thirdPart == null, then n2 == lastNode
    1051             int pos = segmentIndex + 1;
    1052             if( secondPart != null )
    1053                 segments.add(pos++, secondPart);
    1054             if( thirdPart != null )
    1055                 segments.add(pos++, thirdPart);
    1056             RingSegment result = isRing || secondPart == null ? segment : secondPart;
    1057             System.out.println("Returning segment " + result);
    1058             return result;
    1059         }
    1060 
    1061         /**
    1062          * Tries to arrange segments in order for each ring to have at least one.
    1063          * Also, sets source way for all rings.
    1064          *
    1065          * This method should be called, even if there is just one ring.
    1066          */
    1067         public static void redistributeSegments( List<TheRing> rings ) {
    1068             // build segments map
    1069             Map<RingSegment, TheRing> segmentMap = new HashMap<RingSegment, TheRing>();
    1070             for( TheRing ring : rings )
    1071                 for( RingSegment seg : ring.segments )
    1072                     if( !seg.isReference())
    1073                         segmentMap.put(seg, ring);
    1074            
    1075             // rearrange references
    1076             for( int i = 0; i < rings.size(); i++) {
    1077                 TheRing ring = rings.get(i);
    1078                 if( ring.countNonReferenceSegments() == 0 ) {
    1079                     // need to find one non-reference segment
    1080                     for( RingSegment seg : ring.segments ) {
    1081                         TheRing otherRing = segmentMap.get(seg.references);
    1082                         if( otherRing.countNonReferenceSegments() > 1 ) {
    1083                             // we could check for >0, but it is prone to deadlocking
    1084                             seg.swapReference();
    1085                         }
    1086                     }
    1087                 }
    1088             }
    1089 
    1090             // initializing source way for each ring
    1091             for( TheRing ring : rings )
    1092                 ring.putSourceWayFirst();
    1093         }
    1094        
    1095         private int countNonReferenceSegments() {
    1096             int count = 0;
    1097             for( RingSegment seg : segments )
    1098                 if( !seg.isReference() )
    1099                     count++;
    1100             return count;
    1101         }
    1102        
    1103         private void putSourceWayFirst() {
    1104             for( RingSegment seg : segments ) {
    1105                 if( !seg.isReference() ) {
    1106                     seg.overrideWay(source);
    1107                     return;
    1108                 }
    1109             }
    1110         }
    1111        
    1112         /**
    1113          * Returns a list of commands to make a new relation and all newly created ways.
    1114          * The first way is copied from the source one, ChangeCommand is issued in this case.
    1115          */
    1116         public List<Command> getCommands() {
    1117             System.out.println("Making ring " + this);
    1118             Collection<String> linearTags = Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS);
    1119             relation = new Relation();
    1120             relation.put("type", "multipolygon");
    1121             Way sourceCopy = new Way(source);
    1122             for( String key : sourceCopy.keySet() ) {
    1123                 if( !linearTags.contains(key) ) {
    1124                     relation.put(key, sourceCopy.get(key));
    1125                     sourceCopy.remove(key);
    1126                 }
    1127             }
    1128            
    1129             // build a map of referencing relations
    1130             Map<Relation, Integer> referencingRelations = new HashMap<Relation, Integer>();
    1131             List<Command> relationCommands = new ArrayList<Command>();
    1132             for( OsmPrimitive p : source.getReferrers() ) {
    1133                 if( p instanceof Relation ) {
    1134                     Relation rel = new Relation((Relation)p);
    1135                     relationCommands.add(new ChangeCommand((Relation)p, rel));
    1136                     for( int i = 0; i < rel.getMembersCount(); i++ )
    1137                         if( rel.getMember(i).getMember().equals(source) )
    1138                             referencingRelations.put(rel, Integer.valueOf(i));
    1139                 }
    1140             }
    1141 
    1142             List<Command> commands = new ArrayList<Command>();
    1143             boolean foundOwnWay = false;
    1144             for( RingSegment seg : segments ) {
    1145                 boolean needAdding = !seg.isWayConstructed();
    1146                 Way w = seg.constructWay(seg.isReference() ? null : sourceCopy);
    1147                 if( needAdding )
    1148                     commands.add(new AddCommand(w));
    1149                 if( w.equals(source) ) {
    1150                     if( segments.size() == 1 ) {
    1151                         // one segment means that it is a ring
    1152                         List<Node> segnodes = seg.getNodes();
    1153                         segnodes.add(segnodes.get(0));
    1154                         sourceCopy.setNodes(segnodes);
    1155                     } else
    1156                         sourceCopy.setNodes(seg.getNodes());
    1157                     commands.add(new ChangeCommand(source, sourceCopy));
    1158                     foundOwnWay = true;
    1159                 } else {
    1160                     for( Relation rel : referencingRelations.keySet() ) {
    1161                         int relIndex = referencingRelations.get(rel);
    1162                         rel.addMember(new RelationMember(rel.getMember(relIndex).getRole(), w));
    1163                     }
    1164                 }
    1165                 relation.addMember(new RelationMember("outer", w));
    1166             }
    1167             if( !foundOwnWay )
    1168                 commands.add(new DeleteCommand(source));
    1169             commands.addAll(relationCommands);
    1170             commands.add(new AddCommand(relation));
    1171             return commands;
    1172         }
    1173        
    1174         /**
    1175          * Returns the relation created in {@link #getCommands()}.
    1176          */
    1177         public Relation getRelation() {
    1178             return relation;
    1179         }
    1180 
    1181         @Override
    1182         public String toString() {
    1183             StringBuilder sb = new StringBuilder("TheRing@");
    1184             sb.append(this.hashCode()).append('[').append("wayId: ").append(source == null ? "null" : source.getUniqueId()).append("; segments: ");
    1185             if( segments.isEmpty() )
    1186                 sb.append("empty");
    1187             else {
    1188                 sb.append(segments.get(0));
    1189                 for( int i = 1; i < segments.size(); i++ )
    1190                     sb.append(", ").append(segments.get(i));
    1191             }
    1192             return sb.append(']').toString();
    1193         }       
    1194     }
    1195    
    1196     private static class RingSegment {
    1197         private List<Node> nodes;
    1198         private RingSegment references;
    1199         private Way resultingWay = null;
    1200         private boolean wasTemplateApplied = false;
    1201         private boolean isRing;
    1202        
    1203         private RingSegment() {}
    1204        
    1205         public RingSegment( Way w ) {
    1206             this(w.getNodes());
    1207         }
    1208        
    1209         public RingSegment( List<Node> nodes ) {
    1210             this.nodes = nodes;
    1211             isRing = nodes.size() > 1 && nodes.get(0).equals(nodes.get(nodes.size()-1));
    1212             if( isRing )
    1213                 nodes.remove(nodes.size()-1);
    1214             references = null;
    1215         }
    1216        
    1217         public RingSegment( RingSegment ref ) {
    1218             this.nodes = null;
    1219             this.references = ref;
    1220         }
    1221        
    1222         /**
    1223          * Splits this segment at node n. Retains nodes 0..n and moves
    1224          * nodes n..N to a separate segment that is returned.
    1225          * @param n node at which to split.
    1226          * @return new segment, {@code null} if splitting is unnecessary.
    1227          */
    1228         public RingSegment split( Node n ) {
    1229             if( nodes == null )
    1230                 throw new IllegalArgumentException("Cannot split segment: it is a reference");
    1231             int pos = nodes.indexOf(n);
    1232             if( pos <= 0 || pos >= nodes.size()-1 )
    1233                 return null;
    1234             List<Node> newNodes = new ArrayList<Node>(nodes.subList(pos, nodes.size()));
    1235             nodes.subList(pos+1, nodes.size()).clear();
    1236             return new RingSegment(newNodes);
    1237         }
    1238        
    1239         /**
    1240          * Split this segment as a way at two nodes. If one of them is null or at the end,
    1241          * split as an arc. Note: order of nodes is important.
    1242          * @return A new segment from n2 to n1.
    1243          */
    1244         public RingSegment split( Node n1, Node n2 ) {
    1245             if( nodes == null )
    1246                 throw new IllegalArgumentException("Cannot split segment: it is a reference");
    1247             if( !isRing ) {
    1248                 if( n1 == null || nodes.get(0).equals(n1) || nodes.get(nodes.size()-1).equals(n1) )
    1249                     return split(n2);
    1250                 if( n2 == null || nodes.get(0).equals(n2) || nodes.get(nodes.size()-1).equals(n2) )
    1251                     return split(n1);
    1252                 throw new IllegalArgumentException("Split for two nodes is called for not-ring: " + this);
    1253             }
    1254             int pos1 = nodes.indexOf(n1);
    1255             int pos2 = nodes.indexOf(n2);
    1256             if( pos1 == pos2 )
    1257                 return null;
    1258            
    1259             List<Node> newNodes = new ArrayList<Node>();
    1260             if( pos2 > pos1 ) {
    1261                 newNodes.addAll(nodes.subList(pos2, nodes.size()));
    1262                 newNodes.addAll(nodes.subList(0, pos1 + 1));
    1263                 if( pos2+1 < nodes.size() )
    1264                     nodes.subList(pos2+1, nodes.size()).clear();
    1265                 if( pos1 > 0 )
    1266                     nodes.subList(0, pos1).clear();
    1267             } else {
    1268                 newNodes.addAll(nodes.subList(pos2, pos1+1));
    1269                 nodes.addAll(new ArrayList<Node>(nodes.subList(0, pos2+1)));
    1270                 nodes.subList(0, pos1).clear();
    1271             }
    1272             isRing = false;
    1273             return new RingSegment(newNodes);
    1274         }
    1275        
    1276         public List<Node> getNodes() {
    1277             return nodes == null ? references.nodes : nodes;
    1278         }
    1279        
    1280         public boolean isReference() {
    1281             return nodes == null;
    1282         }
    1283        
    1284         public boolean isRing() {
    1285             return isRing;
    1286         }
    1287        
    1288         public void makeReference( RingSegment segment ) {
    1289             this.nodes = null;
    1290             this.references = segment;
    1291         }
    1292        
    1293         public void swapReference() {
    1294             this.nodes = references.nodes;
    1295             references.nodes = null;
    1296             references.references = this;
    1297             this.references = null;
    1298         }
    1299        
    1300         public boolean isWayConstructed() {
    1301             return isReference() ? references.isWayConstructed() : resultingWay != null;
    1302         }
    1303        
    1304         public Way constructWay( Way template ) {
    1305             if( isReference() )
    1306                 return references.constructWay(template);
    1307             if( resultingWay == null ) {
    1308                 resultingWay = new Way();
    1309                 resultingWay.setNodes(nodes);
    1310             }
    1311             if( template != null && !wasTemplateApplied ) {
    1312                 resultingWay.setKeys(template.getKeys());
    1313                 wasTemplateApplied = true;
    1314             }
    1315             return resultingWay;
    1316         }
    1317        
    1318         public void overrideWay( Way source ) {
    1319             if( isReference() )
    1320                 references.overrideWay(source);
    1321             else {
    1322                 resultingWay = source;
    1323                 wasTemplateApplied = true;
    1324             }
    1325         }
    1326        
    1327         /**
    1328          * Compares two segments with respect to referencing.
    1329          * @return true if ways are equals, or one references another.
    1330          */
    1331         public boolean isReferencingEqual( RingSegment other ) {
    1332             return this.equals(other) || (other.isReference() && other.references == this ) || (isReference() && references == other);
    1333         }
    1334 
    1335         @Override
    1336         public String toString() {
    1337             StringBuilder sb = new StringBuilder("RingSegment@");
    1338             sb.append(this.hashCode()).append('[');
    1339             if( isReference() )
    1340                 sb.append("references ").append(references.hashCode());
    1341             else if( nodes.isEmpty() )
    1342                 sb.append("empty");
    1343             else {
    1344                 if( isRing )
    1345                     sb.append("ring:");
    1346                 sb.append(nodes.get(0).getUniqueId());
    1347                 for( int i = 1; i < nodes.size(); i++ )
    1348                     sb.append(',').append(nodes.get(i).getUniqueId());
    1349             }
    1350             return sb.append(']').toString();
    1351         }
    1352     }
    1353663}
Note: See TracChangeset for help on using the changeset viewer.