// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.czechaddress.actions;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JOptionPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.tools.Shortcut;
/**
* Splits all selected areas (= closed {@link Way}s) into separate areas.
* Their border line is any Way, which has no key-value pairs and which
* shares first and last node with the splitted area.
*
* WARNING: The current implementation does not handle relations. If the
* original area is a member of some relation, this action rejects to
* preform the split.
*
* @author Radomír Černoch, radomir.cernoch@gmail.com
*/
public class SplitAreaByEmptyWayAction extends JosmAction {
/**
* Defaults constructor, which registers itself into the JOSM menu.
*/
public SplitAreaByEmptyWayAction() {
super(tr("Split area"),
"splitarea.png",
tr("Splits an area by an untagged way."),
Shortcut.registerShortcut("tools:splitarea",
tr("Tool: {0}", tr("Split area")),
KeyEvent.VK_W, Shortcut.ALT_SHIFT),
true);
}
/**
* Goes over all selected areas (=closed Ways) and splits them into 2
* by any untagged way, which starts and ends at the border line of that
* area.
*/
@Override
public void actionPerformed(ActionEvent e) {
DataSet ds = Main.getLayerManager().getEditDataSet();
Collection<Way> selectedWays = ds.getSelectedWays();
Collection<Way> newSelection = new LinkedList<>(ds.getSelectedWays());
for (Way area : selectedWays) {
if (!area.isClosed()) continue;
for (OsmPrimitive prim2 : ds.allNonDeletedPrimitives()) {
if (!(prim2 instanceof Way)) continue;
if (prim2.equals(area)) continue;
Way border = (Way) prim2;
if (border.getNodes().isEmpty()) continue;
if (border.keySet().size() > 0) continue;
if (!area.getNodes().contains(border.firstNode())) continue;
if (!area.getNodes().contains(border.lastNode())) continue;
Way newArea1 = new Way();
Way newArea2 = new Way();
int errorCode = splitArea(area, border, newArea1, newArea2);
if (errorCode == 2) {
JOptionPane.showMessageDialog(Main.parent,
tr("The selected area cannot be splitted, because it is a member of some relation.\n"+
"Remove the area from the relation before splitting it."));
break;
}
if (errorCode == 0) {
ds.addPrimitive(newArea1);
ds.addPrimitive(newArea2);
area.setDeleted(true);
border.setDeleted(true);
newSelection.remove(area);
newSelection.remove(border);
newSelection.add(newArea1);
newSelection.add(newArea2);
break;
}
}
}
ds.setSelected(newSelection);
}
/**
* Splits a given area into 2 areas. newArea1 and newArea2 must be
* referneces to already existing areas.
*
* @param area the original area
* @param border border line, which goes across the area
* @param newArea1 reference to the first new area
* @param newArea2 reference to the second new area
*/
private int splitArea(Way area, Way border, Way newArea1, Way newArea2) {
for (Relation r : Main.getLayerManager().getEditDataSet().getRelations()) {
for (RelationMember rm : r.getMembers()) {
if (rm.refersTo(area) || rm.refersTo(border))
return 2;
}
}
List<Node> bordNodes = border.getNodes();
List<Node> areaNodes = area.getNodes();
int index1 = areaNodes.indexOf(bordNodes.get(0));
int index2 = areaNodes.indexOf(bordNodes.get(bordNodes.size()-1));
if (index1 == index2)
return 1;
if (index1 > index2) {
Collections.reverse(areaNodes);
index1 = areaNodes.indexOf(bordNodes.get(0));
index2 = areaNodes.indexOf(bordNodes.get(bordNodes.size()-1));
}
for (String key : area.keySet()) {
newArea1.put(key, area.get(key));
newArea2.put(key, area.get(key));
}
List<Node> newNodeList1 = newArea1.getNodes();
List<Node> newNodeList2 = newArea1.getNodes();
newNodeList1.addAll(areaNodes.subList(0, index1));
newNodeList1.addAll(bordNodes);
newNodeList1.addAll(areaNodes.subList(index2 + 1, areaNodes.size()));
Collections.reverse(bordNodes);
newNodeList2.addAll(areaNodes.subList(index1, index2));
newNodeList2.addAll(bordNodes);
newArea1.setNodes(newNodeList1);
newArea2.setNodes(newNodeList2);
return 0;
}
}