Index: resources/styles/default/lines
===================================================================
--- resources/styles/default/lines	(revision 2540)
+++ resources/styles/default/lines	(working copy)
@@ -14,10 +14,6 @@
 aeroway=runway [0x27 resolution 20]
 aeroway=taxiway [0x27 resolution 24]
 
-# Set the routing direction
-(highway=motorway|highway=trunk ) & ref=* & destination=* { add mkgmap:display_name =
-'${ref|subst: =>}(${destination|subst:;=> |subst:/=> })' }
-
 # Remove access=yes from highways.
 highway=* & (access=yes|access=permissive|access=designated|access=official) { delete access }
 # Translate motor_vehicle and vehicle access rules.
@@ -42,13 +38,35 @@
 
 # Set highway names to include the reference if there is one
 highway=motorway {name '${ref|highway-symbol:hbox} ${name}' | '${ref|highway-symbol:hbox}' | '${name}' }
-(highway=motorway_link | highway=trunk_link) & mkgmap:exit_hint=true 
+
+(highway=motorway_link | highway=trunk_link) & mkgmap:exit_hint=true & mkgmap:dest_hint=true
+  { delete mkgmap:display_name; 
+    name '${destionation:ref|subst: =>} ${destination|subst:;=> |subst:/=> }' | 
+         '${ref|subst: =>} ${destination|subst:;=> |subst:/=> }' | 
+         '${destination|subst:;=> |subst:/=> }' |
+         'Exit ${mkgmap:exit_hint_ref} ${mkgmap:exit_hint_name}' | 
+         'Exit ${mkgmap:exit_hint_ref} ${mkgmap:exit_hint_exit_to}' | 
+         'Exit ${mkgmap:exit_hint_exit_to}' |
+         'Exit ${mkgmap:exit_hint_name}' |
+         'Exit ${mkgmap:exit_hint_ref}'
+       }
+
+(highway=motorway_link | highway=trunk_link) & mkgmap:exit_hint!=* & mkgmap:dest_hint=true
+  { delete mkgmap:display_name; 
+    name '${destionation:ref|subst: =>} ${destination|subst:;=> |subst:/=> }' |
+         '${ref|subst: =>} ${destination|subst:;=> |subst:/=> }' | 
+         '${destination|subst:;=> |subst:/=> }'
+       }
+
+(highway=motorway_link | highway=trunk_link) & mkgmap:exit_hint=true & mkgmap:dest_hint!=*
   { delete mkgmap:display_name; 
     name 'Exit ${mkgmap:exit_hint_ref} ${mkgmap:exit_hint_name}' | 
          'Exit ${mkgmap:exit_hint_ref} ${mkgmap:exit_hint_exit_to}' | 
          'Exit ${mkgmap:exit_hint_exit_to}' |
          'Exit ${mkgmap:exit_hint_name}' |
-         'Exit ${mkgmap:exit_hint_ref}' }
+         'Exit ${mkgmap:exit_hint_ref}'
+       }
+              
 highway=trunk {name '${ref|highway-symbol:hbox} ${name}' | '${ref|highway-symbol:hbox}' | '${name}'; add mkgmap:display_name = '${name} (${ref})' }
 highway=primary {name '${ref|highway-symbol:box} ${name}' | '${ref|highway-symbol:box}' | '${name}'; add mkgmap:display_name = '${name} (${ref})' }
 highway=secondary | highway=tertiary {name '${ref|highway-symbol:oval} ${name}' | '${ref|highway-symbol:oval}' | '${name}'; add mkgmap:display_name = '${name} (${ref})' }
@@ -108,11 +126,11 @@
 
 # Ways sorted roughly by descending order of class
 highway=motorway {add oneway = yes; add bicycle = no; add foot = no } [0x01 road_class=4 road_speed=7 resolution 16]
-highway=motorway_link & mkgmap:exit_hint=true { add oneway = yes; add bicycle = no; add foot = no; } [0x01 road_class=3 road_speed=2 resolution 20]
+highway=motorway_link & (mkgmap:exit_hint=true | mkgmap:dest_hint=true) { add oneway = yes; add bicycle = no; add foot = no; } [0x06 road_class=3 road_speed=2 resolution 20]
 highway=motorway_link {add oneway = yes; add bicycle = no; add foot = no } [0x09 road_class=3 road_speed=2 resolution 20]
 highway=* & motorroad=yes {add bicycle = no; add foot = no}
 highway=trunk [0x02 road_class=4 road_speed=5 resolution 18]
-highway=trunk_link & mkgmap:exit_hint=true [0x02 road_class=3 road_speed=2 resolution 20]
+highway=trunk_link & (mkgmap:exit_hint=true | mkgmap:dest_hint=true) [0x06 road_class=3 road_speed=2 resolution 20]
 highway=trunk_link [0x09 road_class=3 road_speed=2 resolution 20]
 highway=* & highway!=proposed & motorroad=yes [0x02 road_class=4 road_speed=4 resolution 18]
 highway=primary [0x03 road_class=3 road_speed=4 resolution 19]
Index: src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java	(revision 2540)
+++ src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java	(working copy)
@@ -17,10 +17,11 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Queue;
 import java.util.Set;
@@ -44,7 +45,7 @@
 	/** Maps which ways can be driven from a given Coord */
 	private IdentityHashMap<Coord, Set<Way>> adjacentWays = new IdentityHashMap<Coord, Set<Way>>();
 	/** Contains all _link ways that have to be processed */
-	private Queue<Way> destinationLinkWays = new ArrayDeque<Way>();
+	private Map<Long, Way> destinationLinkWays = new HashMap<Long, Way>();
 
 	private HashSet<String> tagValues = new HashSet<String>(Arrays.asList(
 			"motorway_link", "trunk_link"));
@@ -53,7 +54,6 @@
 
 	/** Maps which nodes contains to which ways */ 
 	private IdentityHashMap<Coord, Set<Way>> wayNodes = new IdentityHashMap<Coord, Set<Way>>();
-	private boolean onlyMotorwayExitHint;
 	
 	private boolean processDestinations;
 	private boolean processExits;
@@ -61,7 +61,6 @@
 	public boolean init(ElementSaver saver, EnhancedProperties props) {
 		this.saver = saver;
 		nameTags = LocatorUtil.getNameTags(props);
-		onlyMotorwayExitHint = props.containsKey("all-exit-hints") == false;
 		processDestinations = props.containsKey("process-destination");
 		processExits = props.containsKey("process-exits");
 		return processDestinations || processExits;
@@ -114,103 +113,7 @@
 				// put it the list of ways that have to be processed
 				if (tagValues.contains(highwayTag)
 						&& w.getTag("destination") != null) {
-					destinationLinkWays.add(w);
-				}
-			}
-		}
-	}
-	
-	/**
-	 * Copies the destination tags of all motorway_link and trunk_link ways to the adjacent ways.
-	 */
-	private void processDestinations() {
-		// process all links with a destination tag
-		// while checking new ways can be added to the list
-		while (destinationLinkWays.isEmpty() == false) {
-			Way link = destinationLinkWays.poll();
-
-			if (isNotOneway(link)) {
-				// non oneway links cannot be handled. The destination tag is probably wrong.
-				if (log.isInfoEnabled())
-					log.info("Link is not oneway. Do not handle it.", link);
-				continue;
-			}
-
-			// get the last point of the link to retrieve all connected ways
-			Coord connectPoint = link.getPoints().get(link.getPoints().size() - 1);
-			if (isOnewayOppositeDirection(link)) {
-				// for reverse oneway ways it's the first point
-				connectPoint = link.getPoints().get(0);
-			}
-
-			String destinationTag = link.getTag("destination");
-			
-			Set<Way> connectedWays = adjacentWays.get(connectPoint);
-			if (connectedWays == null) {
-				connectedWays = Collections.emptySet();
-			} else {
-				connectedWays = new HashSet<Way>(connectedWays);
-			}
-			connectedWays.remove(link);
-
-			if (log.isInfoEnabled())
-				log.info("Link", link);
-			for (Way connection : connectedWays) {
-				if (connection.getTag("destination") != null) {
-					if (log.isInfoEnabled())
-						log.info("Way already has destionation tag", connection);
-					continue;
-				}
-				if (log.isInfoEnabled())
-					log.info("-", connection);
-
-				// check if the connected way can have an unambiguous direction
-				// => it must not have more than one further connections
-				boolean oneDestination = true;
-				List<Coord> pointsToCheck;
-				if (isOnewayInDirection(connection)) {
-					pointsToCheck = connection.getPoints();
-				} else if (isOnewayOppositeDirection(connection)) {
-					pointsToCheck = new ArrayList<Coord>(
-							connection.getPoints());
-					Collections.reverse(pointsToCheck);
-				} else {
-					pointsToCheck = Collections.emptyList();
-					if (log.isInfoEnabled())
-						log.info("Way is not oneway - do not add destination", connection);
-					oneDestination = false;
-				}
-				// check if the target way has an unambiguous destination
-				// so it has no other connections to other destinations
-				for (Coord c : pointsToCheck) {
-					if (c.equals(connectPoint)) {
-						break;
-					}
-					Set<Way> furtherConnects = adjacentWays.get(c);
-					for (Way fc : furtherConnects) {
-						String fcDest = fc.getTag("destination");
-						if (fcDest != null
-								&& destinationTag.equals(fcDest) == false) {
-							if (log.isInfoEnabled())
-								log.info("Ambiguous destination:", destinationTag, "!=", fcDest);
-							oneDestination = false;
-							break;
-						}
-					}
-					if (oneDestination == false) {
-						break;
-					}
-				}
-
-				if (oneDestination) {
-					if (log.isInfoEnabled())
-						log.info("Add destination=" + destinationTag, "to", connection);
-					connection.addTag("destination", destinationTag);
-
-					if (tagValues.contains(connection.getTag("highway"))) {
-						// it is a link so process that way too
-						destinationLinkWays.add(connection);
-					}
+					destinationLinkWays.put(w.getId(), w);
 				}
 			}
 		}
@@ -416,7 +319,7 @@
 	 * naming of this middle way can be assigned from mkgmap:exit_hint_ref and
 	 * mkgmap:exit_hint_name.
 	 */
-	private void createExitHints() {
+	private void processWays() {
 		// collect all nodes of highway=motorway/trunk ways so that we can check if an exit node
 		// belongs to a motorway/trunk or is a "subexit" within a motorway/trunk junction
 		Set<Coord> motorwayCoords = new HashSet<Coord>();
@@ -431,6 +334,42 @@
 			}
 		}	
 		
+		
+		// remove the adjacent links from the destinationLinkWays list
+		// to avoid duplicate dest_hints
+		Queue<Way> linksWithDestination = new ArrayDeque<Way>();
+		linksWithDestination.addAll(destinationLinkWays.values());
+		log.debug(destinationLinkWays.size(),"links with destination tag");
+		while (linksWithDestination.isEmpty()== false) {
+			Way linkWay = linksWithDestination.poll();
+			String destination = linkWay.getTag("destination");
+
+			log.debug("Check way",linkWay.getId(),linkWay.toTagString());
+			
+			// Retrieve all adjacent ways of the current link
+			Coord c = linkWay.getPoints().get(linkWay.getPoints().size()-1);
+			if (isOnewayOppositeDirection(linkWay)) {
+				c = linkWay.getPoints().get(0);
+			}
+			
+			Set<Way> nextWays = adjacentWays.get(c);
+			if (nextWays != null) {
+				for (Way connectedWay : nextWays) {
+					String nextDest = connectedWay.getTag("destination");
+					log.debug("Followed by",connectedWay.getId(),connectedWay.toTagString());
+
+					if (connectedWay.equals(linkWay) == false && connectedWay.getTag("highway").endsWith("_link")
+							&& destination.equals(nextDest)) {
+						// do not use this way because there is another link before that with the same destination
+						destinationLinkWays.remove(connectedWay.getId());
+						log.debug("Removed",connectedWay.getId(),connectedWay.toTagString());
+					}
+				}
+			}
+		}
+		log.debug(destinationLinkWays.size(),"links with destination tag after cleanup");
+		
+		if (processExits)
 		// get all nodes tagged with highway=motorway_junction
 		for (Node exitNode : saver.getNodes().values()) {
 			if (isTaggedAsExit(exitNode) && saver.getBoundingBox().contains(exitNode.getLocation())) {
@@ -438,8 +377,8 @@
 				boolean isMotorwayExit = motorwayCoords.contains(exitNode.getLocation());
 				boolean isTrunkExit = trunkCoords.contains(exitNode.getLocation());
 				boolean isHighwayExit = isMotorwayExit || isTrunkExit;
-				// use exits only if they are located on a motorway
-				if (onlyMotorwayExitHint && isHighwayExit == false) {
+				// use exits only if they are located on a motorway or trunk
+				if (isHighwayExit == false) {
 					if (log.isDebugEnabled())
 						log.debug("Skip non highway exit:", exitNode.toBrowseURL(), exitNode.toTagString());
 					continue;
@@ -466,6 +405,7 @@
 				
 				// use link ways only
 				for (Way w : exitWays) {
+					destinationLinkWays.remove(w.getId());
 					String highwayLinkTag = w.getTag("highway");
 					if (highwayLinkTag.endsWith("_link")) {
 						log.debug("Try to cut",highwayLinkTag, w, "into three parts for giving hint to exit", exitNode);
@@ -490,6 +430,9 @@
 						} else {
 							hintWay.addTag("mkgmap:exit_hint", "true");
 							
+							if (processDestinations && hintWay.getTag("destination") != null) {
+								hintWay.addTag("mkgmap:dest_hint", "true");
+							}
 							if (exitNode.getTag("ref") != null)
 								hintWay.addTag("mkgmap:exit_hint_ref", exitNode.getTag("ref"));
 							if (exitNode.getTag("exit_to") != null)
@@ -504,6 +447,41 @@
 				}
 			}
 		}
+		
+		if (processDestinations)
+		// use link ways only
+		while (destinationLinkWays.isEmpty() == false) {
+			Way w = destinationLinkWays.values().iterator().next();
+			destinationLinkWays.remove(w.getId());
+			String highwayLinkTag = w.getTag("highway");
+			if (highwayLinkTag.endsWith("_link")) {
+				log.debug("Try to cut",highwayLinkTag, w, "into three parts for giving hint");
+
+				// now create three parts:
+				// wayPart1: 10m having the original tags only
+				// hintWay: 10m having the original tags plus the mkgmap:exit_hint* tags
+				// w: the rest of the original way 
+				
+				Way wayPart1 = cutoffWay(w,10.0, null, null);
+				if (wayPart1 == null) {
+					log.info("Way", w, "is too short to cut at least 10m from it. Cannot create exit hint.");
+					continue;
+				} else {
+					if (log.isDebugEnabled())
+						log.debug("Cut off way", wayPart1, wayPart1.toTagString());
+				}
+				
+				Way hintWay = cutoffWay(w,10.0, null, null);
+				if (hintWay == null) {
+					log.info("Way", w, "is too short to cut at least 20m from it. Cannot create exit hint.");
+				} else {
+					hintWay.addTag("mkgmap:dest_hint", "true");
+					
+					if (log.isInfoEnabled())
+						log.info("Cut off exit hint way", hintWay, hintWay.toTagString());
+				}
+			}
+		}
 	}
 	
 	/**
@@ -552,10 +530,10 @@
 
 		retrieveWays();
 		
-		if (processDestinations)
-			processDestinations();
-		if (processExits)
-			createExitHints();
+//		if (processDestinations)
+//			processDestinations();
+		if (processExits || processDestinations)
+			processWays();
 		
 		cleanup();
 
@@ -591,15 +569,4 @@
 		return "-1".equals(w.getTag("oneway"));
 	}
 
-	/**
-	 * Retrieves if the given way is not oneway.
-	 * @param w the way
-	 * @return <code>true</code> way is not oneway
-	 */
-	private boolean isNotOneway(Way w) {
-		return "no".equals(w.getTag("oneway")) || 
-				(isOnewayInDirection(w) == false
-				 && isOnewayOppositeDirection(w) == false);
-	}
-
 }
