Index: trunk/src/org/openstreetmap/josm/data/coor/ILatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/ILatLon.java	(revision 18588)
+++ trunk/src/org/openstreetmap/josm/data/coor/ILatLon.java	(revision 18589)
@@ -144,3 +144,41 @@
         return bearing;
     }
+
+    /**
+     * Does a linear interpolation between two ILatLon instances.
+     * @param ll2 The other ILatLon instance.
+     * @param proportion The proportion the other instance influences the result.
+     * @return The new {@link ILatLon} position.
+     * @since 18589
+     */
+    default ILatLon interpolate(ILatLon ll2, double proportion) {
+        // this is an alternate form of this.lat() + proportion * (ll2.lat() - this.lat()) that is slightly faster
+        return new LatLon((1 - proportion) * this.lat() + proportion * ll2.lat(),
+                (1 - proportion) * this.lon() + proportion * ll2.lon());
+    }
+
+    /**
+     * Returns the square of euclidean distance from this {@code Coordinate} to a specified coordinate.
+     *
+     * @param lon the X coordinate of the specified point to be measured against this {@code Coordinate}
+     * @param lat the Y coordinate of the specified point to be measured against this {@code Coordinate}
+     * @return the square of the euclidean distance from this {@code Coordinate} to a specified coordinate
+     * @since 18589
+     */
+    default double distanceSq(final double lon, final double lat) {
+        final double dx = this.lon() - lon;
+        final double dy = this.lat() - lat;
+        return dx * dx + dy * dy;
+    }
+
+    /**
+     * Returns the euclidean distance from this {@code ILatLon} to a specified {@code ILatLon}.
+     *
+     * @param other the specified coordinate to be measured against this {@code ILatLon}
+     * @return the euclidean distance from this {@code ILatLon} to a specified {@code ILatLon}
+     * @since 18589
+     */
+    default double distanceSq(final ILatLon other) {
+        return this.distanceSq(other.lon(), other.lat());
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/coor/LatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 18588)
+++ trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 18589)
@@ -259,5 +259,6 @@
 
     /**
-     * Interpolate between this and a other latlon
+     * Interpolate between this and a other latlon. If you don't care about the return type, use {@link ILatLon#interpolate(ILatLon, double)}
+     * instead.
      * @param ll2 The other lat/lon object
      * @param proportion The proportion to interpolate
@@ -265,7 +266,9 @@
      */
     public LatLon interpolate(LatLon ll2, double proportion) {
-        // this is an alternate form of this.lat() + proportion * (ll2.lat() - this.lat()) that is slightly faster
-        return new LatLon((1 - proportion) * this.lat() + proportion * ll2.lat(),
-                (1 - proportion) * this.lon() + proportion * ll2.lon());
+        ILatLon interpolated = ILatLon.super.interpolate(ll2, proportion);
+        if (interpolated instanceof LatLon) {
+            return (LatLon) interpolated;
+        }
+        return new LatLon(interpolated);
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/Geometry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 18588)
+++ trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 18589)
@@ -492,4 +492,37 @@
         double pdx = point.getX() - p1.getX();
         double pdy = point.getY() - p1.getY();
+
+        double offset = (pdx * ldx + pdy * ldy) / (ldx * ldx + ldy * ldy);
+
+        if (segmentOnly && offset <= 0)
+            return p1;
+        else if (segmentOnly && offset >= 1)
+            return p2;
+        else
+            return p1.interpolate(p2, offset);
+    }
+
+    /**
+     * Get the closest point to a segment
+     * @param p1 Point 1 of the segment
+     * @param p2 Point 2 of the segment
+     * @param point The point to use to get the closest point on the segment
+     * @param segmentOnly {@code true} if the point <i>must</i> be on the segment
+     * @return The closest point on the segment if {@code segmentOnly = true}, otherwise the closest point on the infinite line.
+     */
+    private static ILatLon closestPointTo(ILatLon p1, ILatLon p2, ILatLon point, boolean segmentOnly) {
+        CheckParameterUtil.ensureParameterNotNull(p1, "p1");
+        CheckParameterUtil.ensureParameterNotNull(p2, "p2");
+        CheckParameterUtil.ensureParameterNotNull(point, "point");
+
+        double ldx = p2.lon() - p1.lon();
+        double ldy = p2.lat() - p1.lat();
+
+        //segment zero length
+        if (ldx == 0 && ldy == 0)
+            return p1;
+
+        double pdx = point.lon() - p1.lon();
+        double pdy = point.lat() - p1.lat();
 
         double offset = (pdx * ldx + pdy * ldy) / (ldx * ldx + ldy * ldy);
@@ -1501,11 +1534,10 @@
 
         double smallest = Double.MAX_VALUE;
-        EastNorth en0 = node.getEastNorth();
         // go through the nodes as if they were paired
         Iterator<Node> iter = way.getNodes().iterator();
-        EastNorth en1 = iter.next().getEastNorth();
+        Node en1 = iter.next();
         while (iter.hasNext()) {
-            EastNorth en2 = iter.next().getEastNorth();
-            double distance = getSegmentNodeDistSq(en1, en2, en0);
+            Node en2 = iter.next();
+            double distance = getSegmentNodeDistSq(en1, en2, node);
             if (distance < smallest)
                 smallest = distance;
@@ -1561,7 +1593,8 @@
         Iterator<Node> iter1 = w1.getNodes().iterator();
         Node w1N1 = iter1.next();
+        List<Node> w2Nodes = w2.getNodes();
         while (iter1.hasNext()) {
             Node w1N2 = iter1.next();
-            Iterator<Node> iter2 = w2.getNodes().iterator();
+            Iterator<Node> iter2 = w2Nodes.iterator();
             Node w2N1 = iter2.next();
             while (iter2.hasNext()) {
@@ -1602,17 +1635,14 @@
      */
     public static double getDistanceSegmentSegment(Node ws1Node1, Node ws1Node2, Node ws2Node1, Node ws2Node2) {
-        EastNorth enWs1Node1 = ws1Node1.getEastNorth();
-        EastNorth enWs1Node2 = ws1Node2.getEastNorth();
-        EastNorth enWs2Node1 = ws2Node1.getEastNorth();
-        EastNorth enWs2Node2 = ws2Node2.getEastNorth();
-        if (enWs1Node1 == null || enWs1Node2 == null || enWs2Node1 == null || enWs2Node2 == null)
+        if (!ws1Node1.isLatLonKnown() || !ws1Node2.isLatLonKnown() || !ws2Node1.isLatLonKnown() || !ws2Node2.isLatLonKnown()) {
             return Double.NaN;
-        if (getSegmentSegmentIntersection(enWs1Node1, enWs1Node2, enWs2Node1, enWs2Node2) != null)
+        }
+        if (getSegmentSegmentIntersection(ws1Node1, ws1Node2, ws2Node1, ws2Node2) != null)
             return 0;
 
-        double dist1sq = getSegmentNodeDistSq(enWs1Node1, enWs1Node2, enWs2Node1);
-        double dist2sq = getSegmentNodeDistSq(enWs1Node1, enWs1Node2, enWs2Node2);
-        double dist3sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node1);
-        double dist4sq = getSegmentNodeDistSq(enWs2Node1, enWs2Node2, enWs1Node2);
+        double dist1sq = getSegmentNodeDistSq(ws1Node1, ws1Node2, ws2Node1);
+        double dist2sq = getSegmentNodeDistSq(ws1Node1, ws1Node2, ws2Node2);
+        double dist3sq = getSegmentNodeDistSq(ws2Node1, ws2Node2, ws1Node1);
+        double dist4sq = getSegmentNodeDistSq(ws2Node1, ws2Node2, ws1Node2);
         double smallest = Math.min(Math.min(dist1sq, dist2sq), Math.min(dist3sq, dist4sq));
         return smallest != Double.MAX_VALUE ? Math.sqrt(smallest) : Double.NaN;
@@ -1649,7 +1679,7 @@
      * @return the square of the euclidean distance from p to the closest point on the segment
      */
-    private static double getSegmentNodeDistSq(EastNorth s1, EastNorth s2, EastNorth p) {
-        EastNorth c1 = closestPointTo(s1, s2, p, true);
-        return c1.distanceSq(p);
+    private static double getSegmentNodeDistSq(ILatLon s1, ILatLon s2, ILatLon p) {
+        ILatLon c1 = closestPointTo(s1, s2, p, true);
+        return c1.distanceSq(p.lon(), p.lat());
     }
 }
Index: trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java	(revision 18588)
+++ trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java	(revision 18589)
@@ -14,4 +14,5 @@
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -164,4 +165,73 @@
     }
 
+    static Stream<Arguments> testCentroid() {
+        // The expected values use the BigDecimal calculations
+        return Stream.of(
+            Arguments.of(new LatLon(54.10310051693397, 12.094459783282147),
+                new LatLon[]{
+                    new LatLon(54.1031207, 12.094513),
+                    new LatLon(54.1030973, 12.0945423),
+                    new LatLon(54.1031188, 12.0944413),
+                    new LatLon(54.1030578, 12.0945178),
+                    new LatLon(54.1030658, 12.0944275),
+                    new LatLon(54.1030826, 12.0945434),
+                    new LatLon(54.1031079, 12.0944243),
+                    new LatLon(54.1030515, 12.094495),
+                    new LatLon(54.103094, 12.0944157),
+                    new LatLon(54.1031257, 12.0944893),
+                    new LatLon(54.1030687, 12.0945348),
+                    new LatLon(54.1031251, 12.0944641),
+                    new LatLon(54.1030792, 12.0944168),
+                    new LatLon(54.1030508, 12.0944698),
+                    new LatLon(54.1030559, 12.0944461),
+                    new LatLon(54.1031107, 12.0945316)
+                }),
+            Arguments.of(new LatLon(54.10309639216633, 12.09463150330365),
+                new LatLon[]{new LatLon(54.1031205, 12.094653),
+                    new LatLon(54.1030621, 12.0946675),
+                    new LatLon(54.1030866, 12.0946874),
+                    new LatLon(54.1030732, 12.0946816),
+                    new LatLon(54.1030766, 12.0945701),
+                    new LatLon(54.1031148, 12.0945865),
+                    new LatLon(54.1031122, 12.0946719),
+                    new LatLon(54.1030551, 12.0946473),
+                    new LatLon(54.1031037, 12.0945724),
+                    new LatLon(54.1031003, 12.094684),
+                    new LatLon(54.1030647, 12.0945821),
+                    new LatLon(54.1031219, 12.0946068),
+                    new LatLon(54.1031239, 12.0946301),
+                    new LatLon(54.1030903, 12.0945667),
+                    new LatLon(54.1030564, 12.0946011),
+                    new LatLon(54.1030531, 12.0946239)
+                }),
+                Arguments.of(new LatLon(54.103185854296896, 12.09457804609505),
+                    new LatLon[] {
+                        new LatLon(54.1031981, 12.0945501),
+                        new LatLon(54.1031782, 12.0945501),
+                        new LatLon(54.1031726, 12.0946082),
+                        new LatLon(54.1031955, 12.0946015)
+                    }),
+                Arguments.of(new LatLon(54.103180913681705, 12.094425831813119),
+                    new LatLon[] {
+                        new LatLon(54.1032057, 12.0943903),
+                        new LatLon(54.1031517, 12.0944053),
+                        new LatLon(54.1031877, 12.0943743),
+                        new LatLon(54.1031697, 12.0943743),
+                        new LatLon(54.1031517, 12.0944353),
+                        new LatLon(54.1031697, 12.0944663),
+                        new LatLon(54.1031877, 12.0944663),
+                        new LatLon(54.1032057, 12.0944363)
+                    })
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCentroid(LatLon expected, LatLon... coordinates) {
+        LatLon actual = ProjectionRegistry.getProjection()
+                .eastNorth2latlon(Geometry.getCentroid(Stream.of(coordinates).map(Node::new).collect(Collectors.toList())));
+        assertTrue(expected.equalsEpsilon((ILatLon) actual), "Expected " + expected + " but got " + actual);
+    }
+
     /**
      * Test of {@link Geometry#getCentroidEN} method.
@@ -172,5 +242,5 @@
         EastNorth en2 = new EastNorth(150, 400);
         EastNorth en3 = new EastNorth(200, 200);
-        assertEquals(en1, Geometry.getCentroidEN(Arrays.asList(en1)));
+        assertEquals(en1, Geometry.getCentroidEN(Collections.singletonList(en1)));
         assertEquals(new EastNorth(125, 300), Geometry.getCentroidEN(Arrays.asList(en1, en2)));
         assertEquals(new EastNorth(150, 266d + 2d/3d), Geometry.getCentroidEN(Arrays.asList(en1, en2, en3)));
