Index: doc/options.txt
===================================================================
--- doc/options.txt	(revision 4729)
+++ doc/options.txt	(working copy)
@@ -633,25 +633,21 @@
 configuration file is in use. When using a logging configuration file, they are logged
 with custom level DIAGNOSTIC (1100).
 
-;--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
-and that no more than one connecting highway joins at each node.
+;--report-roundabout-issues[=all|loop|direction|overlap|junctions|flares[,...]]
+: 	Report on various types of roundabout issue:
 
-;--check-roundabout-flares
-: 	Check that roundabout flare roads point in the correct
-direction, are one-way and don't extend too far.
+:* all - report on all the types of issue. This is the default if --report-roundabout-issues
+is specified without a value.
+:* loop - check that each roundabout is formed from a single loop with no forks or gaps
+:* direction - check the direction of travel around the roundabout, see also --fix-roundabout-direction
+:* overlap - check that highways do not overlap the roundabout
+:* junctions - check that no more than one connecting highway joins at each node
+:* flares - check that roundabout flare roads are one-way, are in the right direction, and don't extend beyond the flare
 
-;--max-flare-length-ratio=NUM
-: 	When checking flare roads, ignore roads whose length is
-greater than NUM (an integer) times the distance between the
-nodes on the roundabout that the flare roads connect to. Using
-this option with a value of at least 5 will cut down the
-number of legitimate roads that are flagged as flare road
-problems. Default value is 0 (disabled) because it's not a
-completely reliable heuristic.
+;--roundabout-flare-rules-config=filename
+: 	Provide a configuration file containing the rules to be used with the
+--check-roundabouts=flares option in determining whether a pair of roads joining
+a roundabout should be considered to be flares.
 
 ;--report-routing-islands
 : 	Routing islands are small road networks which are not connected to other
@@ -1003,8 +999,31 @@
 --report-routing-islands.
 : This option seems to cause routing problems in BaseCamp.
 
+;--fix-roundabout-direction
+: 	Reverse the direction of travel around roundabouts that do not have the expected
+direction (clockwise when vehicles drive on the left). See also --report-roundabout-issues=direction.
+
 === Deprecated and Obsolete Options ===
 
+;--check-roundabouts
+: 	Deprecated; use --report-roundabout-issues and/or --fix-roundabout-direction instead.
+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
+: 	Deprecated; use --report-roundabout-issues=flares instead.
+Check that roundabout flare roads point in the correct
+direction, are one-way and don't extend too far.
+
+;--max-flare-length-ratio=NUM
+: 	Deprecated; use --roundabout-flare-rules-config instead.
+When checking roundabout flares, ignore roads whose length is
+greater than NUM (an integer) times the distance between the
+nodes on the roundabout that the flare roads connect to.
+
 ;--check-routing-island-len=integer
 : 	Deprecated; use --report-routing-islands and --max-routing-island-len instead.
 Translated to --report-routing-islands if info level logging is enabled, plus
Index: src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java	(revision 4729)
+++ src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java	(working copy)
@@ -55,10 +55,8 @@
 	private final List<RoadDef> roadDefs = new ArrayList<>();
 	private List<RouteCenter> centers = new ArrayList<>();
 	private AngleChecker angleChecker = new AngleChecker();
+	private RoundaboutCheck roundaboutCheck = new RoundaboutCheck();
 
-	private boolean checkRoundabouts;
-	private boolean checkRoundaboutFlares;
-	private int maxFlareLengthRatio ;
 	private boolean reportSimilarArcs;
 	private boolean routable;
 	private boolean reportRoutingIslands;
@@ -67,9 +65,6 @@
 	private int visitId;  
 
 	public void config(EnhancedProperties props) {
-		checkRoundabouts = props.getProperty("check-roundabouts", false);
-		checkRoundaboutFlares = props.getProperty("check-roundabout-flares", false);
-		maxFlareLengthRatio = props.getProperty("max-flare-length-ratio", 0);
 		reportSimilarArcs = props.getProperty("report-similar-arcs", false);
 		int checkRoutingIslandLengths = props.getProperty("check-routing-island-len", -1);
 		if (checkRoutingIslandLengths >= 0) {
@@ -81,6 +76,7 @@
 		reportRoutingIslands = props.getProperty("report-routing-islands", false);
 		routable = props.containsKey("route");
 		angleChecker.config(props);
+		roundaboutCheck.config(props);
 	}
 
 	public void addRoad(RoadDef roadDef, List<Coord> coordList) {
@@ -272,10 +268,7 @@
 
 	private void performChecks(RouteNode node) {
 		if(!node.isBoundary()) {
-			if(checkRoundabouts)
-				node.checkRoundabouts();
-			if(checkRoundaboutFlares)
-				node.checkRoundaboutFlares(maxFlareLengthRatio);
+			roundaboutCheck.check(node);
 			if(reportSimilarArcs)
 				node.reportSimilarArcs();
 		}
Index: src/uk/me/parabola/imgfmt/app/net/RoundaboutCheck.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RoundaboutCheck.java	(nonexistent)
+++ src/uk/me/parabola/imgfmt/app/net/RoundaboutCheck.java	(working copy)
@@ -0,0 +1,824 @@
+package uk.me.parabola.imgfmt.app.net;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
+
+public class RoundaboutCheck {
+	private final Logger log = Logger.getLogger(RoundaboutCheck.class);
+	private boolean checkRoundaboutLoops = false;
+	private boolean checkRoundaboutDirections = false;
+	private boolean checkRoundaboutOverlaps = false;
+	private boolean checkRoundaboutJunctions = false;
+	private boolean checkRoundaboutFlares = false;
+	private boolean checkRoundabouts = false;
+	private int maxFlareLength = 200;
+	private int maxFlareLengthMultiple = 3;
+	private int flareSeparationOverride = 5;
+	private int maxFlareToSeparationMultiple = 7;
+	private int maxFlareAngle = 90;
+	private int maxFlareBearing = 90;
+	private int maxEntryAngle = 145;
+	private boolean discardBothFlaresExtend = true;
+	private boolean discardMultipleFlares = true;
+	private boolean discardDifferentFlareNames = true;
+	private boolean discardDifferentAccesses = true;
+
+	private RouteNode nodeToCheck;
+	private Coord coord;
+
+	private List<RouteNode> roundaboutNodes = new ArrayList<>();
+	
+	/**
+	 * Show valid report-roundabout-issues options
+	 * @param option either "help" or an option that was not recognised
+	 */
+	private static void printOptionHelpMsg(String option) {
+		if(!"help".equals(option))
+			System.err.println("Unknown report-roundabout-issues option '" + option + "'");
+		
+		System.err.println("Report roundabout issues options are:");
+		System.err.println("  all         apply all the checks");
+		System.err.println("  loop        check that each roundabouts is formed from a single loop with no");
+		System.err.println("              forks or gaps");
+		System.err.println("  direction   check the direction of travel around the roundabout");
+		System.err.println("  overlaps    check that highways do not overlap the roundabout");
+		System.err.println("  junctions   check that no more than one connecting highway joins at each node");
+		System.err.println("  flares      check that roundabout flare roads are one-way, are in the right");
+		System.err.println("              direction, and don't extend beyond the flare");
+	}
+
+	/**
+	 * parse the roundabout checking options
+	 */
+	public void config(EnhancedProperties props) {
+		if (props.getProperty("check-roundabouts", false)) {
+			checkRoundaboutLoops = RouteNode.isWarningLogged();
+			checkRoundaboutDirections = checkRoundaboutLoops;
+			checkRoundaboutJunctions = checkRoundaboutLoops;
+			log.error("The check-roundabouts option is deprecated - use report-roundabout-issues.");
+		}
+		if (props.getProperty("check-roundabout-flares", false)) {
+			checkRoundaboutFlares = RouteNode.isWarningLogged();
+			log.error("The check-roundabout-flares option is deprecated - use report-roundabout-issues.");
+		}
+		String mflr = props.getProperty("max-flare-length-ratio");
+		if (mflr != null) {
+			maxFlareToSeparationMultiple = props.getProperty("max-flare-length-ratio", maxFlareToSeparationMultiple);
+			log.error("The max-flare-length-ratio option is deprecated - use roundabout-flare-rules-config.");
+		}
+		String checkRoundaboutsOption = props.getProperty("report-roundabout-issues");
+		if (checkRoundaboutsOption != null) {
+			if ("".equals(checkRoundaboutsOption)) {
+				checkRoundaboutLoops = true;
+				checkRoundaboutDirections = true;
+				checkRoundaboutOverlaps = true;
+				checkRoundaboutJunctions = true;
+				checkRoundaboutFlares = true;
+			}
+			for (String option : checkRoundaboutsOption.split(",")) {
+				if ("all".equals(option)) {
+					checkRoundaboutLoops = true;
+					checkRoundaboutDirections = true;
+					checkRoundaboutOverlaps = true;
+					checkRoundaboutJunctions = true;
+					checkRoundaboutFlares = true;
+				} else if ("loop".equals(option)) {
+					checkRoundaboutLoops = true;
+				} else if ("direction".equals(option)) {
+					checkRoundaboutDirections = true;
+				} else if ("overlaps".equals(option)) {
+					checkRoundaboutOverlaps = true;
+				} else if ("junctions".equals(option)) {
+					checkRoundaboutJunctions = true;
+				} else if ("flares".equals(option)) {
+					checkRoundaboutFlares = true;
+				} else if (option.isEmpty()) {
+					// nothing to do
+				} else {
+					printOptionHelpMsg(option);
+				}
+			}
+		}
+		checkRoundabouts = checkRoundaboutLoops /* || checkRoundaboutDirections */ || checkRoundaboutOverlaps || checkRoundaboutJunctions || checkRoundaboutFlares;
+		String configFilename = props.getProperty("roundabout-flare-rules-config", "");
+		if (!configFilename.isEmpty()) {
+			File file = new File(configFilename);
+			try {
+				List<String> rules = Files.readAllLines(file.toPath());
+				for (int i = 0; i < rules.size(); i++) {
+					String rule = rules.get(i);
+					int hashPos = rule.indexOf('#');
+					if (hashPos >= 0)
+						rule = rule.substring(0, hashPos);
+					rule = rule.replaceAll("\\s+", "");
+					if (!rule.isEmpty()) {
+						String[] ruleParts = rule.split(":");
+						if (ruleParts.length == 2) {
+							try {
+								switch (ruleParts[0]) {
+								case "max-flare-length":
+									maxFlareLength = Integer.decode(ruleParts[1]);
+									break;
+
+								case "max-flare-length-multiple":
+									maxFlareLengthMultiple = Integer.decode(ruleParts[1]);
+									break;
+
+								case "flare-separation-override":
+									flareSeparationOverride = Integer.decode(ruleParts[1]);
+									break;
+
+								case "max-flare-to-separation-multiple":
+									maxFlareToSeparationMultiple = Integer.decode(ruleParts[1]);
+									break;
+
+								case "max-flare-angle":
+									maxFlareAngle = Integer.decode(ruleParts[1]);
+									break;
+
+								case "max-flare-bearing":
+									maxFlareBearing = Integer.decode(ruleParts[1]);
+									break;
+
+								case "max-entry-angle":
+									maxEntryAngle = Integer.decode(ruleParts[1]);
+									break;
+
+								case "discard-both-flares-extend":
+									discardBothFlaresExtend = Boolean.getBoolean(ruleParts[1]);
+									break;
+
+								case "discard-multiple-flares":
+									discardMultipleFlares = Boolean.getBoolean(ruleParts[1]);
+									break;
+
+								case "discard-different-flare-names":
+									discardDifferentFlareNames = Boolean.getBoolean(ruleParts[1]);
+									break;
+
+								case "discard-different-accesses":
+									discardDifferentAccesses = Boolean.getBoolean(ruleParts[1]);
+									break;
+
+								default:
+									log.error("Unrecognised roundabout flare rule", ruleParts[0], "in", configFilename);
+								}
+							}
+							catch (Exception ex) {
+								log.error("Invalid value", ruleParts[1], "for roundabout flare rule", ruleParts[0], "in", configFilename);
+							}
+						}
+						else {
+							log.error("Invalid roundabout flare rule", rule, "in", configFilename);
+						}
+					}
+				}
+			} catch (IOException ex) {
+				log.error("Error reading roundabout flare rules file", configFilename);
+			}
+		}
+	}
+	
+	public void check(RouteNode routeNode) {
+		if (checkRoundabouts) {
+			nodeToCheck = routeNode;
+			coord = nodeToCheck.getCoord();
+			roundaboutNodes.clear();
+			List<RouteArc> roundaboutArcs = new ArrayList<>();
+			List<RouteArc> nonRoundaboutArcs = new ArrayList<>();
+			for(RouteArc ra : nodeToCheck.getArcs()) {
+				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);
+					}
+				}
+			}
+			if (!roundaboutArcs.isEmpty()) {
+				// get the nodes at the roundabout junctions
+				if(log.isDebugEnabled())
+					log.debug("Roundabout node", coord.toOSMURL());
+				roundaboutNodes.add(nodeToCheck);
+				boolean roundaboutComplete = true;
+				for (RouteArc ra : roundaboutArcs) {
+					if (ra.isForward()) {
+						for (RouteArc ra1 : nonRoundaboutArcs) {
+							if ((ra1.getDirectHeading() == ra.getDirectHeading()) && (ra1.getInitialHeading() == ra.getInitialHeading()) && (ra1.getFinalHeading() == ra.getFinalHeading()) && (ra1.getLengthInMeter() == ra.getLengthInMeter())) {
+								// non roundabout highway overlaps roundabout
+								nonRoundaboutArcs.remove(ra1);
+								if(checkRoundaboutOverlaps && !ra.getRoadDef().messagePreviouslyIssued("overlaps roundabout"))
+									log.diagnostic("Highway " + ra1.getRoadDef() + " overlaps roundabout " + ra.getRoadDef() + " at " + coord.toOSMURL());
+								break;
+							}						
+						}
+						RouteNode rn = ra.getDest();
+						while (!roundaboutNodes.contains(rn)) {
+							roundaboutNodes.add(rn);
+							if(log.isDebugEnabled())
+								log.debug("Roundabout node", rn.getCoord().toOSMURL());
+							RouteNode nrn = null;
+							for (RouteArc nra : rn.getArcs()) {
+								if (nra.isDirect() && nra.isForward()) {
+									RoadDef nrd = nra.getRoadDef();
+									if (nrd.isRoundabout() && !nrd.isSynthesised())
+										nrn = nra.getDest();
+								}
+							}
+							rn = nrn;
+							if (rn == null) {
+								roundaboutComplete = false;
+								log.info("Incomplete roundabout", coord.toOSMURL());
+								break;
+							}
+						}
+					}
+				}
+				Coord roundaboutCentre = getRoundaboutCentre();
+	
+				if (nonRoundaboutArcs.size() > 1) {
+					int countNonRoundaboutRoads = 0;
+					int countNonRoundaboutOtherHighways = 0;
+					int countHighwaysInsideRoundabout = 0;
+					for (RouteArc ra : nonRoundaboutArcs) {
+						RoadDef rd = ra.getRoadDef();
+						// ignore footpaths and ways with no access
+						byte access = rd.getAccess();
+						if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+							if (roundaboutComplete && goesInsideRoundabout(ra, nodeToCheck, roundaboutCentre)) {
+								countHighwaysInsideRoundabout++;
+								//skip flare check for all ways meeting this node
+								for (RouteArc ra2 : nonRoundaboutArcs)
+									ra2.getRoadDef().doFlareCheck(false);
+							}
+							else {
+								if ((access & AccessTagsAndBits.CAR) != 0)
+									countNonRoundaboutRoads++;
+								else if ((access & (AccessTagsAndBits.BIKE | AccessTagsAndBits.BUS | AccessTagsAndBits.TAXI | AccessTagsAndBits.TRUCK)) != 0)
+									countNonRoundaboutOtherHighways++;
+							}
+						}
+						else
+							rd.doFlareCheck(false);
+					}
+	
+					RouteArc roundaboutArc = roundaboutArcs.get(0);
+					if (checkRoundaboutLoops) {
+						if (nodeToCheck.getArcs().size() > 1 && roundaboutArcs.size() == 1)
+							log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + (roundaboutArc.isForward() ? " starts at " : " ends 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.diagnostic("Roundabout " + rd + " overlaps " + fb.getRoadDef() + " at " + coord.toOSMURL());
+												}
+											}
+											else if (fb.isForward()
+													&& !rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
+												log.diagnostic("Roundabout " + rd + " forks at " + coord.toOSMURL());
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+
+					if (checkRoundaboutJunctions) {
+						if (countNonRoundaboutRoads > 1) {
+							if (!nodeToCheck.getRestrictions().isEmpty())
+								log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " with restriction is connected to more than one road at " + coord.toOSMURL());
+							else
+								log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to more than one road at " + coord.toOSMURL());
+						} else if (countNonRoundaboutRoads == 1) {
+							if (countNonRoundaboutOtherHighways > 0) {
+								if (countHighwaysInsideRoundabout > 0)
+									log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to a road, " + countNonRoundaboutOtherHighways + " other highway(s) and " + countHighwaysInsideRoundabout + " highways inside the roundabout at " + coord.toOSMURL());
+								else
+									log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to a road and " + countNonRoundaboutOtherHighways + " other highway(s) at " + coord.toOSMURL());
+							}
+							else if (countHighwaysInsideRoundabout > 0)
+								log.diagnostic("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.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to " + countNonRoundaboutOtherHighways+ " highway(s) and " + countHighwaysInsideRoundabout + " inside the roundabout at " + coord.toOSMURL());
+							else if (countNonRoundaboutOtherHighways > 1)
+								log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to " + countNonRoundaboutOtherHighways + " highways at " + coord.toOSMURL());
+						} else if (countHighwaysInsideRoundabout > 1)
+							log.diagnostic("Roundabout " +roundaboutArc.getRoadDef() + " is connected to " + countHighwaysInsideRoundabout + " highways inside the roundabout at " + coord.toOSMURL());
+					}
+				}
+				if (roundaboutComplete && checkRoundaboutFlares)
+					checkRoundaboutFlares(roundaboutCentre);
+			}
+		}
+	}
+
+	// check roundabout flare roads - the flare roads connect a
+	// two-way road to a roundabout using short one-way segments so
+	// the resulting sub-junction looks like a triangle with two
+	// corners of the triangle being attached to the roundabout and
+	// the last corner being connected to the two-way road
+
+	public void checkRoundaboutFlares(Coord centre) {
+		for(RouteArc r : nodeToCheck.getArcs()) {
+			// see if node has a forward arc that is part of a roundabout
+			if(!r.isForward() || !r.isDirect() || !r.getRoadDef().isRoundabout() || r.getRoadDef().isSynthesised())
+				continue;
+
+			// check whether the roundabout has any roads going inside.
+			// if so, disable flare checking for roads at those nodes
+			RouteNode nextExitNode = null;
+			RouteNode nextButOneExitNode = null;
+			RouteNode precedingExitNode = null;
+			for (RouteNode rn : roundaboutNodes) {
+				if (rn == nodeToCheck)
+					continue;
+				
+				for (RouteArc ra : rn.getArcs()) {
+					if (!ra.isDirect())
+						continue;
+					
+					RoadDef rd = ra.getRoadDef();
+					if (!rd.isSynthesised() && !rd.isRoundabout()) {
+						byte access = rd.getAccess();
+						if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+							if (goesInsideRoundabout(ra, rn, centre)) {
+								for (RouteArc ra2 : rn.getArcs())
+									ra2.getRoadDef().doFlareCheck(false);
+							}
+							if (nextExitNode == null)
+								nextExitNode = rn;
+							else {
+								if (nextButOneExitNode == null)
+									nextButOneExitNode = rn;
+								precedingExitNode = rn;
+							}
+						}
+						else
+							rd.doFlareCheck(false);							
+					}
+				}
+			}
+			
+			if(nextExitNode == null) // something is not right so give up
+				continue;		
+
+			// now try and find the two arcs that make up the
+			// triangular flare connected to both ends of the
+			// roundabout segment
+			for(RouteArc fa : nodeToCheck.getArcs()) {
+				if(!fa.isDirect())
+					continue;
+
+				RoadDef rda = fa.getRoadDef();
+				if (rda.isSynthesised() || !rda.doFlareCheck())
+					continue;
+
+				for(RouteArc fb : nextExitNode.getArcs()) {
+					if(!fb.isDirect() || (fa == fb))
+						continue;
+
+					RoadDef rdb = fb.getRoadDef();
+					if(rdb.isSynthesised() || !rdb.doFlareCheck())
+						continue;
+
+					if(fa.getDest() == fb.getDest()) {
+						// found the 3rd point of the triangle that
+						// should be connecting the two flare roads
+
+						// first, special test required to cope with
+						// roundabouts that have a single flare and no
+						// other connections - only check the flare
+						// for the shorter of the two roundabout
+						// segments
+
+						if(roundaboutSegmentLength(nodeToCheck, nextExitNode) >=
+						   roundaboutSegmentLength(nextExitNode, nodeToCheck))
+							continue;
+						
+						// check that that the flare has a road connected to it
+						boolean hasRoad = false;
+						for (RouteArc ra2 : fa.getDest().getArcs()) {
+							if (ra2.isDirect() && (ra2 != fa) && (ra2 != fb)) {
+								RoadDef rd2 = ra2.getRoadDef();
+								byte access = rd2.getAccess();
+								if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+									hasRoad = true;
+									break;
+								}
+							}
+						}
+						if (!hasRoad) {
+							log.info("Discarding possible roundabout flare", rda, "with", rdb, "no ongoing road");
+							continue;							
+						}
+						
+						// check the lengths of the two flares
+						if (maxFlareLength > 0) {
+							float faLength = fa.getLengthInMeter();
+							float fbLength = fb.getLengthInMeter();
+							if (faLength > maxFlareLength || fbLength > maxFlareLength) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where the flares are", faLength, "and", fbLength, "m");
+								continue;
+							}
+						}
+						
+						// check the relative lengths of the two flares
+						if (maxFlareLengthMultiple > 0) {
+							float faLength = fa.getLengthInMeter();
+							float fbLength = fb.getLengthInMeter();
+							if (faLength > maxFlareLengthMultiple * fbLength || fbLength > maxFlareLengthMultiple * faLength) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where one flare is", String.format("%.1f", Math.max(faLength, fbLength)/Math.min(faLength, fbLength)), "times longer than the other");
+								continue;
+							}
+						}
+						
+						//check the angle between the initial bearing from each flare and the centre of the roundabout
+						if (maxFlareBearing > 0) {
+							double flareAngle = fa.getReverseArc().getInitialHeading() - fa.getDest().getCoord().bearingTo(centre);
+							if (flareAngle < -180)
+								flareAngle += 360;
+							if (flareAngle > 180)
+								flareAngle -= 360;
+							if (Math.abs(flareAngle) > maxFlareBearing) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where angle to centre is", String.format("%.1f", flareAngle));
+								continue;
+							}
+							flareAngle = fb.getReverseArc().getInitialHeading() - fb.getDest().getCoord().bearingTo(centre);
+							if (flareAngle < -180)
+								flareAngle += 360;
+							if (flareAngle > 180)
+								flareAngle -= 360;
+							if (Math.abs(flareAngle) > maxFlareBearing) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where angle to centre is", String.format("%.1f", flareAngle));
+								continue;
+							}
+						}
+
+						// check the angle between the flares
+						if (maxFlareAngle > 0) {
+							double flareAngle = fa.getReverseArc().getInitialHeading() - fb.getReverseArc().getInitialHeading();
+							if (flareAngle < -180)
+								flareAngle += 360;
+							if (flareAngle > 180)
+								flareAngle -= 360;
+							if (Math.abs(flareAngle) > maxFlareAngle) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where subtended angle is", String.format("%.1f", flareAngle));
+								continue;
+							}
+						}
+						
+						// check that both roads don't extend beyond the common point
+						boolean faExtended = false;
+						boolean fbExtended = false;
+						boolean thirdArcToRoundabout = false;
+						for (RouteArc ra2 : fa.getDest().getArcs()) {
+							if (ra2.isDirect()) {
+								if (ra2.getDest() != nodeToCheck && ra2.getSource() != nodeToCheck && ra2.getDest() != nextExitNode && ra2.getSource() != nextExitNode)
+								{
+									RoadDef rd2 = ra2.getRoadDef();
+									if (rd2 == rda)
+										faExtended = true;
+									if (rd2 == rdb)
+										fbExtended = true;
+									if (!thirdArcToRoundabout) {
+										byte access = rd2.getAccess();
+										if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+											for (RouteNode rn : roundaboutNodes) {
+												if (rn == ra2.getSource() || rn == ra2.getDest()) {
+													thirdArcToRoundabout = true;
+													break;
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+						if (faExtended && fbExtended && discardBothFlaresExtend) {
+							log.info("Discarding possible roundabout flare", rda, "with", rdb, "where flares both extend at", fa.getDest().getCoord().toOSMURL());
+							continue;
+						}
+						if (discardMultipleFlares && thirdArcToRoundabout) {
+							log.info("Discarding possible roundabout flare", rda, "with", rdb, "where more than two roads join roundabout from", fa.getDest().getCoord().toOSMURL());
+							continue;
+						}
+						
+						// check whether they have names that are different
+						String namea = rda.getName();
+						String nameb = rdb.getName();
+						if (discardDifferentFlareNames) {
+							if (namea != null && nameb != null && !namea.equals(nameb) && !namea.contains(nameb) && !nameb.contains(namea)) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where flares have different names", namea, "and", nameb);
+								continue;
+							}
+						}
+
+						// check whether they have accesses that are different
+						byte accessa = rda.getAccess();
+						byte accessb = rdb.getAccess();
+						if (discardDifferentAccesses) {
+							if (accessa != accessb) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where flares have different accesses");
+								continue;
+							}
+						}
+
+						// check whether the two roads are very far apart on the roundabout
+						if ((centre != null) && (maxEntryAngle > 0)) {
+							double bearingFrom = centre.bearingTo(coord);
+							double bearingTo = centre.bearingTo(nextExitNode.getCoord());
+							double angle = bearingTo - bearingFrom;
+							if (angle < 0)
+								angle = -angle;
+							if (angle > 180)
+								angle = 360 - angle;
+							if (angle > maxEntryAngle) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, coord.toOSMURL(), nextExitNode.getCoord().toOSMURL(), "where angle", angle, "from centre is too large");
+								continue;
+							}
+						}
+						
+						if(maxFlareToSeparationMultiple > 0) {
+							// if both of the flare roads are much
+							// longer than the length of the
+							// roundabout segment, they are probably
+							// not flare roads at all but just two
+							// roads that meet up - so ignore them
+							int roundaboutSegmentLength = roundaboutSegmentLength(nodeToCheck, nextExitNode);
+							int maxLength = (flareSeparationOverride == 0 ? roundaboutSegmentLength : Math.max(flareSeparationOverride,  roundaboutSegmentLength)) * maxFlareToSeparationMultiple;
+							if(maxLength > 0 &&
+							   fa.getLength() > maxLength &&
+							   fb.getLength() > maxLength) {
+								log.info("Discarding possible roundabout flare", rda, "with", rdb, "where roads are", fa.getLength(), "and", fb.getLength(), "m long with a separation of", roundaboutSegmentLength, "m");
+								continue;
+							}
+						}
+
+						// check whether there is also a bigger flare
+						boolean processed = false;
+						RoadDef extendedRoad = null;
+						for(RouteArc a : fa.getDest().getArcs()) {
+							if(a.isDirect() && a.getDest() != nodeToCheck && a.getDest() != nextExitNode) {
+								RoadDef ard = a.getRoadDef();
+								if((ard == rda) || (ard == rdb)) {
+									// one of the two roads goes further than the flare
+
+									if (nextButOneExitNode != null) {
+										// check whether there is a bigger flare to the next exit point
+										for(RouteArc fc : nextButOneExitNode.getArcs()) {
+											if(!fc.isDirect())
+												continue;
+
+											RoadDef rdc = fc.getRoadDef();
+											if (rdc.isSynthesised())
+												continue;
+
+											byte access = rdc.getAccess();
+											if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+												if(a.getDest() == fc.getDest()) {
+													// we have a bigger flare
+													processed = true;
+													log.info("Discarding possible roundabout flare", rda, "at", fa.getDest().getCoord().toOSMURL(), "multiple flares found");
+													break;
+												}
+											}
+										}
+									}
+									if (!processed && (precedingExitNode != null)) {
+										// check whether there is a bigger flare from the preceding exit point
+										for(RouteArc fc : precedingExitNode.getArcs()) {
+											if(!fc.isDirect())
+												continue;
+
+											RoadDef rdc = fc.getRoadDef();
+											if (rdc.isSynthesised())
+												continue;
+
+											byte access = rdc.getAccess();
+											if (access != 0 && (access != AccessTagsAndBits.FOOT)) {
+												if(a.getDest() == fc.getDest()) {
+													// we have a bigger flare
+													processed = true;
+													log.info("Discarding possible roundabout flare", rda, "at", fa.getDest().getCoord().toOSMURL(), "multiple flares found");
+													break;
+												}
+											}
+										}
+									}
+									if (processed)
+										break;
+									extendedRoad = ard;
+								}
+							}
+						}
+						if (processed)
+							continue;
+
+						// check the flare roads for one way direction
+						if(!rda.isOneway())
+							log.diagnostic("Outgoing roundabout flare road " + rda + " is not oneway " + fa.getSource().getCoord().toOSMURL());
+						else if(!rdb.isOneway())
+							log.diagnostic("Incoming roundabout flare road " + rdb + " is not oneway " + fb.getDest().getCoord().toOSMURL());
+						else if(!fa.isForward())
+							log.diagnostic("Outgoing roundabout flare road " + rda + " points in wrong direction " + fa.getSource().getCoord().toOSMURL());
+						else if(fb.isForward())
+							log.diagnostic("Incoming roundabout flare road " + rdb + " points in wrong direction " +  fb.getSource().getCoord().toOSMURL());
+						else if (extendedRoad != null) {
+							if(extendedRoad == rda)
+								log.diagnostic("Outgoing roundabout flare road " + rda + " does not finish at flare " + fa.getDest().getCoord().toOSMURL());
+							else if(extendedRoad == rdb)
+								log.diagnostic("Incoming roundabout flare road " + rdb + " does not start at flare " + fb.getDest().getCoord().toOSMURL());
+						}
+						else if (namea != null && nameb != null && !namea.equals(nameb))
+							log.diagnostic("Roundabout flare roads " + rda + " and " + rdb + " have different names " + namea + " and " + nameb);
+						else if (namea != null && nameb == null)
+							log.diagnostic("Roundabout flare road " + rda + " has name " + namea + " but " + rdb + " is unnamed");
+						else if (namea == null && nameb != null)
+							log.diagnostic("Roundabout flare road " + rdb + " has name " + nameb + " but " + rda + " is unnamed");
+					}
+				}
+			}
+		}
+	}
+
+	// determine "distance" between two nodes on a roundabout
+	private static int roundaboutSegmentLength(final RouteNode n1, final RouteNode n2) {
+		List<RouteNode> seen = new ArrayList<>();
+		int len = 0;
+		RouteNode n = n1;
+		boolean checkMoreLinks = true;
+		while(checkMoreLinks && !seen.contains(n)) {
+			checkMoreLinks = false;
+			seen.add(n);
+			for(RouteArc a : n.getArcs()) {
+				if(a.isForward() &&
+				   a.getRoadDef().isRoundabout() &&
+				   !a.getRoadDef().isSynthesised()) {
+					len += a.getLength();
+					n = a.getDest();
+					if(n == n2)
+						return len;
+					checkMoreLinks = true;
+					break;
+				}
+			}
+		}
+		// didn't find n2
+		return Integer.MAX_VALUE;
+	}
+
+	/**
+	 * determine whether a RouteArc goes inside the roundabout
+	 * @param ra a RouteArc that has a node on the roundabout
+	 * @param rn the RouteNode that is on the roundabout
+	 * @param roundaboutCentre the centre of the roundabout
+	 */
+	private boolean goesInsideRoundabout(RouteArc ra, RouteNode rn, Coord roundaboutCentre) {
+		// check whether the way is inside the roundabout by seeing if the next point is nearer to the centre of the roundabout than this
+		RouteNode nextNode = ra.getSource() == rn ? ra.getDest() : ra.getSource();
+		Coord nextCoord = nextNode.getCoord();
+		boolean rejoinsRoundabout = false;
+		for (RouteNode roundaboutNode : roundaboutNodes) {
+			if (roundaboutNode == nextNode) {
+				// arc rejoins roundabout, so calculate another point to use
+				rejoinsRoundabout = true;
+				nextCoord = rn.getCoord().offset(ra.getSource() == rn ? ra.getInitialHeading() : ra.getReverseArc().getInitialHeading(), rn.getCoord().distance(nextCoord) / 2);
+				break;
+			}
+		}
+		double distanceToCentre = roundaboutCentre.distance(rn.getCoord());
+		double nextDistanceToCentre = roundaboutCentre.distance(nextCoord);
+		RoadDef rd = ra.getRoadDef();
+		if (Math.abs(nextDistanceToCentre - distanceToCentre) < 5) {
+			if (rejoinsRoundabout) {
+				Coord nextCoord2 = nextCoord.offset(rn.getCoord().bearingTo(nextCoord), nextCoord.distance(rn.getCoord()) / 2);
+				double nextDistanceToCentre2 = roundaboutCentre.distance(nextCoord2);
+				if (Math.abs(nextDistanceToCentre2 - distanceToCentre) > 2)
+					return nextDistanceToCentre2 < distanceToCentre;
+			}
+			else {
+				// have a look at the next point to more accurately determine whether inside
+				for (RouteArc nra : nextNode.getArcs()) {
+					if ((nra != ra) && (nra.getRoadDef() == rd)) {
+						RouteNode nextNode2 = nra.getSource() == nextNode ? nra.getDest() : nra.getSource();
+						if (nextNode2 == rn)
+							continue;
+						
+						Coord nextCoord2 = nextNode2.getCoord();
+						for (RouteNode roundaboutNode : roundaboutNodes) {
+							if (roundaboutNode == nextNode2) {
+								// arc rejoins roundabout, so calculate another point to use
+								nextCoord2 = nextCoord.offset(nra.getSource() == nextNode ? nra.getInitialHeading() : nra.getReverseArc().getInitialHeading(), nextCoord.distance(nextCoord2) / 2);
+								break;
+							}
+						}
+						if (nextCoord.distance(nextCoord2) > distanceToCentre)
+							nextCoord2 = nextCoord.offset(nextCoord.bearingTo(nextCoord2), distanceToCentre / 2);
+						double nextDistanceToCentre2 = roundaboutCentre.distance(nextCoord2);
+						if (Math.abs(nextDistanceToCentre2 - distanceToCentre) > 2)
+							return nextDistanceToCentre2 < distanceToCentre;
+					}
+				}
+				// no luck there, so try following the existing bearing further
+				Coord nextCoord3 = nextCoord.offset(rn.getCoord().bearingTo(nextCoord), distanceToCentre / 2);
+				double nextDistanceToCentre3 = roundaboutCentre.distance(nextCoord3);
+				if (Math.abs(nextDistanceToCentre3 - distanceToCentre) > 2)
+					return nextDistanceToCentre3 < distanceToCentre;
+			}
+		}
+		if ((Math.abs(nextDistanceToCentre - distanceToCentre) < 2) && !rd.messagePreviouslyIssued("inside roundabout"))
+			log.info("Way",rd,"unable to accurately determine whether",nextCoord.toOSMURL()," is inside roundabout centred at", roundaboutCentre.toOSMURL());
+
+		return nextDistanceToCentre < distanceToCentre;
+	}
+	
+	/**
+	 * helper class for finding the centre of a roundabout
+	 */
+	private class CentreFinder {
+		double delta;
+		RouteNode nearestNodeToCentre;
+		RouteNode furthestNodeFromCentre;
+	}	
+		
+	private Coord getRoundaboutCentre() {
+		int numPoints = roundaboutNodes.size();
+		double centreLat = 0;
+		double centreLon = 0;
+		for (RouteNode rn : roundaboutNodes) {
+			centreLat += rn.getCoord().getHighPrecLat();
+			centreLon += rn.getCoord().getHighPrecLon();
+		}
+		// get centre of gravity as an approximate centre of roundabout
+		Coord roundaboutCentre = Coord.makeHighPrecCoord((int)Math.round(centreLat / numPoints), (int)Math.round(centreLon / numPoints));
+		if(log.isDebugEnabled())
+			log.debug("Roundabout centre of gravity", roundaboutCentre.toOSMURL());
+		if (roundaboutNodes.size() > 2) {
+			CentreFinder cf = getDeltaDistanceFromCentre(roundaboutCentre);
+			while (cf.delta > 1.0) {
+				double bearing = roundaboutCentre.bearingTo(cf.furthestNodeFromCentre.getCoord());
+				Coord betterCentre = roundaboutCentre.offset(bearing, cf.delta / 3);
+				CentreFinder bettercf = getDeltaDistanceFromCentre(betterCentre);
+				if(log.isDebugEnabled())
+					log.debug("Trying centre", betterCentre.toOSMURL());
+				if (bettercf.delta < cf.delta) {
+					cf = bettercf;
+					roundaboutCentre = betterCentre;
+				}
+				else {
+					bearing = cf.nearestNodeToCentre.getCoord().bearingTo(roundaboutCentre);
+					betterCentre = roundaboutCentre.offset(bearing, cf.delta / 3);
+					if(log.isDebugEnabled())
+						log.debug("Trying centre", betterCentre.toOSMURL());
+					bettercf = getDeltaDistanceFromCentre(betterCentre);
+					if (bettercf.delta < cf.delta) {
+						cf = bettercf;
+						roundaboutCentre = betterCentre;
+					}
+					else
+						break;
+				}
+			}
+		}
+		if(log.isDebugEnabled())
+			log.debug("Roundabout centre", roundaboutCentre.toOSMURL());
+		return roundaboutCentre;
+	}
+		
+	private CentreFinder getDeltaDistanceFromCentre(Coord centreCoord) {
+		CentreFinder cf = new CentreFinder();
+		double minDistanceToCentre = 9999999;
+		double maxDistanceToCentre = 0;
+		for (RouteNode rn : roundaboutNodes) {
+			double distanceToCentre = centreCoord.distance(rn.getCoord());
+			if (minDistanceToCentre > distanceToCentre) {
+				minDistanceToCentre = distanceToCentre;
+				cf.nearestNodeToCentre = rn;
+			}
+			if (maxDistanceToCentre < distanceToCentre) {
+				maxDistanceToCentre = distanceToCentre;
+				cf.furthestNodeFromCentre = rn;
+			}
+		}
+		cf.delta = maxDistanceToCentre - minDistanceToCentre;
+		return cf;
+	}
+		
+}
Index: src/uk/me/parabola/imgfmt/app/net/RouteNode.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(revision 4729)
+++ src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(working copy)
@@ -20,6 +20,7 @@
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.List;
+import java.util.logging.Level;
 
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import uk.me.parabola.imgfmt.Utils;
@@ -355,318 +356,6 @@
 		return coord.compareTo(otherNode.getCoord());
 	}
 
-	public void checkRoundabouts() {
-		List<RouteArc> roundaboutArcs = new ArrayList<>();
-		List<RouteArc> nonRoundaboutArcs = new ArrayList<>();
-		int countNonRoundaboutRoads = 0;
-		int countNonRoundaboutOtherHighways = 0;
-		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);
-				}
-			}
-		}
-		if (!roundaboutArcs.isEmpty()) {
-			// 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<>();
-			processedNodes.add(this);
-			for (RouteArc ra : roundaboutArcs) {
-				if (ra.isForward()) {
-					for (RouteArc ra1 : nonRoundaboutArcs) {
-						if ((ra1.getDirectHeading() == ra.getDirectHeading()) && (ra1.getInitialHeading() == ra.getInitialHeading()) && (ra1.getFinalHeading() == ra.getFinalHeading()) && (ra1.getLengthInMeter() == ra.getLengthInMeter())) {
-							// non roundabout highway overlaps roundabout
-							nonRoundaboutArcs.remove(ra1);
-							if(!ra.getRoadDef().messagePreviouslyIssued("roundabout forks/overlaps"))
-								log.diagnostic("Highway " + ra1.getRoadDef() + " overlaps roundabout " + ra.getRoadDef() + " at " + coord.toOSMURL());
-							break;
-						}						
-					}
-					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();
-							}
-						}
-						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.0 + 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.0 + 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.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + (roundaboutArc.isForward() ? " starts at " : " ends at ") + coord.toOSMURL());
-				if (countNonRoundaboutRoads > 1)
-					log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to more than one road at " + coord.toOSMURL());
-				else if (countNonRoundaboutRoads == 1) {
-					if (countNonRoundaboutOtherHighways > 0) {
-						if (countHighwaysInsideRoundabout > 0)
-							log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to a road, " + countNonRoundaboutOtherHighways + " other highway(s) and " + countHighwaysInsideRoundabout + " highways inside the roundabout at " + coord.toOSMURL());
-						else
-							log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to a road and " + countNonRoundaboutOtherHighways + " other highway(s) at " + coord.toOSMURL());
-					}
-					else if (countHighwaysInsideRoundabout > 0)
-						log.diagnostic("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.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to " + countNonRoundaboutOtherHighways+ " highway(s) and " + countHighwaysInsideRoundabout + " inside the roundabout at " + coord.toOSMURL());
-					else if (countNonRoundaboutOtherHighways > 1)
-						log.diagnostic("Roundabout " + roundaboutArc.getRoadDef() + " is connected to " + countNonRoundaboutOtherHighways + " highways at " + coord.toOSMURL());
-				}
-				else if (countHighwaysInsideRoundabout > 1)
-					log.diagnostic("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.diagnostic("Roundabout " + rd + " overlaps " + fb.getRoadDef() + " at " + coord.toOSMURL());
-										}
-									}
-									else if (fb.isForward()
-											&& !rd.messagePreviouslyIssued("roundabout forks/overlaps")) {
-										log.diagnostic("Roundabout " + rd + " forks at " + coord.toOSMURL());
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	// determine "distance" between two nodes on a roundabout
-	private static int roundaboutSegmentLength(final RouteNode n1, final RouteNode n2) {
-		List<RouteNode> seen = new ArrayList<>();
-		int len = 0;
-		RouteNode n = n1;
-		boolean checkMoreLinks = true;
-		while(checkMoreLinks && !seen.contains(n)) {
-			checkMoreLinks = false;
-			seen.add(n);
-			for(RouteArc a : n.arcs) {
-				if(a.isForward() &&
-				   a.getRoadDef().isRoundabout() &&
-				   !a.getRoadDef().isSynthesised()) {
-					len += a.getLength();
-					n = a.getDest();
-					if(n == n2)
-						return len;
-					checkMoreLinks = true;
-					break;
-				}
-			}
-		}
-		// didn't find n2
-		return Integer.MAX_VALUE;
-	}
-
-	// sanity check roundabout flare roads - the flare roads connect a
-	// two-way road to a roundabout using short one-way segments so
-	// the resulting sub-junction looks like a triangle with two
-	// corners of the triangle being attached to the roundabout and
-	// the last corner being connected to the two-way road
-
-	public void checkRoundaboutFlares(int maxFlareLengthRatio) {
-		for(RouteArc r : arcs) {
-			// see if node has a forward arc that is part of a
-			// roundabout
-			if(!r.isForward() || !r.isDirect() || !r.getRoadDef().isRoundabout() || r.getRoadDef().isSynthesised())
-				continue;
-
-			// follow the arc to find the first node that connects the
-			// roundabout to a non-roundabout segment
-			RouteNode nb = r.getDest();
-			List<RouteNode> seen = new ArrayList<>();
-			seen.add(this);
-
-			while (true) {
-
-				if (seen.contains(nb)) {
-					// looped - give up
-					nb = null;
-					break;
-				}
-
-				// remember we have seen this node
-				seen.add(nb);
-
-				boolean connectsToNonRoundaboutSegment = false;
-				RouteArc nextRoundaboutArc = null;
-				for (RouteArc nba : nb.arcs) {
-					if (!nba.isDirect())
-						continue;
-					if (!nba.getRoadDef().isSynthesised()) {
-						if (nba.getRoadDef().isRoundabout()) {
-							if (nba.isForward())
-								nextRoundaboutArc = nba;
-						} else {
-							connectsToNonRoundaboutSegment = true;
-						}
-					}
-				}
-
-				if (connectsToNonRoundaboutSegment) {
-					// great, that's what we're looking for
-					break;
-				}
-
-				if (nextRoundaboutArc == null) {
-					// not so good, the roundabout stops in mid air?
-					nb = null;
-					break;
-				}
-
-				nb = nextRoundaboutArc.getDest();
-			}
-
-			if(nb == null) {
-				// something is not right so give up
-				continue;
-			}
-
-			// now try and find the two arcs that make up the
-			// triangular "flare" connected to both ends of the
-			// roundabout segment
-			for(RouteArc fa : arcs) {
-				if(!fa.isDirect() || !fa.getRoadDef().doFlareCheck())
-					continue;
-
-				for(RouteArc fb : nb.arcs) {
-					if(!fb.isDirect() || !fb.getRoadDef().doFlareCheck())
-						continue;
-					if(fa.getDest() == fb.getDest()) {
-						// found the 3rd point of the triangle that
-						// should be connecting the two flare roads
-
-						// first, special test required to cope with
-						// roundabouts that have a single flare and no
-						// other connections - only check the flare
-						// for the shorter of the two roundabout
-						// segments
-
-						if(roundaboutSegmentLength(this, nb) >=
-						   roundaboutSegmentLength(nb, this))
-							continue;
-
-						if(maxFlareLengthRatio > 0) {
-							// if both of the flare roads are much
-							// longer than the length of the
-							// roundabout segment, they are probably
-							// not flare roads at all but just two
-							// roads that meet up - so ignore them
-							final int maxFlareLength = roundaboutSegmentLength(this, nb) * maxFlareLengthRatio;
-							if(maxFlareLength > 0 &&
-							   fa.getLength() > maxFlareLength &&
-							   fb.getLength() > maxFlareLength) {
-								continue;
-							}
-						}
-
-						// now check the flare roads for direction and
-						// oneway
-
-						// only issue one warning per flare
-						if(!fa.isForward())
-							log.diagnostic("Outgoing roundabout flare road " + fa.getRoadDef() + " points in wrong direction? " + fa.getSource().coord.toOSMURL());
-						else if(fb.isForward())
-							log.diagnostic("Incoming roundabout flare road " + fb.getRoadDef() + " points in wrong direction? " + fb.getSource().coord.toOSMURL());
-						else if(!fa.getRoadDef().isOneway())
-							log.diagnostic("Outgoing roundabout flare road " + fa.getRoadDef() + " is not oneway? " + fa.getSource().coord.toOSMURL());
-
-						else if(!fb.getRoadDef().isOneway())
-							log.diagnostic("Incoming roundabout flare road " + fb.getRoadDef() + " is not oneway? " + fb.getDest().coord.toOSMURL());
-						else {
-							// check that the flare road arcs are not
-							// part of a longer way
-							for(RouteArc a : fa.getDest().arcs) {
-								if(a.isDirect() && a.getDest() != this && a.getDest() != nb) {
-									if(a.getRoadDef() == fa.getRoadDef())
-										log.diagnostic("Outgoing roundabout flare road " + fb.getRoadDef() + " does not finish at flare? " + fa.getDest().coord.toOSMURL());
-									else if(a.getRoadDef() == fb.getRoadDef())
-										log.diagnostic("Incoming roundabout flare road " + fb.getRoadDef() + " does not start at flare? " + fb.getDest().coord.toOSMURL());
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
 	public void reportSimilarArcs() {
 		for(int i = 0; i < arcs.size(); ++i) {
 			RouteArc arci = arcs.get(i);
@@ -923,6 +612,10 @@
 	public void setRoadClass(int roadClass) {
 		nodeClass = (byte) roadClass;
 	}
+	
+	public static boolean isWarningLogged() {
+		return log.isLoggable(Level.WARNING);
+	}
 
 //	/**
 //	 * simple-minded quick test for enabling RouteNode diagnostic for an area of interest.
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 4729)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -155,8 +155,9 @@
 	
 	private String countryAbbr;
 
-	private final boolean checkRoundabouts;
-	private int reportDeadEnds; 
+	private final boolean checkRoundaboutDirections;
+	private final boolean fixRoundaboutDirections;
+	private final int reportDeadEnds; 
 	private final boolean linkPOIsToWays;
 	private final boolean mergeRoads;
 	private final boolean allowReverseMerge;
@@ -207,13 +208,29 @@
 		case "detect,right":
 			break;
 		default:
-			throw new ExitException("invalid parameters for option drive-on:"+driveOn);
+			throw new ExitException("invalid parameters for option drive-on:" + driveOn);
 		}
 		countryAbbr = props.getProperty("country-abbr", null);
 		if (countryAbbr != null)
 			countryAbbr = countryAbbr.toUpperCase();
-			
-		checkRoundabouts = props.getProperty("check-roundabouts",false);
+
+		if (props.getProperty("check-roundabouts", false)) {//deprecated
+			checkRoundaboutDirections = log.isLoggable(Level.WARNING);
+			fixRoundaboutDirections = true;
+		} else {
+			String checkRoundaboutsOption = props.getProperty("report-roundabout-issues");
+			boolean check = false;
+			if (checkRoundaboutsOption != null) {
+				if ("".equals(checkRoundaboutsOption))
+					check = true;
+				for (String option : checkRoundaboutsOption.split(",")) {
+					if ("all".equals(option) || "direction".equals(option))
+						check = true;
+				}
+			}
+			checkRoundaboutDirections = check;
+			fixRoundaboutDirections = props.getProperty("fix-roundabout-direction",false);
+		}
 		reportDeadEnds = (props.getProperty("report-dead-ends") != null) ? props.getProperty("report-dead-ends", 1) : 0;  
 		prefixSuffixFilter = new PrefixSuffixFilter(props);
 
@@ -407,7 +424,7 @@
 						numDriveOnSideUnknown++;
 					}
 				}
-				if (cw.isRoundabout() && wasReversed && checkRoundabouts) {
+				if (cw.isRoundabout() && wasReversed && checkRoundaboutDirections) {
 					log.diagnostic("Roundabout " + way.getId() +
 							" has reverse oneway tag (" + way.getFirstPoint().toOSMURL() + ")");
 				}
@@ -1094,7 +1111,7 @@
 		// if roundabout checking is enabled and roundabout has at
 		// least 3 points and it has not been marked as "don't
 		// check", check its direction
-		if (checkRoundabouts && points.size() > 2
+		if ((checkRoundaboutDirections || fixRoundaboutDirections) && points.size() > 2
 				&& !way.tagIsLikeYes("mkgmap:no-dir-check")
 				&& !way.tagIsLikeNo("mkgmap:dir-check")) {
 			Coord centre = way.getCofG();
@@ -1133,13 +1150,16 @@
 				if (points.get(0) == points.get(points.size() - 1)) {
 					// roundabout is a loop
 					if (dirIsWrong) {
-						log.diagnostic("Roundabout " + way.getId() + " direction is wrong - reversing it (see "
-								+ centre.toOSMURL() + ")");
-						way.reverse();
+						if (checkRoundaboutDirections)
+							log.diagnostic("Roundabout " + way.getId() + " direction is wrong " + (fixRoundaboutDirections ? "- reversing it " : "") +
+								"(see " + centre.toOSMURL() + ")");
+						if (fixRoundaboutDirections)
+							way.reverse();
 					}
 				} else if (dirIsWrong) {
 					// roundabout is a line
-					log.diagnostic("Roundabout segment " + way.getId() + " direction looks wrong (see "
+					if (checkRoundaboutDirections)
+						log.diagnostic("Roundabout segment " + way.getId() + " direction looks wrong (see "
 							+ points.get(0).toOSMURL() + ")");
 				}
 			}
