Index: doc/options.txt
===================================================================
--- doc/options.txt	(revision 3741)
+++ doc/options.txt	(working copy)
@@ -393,13 +393,14 @@
 The options are translated to drive-on=left|right. 
 <p>
 ;--check-roundabouts
-: 	Check that roundabouts have the expected direction (clockwise
-when vehicles drive on the left). Roundabouts that are complete
-loops and have the wrong direction are reversed. Also checks
-that the roundabouts do not fork or overlap other roundabouts.
-<p>
-;--check-roundabout-flares
-: 	Sanity check roundabout flare roads - warn if they don't point
+: 	Check that roundabouts have the expected direction (clockwise
+when vehicles drive on the left). Roundabouts that are complete
+loops and have the wrong direction are reversed. Also checks
+that the roundabouts do not fork or overlap other roundabouts
+and that no more than one connecting highway joins at each node.
+<p>
+;--check-roundabout-flares
+: 	Sanity check roundabout flare roads - warn if they don't point
 in the correct direction or if they are not one-way or if they
 extend too far.
 <p>
Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(revision 3741)
+++ resources/help/en/options	(working copy)
@@ -390,13 +390,14 @@
 	The options are translated to drive-on=left|right. 
 
 --check-roundabouts
-	Check that roundabouts have the expected direction (clockwise
-	when vehicles drive on the left). Roundabouts that are complete
-	loops and have the wrong direction are reversed. Also checks
-	that the roundabouts do not fork or overlap other roundabouts.
-
---check-roundabout-flares
-	Sanity check roundabout flare roads - warn if they don't point
+	Check that roundabouts have the expected direction (clockwise
+	when vehicles drive on the left). Roundabouts that are complete
+	loops and have the wrong direction are reversed. Also checks
+	that the roundabouts do not fork or overlap other roundabouts
+	and that no more than one connecting highway joins at each node.
+
+--check-roundabout-flares
+	Sanity check roundabout flare roads - warn if they don't point
 	in the correct direction or if they are not one-way or if they
 	extend too far.
 
Index: src/uk/me/parabola/imgfmt/app/Coord.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/Coord.java	(revision 3741)
+++ src/uk/me/parabola/imgfmt/app/Coord.java	(working copy)
@@ -54,8 +54,9 @@
 	public final static int HIGH_PREC_BITS = 30;
 	public final static int DELTA_SHIFT = 6;
 	
-	public final static double R = 6378137.0; // Radius of earth as defined by WGS84
-	public final static double U = R * 2 * Math.PI; // circumference of earth (WGS84)
+	public final static double R = 6378137.0; // Radius of earth at equator as defined by WGS84
+	public final static double U = R * 2 * Math.PI; // circumference of earth at equator (WGS84)
+	public final static double MEAN_EARTH_RADIUS = 6371000; // earth is a flattened sphere
 	
 	private final int latitude;
 	private final int longitude;
@@ -842,4 +843,19 @@
 		return distance;
 	}
 	
+	/**
+	 * @return a new coordinate at the specified distance (metres) away along the specified bearing (degrees)
+	 * uses "Destination point given distance and bearing from start point" formula from 
+	 * http://www.movable-type.co.uk/scripts/latlong.html
+	 */
+	public Coord offset(double bearingInDegrees, double distanceInMetres) {
+		double bearing = Math.toRadians(bearingInDegrees);
+		double angularDistance = distanceInMetres / MEAN_EARTH_RADIUS;
+		double lat = Math.toRadians(getLatDegrees());
+		double lon = Math.toRadians(getLonDegrees());
+		double newLat = Math.asin(Math.sin(lat) * Math.cos(angularDistance) + Math.cos(lat) * Math.sin(angularDistance) * Math.cos(bearing));
+		double newLon = lon + Math.atan2(Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(lat), Math.cos(angularDistance) - Math.sin(lat) * Math.sin(newLat));
+		return new Coord(Math.toDegrees(newLat), Math.toDegrees(newLon));
+	}
+	
 }
Index: src/uk/me/parabola/imgfmt/app/net/RouteNode.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(revision 3741)
+++ src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(working copy)
@@ -348,58 +348,145 @@
 
 	public void checkRoundabouts() {
 		List<RouteArc> roundaboutArcs = new ArrayList<RouteArc>();
+		List<RouteArc> nonRoundaboutArcs = new ArrayList<RouteArc>();
 		int countNonRoundaboutRoads = 0;
 		int countNonRoundaboutOtherHighways = 0;
-		RouteArc roundaboutArc = null;
-		for(RouteArc a : arcs) {
-			// ignore ways that have been synthesised by mkgmap
-			RoadDef r = a.getRoadDef();
-			if (!r.isSynthesised() && a.isDirect()){
-				if(r.isRoundabout())
-				{
-					roundaboutArcs.add(a);
-					if (roundaboutArc == null)
-						roundaboutArc = a;
+		int countHighwaysInsideRoundabout = 0;
+		for(RouteArc ra : arcs) {
+			if (ra.isDirect()) {
+				// ignore ways that have been synthesised by mkgmap
+				RoadDef rd = ra.getRoadDef();
+				if (!rd.isSynthesised()) {
+					if (rd.isRoundabout())
+						roundaboutArcs.add(ra);
+					else
+						nonRoundaboutArcs.add(ra);
 				}
-				else {
-					// ignore footpaths and ways with no access
-					byte access = r.getAccess();
-					if ((access & AccessTagsAndBits.CAR) != 0)
-						countNonRoundaboutRoads++;
-					else if ((access & (AccessTagsAndBits.BIKE | AccessTagsAndBits.BUS | AccessTagsAndBits.TAXI | AccessTagsAndBits.TRUCK)) != 0)
-						countNonRoundaboutOtherHighways++;
-				}
 			}
 		}
-			
-		if(arcs.size() > 1 && roundaboutArcs.size() == 1)
-			log.warn("Roundabout",roundaboutArc.getRoadDef(),roundaboutArc.isForward() ? "starts at" : "ends at", coord.toOSMURL());
 		if (roundaboutArcs.size() > 0) {
-			if (countNonRoundaboutRoads > 1)
-				log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to more than one road at",coord.toOSMURL());
-			else if ((countNonRoundaboutRoads == 1) && (countNonRoundaboutOtherHighways > 0))
-				log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to a road and",countNonRoundaboutOtherHighways,"other highways at",coord.toOSMURL());
-		}
-		if(roundaboutArcs.size() > 2) {
-			for(RouteArc fa : arcs) {
-				if(fa.isForward() && fa.isDirect()) {
-					RoadDef rd = fa.getRoadDef();
-					for(RouteArc fb : arcs) {
-						if(fb != fa && fb.isDirect() && 
-						   fa.getPointsHash() == fb.getPointsHash() &&
-						   ((fb.isForward() && fb.getDest() == fa.getDest()) ||
-							(!fb.isForward() && fb.getSource() == fa.getDest()))) {
-							if(!rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
-								log.warn("Roundabout " + rd + " overlaps " + fb.getRoadDef() + " at " + coord.toOSMURL());
+			// get the coordinates of a box bounding the junctions of the roundabout
+			int minRoundaboutLat = coord.getHighPrecLat();
+			int maxRoundaboutLat = minRoundaboutLat;
+			int minRoundaboutLon = coord.getHighPrecLon();
+			int maxRoundaboutLon = minRoundaboutLon;
+			List<RouteNode> processedNodes = new ArrayList<RouteNode>();
+			processedNodes.add(this);
+			for(RouteArc ra : roundaboutArcs) {
+				if (ra.isForward()) {
+					RouteNode rn = ra.getDest();
+					while (rn != null && !processedNodes.contains(rn)) {
+						processedNodes.add(rn);
+						int lat = rn.coord.getHighPrecLat();
+						int lon = rn.coord.getHighPrecLon();
+						minRoundaboutLat = Math.min(minRoundaboutLat, lat);
+						maxRoundaboutLat = Math.max(maxRoundaboutLat, lat);
+						minRoundaboutLon = Math.min(minRoundaboutLon, lon);
+						maxRoundaboutLon = Math.max(maxRoundaboutLon, lon);
+						RouteNode nrn = null;
+						for (RouteArc nra : rn.arcs) {
+							if (nra.isDirect() && nra.isForward()) {
+								RoadDef nrd = nra.getRoadDef();
+								if (nrd.isRoundabout() && !nrd.isSynthesised())
+									nrn = nra.getDest();
 							}
 						}
-						else if(fa != fb && fb.isForward()) {
-							if(!rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
-								log.warn("Roundabout " + rd + " forks at " + coord.toOSMURL());
+						rn = nrn;
+					}
+				}
+			}
+			if (nonRoundaboutArcs.size() > 1) {
+				// get an approximate centre of the roundabout
+				Coord roundaboutCentre =  Coord.makeHighPrecCoord((minRoundaboutLat + maxRoundaboutLat) / 2, (minRoundaboutLon + maxRoundaboutLon) / 2);
+				for(RouteArc ra : nonRoundaboutArcs) {
+					double distanceToCentre = roundaboutCentre.distance(coord);
+					RoadDef rd = ra.getRoadDef();
+					// ignore footpaths and ways with no access
+					byte access = rd.getAccess();
+					if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+						// check whether the way is inside the roundabout by seeing if the next point is nearer to the centre of the bounding box than this
+						RouteNode nextNode = ra.getSource().coord == coord ? ra.getDest() : ra.getSource();
+						Coord nextCoord = nextNode.coord;
+						for (RouteNode roundaboutNode : processedNodes) {
+							if (roundaboutNode.coord.equals(nextCoord)) {
+								// arc rejoins roundabout, so calculate another point to use half the distance away at the initial bearing
+								double heading1 = ra.getSource().coord == coord ? ra.getInitialHeading() : 180 + ra.getFinalHeading();
+								double distance = coord.distance(nextCoord) / 2;
+								Coord nextCoord1 = coord.offset(heading1, distance);
+								// now calculate a point the same distance away from the end point 180 degrees from the final bearing
+								double heading2 = ra.getSource().coord == coord ? 180 + ra.getFinalHeading() : ra.getInitialHeading();
+								Coord nextCoord2 = nextCoord.offset(heading2, distance);
+								double distanceToCentreOfNextCoord = roundaboutCentre.distance(nextCoord);
+								// use the point which has a bigger difference in distance from the centre to increase accuracy
+								if (Math.abs(distanceToCentre - roundaboutCentre.distance(nextCoord1)) >= Math.abs(distanceToCentreOfNextCoord - roundaboutCentre.distance(nextCoord2)))
+									nextCoord = nextCoord1;
+								else {
+									distanceToCentre = distanceToCentreOfNextCoord;
+									nextCoord = nextCoord2;
+								}
+								break;
 							}
 						}
+						double nextDistanceToCentre = roundaboutCentre.distance(nextCoord);
+						if (Math.abs(nextDistanceToCentre - distanceToCentre) < 2)
+							log.info("Way",rd,"unable to accurately determine whether",nextCoord.toOSMURL()," is inside roundabout");
+						if (nextDistanceToCentre < distanceToCentre)
+							countHighwaysInsideRoundabout++;
+						else {
+							if ((access & AccessTagsAndBits.CAR) != 0)
+								countNonRoundaboutRoads++;
+							else if ((access & (AccessTagsAndBits.BIKE | AccessTagsAndBits.BUS | AccessTagsAndBits.TAXI | AccessTagsAndBits.TRUCK)) != 0)
+								countNonRoundaboutOtherHighways++;
+						}
 					}
 				}
+			
+				RouteArc roundaboutArc = roundaboutArcs.get(0);
+				if(arcs.size() > 1 && roundaboutArcs.size() == 1)
+					log.warn("Roundabout",roundaboutArc.getRoadDef(),roundaboutArc.isForward() ? "starts at" : "ends at", coord.toOSMURL());
+				if (countNonRoundaboutRoads > 1)
+					log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to more than one road at",coord.toOSMURL());
+				else if (countNonRoundaboutRoads == 1) {
+					if (countNonRoundaboutOtherHighways > 0) {
+						if (countHighwaysInsideRoundabout > 0)
+							log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to a road",countNonRoundaboutOtherHighways,"other highway(s) and",countHighwaysInsideRoundabout,"highways inside the roundabout at",coord.toOSMURL());
+						else
+							log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to a road and",countNonRoundaboutOtherHighways,"other highway(s) at",coord.toOSMURL());
+					}
+					else if (countNonRoundaboutOtherHighways > 0)
+						log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to a road and",countHighwaysInsideRoundabout,"highway(s) inside the roundabout at",coord.toOSMURL());
+				}
+				else if (countNonRoundaboutOtherHighways > 0) {
+					if (countHighwaysInsideRoundabout > 0)
+						log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to",countNonRoundaboutOtherHighways,"highway(s) and",countHighwaysInsideRoundabout,"inside the roundabout at",coord.toOSMURL());
+					else if (countNonRoundaboutOtherHighways > 1)
+						log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to",countNonRoundaboutOtherHighways,"highways at",coord.toOSMURL());
+				}
+				else if (countNonRoundaboutOtherHighways > 1)
+					log.warn("Roundabout",roundaboutArc.getRoadDef(),"is connected to",countHighwaysInsideRoundabout,"highways inside the roundabout at",coord.toOSMURL());
+				if(roundaboutArcs.size() > 2) {
+					for(RouteArc fa : roundaboutArcs) {
+						if(fa.isForward()) {
+							RoadDef rd = fa.getRoadDef();
+							for(RouteArc fb : roundaboutArcs) {
+								if(fb != fa) { 
+									if(fa.getPointsHash() == fb.getPointsHash() &&
+									   ((fb.isForward() && fb.getDest() == fa.getDest()) ||
+										(!fb.isForward() && fb.getSource() == fa.getDest()))) {
+										if(!rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
+											log.warn("Roundabout " + rd + " overlaps " + fb.getRoadDef() + " at " + coord.toOSMURL());
+										}
+									}
+									else if(fb.isForward()) {
+										if(!rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
+											log.warn("Roundabout " + rd + " forks at " + coord.toOSMURL());
+										}
+									}
+								}
+							}
+						}
+					}
+				}
 			}
 		}
 	}
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 3741)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -1051,9 +1051,9 @@
 		if (dupPOI){
 			if (log.isInfoEnabled()){
 				if (FakeIdGenerator.isFakeId(node.getId()))
-					log.info("ignmoring duplicate POI with type",GType.formatType(type),mp.getName(),"for generated element with id",node.getId(),"at",mp.getLocation().toDegreeString());
+					log.info("ignoring duplicate POI with type",GType.formatType(type),mp.getName(),"for generated element with id",node.getId(),"at",mp.getLocation().toDegreeString());
 				else 
-					log.info("ignmoring duplicate POI with type",GType.formatType(type),mp.getName(),"for element",node.toBrowseURL());
+					log.info("ignoring duplicate POI with type",GType.formatType(type),mp.getName(),"for element",node.toBrowseURL());
 			}
 			return;
 		}
Index: test/uk/me/parabola/imgfmt/app/CoordTest.java
===================================================================
--- test/uk/me/parabola/imgfmt/app/CoordTest.java	(revision 3741)
+++ test/uk/me/parabola/imgfmt/app/CoordTest.java	(working copy)
@@ -86,4 +86,9 @@
 		Coord russia4 = russia1.destOnRhumLine(10000, 0.0);
 		assertEquals(russia4.getLongitude(), russia1.getLongitude());
 	}
+	
+	@Test
+	public void testOffset() {
+		assertEquals(pLAX, pLAX.offset(60, 100).offset(120, 100).offset(180, 100).offset(240, 100).offset(300, 100).offset(360, 100));
+	}
 }
