Ignore:
Timestamp:
2011-10-12T09:22:23+02:00 (13 years ago)
Author:
zverik
Message:

Refactor, new splitting option

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

Legend:

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

    r26290 r26837  
    516516            addMenuItem("tags", tr("Move area tags from contour to relation"));
    517517            addMenuItem("alltags", tr("When moving tags, consider even non-repeating ones"));
     518            addMenuItem("allowsplit", tr("Allow splitting of ways in neighbouring multipolygons"));
    518519        }
    519520
  • applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/CreateMultipolygonAction.java

    r26834 r26837  
    1212import org.openstreetmap.josm.actions.JosmAction;
    1313import org.openstreetmap.josm.command.*;
    14 import org.openstreetmap.josm.data.coor.EastNorth;
    1514import org.openstreetmap.josm.data.osm.*;
    16 import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1715import org.openstreetmap.josm.tools.GBC;
    18 import org.openstreetmap.josm.tools.Geometry;
    19 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
    2016import org.openstreetmap.josm.tools.Shortcut;
    2117import relcontext.ChosenRelation;
     
    5551        else if( property.equals("single") )
    5652            return true;
     53        else if( property.equals("allowsplit") )
     54            return false;
    5755        throw new IllegalArgumentException(property);
    5856    }
     
    6664        Collection<Way> selectedWays = getCurrentDataSet().getSelectedWays();
    6765        if( !isBoundary && getPref("tags") ) {
    68             if( selectedWays.size() == 1 && !selectedWays.iterator().next().isClosed() ) {
    69                 Relation newRelation = tryToCloseOneWay(selectedWays.iterator().next());
    70                 if( newRelation != null ) {
    71                     if( chRel != null )
    72                         chRel.set(newRelation);
    73                     getCurrentDataSet().setSelected(newRelation);
    74                     return;
    75                 }
    76             }
    77             if( areAllOfThoseRings(getCurrentDataSet().getSelectedWays()) ) {
    78                 List<Relation> rels = makeManySimpleMultipolygons(getCurrentDataSet().getSelectedWays());
     66            List<Command> commands = new ArrayList<Command>();
     67            List<Relation> rels = null;
     68            if( getPref("allowsplit")) {
     69                if( SplittingMultipolygons.canProcess(selectedWays) )
     70                    rels = SplittingMultipolygons.process(getCurrentDataSet().getSelectedWays(), commands);
     71            } else {
     72                if( TheRing.areAllOfThoseRings(selectedWays) )
     73                    rels = TheRing.makeManySimpleMultipolygons(getCurrentDataSet().getSelectedWays(), commands);
     74            }
     75            if( !commands.isEmpty() && rels != null && !rels.isEmpty() ) {
     76                Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygons from rings"), commands));
    7977                if( chRel != null )
    8078                    chRel.set(rels.size() == 1 ? rels.get(0) : null);
     
    405403        return true;
    406404    }
    407 
    408     private boolean areAllOfThoseRings( Collection<Way> ways ) {
    409         List<Way> rings = new ArrayList<Way>();
    410         for( Way way : ways ) {
    411             if( way.isClosed() )
    412                 rings.add(way);
    413             else
    414                 return false;
    415         }
    416         if( rings.isEmpty() || ways.size() == 1 )
    417             return false; // todo: for one ring, attach it to neares multipolygons
    418 
    419         // check for non-containment of rings
    420         for( int i = 0; i < rings.size() - 1; i++ ) {
    421             for( int j = i + 1; j < rings.size(); j++ ) {
    422                 PolygonIntersection intersection = Geometry.polygonIntersection(rings.get(i).getNodes(), rings.get(j).getNodes());
    423                 if( intersection == PolygonIntersection.FIRST_INSIDE_SECOND || intersection == PolygonIntersection.SECOND_INSIDE_FIRST )
    424                     return false;
    425             }
    426         }
    427 
    428         return true;
    429     }
    430 
    431     /**
    432      * Creates ALOT of Multipolygons and pets him gently.
    433      * @return list of new relations.
    434      */
    435     private List<Relation> makeManySimpleMultipolygons( Collection<Way> selection ) {
    436         System.out.println("---------------------------------------");
    437         List<TheRing> rings = new ArrayList<TheRing>(selection.size());
    438         for( Way w : selection )
    439             rings.add(new TheRing(w));
    440         for( int i = 0; i < rings.size()-1; i++ )
    441             for( int j = i+1; j < rings.size(); j++ )
    442                 rings.get(i).collide(rings.get(j));
    443         TheRing.redistributeSegments(rings);
    444         List<Command> commands = new ArrayList<Command>();
    445         List<Relation> relations = new ArrayList<Relation>();
    446         for( TheRing r : rings ) {
    447             commands.addAll(r.getCommands());
    448             relations.add(r.getRelation());
    449         }
    450         Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygons from rings"), commands));
    451         return relations;
    452     }
    453 
    454     /**
    455      * Appends "append" to "base" so the closed polygon forms.
    456      */
    457     private static void closePolygon( List<Node> base, List<Node> append ) {
    458         if( append.get(0).equals(base.get(0)) && append.get(append.size() - 1).equals(base.get(base.size() - 1)) ) {
    459             List<Node> ap2 = new ArrayList<Node>(append);
    460             Collections.reverse(ap2);
    461             append = ap2;
    462         }
    463         base.remove(base.size() - 1);
    464         base.addAll(append);
    465     }
    466 
    467     /**
    468      * Checks if a middle point between two nodes is inside a polygon. Useful to check if the way is inside.
    469      */
    470     private static boolean segmentInsidePolygon( Node n1, Node n2, List<Node> polygon ) {
    471         EastNorth en1 = n1.getEastNorth();
    472         EastNorth en2 = n2.getEastNorth();
    473         Node testNode = new Node(new EastNorth((en1.east() + en2.east()) / 2.0, (en1.north() + en2.north()) / 2.0));
    474         return Geometry.nodeInsidePolygon(testNode, polygon);
    475     }
    476 
    477     /**
    478      * Splits a way with regard to containing relations. This modifies the way and the relation, be prepared.
    479      * @param w The way.
    480      * @param n The node to split at.
    481      * @param commands List of commands to add way/relation changing to. If null, never mind.
    482      * @return Newly created ways. <b>Warning:</b> if commands is no not, newWays contains {@code w}.
    483      */
    484     public static List<Way> splitWay( Way w, Node n1, Node n2, List<Command> commands ) {
    485         List<Node> nodes = new ArrayList<Node>(w.getNodes());
    486         if( w.isClosed())
    487             nodes.remove(nodes.size()-1);
    488         int index1 = nodes.indexOf(n1);
    489         int index2 = n2 == null ? -1 : nodes.indexOf(n2);
    490         if( index1 > index2 ) {
    491             int tmp = index1;
    492             index1 = index2;
    493             index2 = tmp;
    494         }
    495         // right now index2 >= index1
    496         if( index2 < 1 || index1 >= w.getNodesCount() - 1 || index2 >= w.getNodesCount() )
    497             return Collections.emptyList();
    498         if( w.isClosed() && (index1 < 0 || index1 == index2 || index1 + w.getNodesCount() == index2) )
    499             return Collections.emptyList();
    500 
    501         // make a list of segments
    502         List<List<Node>> chunks = new ArrayList<List<Node>>(2);
    503         List<Node> chunk = new ArrayList<Node>();
    504         for( int i = 0; i < nodes.size(); i++ ) {
    505             chunk.add(nodes.get(i));
    506             if( (w.isClosed() || chunk.size() > 1) && (i == index1 || i == index2) ) {
    507                 chunks.add(chunk);
    508                 chunk = new ArrayList<Node>();
    509                 chunk.add(nodes.get(i));
    510             }
    511         }
    512         chunks.add(chunk);
    513 
    514         // for closed way ignore the way boundary
    515         if( w.isClosed() ) {
    516             chunks.get(chunks.size() - 1).addAll(chunks.get(0));
    517             chunks.remove(0);
    518         } else if( chunks.get(chunks.size()-1).size() < 2 )
    519             chunks.remove(chunks.size()-1);
    520        
    521         // todo remove debug: show chunks array contents
    522         /*for( List<Node> c1 : chunks ) {
    523             for( Node cn1 : c1 )
    524                 System.out.print(cn1.getId() + ",");
    525             System.out.println();
    526         }*/
    527 
    528         // build a map of referencing relations
    529         Map<Relation, Integer> references = new HashMap<Relation, Integer>();
    530         List<Command> relationCommands = new ArrayList<Command>();
    531         for( OsmPrimitive p : w.getReferrers() ) {
    532             if( p instanceof Relation ) {
    533                 Relation rel = commands == null ? (Relation)p : new Relation((Relation)p);
    534                 if( commands != null )
    535                     relationCommands.add(new ChangeCommand((Relation)p, rel));
    536                 for( int i = 0; i < rel.getMembersCount(); i++ )
    537                     if( rel.getMember(i).getMember().equals(w) )
    538                         references.put(rel, Integer.valueOf(i));
    539             }
    540         }
    541 
    542         // build ways
    543         List<Way> result = new ArrayList<Way>();
    544         Way updatedWay = commands == null ? w : new Way(w);
    545         updatedWay.setNodes(chunks.get(0));
    546         if( commands != null ) {
    547             commands.add(new ChangeCommand(w, updatedWay));
    548             result.add(updatedWay);
    549         }
    550 
    551         for( int i = 1; i < chunks.size(); i++ ) {
    552             List<Node> achunk = chunks.get(i);
    553             Way newWay = new Way();
    554             newWay.setKeys(w.getKeys());
    555             result.add(newWay);
    556             for( Relation rel : references.keySet() ) {
    557                 int relIndex = references.get(rel);
    558                 rel.addMember(relIndex + 1, new RelationMember(rel.getMember(relIndex).getRole(), newWay));
    559             }
    560             newWay.setNodes(achunk);
    561             if( commands != null )
    562                 commands.add(new AddCommand(newWay));
    563         }
    564         if( commands != null )
    565             commands.addAll(relationCommands);
    566         return result;
    567     }
    568 
    569     public static List<Way> splitWay( Way w, Node n1, Node n2 ) {
    570         return splitWay(w, n1, n2, null);
    571     }
    572 
    573     /**
    574      * Find a way the tips of a segment, ensure it's in a multipolygon and try to close the relation.
    575      */
    576     private Relation tryToCloseOneWay( Way segment ) {
    577         if( segment.isClosed() || segment.isIncomplete() )
    578             return null;
    579 
    580         List<Way> ways = intersection(
    581                 OsmPrimitive.getFilteredList(segment.firstNode().getReferrers(), Way.class),
    582                 OsmPrimitive.getFilteredList(segment.lastNode().getReferrers(), Way.class));
    583         ways.remove(segment);
    584         for( Iterator<Way> iter = ways.iterator(); iter.hasNext(); ) {
    585             boolean save = false;
    586             for( OsmPrimitive ref : iter.next().getReferrers() )
    587                 if( ref instanceof Relation && ((Relation)ref).isMultipolygon() && !ref.isDeleted() )
    588                     save = true;
    589             if( !save )
    590                 iter.remove();
    591         }
    592         if( ways.isEmpty() )
    593             return null; // well...
    594         Way target = ways.get(0);
    595 
    596         // time to create a new multipolygon relation and a command stack
    597         List<Command> commands = new ArrayList<Command>();
    598         Relation newRelation = new Relation();
    599         newRelation.put("type", "multipolygon");
    600         newRelation.addMember(new RelationMember("outer", segment));
    601         Collection<String> linearTags = Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS);
    602         Way segmentCopy = new Way(segment);
    603         boolean changed = false;
    604         for( String key : segmentCopy.keySet() ) {
    605             if( !linearTags.contains(key) ) {
    606                 newRelation.put(key, segmentCopy.get(key));
    607                 segmentCopy.remove(key);
    608                 changed = true;
    609             }
    610         }
    611         if( changed )
    612             commands.add(new ChangeCommand(segment, segmentCopy));
    613 
    614         // now split the way, at last
    615         List<Way> newWays = new ArrayList<Way>(splitWay(target, segment.firstNode(), segment.lastNode(), commands));
    616 
    617         Way addingWay = null;
    618         if( target.isClosed() ) {
    619             Way utarget = newWays.get(1);
    620             Way alternate = newWays.get(0);
    621             List<Node> testRing = new ArrayList<Node>(segment.getNodes());
    622             closePolygon(testRing, utarget.getNodes());
    623             addingWay = segmentInsidePolygon(alternate.getNode(0), alternate.getNode(1), testRing) ? alternate : utarget;
    624         } else {
    625             for( Way w : newWays ) {
    626                 if( (w.firstNode().equals(segment.firstNode()) && w.lastNode().equals(segment.lastNode()))
    627                         || (w.firstNode().equals(segment.lastNode()) && w.lastNode().equals(segment.firstNode())) ) {
    628                     addingWay = w;
    629                     break;
    630                 }
    631             }
    632         }
    633         newRelation.addMember(new RelationMember("outer", addingWay.getUniqueId() == target.getUniqueId() ? target : addingWay));
    634         commands.add(new AddCommand(newRelation));
    635         Main.main.undoRedo.add(new SequenceCommand(tr("Complete multipolygon for way {0}",
    636                 DefaultNameFormatter.getInstance().format(segment)), commands));
    637         return newRelation;
    638     }
    639 
    640     /**
    641      * Returns all elements from {@code list1} that are in {@code list2}.
    642      */
    643     private static <T> List<T> intersection( Collection<T> list1, Collection<T> list2 ) {
    644         List<T> result = new ArrayList<T>();
    645         for( T item : list1 )
    646             if( list2.contains(item) )
    647                 result.add(item);
    648         return result;
    649     }
    650405}
  • applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/TheRing.java

    r26834 r26837  
    88import org.openstreetmap.josm.data.osm.*;
    99import org.openstreetmap.josm.tools.Geometry;
     10import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
    1011
    1112/**
     
    2829    }
    2930   
     31    public static boolean areAllOfThoseRings( Collection<Way> ways ) {
     32        List<Way> rings = new ArrayList<Way>();
     33        for( Way way : ways ) {
     34            if( way.isClosed() )
     35                rings.add(way);
     36            else
     37                return false;
     38        }
     39        if( rings.isEmpty() || ways.size() == 1 )
     40            return false;
     41
     42        // check for non-containment of rings
     43        for( int i = 0; i < rings.size() - 1; i++ ) {
     44            for( int j = i + 1; j < rings.size(); j++ ) {
     45                PolygonIntersection intersection = Geometry.polygonIntersection(rings.get(i).getNodes(), rings.get(j).getNodes());
     46                if( intersection == PolygonIntersection.FIRST_INSIDE_SECOND || intersection == PolygonIntersection.SECOND_INSIDE_FIRST )
     47                    return false;
     48            }
     49        }
     50
     51        return true;
     52    }
     53
     54    /**
     55     * Creates ALOT of Multipolygons and pets him gently.
     56     * @return list of new relations.
     57     */
     58    public static List<Relation> makeManySimpleMultipolygons( Collection<Way> selection, List<Command> commands ) {
     59        System.out.println("---------------------------------------");
     60        List<TheRing> rings = new ArrayList<TheRing>(selection.size());
     61        for( Way w : selection )
     62            rings.add(new TheRing(w));
     63        for( int i = 0; i < rings.size() - 1; i++ )
     64            for( int j = i + 1; j < rings.size(); j++ )
     65                rings.get(i).collide(rings.get(j));
     66        redistributeSegments(rings);
     67        List<Relation> relations = new ArrayList<Relation>();
     68        for( TheRing r : rings ) {
     69            commands.addAll(r.getCommands());
     70            relations.add(r.getRelation());
     71        }
     72        return relations;
     73    }
     74
    3075    public void collide( TheRing other ) {
    3176        boolean collideNoted = false;
Note: See TracChangeset for help on using the changeset viewer.