Ignore:
Timestamp:
2011-10-09T18:09:20+02:00 (13 years ago)
Author:
zverik
Message:

probably works, needs testing (there are border cases)

File:
1 edited

Legend:

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

    r26811 r26812  
    1111import org.openstreetmap.josm.Main;
    1212import org.openstreetmap.josm.actions.JosmAction;
    13 import org.openstreetmap.josm.actions.SplitWayAction;
    1413import org.openstreetmap.josm.command.*;
    1514import org.openstreetmap.josm.data.coor.EastNorth;
    1615import org.openstreetmap.josm.data.osm.*;
    1716import org.openstreetmap.josm.data.osm.MultipolygonCreate.JoinedPolygon;
    18 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
    1917import org.openstreetmap.josm.gui.DefaultNameFormatter;
    2018import org.openstreetmap.josm.tools.GBC;
     
    7472                    if( chRel != null )
    7573                        chRel.set(newRelation);
     74                    getCurrentDataSet().setSelected(newRelation);
    7675                    return;
    7776                }
     
    8180                if( chRel != null )
    8281                    chRel.set(rels.size() == 1 ? rels.get(0) : null);
     82                if( rels.size() == 1 )
     83                    getCurrentDataSet().setSelected(rels);
     84                else
     85                    getCurrentDataSet().clearSelection();
    8386                return;
    8487            }
     
    449452     */
    450453    private List<Relation> makeManySimpleMultipolygons( Collection<Way> selection ) {
     454        System.out.println("---------------------------------------");
     455        List<TheRing> rings = new ArrayList<TheRing>(selection.size());
     456        for( Way w : selection )
     457            rings.add(new TheRing(w));
     458        for( int i = 0; i < rings.size()-1; i++ )
     459            for( int j = i+1; j < rings.size(); j++ )
     460                rings.get(i).collide(rings.get(j));
     461        redistributeSegments(rings);
     462        List<Command> commands = new ArrayList<Command>();
     463        List<Relation> relations = new ArrayList<Relation>();
     464        for( TheRing r : rings ) {
     465            commands.addAll(r.getCommands());
     466            relations.add(r.getRelation());
     467        }
     468        Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygons from rings"), commands));
     469        return relations;
     470    }
     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 ) {
    451477        List<Command> commands = new ArrayList<Command>();
    452478        List<Way> ways = new ArrayList<Way>(selection.size());
     
    539565     * Appends "append" to "base" so the closed polygon forms.
    540566     */
    541     private void closePolygon( List<Node> base, List<Node> append ) {
     567    private static void closePolygon( List<Node> base, List<Node> append ) {
    542568        if( append.get(0).equals(base.get(0)) && append.get(append.size() - 1).equals(base.get(base.size() - 1)) ) {
    543569            List<Node> ap2 = new ArrayList<Node>(append);
     
    552578     * Checks if a middle point between two nodes is inside a polygon. Useful to check if the way is inside.
    553579     */
    554     private boolean segmentInsidePolygon( Node n1, Node n2, List<Node> polygon ) {
     580    private static boolean segmentInsidePolygon( Node n1, Node n2, List<Node> polygon ) {
    555581        EastNorth en1 = n1.getEastNorth();
    556582        EastNorth en2 = n2.getEastNorth();
     
    568594     */
    569595    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;
    570607    }
    571608
     
    873910        return result;
    874911    }
     912   
     913    /**
     914     * Tries to arrange segments in order for each ring to have at least one.
     915     */
     916    public static void redistributeSegments( List<TheRing> rings ) {
     917        // todo
     918    }
     919   
     920    public static class TheRing {
     921        private Way source;
     922        private List<RingSegment> segments;
     923        private Relation relation = null;
     924
     925        public TheRing( Way source ) {
     926            this.source = source;
     927            segments = new ArrayList<RingSegment>(1);
     928            segments.add(new RingSegment(source));
     929        }
     930       
     931        public void collide( TheRing other ) {
     932            System.out.println("Ring 1: " + this);
     933            System.out.println("Ring 2: " + other);
     934            List<Node> intersectionNodes = new ArrayList<Node>();
     935            List<RingSegment> segmentsList1 = new ArrayList<RingSegment>(segments);
     936            List<RingSegment> segmentsList2 = new ArrayList<RingSegment>(other.segments);
     937            for( int i = 0; i < segmentsList1.size(); i++ ) {
     938                if( !segmentsList1.get(i).isReference() )
     939                for( int j = 0; j < segmentsList2.size(); j++ ) {
     940                    // not colliding referencing nodes: they've already collided, and
     941                    // there should be no more than two ways passing through two points.
     942                    if( !segmentsList1.get(i).isReference() ) {
     943                        intersectionNodes.clear();
     944                        boolean colliding = false;
     945                        List<Node> nodes1 = segmentsList1.get(i).getNodes();
     946                        List<Node> nodes2 = segmentsList2.get(j).getNodes();
     947                        for( int ni = 0; ni < nodes2.size(); ni++ ) {
     948                            if( nodes1.contains(nodes2.get(ni)) != colliding ) {
     949                                intersectionNodes.add(nodes2.get(colliding ? ni-1 : ni));
     950                                colliding = !colliding;
     951                            }
     952                        }
     953                        if( colliding )
     954                            intersectionNodes.add(nodes2.get(nodes2.size()-1));
     955                        // todo: when an intersection of two rings spans a ring's beginning
     956                        if( segmentsList1.get(i).isRing() && segmentsList2.get(j).isRing() && intersectionNodes.contains(nodes2.get(0)) && intersectionNodes.contains(nodes2.get(nodes2.size()-1)) ) {
     957                            intersectionNodes.remove(0);
     958                            intersectionNodes.remove(intersectionNodes.size()-1);
     959                            intersectionNodes.add(intersectionNodes.get(0));
     960                            intersectionNodes.remove(0);
     961                        }
     962                        System.out.print("Intersection nodes for segments " + segmentsList1.get(i) + " and " + segmentsList2.get(j) + ": ");
     963                        for( Node inode : intersectionNodes )
     964                            System.out.print(inode.getUniqueId() + ",");
     965                        System.out.println();
     966                        // unclosed ways produce duplicate nodes
     967                        int ni = 1;
     968                        while( ni < intersectionNodes.size() ) {
     969                            if( intersectionNodes.get(ni-1).equals(intersectionNodes.get(ni)) )
     970                                intersectionNodes.remove(ni-1);
     971                            else
     972                                ni++;
     973                        }
     974//                      boolean thisWayIsReversed = !intersectionNodes.isEmpty() && nodes1.indexOf(intersectionNodes.get(0)) > nodes1.indexOf(intersectionNodes.get(1));
     975                        // now split both ways at control points and remove duplicate parts
     976                        ni = 0;
     977                        while( ni+1 < intersectionNodes.size() ) {
     978                            if( !segmentsList1.get(i).isReferencingEqual(segmentsList2.get(j)) ) {
     979                                System.out.println("Splitting segment 1: " + segmentsList1.get(i));
     980                                System.out.println("Splitting segment 2: " + segmentsList2.get(j));
     981                                boolean[] isarc = new boolean[] {
     982                                    segments.size() == 1 && !segments.get(0).isRing(),
     983                                    other.segments.size() == 1 && !other.segments.get(0).isRing()
     984                                };
     985                                RingSegment segment = splitRingAt(i, intersectionNodes.get(ni), intersectionNodes.get(ni+1));
     986                                RingSegment otherSegment = other.splitRingAt(j, intersectionNodes.get(ni), intersectionNodes.get(ni+1));
     987                                if( !isarc[0] && !isarc[1] )
     988                                    segment.makeReference(otherSegment);
     989                                else {
     990                                    // 1. A ring is an arc. It should have only 2 segments after this
     991                                    // 2. But it has one, so add otherSegment as the second.
     992                                    // todo: determine which segment!
     993                                    if( isarc[0] ) {
     994                                        if( other.segments.size() > 2 )
     995                                            segments.add(new RingSegment(otherSegment));
     996                                        else {
     997                                            // choose between 2 segments
     998                                            List<Node> testRing = new ArrayList<Node>(segments.get(0).getNodes());
     999                                            closePolygon(testRing, other.segments.get(0).getNodes());
     1000                                            int segmentToAdd = segmentInsidePolygon(other.segments.get(1).getNodes().get(0),
     1001                                                    other.segments.get(1).getNodes().get(1), testRing) ? 1 : 0;
     1002                                            segments.add(new RingSegment(other.segments.get(segmentToAdd)));
     1003                                        }
     1004                                    } else
     1005                                        if( segments.size() > 2 )
     1006                                            other.segments.add(new RingSegment(segment));
     1007                                        else {
     1008                                            // choose between 2 segments
     1009                                            List<Node> testRing = new ArrayList<Node>(other.segments.get(0).getNodes());
     1010                                            closePolygon(testRing, segments.get(0).getNodes());
     1011                                            int segmentToAdd = segmentInsidePolygon(segments.get(1).getNodes().get(0),
     1012                                                    segments.get(1).getNodes().get(1), testRing) ? 1 : 0;
     1013                                            other.segments.add(new RingSegment(segments.get(segmentToAdd)));
     1014                                        }
     1015                                }
     1016                            }
     1017                            ni += 2;
     1018                        }
     1019                    }
     1020                }
     1021            }
     1022            System.out.println("Resulting ring 1: " + this);
     1023            System.out.println("Resulting ring 2: " + other);
     1024        }
     1025       
     1026        /**
     1027         * Split the segment in this ring at those nodes.
     1028         * @return The segment between nodes.
     1029         */
     1030        private RingSegment splitRingAt( int segmentIndex, Node n1, Node n2 ) {
     1031            if( n1.equals(n2) )
     1032                throw new IllegalArgumentException("Both nodes are equal, id=" + n1.getUniqueId());
     1033            RingSegment segment = segments.get(segmentIndex);
     1034            boolean isRing = segment.isRing();
     1035            System.out.println("Split segment " + segment + " at nodes " + n1.getUniqueId() + " and " + n2.getUniqueId());
     1036            boolean reversed = segment.getNodes().indexOf(n2) < segment.getNodes().indexOf(n1);
     1037            if( reversed && !isRing ) {
     1038                // order nodes
     1039                Node tmp = n1;
     1040                n1 = n2;
     1041                n2 = tmp;
     1042            }
     1043            RingSegment secondPart = isRing ? segment.split(n1, n2) : segment.split(n1);
     1044            // if secondPart == null, then n1 == firstNode
     1045            RingSegment thirdPart = isRing ? null : secondPart == null ? segment.split(n2) : secondPart.split(n2);
     1046            // if secondPart == null, then thirdPart is between n1 and n2
     1047            // otherwise, thirdPart is between n2 and lastNode
     1048            // if thirdPart == null, then n2 == lastNode
     1049            int pos = segmentIndex + 1;
     1050            if( secondPart != null )
     1051                segments.add(pos++, secondPart);
     1052            if( thirdPart != null )
     1053                segments.add(pos++, thirdPart);
     1054            for( int i = segmentIndex; i < pos; i++ )
     1055                System.out.println("Split result "+(i-segmentIndex+1) + ": "+segments.get(i));
     1056            RingSegment result = isRing || secondPart == null ? segment : secondPart;
     1057            System.out.println("Returning segment " + result);
     1058            return result;
     1059        }
     1060       
     1061        /**
     1062         * Returns a list of commands to make a new relation and all newly created ways.
     1063         * The first way is copied from the source one, ChangeCommand is issued in this case.
     1064         */
     1065        public List<Command> getCommands() {
     1066            System.out.println("Making ring " + this);
     1067            Collection<String> linearTags = Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS);
     1068            relation = new Relation();
     1069            relation.put("type", "multipolygon");
     1070            Way sourceCopy = new Way(source);
     1071            for( String key : sourceCopy.keySet() ) {
     1072                if( !linearTags.contains(key) ) {
     1073                    relation.put(key, sourceCopy.get(key));
     1074                    sourceCopy.remove(key);
     1075                }
     1076            }
     1077           
     1078            // todo: copy relations from source to all new ways
     1079           
     1080            List<Command> commands = new ArrayList<Command>();
     1081            boolean foundOwnWay = false;
     1082            Way w;
     1083            for( RingSegment seg : segments ) {
     1084                if( seg.isWayConstructed() || seg.isReference() || foundOwnWay ) {
     1085                    boolean needAdding = !seg.isWayConstructed();
     1086                    w = seg.constructWay(seg.isReference() ? null : sourceCopy);
     1087                    if( needAdding )
     1088                        commands.add(new AddCommand(w));
     1089                } else {
     1090                    w = source;
     1091                    if( segments.size() == 1 ) {
     1092                        // one segment means that it is a ring
     1093                        List<Node> segnodes = seg.getNodes();
     1094                        segnodes.add(segnodes.get(0));
     1095                        sourceCopy.setNodes(segnodes);
     1096                    } else
     1097                        sourceCopy.setNodes(seg.getNodes());
     1098                    seg.overrideWay(source);
     1099                    commands.add(new ChangeCommand(source, sourceCopy));
     1100                    foundOwnWay = true;
     1101                }
     1102                relation.addMember(new RelationMember("outer", w));
     1103            }
     1104            if( !foundOwnWay )
     1105                commands.add(new DeleteCommand(source));
     1106            commands.add(new AddCommand(relation));
     1107            return commands;
     1108        }
     1109       
     1110        private List<Node> constructRing() {
     1111            List<Node> result = new ArrayList<Node>();
     1112            for( RingSegment segment : segments )
     1113                result.addAll(segment.getNodes());
     1114            return result;
     1115        }
     1116       
     1117        /**
     1118         * Returns the relation created in {@link #getCommands()}.
     1119         */
     1120        public Relation getRelation() {
     1121            return relation;
     1122        }
     1123
     1124        @Override
     1125        public String toString() {
     1126            StringBuilder sb = new StringBuilder("TheRing@");
     1127            sb.append(this.hashCode()).append('[').append("wayId: ").append(source == null ? "null" : source.getUniqueId()).append("; segments: ");
     1128            if( segments.isEmpty() )
     1129                sb.append("empty");
     1130            else {
     1131                sb.append(segments.get(0));
     1132                for( int i = 1; i < segments.size(); i++ )
     1133                    sb.append(", ").append(segments.get(i));
     1134            }
     1135            return sb.append(']').toString();
     1136        }       
     1137    }
     1138   
     1139    private static class RingSegment {
     1140        private List<Node> nodes;
     1141        private RingSegment references;
     1142        private Way resultingWay = null;
     1143        private boolean wasTemplateApplied = false;
     1144        private boolean isRing;
     1145       
     1146        private RingSegment() {}
     1147       
     1148        public RingSegment( Way w ) {
     1149            this(w.getNodes());
     1150        }
     1151       
     1152        public RingSegment( List<Node> nodes ) {
     1153            this.nodes = nodes;
     1154            isRing = nodes.size() > 1 && nodes.get(0).equals(nodes.get(nodes.size()-1));
     1155            if( isRing )
     1156                nodes.remove(nodes.size()-1);
     1157            references = null;
     1158        }
     1159       
     1160        public RingSegment( RingSegment ref ) {
     1161            this.nodes = null;
     1162            this.references = ref;
     1163        }
     1164       
     1165        /**
     1166         * Splits this segment at node n. Retains nodes 0..n and moves
     1167         * nodes n..N to a separate segment that is returned.
     1168         * @param n node at which to split.
     1169         * @return new segment, {@code null} if splitting is unnecessary.
     1170         */
     1171        public RingSegment split( Node n ) {
     1172            if( nodes == null )
     1173                throw new IllegalArgumentException("Cannot split segment: it is a reference");
     1174            int pos = nodes.indexOf(n);
     1175            if( pos <= 0 || pos >= nodes.size()-1 )
     1176                return null;
     1177            List<Node> newNodes = new ArrayList<Node>(nodes.subList(pos, nodes.size()));
     1178            nodes.subList(pos+1, nodes.size()).clear();
     1179            return new RingSegment(newNodes);
     1180        }
     1181       
     1182        /**
     1183         * Split this segment as a way at two nodes. If one of them is null or at the end,
     1184         * split as an arc. Note: order of nodes is important.
     1185         * @return A new segment from n2 to n1.
     1186         */
     1187        public RingSegment split( Node n1, Node n2 ) {
     1188            if( nodes == null )
     1189                throw new IllegalArgumentException("Cannot split segment: it is a reference");
     1190            if( !isRing ) {
     1191                if( n1 == null || nodes.get(0).equals(n1) || nodes.get(nodes.size()-1).equals(n1) )
     1192                    return split(n2);
     1193                if( n2 == null || nodes.get(0).equals(n2) || nodes.get(nodes.size()-1).equals(n2) )
     1194                    return split(n1);
     1195                throw new IllegalArgumentException("Split for two nodes is called for not-ring: " + this);
     1196            }
     1197            int pos1 = nodes.indexOf(n1);
     1198            int pos2 = nodes.indexOf(n2);
     1199            if( pos1 == pos2 )
     1200                return null;
     1201           
     1202            List<Node> newNodes = new ArrayList<Node>();
     1203            if( pos2 > pos1 ) {
     1204                newNodes.addAll(nodes.subList(pos2, nodes.size()));
     1205                newNodes.addAll(nodes.subList(0, pos1 + 1));
     1206                if( pos2+1 < nodes.size() )
     1207                    nodes.subList(pos2+1, nodes.size()).clear();
     1208                if( pos1 > 0 )
     1209                    nodes.subList(0, pos1).clear();
     1210            } else {
     1211                newNodes.addAll(nodes.subList(pos2, pos1+1));
     1212                nodes.addAll(new ArrayList<Node>(nodes.subList(0, pos2+1)));
     1213                nodes.subList(0, pos1).clear();
     1214            }
     1215            isRing = false;
     1216            return new RingSegment(newNodes);
     1217        }
     1218       
     1219        public List<Node> getNodes() {
     1220            return nodes == null ? references.nodes : nodes;
     1221        }
     1222       
     1223        public boolean isReference() {
     1224            return nodes == null;
     1225        }
     1226       
     1227        public boolean isRing() {
     1228            return isRing;
     1229        }
     1230       
     1231        public void makeReference( RingSegment segment ) {
     1232            this.nodes = null;
     1233            this.references = segment;
     1234        }
     1235       
     1236        public void swapReference() {
     1237            this.nodes = references.nodes;
     1238            references.nodes = null;
     1239            references.references = this;
     1240            this.references = null;
     1241        }
     1242       
     1243        public boolean isWayConstructed() {
     1244            return isReference() ? references.isWayConstructed() : resultingWay != null;
     1245        }
     1246       
     1247        public Way constructWay( Way template ) {
     1248            if( isReference() )
     1249                return references.constructWay(template);
     1250            if( resultingWay == null ) {
     1251                resultingWay = new Way();
     1252                resultingWay.setNodes(nodes);
     1253            }
     1254            if( template != null && !wasTemplateApplied ) {
     1255                resultingWay.setKeys(template.getKeys());
     1256                wasTemplateApplied = true;
     1257            }
     1258            return resultingWay;
     1259        }
     1260       
     1261        public void overrideWay( Way source ) {
     1262            if( isReference() )
     1263                references.overrideWay(source);
     1264            else {
     1265                resultingWay = source;
     1266                wasTemplateApplied = true;
     1267            }
     1268        }
     1269       
     1270        /**
     1271         * Compares two segments with respect to referencing.
     1272         * @return true if ways are equals, or one references another.
     1273         */
     1274        public boolean isReferencingEqual( RingSegment other ) {
     1275            return this.equals(other) || (other.isReference() && other.references == this ) || (isReference() && references == other);
     1276        }
     1277
     1278        @Override
     1279        public String toString() {
     1280            StringBuilder sb = new StringBuilder("RingSegment@");
     1281            sb.append(this.hashCode()).append('[');
     1282            if( isReference() )
     1283                sb.append("references ").append(references.hashCode());
     1284            else if( nodes.isEmpty() )
     1285                sb.append("empty");
     1286            else {
     1287                if( isRing )
     1288                    sb.append("ring:");
     1289                sb.append(nodes.get(0).getUniqueId());
     1290                for( int i = 1; i < nodes.size(); i++ )
     1291                    sb.append(',').append(nodes.get(i).getUniqueId());
     1292            }
     1293            return sb.append(']').toString();
     1294        }
     1295    }
    8751296}
Note: See TracChangeset for help on using the changeset viewer.