diff --git a/resources/styles/default/lines b/resources/styles/default/lines
index f11cc9c..706db12 100644
--- a/resources/styles/default/lines
+++ b/resources/styles/default/lines
@@ -10,25 +10,27 @@ contour=elevation | contour_ext=elevation
 
 # Set highway names to include the reference if there is one
 highway=* {name '${name} (${ref})' | '${ref}' | '${name}' }
-highway=bridleway [0x16 resolution 23]
-highway=byway [0x16 resolution 23]
-highway=cycleway [0x16 resolution 23]
-highway=footway [0x16 resolution 23]
-highway=minor [0x06 resolution 21]
-highway=motorway {add oneway = yes } [0x01 resolution 12]
-highway=motorway_link [0x01 resolution 16]
-highway=pedestrian [0x06 resolution 22]
-highway=primary | highway=primary_link [0x04 resolution 19]
-highway=residential | highway=living_street [0x06 resolution 22]
-highway=secondary [0x05 resolution 20]
-highway=path [0x16 resolution 20]
-highway=service [0x07 resolution 22]
-highway=steps [0x16 resolution 23]
-highway=tertiary [0x06 resolution 21]
-highway=track [0x0a resolution 21]
-highway=trunk | highway=trunk_link [0x02 resolution 16]
-highway=unclassified [0x06 resolution 21]
-highway=unsurfaced [0x0a resolution 21]
+highway=bridleway [0x16 road_class=0 road_speed=0 resolution 23]
+highway=byway [0x16 road_class=0 road_speed=0 resolution 23]
+highway=cycleway [0x16 road_class=0 road_speed=1 resolution 23]
+highway=footway [0x16 road_class=0 road_speed=0 resolution 23]
+highway=minor [0x06 road_class=1 road_speed=2 resolution 21]
+highway=motorway {add oneway = yes } [0x01 road_class=4 road_speed=6 resolution 12]
+highway=motorway_link [0x01 road_class=4 road_speed=3 resolution 16]
+highway=pedestrian [0x06 road_class=0 road_speed=0 resolution 22]
+highway=primary [0x02 road_class=3 road_speed=4 resolution 19]
+highway=primary_link [0x03 road_class=3 road_speed=3 resolution 19]
+highway=residential | highway=living_street [0x06 road_class=0 road_speed=2 resolution 22]
+highway=secondary [0x04 road_class=2 road_speed=3 resolution 20]
+highway=path [0x16 road_class=0 road_speed=0 resolution 20]
+highway=service [0x07 road_class=0 road_speed=1 resolution 22]
+highway=steps [0x16 road_class=0 road_speed=0 resolution 23]
+highway=tertiary [0x05 road_class=1 road_speed=3 resolution 20]
+highway=track [0x0a road_class=0 road_speed=1 resolution 21]
+highway=trunk  [0x02 road_class=3 road_speed=5 resolution 16]
+highway=trunk_link [0x02 road_class=3 road_speed=3 resolution 16]
+highway=unclassified [0x06 road_class=1 road_speed=2 resolution 21]
+highway=unsurfaced [0x0a road_class=0 road_speed=1 resolution 21]
 
 # Mop up any unrecognised highway types
 highway=* [0x07 ]
@@ -41,7 +43,7 @@ railway=rail [0x14 resolution 17]
 railway=subway [0x14 resolution 17]
 railway=tram [0x14 resolution 18]
 
-route=ferry [0x1a resolution 18]
+route=ferry [0x1a road_class=0 road_speed=0 resolution 18]
 
 waterway=canal [0x1f resolution 21]
 waterway=drain [0x1f resolution 22]
diff --git a/src/uk/me/parabola/imgfmt/app/Coord.java b/src/uk/me/parabola/imgfmt/app/Coord.java
index 414817c..6111b83 100644
--- a/src/uk/me/parabola/imgfmt/app/Coord.java
+++ b/src/uk/me/parabola/imgfmt/app/Coord.java
@@ -36,6 +36,7 @@ import uk.me.parabola.imgfmt.Utils;
 public class Coord {
 	private final int latitude;
 	private final int longitude;
+	private int highwayCount; // number of highways that use this point
 
 	/**
 	 * Construct from co-ordinates that are already in map-units.
@@ -69,6 +70,14 @@ public class Coord {
 		return 0;
 	}
 
+	public int getHighwayCount() {
+		return highwayCount;
+	}
+
+	public void incHighwayCount() {
+		++highwayCount;
+	}
+
 	public int hashCode() {
 		return latitude+longitude;
 	}
diff --git a/src/uk/me/parabola/mkgmap/main/MapMaker.java b/src/uk/me/parabola/mkgmap/main/MapMaker.java
index dbedbc6..d7ca724 100644
--- a/src/uk/me/parabola/mkgmap/main/MapMaker.java
+++ b/src/uk/me/parabola/mkgmap/main/MapMaker.java
@@ -148,8 +148,7 @@ public class MapMaker implements MapProcessor {
 			// collect lists of roads that have the same name
 			java.util.Map<String, List<MapLine>> namedRoads = new HashMap<String, List<MapLine>>();
 			for(MapLine l : src.getLines()) {
-				// isRoad() needed until OSM loader marks lines as roads
-				if(l.isRoad() || isRoad(l)) {
+				if(l.isRoad()) {
 					String ln = l.getName();
 					if(ln != null) {
 						List<MapLine> rl = namedRoads.get(ln);
@@ -302,9 +301,4 @@ public class MapMaker implements MapProcessor {
 		rnp.setLocation(coord);
 		return rnp;
 	}
-
-	// this is only needed until OSM reader identifies roads
-	boolean isRoad(MapLine l) {
-		return l.getType() <= 0x0c;
-	}
 }
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index 3b65958..ae878a2 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -16,7 +16,14 @@
  */
 package uk.me.parabola.mkgmap.osmstyle;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import uk.me.parabola.imgfmt.app.Area;
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.imgfmt.app.CoordNode;
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.AreaClipper;
 import uk.me.parabola.mkgmap.general.Clipper;
@@ -27,6 +34,7 @@ import uk.me.parabola.mkgmap.general.MapLine;
 import uk.me.parabola.mkgmap.general.MapPoint;
 import uk.me.parabola.mkgmap.general.MapRoad;
 import uk.me.parabola.mkgmap.general.MapShape;
+import uk.me.parabola.mkgmap.general.RoadNetwork;
 import uk.me.parabola.mkgmap.reader.osm.Element;
 import uk.me.parabola.mkgmap.reader.osm.GType;
 import uk.me.parabola.mkgmap.reader.osm.Node;
@@ -53,6 +61,12 @@ public class StyledConverter implements OsmConverter {
 	private Clipper clipper = Clipper.NULL_CLIPPER;
 
 	private int roadId;
+
+	private final int MAX_NODES_IN_WAY = 16;
+
+	// nodeIdMap maps a Coord into a nodeId
+	private Map<Coord, Integer> nodeIdMap = new HashMap<Coord, Integer>();
+	private int nextNodeId;
 	
 	private final Rule wayRules;
 	private final Rule nodeRules;
@@ -101,8 +115,12 @@ public class StyledConverter implements OsmConverter {
 
 		postConvertRules(way, foundType);
 
-		if (foundType.getFeatureKind() == GType.POLYLINE)
-            addLine(way, foundType);
+		if (foundType.getFeatureKind() == GType.POLYLINE) {
+		    if(foundType.isRoad())
+			addRoad(way, foundType);
+		    else
+			addLine(way, foundType);
+		}
 		else
 			addShape(way, foundType);
 	}
@@ -195,12 +213,6 @@ public class StyledConverter implements OsmConverter {
 
 		if (way.isBoolTag("oneway"))
 			line.setDirection(true);
-		if (gt.isRoad()) {
-			MapRoad r = new MapRoad(roadId++, line);
-			r.setRoadClass(gt.getRoadClass());
-			r.setSpeed(gt.getRoadSpeed());
-			line = r;
-		}
 
 		clipper.clipLine(line, lineAdder);
 	}
@@ -230,4 +242,180 @@ public class StyledConverter implements OsmConverter {
 		ms.setMinResolution(gt.getMinResolution());
 		ms.setMaxResolution(gt.getMaxResolution());
 	}
+
+	void addRoad(Way way, GType gt) {
+		boolean looped = false;
+		List<Coord> wayPoints = way.getPoints();
+		int numPointsInWay = wayPoints.size();
+
+		if(numPointsInWay > 1) {
+			// check if the last point in the way is the same as
+			// any other point in the way - if it is, there's a loop
+			Coord wayEndCoord = wayPoints.get(numPointsInWay - 1);
+			for(int i = 0; !looped && i < numPointsInWay - 1; ++i) {
+				if(wayPoints.get(i) == wayEndCoord)
+					looped = true;
+			}
+		}
+	
+		if(looped) {
+			if(numPointsInWay > 2) {
+				// create a new way to replace the last segment
+				Way loopTail = splitWayAt(way, numPointsInWay - 2);
+				// make roads from the un-looped ways
+				addRoadWithoutLoops(way, gt);
+				addRoadWithoutLoops(loopTail, gt);
+			}
+			else {
+				System.err.println("Ignoring looped way with only 2 points located at " + wayPoints.get(0).toDegreeString());
+			}
+		}
+		else {
+			// make a road from the way (it wasn't looped)
+			addRoadWithoutLoops(way, gt);
+		}
+	}
+
+	void addRoadWithoutLoops(Way way, GType gt) {
+		List<Integer> nodeIndices = new ArrayList<Integer>();
+		List<Coord> points = way.getPoints();
+		Way trailingWay = null;
+
+		// collect the Way's nodes
+		for(int i = 0; i < points.size(); ++i) {
+			Coord p = points.get(i);
+			int highwayCount = p.getHighwayCount();
+			if(highwayCount > 1) {
+				// this point is a node connecting highways
+				Integer nodeId = nodeIdMap.get(p);
+				if(nodeId == null) {
+					// assign a node id
+					nodeId = nextNodeId++;
+					nodeIdMap.put(p, nodeId);
+				}
+				nodeIndices.add(i);
+		//		System.err.println("Found node " + nodeId + " at " + p.toDegreeString());
+
+				if((i + 1) < points.size() &&
+				   nodeIndices.size() == MAX_NODES_IN_WAY) {
+					// this isn't the last point in the way
+					// so split it here to avoid exceeding
+					// the max nodes in way limit
+					trailingWay = splitWayAt(way, i);
+					// this will have truncated
+					// the current Way's points so
+					// the loop will now terminate
+					//					System.err.println("Splitting way " + way.getName() + " at " + points.get(i).toDegreeString() + " as it has at least " + MAX_NODES_IN_WAY + " nodes");
+				}
+			}
+		}
+
+		MapLine line = new MapLine();
+		elementSetup(line, gt, way);
+		line.setPoints(points);
+
+		if(way.isBoolTag("oneway"))
+			line.setDirection(true);
+
+		MapRoad road = new MapRoad(roadId++, line);
+
+		// set road parameters.
+		road.setRoadClass(gt.getRoadClass());
+		road.setSpeed(gt.getRoadSpeed());
+		road.setOneway(line.isDirection());
+
+		boolean noAccess[] = new boolean[RoadNetwork.NO_MAX];
+		String highwayType = way.getTag("highway");
+		if(highwayType == null) {
+		    // it's a routable way but not a highway (e.g. a ferry)
+		    // use the value of the route tag as the highwayType
+		    // for the purpose of testing for access restrictions
+		    highwayType = way.getTag("route");
+		}
+		String access = way.getTag("access");
+		if(access != null &&
+		   (access.toUpperCase().equals("NO") ||
+		    access.toUpperCase().equals("PRIVATE"))) {
+			for(int i = 0; i < noAccess.length; ++i)
+				noAccess[i] = true;
+		}
+		else if(highwayType.equals("footway")) {
+			noAccess[RoadNetwork.EMERGENCY] = true;
+			noAccess[RoadNetwork.DELIVERY] = true;
+			noAccess[RoadNetwork.NO_CAR] = true;
+			noAccess[RoadNetwork.NO_BUS] = true;
+			noAccess[RoadNetwork.NO_TAXI] = true;
+			if(!way.isBoolTag("bicycle"))
+				noAccess[RoadNetwork.NO_BIKE] = true;
+			noAccess[RoadNetwork.NO_TRUCK] = true;
+		}
+		else if(highwayType.equals("cycleway") ||
+			highwayType.equals("bridleway")) {
+			noAccess[RoadNetwork.EMERGENCY] = true;
+			noAccess[RoadNetwork.DELIVERY] = true;
+			noAccess[RoadNetwork.NO_CAR] = true;
+			noAccess[RoadNetwork.NO_BUS] = true;
+			noAccess[RoadNetwork.NO_TAXI] = true;
+			noAccess[RoadNetwork.NO_TRUCK] = true;
+		}
+
+		road.setAccess(noAccess);
+
+		if(way.isBoolTag("toll"))
+			road.setToll(true);
+
+		//road.setDirIndicator(dirIndicator); // FIXME
+
+		int numNodes = nodeIndices.size();
+		road.setNumNodes(numNodes);
+
+		if(numNodes > 0) {
+			// replace Coords that are nodes with CoordNodes
+			boolean hasInternalNodes = false;
+			for(int i = 0; i < numNodes; ++i) {
+				int n = nodeIndices.get(i);
+				if(n > 0 && n < points.size() - 1)
+					hasInternalNodes = true;
+				Coord coord = points.get(n);
+				Integer nodeId = nodeIdMap.get(coord);
+				boolean boundary = way.isBoolTag("mkg:boundary_node");
+				points.set(n, new CoordNode(coord.getLatitude(), coord.getLongitude(), nodeId, boundary));
+				//		System.err.println("Road " + road.getRoadId() + " node[" + i + "] " + nodeId + " at " + coord.toDegreeString());
+			}
+
+			road.setStartsWithNode(nodeIndices.get(0) == 0);
+			road.setInternalNodes(hasInternalNodes);
+		}
+
+		clipper.clipLine(road, lineAdder);
+
+		if(trailingWay != null)
+		    addRoadWithoutLoops(trailingWay, gt);
+	}
+
+	// split a Way at the specified point and return the new Way
+        // (the original Way is truncated)
+
+	Way splitWayAt(Way way, int index) {
+		Way trailingWay = new Way();
+		List<Coord> wayPoints = way.getPoints();
+		int numPointsInWay = wayPoints.size();
+
+		for(int i = index; i < numPointsInWay; ++i)
+			trailingWay.addPoint(wayPoints.get(i));
+
+		// ensure split point becomes a node
+		wayPoints.get(index).incHighwayCount();
+
+		// copy the way's name and tags to the new way
+		trailingWay.setName(way.getName());
+		trailingWay.copyTags(way);
+
+		// remove the points after the split from the original way
+		// it's probably more efficient to remove from the end first
+		for(int i = numPointsInWay - 1; i > index; --i)
+			wayPoints.remove(i);
+
+		return trailingWay;
+	}
 }
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Element.java b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
index bb6badf..a49e7c7 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/Element.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
@@ -85,7 +85,7 @@ public abstract class Element implements Iterable<String> {
 	 * @param other The other element.  All its tags will be copied to this
 	 * element.
 	 */
-	protected void copyTags(Element other) {
+	public void copyTags(Element other) {
 		tags = other.tags.copy();
 	}
 
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java
index 55962bb..eeece9c 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5MapDataSource.java
@@ -63,11 +63,10 @@ public class Osm5MapDataSource extends OsmMapDataSource {
 		try {
 			InputStream is = openFile(name);
 			SAXParserFactory parserFactory = SAXParserFactory.newInstance();
-			boolean ignoreBounds = getConfig().getProperty("ignore-osm-bounds", false);
 			SAXParser parser = parserFactory.newSAXParser();
 
 			try {
-				Osm5XmlHandler handler = new Osm5XmlHandler(ignoreBounds);
+				Osm5XmlHandler handler = new Osm5XmlHandler(getConfig());
 				handler.setCollector(mapper);
 				Runnable task = new Runnable() {
 					public void run() {
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
index fb3bbe2..57018db 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
@@ -31,6 +31,7 @@ import uk.me.parabola.mkgmap.reader.osm.Node;
 import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
 import uk.me.parabola.mkgmap.reader.osm.Relation;
 import uk.me.parabola.mkgmap.reader.osm.Way;
+import uk.me.parabola.util.EnhancedProperties;
 
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
@@ -73,9 +74,11 @@ class Osm5XmlHandler extends DefaultHandler {
 	private long nextFakeId = 1;
 
 	private final boolean ignoreBounds;
+	private final boolean routing;
 
-	public Osm5XmlHandler(boolean ignoreBounds) {
-		this.ignoreBounds = ignoreBounds;
+	public Osm5XmlHandler(EnhancedProperties props) {
+		ignoreBounds = props.getProperty("ignore-osm-bounds", false);
+		routing = props.containsKey("route");
 	}
 
 	/**
@@ -211,6 +214,23 @@ class Osm5XmlHandler extends DefaultHandler {
 		} else if (mode == MODE_WAY) {
 			if (qName.equals("way")) {
 				mode = 0;
+				if(routing &&
+				   (currentWay.getTag("highway") != null ||
+				    "ferry".equals(currentWay.getTag("route")))) {
+				    // the way is a highway (or ferry
+				    // route), so for each of it's
+				    // points, increment the number of
+				    // highways using that point
+				    for(Coord p : currentWay.getPoints())
+					p.incHighwayCount();
+				    // if the way is a roundabout but isn't
+				    // already flagged as "oneway", flag it here
+				    if(currentWay.getTag("oneway") == null) {
+					String junction = currentWay.getTag("junction");
+					if(junction != null && junction.equals("roundabout"))
+					    currentWay.addTag("oneway", "yes");
+				    }
+				}
 				currentWay = null;
 				// ways are processed at the end of the document,
 				// may be changed by a Relation class
