Index: src/org/openstreetmap/josm/actions/JoinAreasAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/JoinAreasAction.java	(revision 3648)
+++ src/org/openstreetmap/josm/actions/JoinAreasAction.java	(working copy)
@@ -47,6 +47,7 @@
 import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
 
+
 /**
  * Join Areas (i.e. closed ways and multipolygons)
  */
@@ -401,7 +402,7 @@
         }
 
         //find intersection points
-        ArrayList<Node> nodes = Geometry.addIntersections(allStartingWays, true, cmds);
+        Set<Node> nodes = Geometry.addIntersections(allStartingWays, true, cmds);
         return nodes.size() > 0;
     }
 
@@ -437,7 +438,7 @@
         }
 
         //find intersection points
-        ArrayList<Node> nodes = Geometry.addIntersections(allStartingWays, false, cmds);
+        Set<Node> nodes = Geometry.addIntersections(allStartingWays, false, cmds);
 
         //no intersections, return.
         if (nodes.isEmpty())
@@ -473,8 +474,11 @@
         //find polygons
         List<AssembledMultipolygon> preparedPolygons = findPolygons(bounadries);
 
+
         //assemble final polygons
         List<Multipolygon> polygons = new ArrayList<Multipolygon>();
+        Set<Relation> relationsToDelete = new LinkedHashSet<Relation>();
+
         for (AssembledMultipolygon pol : preparedPolygons) {
 
             //create the new ways
@@ -484,7 +488,7 @@
             RelationRole ownMultipolygonRelation = addOwnMultigonRelation(resultPol.innerWays, resultPol.outerWay);
 
             //add back the original relations, merged with our new multipolygon relation
-            fixRelations(relations, resultPol.outerWay, ownMultipolygonRelation);
+            fixRelations(relations, resultPol.outerWay, ownMultipolygonRelation, relationsToDelete);
 
             //strip tags from inner ways
             //TODO: preserve tags on existing inner ways
@@ -495,6 +499,12 @@
 
         commitCommands(marktr("Assemble new polygons"));
 
+        for(Relation rel: relationsToDelete) {
+            cmds.add(new DeleteCommand(rel));
+        }
+
+        commitCommands(marktr("Delete relations"));
+
         // Delete the discarded inner ways
         if (discardedWays.size() > 0) {
             cmds.add(DeleteCommand.delete(Main.map.mapView.getEditLayer(), discardedWays, true));
@@ -827,7 +837,7 @@
      * Uses  SplitWayAction.splitWay for the heavy lifting.
      * @return list of split ways (or original ways if no splitting is done).
      */
-    private ArrayList<Way> splitWayOnNodes(Way way, Collection<Node> nodes) {
+    private ArrayList<Way> splitWayOnNodes(Way way, Set<Node> nodes) {
 
         ArrayList<Way> result = new ArrayList<Way>();
         List<List<Node>> chunks = buildNodeChunks(way, nodes);
@@ -1094,8 +1104,9 @@
      */
     public static boolean wayInsideWay(AssembledPolygon inside, AssembledPolygon outside) {
         Set<Node> outsideNodes = new HashSet<Node>(outside.getNodes());
+        List<Node> insideNodes = inside.getNodes();
 
-        for (Node insideNode : inside.getNodes()) {
+        for (Node insideNode : insideNodes) {
 
             if (!outsideNodes.contains(insideNode))
                 //simply test the one node
@@ -1363,8 +1374,9 @@
      * members of both. This function depends on multigon relations to be valid already, it won't fix them.
      * @param ArrayList<RelationRole> List of relations with roles the (original) ways were part of
      * @param Way The newly created outer area/way
+     * @param relationsToDelete - set of relations to delete.
      */
-    private void fixRelations(ArrayList<RelationRole> rels, Way outer, RelationRole ownMultipol) {
+    private void fixRelations(ArrayList<RelationRole> rels, Way outer, RelationRole ownMultipol, Set<Relation> relationsToDelete) {
         ArrayList<RelationRole> multiouters = new ArrayList<RelationRole>();
 
         if (ownMultipol != null){
@@ -1409,7 +1421,7 @@
                     newRel.put(key, r.rel.get(key));
                 }
                 // Delete old relation
-                cmds.add(new DeleteCommand(r.rel));
+                relationsToDelete.add(r.rel);
             }
             newRel.addMember(new RelationMember("outer", outer));
             cmds.add(new AddCommand(newRel));
Index: src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(revision 3648)
+++ src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(working copy)
@@ -40,6 +40,7 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.MapViewPaintable;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.tools.Geometry;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -305,7 +306,7 @@
 
                     //find if the new points overlap existing segments (in case of 90 degree angles)
                     Node prevNode = getPreviousNode(selectedSegment.lowerIndex);
-                    boolean nodeOverlapsSegment = prevNode != null && segmentsParralel(initialN1en, prevNode.getEastNorth(), initialN1en, newN1en);
+                    boolean nodeOverlapsSegment = prevNode != null && Geometry.segmentsParralel(initialN1en, prevNode.getEastNorth(), initialN1en, newN1en);
                     boolean hasOtherWays = this.hasNodeOtherWays(selectedSegment.getFirstNode(), selectedSegment.way);
 
                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
@@ -322,7 +323,7 @@
 
                     //find if the new points overlap existing segments (in case of 90 degree angles)
                     Node nextNode = getNextNode(selectedSegment.lowerIndex + 1);
-                    nodeOverlapsSegment = nextNode != null && segmentsParralel(initialN2en, nextNode.getEastNorth(), initialN2en, newN2en);
+                    nodeOverlapsSegment = nextNode != null && Geometry.segmentsParralel(initialN2en, nextNode.getEastNorth(), initialN2en, newN2en);
                     hasOtherWays = hasNodeOtherWays(selectedSegment.getSecondNode(), selectedSegment.way);
 
                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
@@ -386,7 +387,7 @@
      */
     private static EastNorth calculateSegmentOffset(EastNorth segmentP1, EastNorth segmentP2, EastNorth moveDirection,
             EastNorth targetPos) {
-        EastNorth intersectionPoint = getLineLineIntersection(segmentP1, segmentP2, targetPos,
+        EastNorth intersectionPoint = Geometry.getLineLineIntersection(segmentP1, segmentP2, targetPos,
                 new EastNorth(targetPos.getX() + moveDirection.getX(), targetPos.getY() + moveDirection.getY()));
 
         if (intersectionPoint == null)
@@ -394,45 +395,10 @@
         else
             //return distance form base to target position
             return new EastNorth(targetPos.getX() - intersectionPoint.getX(),
-                        targetPos.getY() - intersectionPoint.getY());
+                    targetPos.getY() - intersectionPoint.getY());
     }
 
-    /**
-     * Finds the intersection of two lines of infinite length.
-     * @return EastNorth null if no intersection was found, the coordinates of the intersection otherwise
-     */
-    public static EastNorth getLineLineIntersection(EastNorth p1, EastNorth p2, EastNorth p3, EastNorth p4) {
-        // Convert line from (point, point) form to ax+by=c
-        double a1 = p2.getY() - p1.getY();
-        double b1 = p1.getX() - p2.getX();
-        double c1 = p2.getX() * p1.getY() - p1.getX() * p2.getY();
 
-        double a2 = p4.getY() - p3.getY();
-        double b2 = p3.getX() - p4.getX();
-        double c2 = p4.getX() * p3.getY() - p3.getX() * p4.getY();
-
-        // Solve the equations
-        double det = a1 * b2 - a2 * b1;
-        if (det == 0)
-            return null; // Lines are parallel
-
-        return new EastNorth((b1 * c2 - b2 * c1) / det, (a2 * c1 - a1 * c2) / det);
-    }
-
-    private static boolean segmentsParralel(EastNorth p1, EastNorth p2, EastNorth p3, EastNorth p4) {
-
-        // Convert line from (point, point) form to ax+by=c
-        double a1 = p2.getY() - p1.getY();
-        double b1 = p1.getX() - p2.getX();
-
-        double a2 = p4.getY() - p3.getY();
-        double b2 = p3.getX() - p4.getX();
-
-        // Solve the equations
-        double det = a1 * b2 - a2 * b1;
-        return Math.abs(det) < 1e-13;
-    }
-
     /**
      * Gets a node from selected way before given index.
      * @param index  index of current node
Index: src/org/openstreetmap/josm/data/osm/NodePositionComparator.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/NodePositionComparator.java	(revision 3648)
+++ src/org/openstreetmap/josm/data/osm/NodePositionComparator.java	(working copy)
@@ -11,6 +11,10 @@
 public class NodePositionComparator implements Comparator<Node> {
     @Override
     public int compare(Node n1, Node n2) {
+
+        if (n1.getCoor().equalsEpsilon(n2.getCoor()))
+            return 0;
+
         double dLat = n1.getCoor().lat() - n2.getCoor().lat();
         if (dLat > 0)
             return 1;
Index: src/org/openstreetmap/josm/tools/Geometry.java
===================================================================
--- src/org/openstreetmap/josm/tools/Geometry.java	(revision 3648)
+++ src/org/openstreetmap/josm/tools/Geometry.java	(working copy)
@@ -4,15 +4,17 @@
 import java.awt.geom.Line2D;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.AddCommand;
 import org.openstreetmap.josm.command.ChangeCommand;
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.NodePositionComparator;
 import org.openstreetmap.josm.data.osm.Way;
@@ -23,6 +25,10 @@
  * @author viesturs
  */
 public class Geometry {
+    public enum PolygonIntersection {FIRST_INSIDE_SECOND, SECOND_INSIDE_FIRST, OUTSIDE, CROSSING}
+
+
+
     /**
      * Will find all intersection and add nodes there for list of given ways. Handles self-intersections too.
      * And make commands to add the intersection points to ways.
@@ -30,154 +36,141 @@
      * @return ArrayList<Node> List of new nodes
      * Prerequisite: no two nodes have the same coordinates.
      */
-    public static ArrayList<Node> addIntersections(List<Way> ways, boolean test, List<Command> cmds) {
-        //TODO: this is a bit slow - O( (number of nodes)^2 + numberOfIntersections * numberOfNodes )
+    public static Set<Node> addIntersections(List<Way> ways, boolean test, List<Command> cmds) {
 
         //stupid java, cannot instantiate array of generic classes..
         @SuppressWarnings("unchecked")
         ArrayList<Node>[] newNodes = new ArrayList[ways.size()];
+        BBox[] wayBounds = new BBox[ways.size()];
         boolean[] changedWays = new boolean[ways.size()];
 
         Set<Node> intersectionNodes = new LinkedHashSet<Node>();
 
+        //copy node arrays for local usage.
         for (int pos = 0; pos < ways.size(); pos ++) {
             newNodes[pos] = new ArrayList<Node>(ways.get(pos).getNodes());
+            wayBounds[pos] = getNodesBounds(newNodes[pos]);
             changedWays[pos] = false;
         }
 
-        //iterate over all segment pairs and introduce the intersections
-
+        //iterate over all way pairs and introduce the intersections
         Comparator<Node> coordsComparator = new NodePositionComparator();
 
-        int seg1Way = 0;
-        int seg1Pos = -1;
+        WayLoop: for (int seg1Way = 0; seg1Way < ways.size(); seg1Way ++) {
+            for (int seg2Way = seg1Way; seg2Way < ways.size(); seg2Way ++) {
 
-        while (true) {
-            //advance to next segment
-            seg1Pos++;
-            if (seg1Pos > newNodes[seg1Way].size() - 2) {
-                seg1Way++;
-                seg1Pos = 0;
-
-                if (seg1Way == ways.size()) { //finished
-                    break;
+                //do not waste time on bounds that do not intersect
+                if (!wayBounds[seg1Way].intersects(wayBounds[seg2Way])) {
+                    continue;
                 }
-            }
 
+                ArrayList<Node> way1Nodes = newNodes[seg1Way];
+                ArrayList<Node> way2Nodes = newNodes[seg2Way];
 
-            //iterate over secondary segment
+                //iterate over primary segmemt
+                for (int seg1Pos = 0; seg1Pos + 1 < way1Nodes.size(); seg1Pos ++) {
 
-            int seg2Way = seg1Way;
-            int seg2Pos = seg1Pos + 1;//skip the adjacent segment
+                    //iterate over secondary segment
+                    int seg2Start = seg1Way != seg2Way ? 0: seg1Pos + 2;//skip the adjacent segment
 
-            while (true) {
+                    for (int seg2Pos = seg2Start; seg2Pos + 1< way2Nodes.size(); seg2Pos ++) {
 
-                //advance to next segment
-                seg2Pos++;
-                if (seg2Pos > newNodes[seg2Way].size() - 2) {
-                    seg2Way++;
-                    seg2Pos = 0;
+                        //need to get them again every time, because other segments may be changed
+                        Node seg1Node1 = way1Nodes.get(seg1Pos);
+                        Node seg1Node2 = way1Nodes.get(seg1Pos + 1);
+                        Node seg2Node1 = way2Nodes.get(seg2Pos);
+                        Node seg2Node2 = way2Nodes.get(seg2Pos + 1);
 
-                    if (seg2Way == ways.size()) { //finished
-                        break;
-                    }
-                }
+                        int commonCount = 0;
+                        //test if we have common nodes to add.
+                        if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) {
+                            commonCount ++;
 
-                //need to get them again every time, because other segments may be changed
-                Node seg1Node1 = newNodes[seg1Way].get(seg1Pos);
-                Node seg1Node2 = newNodes[seg1Way].get(seg1Pos + 1);
-                Node seg2Node1 = newNodes[seg2Way].get(seg2Pos);
-                Node seg2Node2 = newNodes[seg2Way].get(seg2Pos + 1);
+                            if (seg1Way == seg2Way &&
+                                    seg1Pos == 0 &&
+                                    seg2Pos == way2Nodes.size() -2) {
+                                //do not add - this is first and last segment of the same way.
+                            } else {
+                                intersectionNodes.add(seg1Node1);
+                            }
+                        }
 
-                int commonCount = 0;
-                //test if we have common nodes to add.
-                if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) {
-                    commonCount ++;
+                        if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) {
+                            commonCount ++;
 
-                    if (seg1Way == seg2Way &&
-                            seg1Pos == 0 &&
-                            seg2Pos == newNodes[seg2Way].size() -2) {
-                        //do not add - this is first and last segment of the same way.
-                    } else {
-                        intersectionNodes.add(seg1Node1);
-                    }
-                }
+                            intersectionNodes.add(seg1Node2);
+                        }
 
-                if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) {
-                    commonCount ++;
+                        //no common nodes - find intersection
+                        if (commonCount == 0) {
+                            EastNorth intersection = getSegmentSegmentIntersection(
+                                    seg1Node1.getEastNorth(), seg1Node2.getEastNorth(),
+                                    seg2Node1.getEastNorth(), seg2Node2.getEastNorth());
 
-                    intersectionNodes.add(seg1Node2);
-                }
+                            if (intersection != null) {
+                                if (test) {
+                                    intersectionNodes.add(seg2Node1);
+                                    return intersectionNodes;
+                                }
 
-                //no common nodes - find intersection
-                if (commonCount == 0) {
-                    LatLon intersection = getLineLineIntersection(
-                            seg1Node1.getEastNorth().east(), seg1Node1.getEastNorth().north(),
-                            seg1Node2.getEastNorth().east(), seg1Node2.getEastNorth().north(),
-                            seg2Node1.getEastNorth().east(), seg2Node1.getEastNorth().north(),
-                            seg2Node2.getEastNorth().east(), seg2Node2.getEastNorth().north());
+                                Node newNode = new Node(Main.proj.eastNorth2latlon(intersection));
+                                Node intNode = newNode;
+                                boolean insertInSeg1 = false;
+                                boolean insertInSeg2 = false;
 
-                    if (intersection != null) {
-                        if (test) {
-                            intersectionNodes.add(seg2Node1);
-                            return new ArrayList<Node>(intersectionNodes);
-                        }
+                                //find if the intersection point is at end point of one of the segments, if so use that point
 
-                        Node newNode = new Node(intersection);
-                        Node intNode = newNode;
-                        boolean insertInSeg1 = false;
-                        boolean insertInSeg2 = false;
+                                //segment 1
+                                if (coordsComparator.compare(newNode, seg1Node1) == 0) {
+                                    intNode = seg1Node1;
+                                } else if (coordsComparator.compare(newNode, seg1Node2) == 0) {
+                                    intNode = seg1Node2;
+                                } else {
+                                    insertInSeg1 = true;
+                                }
 
-                        //find if the intersection point is at end point of one of the segments, if so use that point
+                                //segment 2
+                                if (coordsComparator.compare(newNode, seg2Node1) == 0) {
+                                    intNode = seg2Node1;
+                                } else if (coordsComparator.compare(newNode, seg2Node2) == 0) {
+                                    intNode = seg2Node2;
+                                } else {
+                                    insertInSeg2 = true;
+                                }
 
-                        //segment 1
-                        if (coordsComparator.compare(newNode, seg1Node1) == 0) {
-                            intNode = seg1Node1;
-                        } else if (coordsComparator.compare(newNode, seg1Node2) == 0) {
-                            intNode = seg1Node2;
-                        } else {
-                            insertInSeg1 = true;
-                        }
+                                if (insertInSeg1) {
+                                    way1Nodes.add(seg1Pos +1, intNode);
+                                    changedWays[seg1Way] = true;
 
-                        //segment 2
-                        if (coordsComparator.compare(newNode, seg2Node1) == 0) {
-                            intNode = seg2Node1;
-                        } else if (coordsComparator.compare(newNode, seg2Node2) == 0) {
-                            intNode = seg2Node2;
-                        } else {
-                            insertInSeg2 = true;
-                        }
+                                    //fix seg2 position, as indexes have changed, seg2Pos is always bigger than seg1Pos on the same segment.
+                                    if (seg2Way == seg1Way) {
+                                        seg2Pos ++;
+                                    }
+                                }
 
-                        if (insertInSeg1) {
-                            newNodes[seg1Way].add(seg1Pos +1, intNode);
-                            changedWays[seg1Way] = true;
+                                if (insertInSeg2) {
+                                    way2Nodes.add(seg2Pos +1, intNode);
+                                    changedWays[seg2Way] = true;
 
-                            //fix seg2 position, as indexes have changed, seg2Pos is always bigger than seg1Pos on the same segment.
-                            if (seg2Way == seg1Way) {
-                                seg2Pos ++;
-                            }
-                        }
+                                    //Do not need to compare again to already split segment
+                                    seg2Pos ++;
+                                }
 
-                        if (insertInSeg2) {
-                            newNodes[seg2Way].add(seg2Pos +1, intNode);
-                            changedWays[seg2Way] = true;
+                                intersectionNodes.add(intNode);
 
-                            //Do not need to compare again to already split segment
-                            seg2Pos ++;
+                                if (intNode == newNode) {
+                                    cmds.add(new AddCommand(intNode));
+                                }
+                            }
                         }
-
-                        intersectionNodes.add(intNode);
-
-                        if (intNode == newNode) {
-                            cmds.add(new AddCommand(intNode));
-                        }
+                        else if (test && intersectionNodes.size() > 0)
+                            return intersectionNodes;
                     }
                 }
-                else if (test && intersectionNodes.size() > 0)
-                    return new ArrayList<Node>(intersectionNodes);
             }
         }
 
+
         for (int pos = 0; pos < ways.size(); pos ++) {
             if (changedWays[pos] == false) {
                 continue;
@@ -190,38 +183,23 @@
             cmds.add(new ChangeCommand(way, newWay));
         }
 
-        return new ArrayList<Node>(intersectionNodes);
+        return intersectionNodes;
     }
 
-    /**
-     * Finds the intersection of two lines
-     * @return LatLon null if no intersection was found, the LatLon coordinates of the intersection otherwise
-     */
-    static private LatLon getLineLineIntersection(
-            double x1, double y1, double x2, double y2,
-            double x3, double y3, double x4, double y4) {
 
-        if (!Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return null;
 
-        // Convert line from (point, point) form to ax+by=c
-        double a1 = y2 - y1;
-        double b1 = x1 - x2;
-        double c1 = x2*y1 - x1*y2;
+    private static BBox getNodesBounds(ArrayList<Node> nodes) {
 
-        double a2 = y4 - y3;
-        double b2 = x3 - x4;
-        double c2 = x4*y3 - x3*y4;
+        BBox bounds = new BBox(nodes.get(0));
 
-        // Solve the equations
-        double det = a1*b2 - a2*b1;
-        if (det == 0) return null; // Lines are parallel
+        for(Node n: nodes) {
+            bounds.add(n.getCoor());
+        }
 
-        return Main.proj.eastNorth2latlon(new EastNorth(
-                (b1*c2 - b2*c1)/det,
-                (a2*c1 -a1*c2)/det
-        ));
+        return bounds;
     }
-    
+
+
     /**
      * Tests if given point is to the right side of path consisting of 3 points.
      * @param lineP1 first point in path
@@ -258,13 +236,201 @@
     }
 
     /**
+     * Finds the intersection of two line segments
+     * @return EastNorth null if no intersection was found, the EastNorth coordinates of the intersection otherwise
+     */
+    static public EastNorth getSegmentSegmentIntersection(
+            EastNorth p1, EastNorth p2,
+            EastNorth p3, EastNorth p4) {
+        double x1 = p1.getX();
+        double y1 = p1.getY();
+        double x2 = p2.getX();
+        double y2 = p2.getY();
+        double x3 = p3.getX();
+        double y3 = p3.getY();
+        double x4 = p4.getX();
+        double y4 = p4.getY();
+
+        //TODO: do this locally.
+        if (!Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return null;
+
+        // Convert line from (point, point) form to ax+by=c
+        double a1 = y2 - y1;
+        double b1 = x1 - x2;
+        double c1 = x2*y1 - x1*y2;
+
+        double a2 = y4 - y3;
+        double b2 = x3 - x4;
+        double c2 = x4*y3 - x3*y4;
+
+        // Solve the equations
+        double det = a1*b2 - a2*b1;
+        if (det == 0) return null; // Lines are parallel
+
+        double x = (b1*c2 - b2*c1)/det;
+        double y = (a2*c1 -a1*c2)/det;
+
+        return new EastNorth(x, y);
+    }
+
+    /**
+     * Finds the intersection of two lines of infinite length.
+     * @return EastNorth null if no intersection was found, the coordinates of the intersection otherwise
+     */
+    public static EastNorth getLineLineIntersection(EastNorth p1, EastNorth p2, EastNorth p3, EastNorth p4) {
+
+        // Convert line from (point, point) form to ax+by=c
+        double a1 = p2.getY() - p1.getY();
+        double b1 = p1.getX() - p2.getX();
+        double c1 = p2.getX() * p1.getY() - p1.getX() * p2.getY();
+
+        double a2 = p4.getY() - p3.getY();
+        double b2 = p3.getX() - p4.getX();
+        double c2 = p4.getX() * p3.getY() - p3.getX() * p4.getY();
+
+        // Solve the equations
+        double det = a1 * b2 - a2 * b1;
+        if (det == 0)
+            return null; // Lines are parallel
+
+        return new EastNorth((b1 * c2 - b2 * c1) / det, (a2 * c1 - a1 * c2) / det);
+    }
+
+    public static boolean segmentsParralel(EastNorth p1, EastNorth p2, EastNorth p3, EastNorth p4) {
+
+        // Convert line from (point, point) form to ax+by=c
+        double a1 = p2.getY() - p1.getY();
+        double b1 = p1.getX() - p2.getX();
+
+        double a2 = p4.getY() - p3.getY();
+        double b2 = p3.getX() - p4.getX();
+
+        // Solve the equations
+        double det = a1 * b2 - a2 * b1;
+        return Math.abs(det) < 1e-13;
+    }
+
+    /**
+     * Calculates closest point to a line segment.
+     * @param segmentP1
+     * @param segmentP2
+     * @param point
+     * @return segmentP1 if it is the closest point, segmentP2 if it is the closest point,
+     * a new point if closest point is between segmentP1 and segmentP2.
+     */
+    public static EastNorth closestPointToSegment(EastNorth segmentP1, EastNorth segmentP2, EastNorth point) {
+
+        double ldx = segmentP2.getX() - segmentP1.getX();
+        double ldy = segmentP2.getY() - segmentP1.getY();
+
+        if (ldx == 0 && ldy == 0) //segment zero length
+            return segmentP1;
+
+        double pdx = point.getX() - segmentP1.getX();
+        double pdy = point.getY() - segmentP1.getY();
+
+        double offset = (pdx * ldx + pdy * ldy) / (ldx * ldx + ldy * ldy);
+
+        if (offset <= 0)
+            return segmentP1;
+        else if (offset >= 1)
+            return segmentP2;
+        else
+            return new EastNorth(segmentP1.getX() + ldx * offset, segmentP1.getY() + ldy * offset);
+
+    }
+
+    /**
+     * This method tests if secondNode is clockwise to first node.
+     * @param commonNode starting point for both vectors
+     * @param firstNode first vector end node
+     * @param secondNode second vector end node
+     * @return true if first vector is clockwise before second vector.
+     */
+
+    public static boolean angleIsClockwise(EastNorth commonNode, EastNorth firstNode, EastNorth secondNode) {
+        double dy1 = (firstNode.getY() - commonNode.getY());
+        double dy2 = (secondNode.getY() - commonNode.getY());
+        double dx1 = (firstNode.getX() - commonNode.getX());
+        double dx2 = (secondNode.getX() - commonNode.getX());
+
+        return dy1 * dx2 - dx1 * dy2 > 0;
+    }
+
+
+    /**
+     * Tests if two polygons intersect.
+     * @param first
+     * @param second
+     * @return intersection kind
+     * TODO: test segments, not only points
+     * TODO: is O(N*M), should use sweep for better performance.
+     */
+    public static PolygonIntersection polygonIntersection(List<Node> first, List<Node> second) {
+        Set<Node> firstSet = new HashSet<Node>(first);
+        Set<Node> secondSet = new HashSet<Node>(second);
+
+        int nodesInsideSecond = 0;
+        int nodesOutsideSecond = 0;
+        int nodesInsideFirst = 0;
+        int nodesOutsideFirst = 0;
+
+        for (Node insideNode : first) {
+            if (secondSet.contains(insideNode)) {
+                continue;
+                //ignore touching nodes.
+            }
+
+            if (nodeInsidePolygon(insideNode, second)) {
+                nodesInsideSecond ++;
+            }
+            else {
+                nodesOutsideSecond ++;
+            }
+        }
+
+        for (Node insideNode : second) {
+            if (firstSet.contains(insideNode)) {
+                continue;
+                //ignore touching nodes.
+            }
+
+            if (nodeInsidePolygon(insideNode, first)) {
+                nodesInsideFirst ++;
+            }
+            else {
+                nodesOutsideFirst ++;
+            }
+        }
+
+        if (nodesInsideFirst == 0) {
+            if (nodesInsideSecond == 0){
+                if (nodesOutsideFirst + nodesInsideSecond > 0)
+                    return PolygonIntersection.OUTSIDE;
+                else
+                    //all nodes common
+                    return PolygonIntersection.CROSSING;
+            } else
+                return PolygonIntersection.FIRST_INSIDE_SECOND;
+        }
+        else
+        {
+            if (nodesInsideSecond == 0)
+                return PolygonIntersection.SECOND_INSIDE_FIRST;
+            else
+                return PolygonIntersection.CROSSING;
+        }
+    }
+
+
+    /**
      * Tests if point is inside a polygon. The polygon can be self-intersecting. In such case the contains function works in xor-like manner.
      * @param polygonNodes list of nodes from polygon path.
      * @param point the point to test
      * @return true if the point is inside polygon.
      */
     public static boolean nodeInsidePolygon(Node point, List<Node> polygonNodes) {
-        if (polygonNodes.size() < 3)
+        if (polygonNodes.size() < 2)
             return false;
 
         boolean inside = false;
