Index: resources/styles/default/points
===================================================================
--- resources/styles/default/points	(revision 4525)
+++ resources/styles/default/points	(working copy)
@@ -191,9 +191,9 @@
 healthcare=hospital | amenity=hospital | amenity=clinic [0x3002 resolution 22]
 healthcare=* | amenity=dentist | amenity=doctors [0x3002 resolution 24]
 
-highway=motorway_junction [0x2100 resolution 24]
+highway=motorway_junction & exit:road_ref=* {add exit:to='${exit_to}' | '${destination}'} [0x2000 resolution 24]
 
-highway=services & mkgmap:area2poi!=true [0x210f resolution 24 default_name 'Services']
+highway=services {add exit:facility="0x02,I,0x47,Features"} [0x230f resolution 20 default_name 'Services']
 
 historic=museum [0x2c02 resolution 24]
 historic=archaeological_site | historic=ruins [0x2c02 resolution 24]
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java	(revision 4525)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java	(working copy)
@@ -661,17 +661,22 @@
 
 	private void processExit(Map map, MapExitPoint mep) {
 		LBLFile lbl = map.getLblFile();
+		String exitName = mep.getName();
 		String ref = mep.getMotorwayRef();
 		String osmId = mep.getOSMId();
-		if(ref != null) {
+		if (ref == null)
+			log.warn("Can't create exit", exitName, "(OSM id", osmId, ") doesn't have exit:road_ref tag");
+		else {
 			Highway hw = highways.get(ref);
-			if(hw == null)
-				hw = makeHighway(map, ref);
-			if(hw == null) {
-			    log.warn("Can't create exit", mep.getName(), "(OSM id", osmId, ") on unknown highway", ref);
-			    return;
+			if (hw == null) {
+				String countryStr = mep.getCountry();
+				Country thisCountry = countryStr != null ? lbl.createCountry(locator.normalizeCountry(countryStr), locator.getCountryISOCode(countryStr)) : getDefaultCountry();
+				String regionStr = regionName != null ? regionName : mep.getRegion(); // use --region-name if set because highway will likely span regions
+				Region thisRegion = regionStr != null ? lbl.createRegion(thisCountry, regionStr, null) : getDefaultRegion(thisCountry);
+				hw = lbl.createHighway(thisRegion, ref);
+				log.info("creating highway", ref, "region:", regionStr, "country:", countryStr, "for exit:", exitName);
+				highways.put(ref, hw);
 			}
-			String exitName = mep.getName();
 			String exitTo = mep.getTo();
 			Exit exit = new Exit(hw);
 			String facilityDescription = mep.getFacilityDescription();
@@ -1283,16 +1288,6 @@
 		}
 	}
 
-	Highway makeHighway(Map map, String ref) {
-		if (getDefaultRegion(null) == null) {
-			log.warn("Highway " + ref + " has no region (define a default region to zap this warning)");
-		}
-		return highways.computeIfAbsent(ref, k-> {
-			log.info("creating highway", ref);
-			return map.getLblFile().createHighway(getDefaultRegion(null), ref);
-		});
-	}
-
 	/**
 	 * It is not possible to represent large maps at the 24 bit resolution.  This
 	 * gets the largest resolution that can still cover the whole area of the
Index: src/uk/me/parabola/mkgmap/reader/osm/GType.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/GType.java	(revision 4525)
+++ src/uk/me/parabola/mkgmap/reader/osm/GType.java	(working copy)
@@ -57,21 +57,38 @@
 	// actions will always be executed
 	private boolean propogateActionsOnContinue;
 
+	@SuppressWarnings("incomplete-switch")
 	public static boolean checkType(FeatureKind featureKind, int type) {
 		if (type >= 0x010000) {
 			if ((type & 0xff) > 0x1f)
 				return false;
 		} else {
-			if (featureKind == FeatureKind.POLYLINE && type > 0x3f
-					|| (featureKind == FeatureKind.POLYGON && (type > 0x7f || type == 0x4a))) {
-				return false;
-			} else if (featureKind == FeatureKind.POINT) {
+			switch (featureKind) {
+			case POLYLINE:
+				if (type > 0x3f)
+					return false;
+				break;
+			case POLYGON:
+				if (type > 0x7f || type == 0x4a)
+					return false;
+				break;
+			case POINT:
 				if (type < 0x0100)
 					return false;
 				int subtype = type & 0xff;
-				if (subtype > 0x1f || MapPoint.isCityType(type) && subtype != 0) {
-					return false;
+				if (MapPoint.isCityType(type)) {
+					if (subtype != 0)
+						return false;
+				} else if (type >= 0x1600 && type < 0x1e00 || // Andrzej Popowski says.
+						   type >= 0x2a00 && type < 0x3100 || // These may be indexed and this
+						   type >= 0x6400 && type < 0x6700) { // confines subtype to 5 bits
+					if (subtype > 0x1f)
+						return false;
+				} else {
+					if (subtype > 0x3f)
+						return false;
 				}
+				break;
 			}
 		}
 		return true;
@@ -243,7 +260,7 @@
 	 * known to cause routing errors if used for non-routable lines. 
 	 */
 	public static boolean isSpecialRoutableLineType(int type){
-		return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1b; 
+		return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1a || type == 0x1b;
 	}
 	
 	/**
Index: src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java	(revision 4525)
+++ src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java	(working copy)
@@ -15,13 +15,16 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.Exit;
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.ElementQuadTree;
 import uk.me.parabola.util.EnhancedProperties;
 
 /**
@@ -33,8 +36,8 @@
 public class HighwayHooks implements OsmReadingHooks {
 	private static final Logger log = Logger.getLogger(HighwayHooks.class);
 
-	private final List<Way> motorways = new ArrayList<>();
-	private final List<Node> exits = new ArrayList<>();
+	private final List<Element> motorways = new ArrayList<>(); // will all be Ways
+	private final List<Element> exits = new ArrayList<>();
 
 	private boolean makeOppositeCycleways;
 	private ElementSaver saver;
@@ -77,8 +80,8 @@
 	
 	@Override
 	public void onAddNode(Node node) {
-		String val = node.getTag("highway");
-		if (val != null && ("motorway_junction".equals(val) || "services".equals(val))) {
+		String highway = node.getTag("highway");
+		if (highway != null && ("motorway_junction".equals(highway) || "services".equals(highway) || "rest_area".equals(highway))) {
 			exits.add(node);
 			node.addTag("mkgmap:osmid", String.valueOf(node.getId()));
 		}
@@ -166,8 +169,12 @@
 			}
 		}
 
-		if("motorway".equals(highway) || "trunk".equals(highway))
+		if ("motorway".equals(highway) || "trunk".equals(highway) || "primary".equals(highway) || way.tagIsLikeYes("motorroad"))
 			motorways.add(way);
+		else if (linkPOIsToWays && ("services".equals(highway) || "rest_area".equals(highway))) {
+			exits.add(way);
+			way.addTag("mkgmap:osmid", String.valueOf(way.getId()));
+			}
 	}
 
 	@Override
@@ -177,8 +184,12 @@
 		motorways.clear();
 	}
 
+	private static final int XTRA = 150; // very approx 300m
 	private void finishExits() {
-		for (Node e : exits) {
+		if (exits.isEmpty() || motorways.isEmpty())
+			return;
+		ElementQuadTree majorRoads = new ElementQuadTree(saver.getBoundingBox(), motorways);
+		for (Element e : exits) {
 			String refTag = Exit.TAG_ROAD_REF;
 			if (e.getTag(refTag) == null) {
 				String exitName = e.getTag("name");
@@ -187,21 +198,86 @@
 
 				String ref = null;
 				Way motorway = null;
-				for (Way w : motorways) {
-					// uses an implicit call of Coord.equals()
-					if (w.getPoints().contains(e.getLocation())) {
-						motorway = w;
-						ref = w.getTag("ref");
-						if(ref != null)
-						    break;
+				Area bBox;
+				if (e instanceof Node)
+					bBox = Area.getBBox(Collections.singletonList(((Node)e).getLocation()));
+				else
+					bBox = Area.getBBox(((Way)e).getPoints());
+				String highway = e.getTag("highway");
+				boolean isServices = "services".equals(highway) || "rest_area".equals(highway);
+				if (isServices) // services will be just off the road, so increase size
+					bBox = new Area(bBox.getMinLat() - XTRA, bBox.getMinLong() - XTRA, bBox.getMaxLat() + XTRA, bBox.getMaxLong() + XTRA);
+				List<Way> possibleRoads = new ArrayList<>();
+				for (Element w : majorRoads.get(bBox)) {
+					motorway = (Way)w;
+					ref = motorway.getTag("ref");
+					if (ref != null) {
+						if (isServices) {
+							possibleRoads.add(motorway);  // save all possibilities
+						} else { // probably on 2+ roads, save possibilities to find the more major road (doesn't have to be motorway)
+							// uses an implicit call of Coord.equals()
+							if (motorway.getPoints().contains(((Node)e).getLocation()))
+								possibleRoads.add(motorway);
+						}
 					}
 				}
 				
+				if (possibleRoads.size() > 1) {
+					if (isServices) { // pick the closest road
+						Coord serviceCoord;
+						if (e instanceof Node)
+							serviceCoord = ((Node)e).getLocation();
+						else
+							// Simple-minded logic to see if a Way that probably defines [part of] a
+							// services area, hence might become a POI if option --link-pois-to-ways,
+							// is near the specified road.
+							// Just pick an arbitary Coord on the services boundary and find the nearest
+							// any Coord in the roads.
+							// No need to check if it is near the line between far-apart Coords because
+							// there also needs to be a junction so that can get off the road to the
+							// services.
+							serviceCoord = ((Way)e).getFirstPoint();
+						long closestRoad = Long.MAX_VALUE;
+						for (Way road : possibleRoads) {
+							long closestCoord = Long.MAX_VALUE;
+							for (Coord pointOnRoad : road.getPoints()) {
+								long dist = pointOnRoad.distanceInHighPrecSquared(serviceCoord);
+								if (dist < closestCoord)
+									closestCoord = dist;
+							}
+							if (closestCoord < closestRoad) {
+								closestRoad = closestCoord;
+								motorway = road;
+								ref = motorway.getTag("ref");
+							}
+						}
+					} else { // pick the most major road
+						int bestRoad = Integer.MAX_VALUE;
+						for (Way road : possibleRoads) {
+							String roadType = road.getTag("highway");
+							int thisRoad = 4;
+							if ("motorway".equals(roadType))
+								thisRoad = 0;
+							else if (road.tagIsLikeYes("motorroad"))
+								thisRoad = 1;
+							else if ("trunk".equals(roadType))
+								thisRoad = 2;
+							else if ("primary".equals(roadType))
+								thisRoad = 3;
+							if (thisRoad < bestRoad) {
+								bestRoad = thisRoad;
+								motorway = road;
+								ref = motorway.getTag("ref");
+							}
+						}
+					}
+					//log.info("Exit", exit, possibleRoads.size(), "options, chosen:", motorway, ref);
+				} // else 0 or 1 road; ref/motorway null or set correctly
 				if (ref != null) {
 					log.info("Adding", refTag + "=" + ref, "to exit", exitName);
 					e.addTag(refTag, ref);
 				} else if(motorway != null) {
-					log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag (" + e.getLocation().toOSMURL() + ")");
+					log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag", e);
 				}
 			}
 		}
