Index: src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java	(revision 3390)
+++ src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java	(working copy)
@@ -16,6 +16,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -29,8 +30,10 @@
 import uk.me.parabola.mkgmap.general.MapRoad;
 import uk.me.parabola.mkgmap.reader.osm.Element;
 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
+import uk.me.parabola.mkgmap.reader.osm.HousenumberHooks;
 import uk.me.parabola.mkgmap.reader.osm.Node;
 import uk.me.parabola.mkgmap.reader.osm.Relation;
+import uk.me.parabola.mkgmap.reader.osm.TagDict;
 import uk.me.parabola.mkgmap.reader.osm.Way;
 import uk.me.parabola.util.MultiHashMap;
 
@@ -53,11 +56,15 @@
 	private MultiHashMap<String, MapRoad> roadByNames;
 	private List<MapRoad> roads;
 	private MultiHashMap<String, Element> houseNumbers;
+	private Map<Long,Node> houseNodes;
 	
+	private static final short streetTagKey = TagDict.getInstance().xlate("mkgmap:street");
+	
 	public HousenumberGenerator(Properties props) {
 		this.roadByNames = new MultiHashMap<String,MapRoad>();
 		this.houseNumbers = new MultiHashMap<String,Element>();
 		this.roads = new ArrayList<MapRoad>();
+		this.houseNodes = new HashMap<>();
 		
 		numbersEnabled=props.containsKey("housenumbers");
 	}
@@ -68,9 +75,9 @@
 	 * @return the street name (or {@code null} if no street name set)
 	 */
 	private static String getStreetname(Element e) {
-		String streetname = e.getTag("mkgmap:street");
+		String streetname = e.getTag(streetTagKey);
 		if (streetname == null) {
-			streetname = e.getTag("addr:street");
+			streetname = e.getTag(HousenumberHooks.addrStreetTagKey);
 		}	
 		return streetname;
 	}
@@ -87,6 +94,8 @@
 			String streetname = getStreetname(n);
 			if (streetname != null) {
 				houseNumbers.add(streetname, n);
+				if (n.getTag("mkgmap:part-of-interpolation") != null)
+					houseNodes.put(n.getId(),n);
 			} else {
 				if (log.isDebugEnabled())
 					log.debug(n.toBrowseURL()," ignored, doesn't contain a street name.");
@@ -102,6 +111,24 @@
 		if (numbersEnabled == false) {
 			return;
 		}
+		
+		String ai = w.getTag(HousenumberHooks.addrInterpolationTagKey);
+		if (ai != null){
+			String nodeIds = w.getTag("mkgmap:node-ids");
+			if (nodeIds != null){
+				List<Node> nodes = new ArrayList<>();
+				String[] ids = nodeIds.split(",");
+				for (String id : ids){
+					Node node = houseNodes.get(Long.decode(id));
+					if (node != null){
+						nodes.add(node);
+					}
+				}
+				doInterpolation(w,nodes);
+			}
+		}
+		
+		
 		if (HousenumberMatch.getHousenumber(w) != null) {
 			String streetname = getStreetname(w);
 			if (streetname != null) {
@@ -117,6 +144,179 @@
 		}
 	}
 	
+	/**
+	 * Use the information provided by the addr:interpolation tag
+	 * to generate additional house number elements. This increases
+	 * the likelihood that a road segment is associated with the right 
+	 * number ranges. 
+	 * @param w
+	 * @param nodes
+	 */
+	private void doInterpolation(Way w, List<Node> nodes) {
+		int numNodes = nodes.size();
+		String addrInterpolationMethod = w.getTag(HousenumberHooks.addrInterpolationTagKey);
+		int step = 0;
+		switch (addrInterpolationMethod) {
+		case "all":
+		case "1":
+			step = 1;
+			break;
+		case "even":
+		case "odd":
+		case "2":
+			step = 2;
+			break;
+		default:
+			break;
+		}
+		if (step == 0)
+			return; // should not happen here
+		int pos = 0;
+		for (int i = 0; i+1 < numNodes; i++){
+			// the way have other points, find the sequence including the pair of nodes    
+			Node n1 = nodes.get(i);
+			Node n2 = nodes.get(i+1);
+			int pos1 = -1, pos2 = -1;
+			for (int k = pos; k < w.getPoints().size(); k++){
+				if (w.getPoints().get(k) == n1.getLocation()){
+					pos1 = k;
+					break;
+				}
+			}
+			if (pos1 < 0){
+				log.error("node not found in way",w);
+				return;
+			}
+			for (int k = pos1+1; k < w.getPoints().size(); k++){
+				if (w.getPoints().get(k) == n2.getLocation()){
+					pos2 = k;
+					break;
+				}
+			}
+			if (pos2 < 0){
+				log.error("node not found in way",w);
+				return;
+			}
+			pos = pos2;
+			String street1 = getStreetname(n1);
+			String street2 = getStreetname(n2);
+			if (street1 != null && street1.equals(street2)){
+				try {
+					HousenumberMatch m1 = new HousenumberMatch(n1);
+					HousenumberMatch m2 = new HousenumberMatch(n2);
+					int start = m1.getHousenumber();
+					int end = m2.getHousenumber();
+					int steps, usedStep;
+					if (start < end){
+						steps = (end - start) / step - 1;
+						usedStep = step;
+						
+					} else {
+						steps = (start - end) / step - 1;
+						usedStep = - step;
+					}
+					if (steps <= 0){
+						log.info(w.toBrowseURL(),"addr:interpolation way segment ignored, no number between",start,"and",end);
+						continue;
+					}
+					if ("even".equals(addrInterpolationMethod) && (start % 2 != 0 || end % 2 != 0)){
+						log.info(w.toBrowseURL(),"addr:interpolation=even is used with odd housenumber(s)",start,end);
+						continue;
+					}
+					if ("odd".equals(addrInterpolationMethod) && (start % 2 == 0 || end % 2 == 0)){
+						log.info(w.toBrowseURL(),"addr:interpolation=odd is used with even housenumber(s)",start,end);
+						continue;
+					}
+					List<Coord> interpolated = getPointsOnWay(w, pos1, pos2, steps);
+					if (interpolated == null || interpolated.isEmpty())
+						continue;
+					int hn = start; 
+					StringBuilder sb = new StringBuilder();
+					for (Coord co : interpolated){
+						hn += usedStep;
+						Node generated = new Node(FakeIdGenerator.makeFakeId(), co);
+						generated.addTag(streetTagKey, street1);
+						String number = String.valueOf(hn);
+						generated.addTag("mkgmap:housenumber", number);
+						if (log.isDebugEnabled()){
+							sb.append(number);
+							sb.append(",");
+						}
+						houseNumbers.add(street1, generated);
+					}
+					if (log.isDebugEnabled()){
+						if (sb.length() > 0)
+							sb.setLength(sb.length()-1);
+						log.debug("http://www.openstreetmap.org/api/0.6/way/" + w.getId() + "/full " + addrInterpolationMethod, "added interpolated number(s)" , sb.toString(),"to",street1);
+					}
+				} catch (IllegalArgumentException exp) {
+					log.debug(exp);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Calculate the wanted number of coords on a way so that they have
+	 * similar distances to each other (and to the first and last point 
+	 * of the way).
+	 * @param points list of points that build the way
+	 * @param num the wanted number 
+	 * @return a list with the number of points or the empty list in 
+	 * case of errors
+	 */
+	private List<Coord> getPointsOnWay(Way w, int i1, int i2, int num){
+		List<Coord> interpolated = new ArrayList<>(num);
+		if (i2 >= w.getPoints().size())
+			return interpolated;
+		List<Coord> points = w.getPoints().subList(i1, i2+1);
+		if (points.size() < 2)
+			return interpolated;
+		double wayLen = 0;
+		for (int i = 0; i+1 < points.size(); i++){
+			wayLen += points.get(i).distance(points.get(i+1));
+		}
+		double ivlLen = wayLen / (num+1);
+		if (ivlLen < 0.1){
+			log.info(w.toBrowseURL(),"segment ignored, would generate",num,"houses with distance of",ivlLen,"m");
+			return interpolated;
+		}
+		int pos = 0;
+		double rest = 0;
+		while (pos+1 < points.size()){
+			Coord c1 = points.get(pos);
+			Coord c2 = points.get(pos+1);
+			pos++;
+			double neededPartOfSegment = 0;
+			double segmentLen = c1.distance(c2);
+			for(;;){
+				neededPartOfSegment += ivlLen - rest;
+				if (neededPartOfSegment <= segmentLen){
+					double fraction = neededPartOfSegment / segmentLen;
+					Coord c = c1.makeBetweenPoint(c2, fraction);
+					interpolated.add(c);
+					if (interpolated.size() >= num){
+//						double last = c.distance(c2);
+//						if (segmentLen > ivlLen && Math.abs(last - ivlLen) > 1){
+//							log.error("interpolation failed");
+//							GpxCreator.createGpx("e:/ld/w", points, interpolated);
+//							System.out.println(ivlLen + " " + last);
+//						}
+						return interpolated;
+					}
+//					c1 = c;
+					rest = 0;
+				} else {
+					rest = segmentLen - neededPartOfSegment + ivlLen;
+					break;
+				}
+			}
+			
+		}
+		log.warn(w.toBrowseURL(),"interpolation for segment",i1,"-",i2,"failed");
+//		GpxCreator.createGpx("e:/ld/w", points, interpolated);
+		return interpolated;
+	}
 	
 	/**
 	 * Adds a road to be processed by the house number generator.
@@ -174,6 +374,7 @@
 							log.warn("Relation",r.toBrowseURL(),": role of member",w.toBrowseURL(),"unclear");
 						break;
 					default:
+						log.warn("Relation",r.toBrowseURL(),": don't know how to handle member with role",role);
 						break;
 					}
 				}
@@ -237,15 +438,15 @@
 	private boolean addStreetTagFromRel(Relation r, Element house, String streetName){
 		String addrStreet = getStreetname(house);
 		if (addrStreet == null){
-			house.addTag("mkgmap:street", streetName);
+			house.addTag(streetTagKey, streetName);
 			if (log.isDebugEnabled())
 				log.debug("Relation",r.toBrowseURL(),": adding tag mkgmap:street=" + streetName, "to house",house.toBrowseURL());
 			return true;
 		}
 		else if (addrStreet.equals(streetName) == false){
-			if (house.getTag("mkgmap:street") != null){
+			if (house.getTag(streetTagKey) != null){
 				log.warn("Relation",r.toBrowseURL(),": street name from relation doesn't match existing mkgmap:street tag for house",house.toBrowseURL(),"the house seems to be member of another type=associatedStreet relation");
-				house.deleteTag("mkgmap:street");
+				house.deleteTag(streetTagKey);
 			}
 			else 
 				log.warn("Relation",r.toBrowseURL(),": street name from relation doesn't match existing name for house",house.toBrowseURL());
@@ -531,7 +732,7 @@
 		} else if (frac >= 1) {
 			return spoint2.distance(point);
 		} else {
-			return spoint1.makeBetweenPoint(spoint2, frac).distance(point);
+			return point.distToLineSegment(spoint1, spoint2);
 		}
 
 	}
@@ -545,15 +746,15 @@
 	 */
 	private static double getFrac(Coord spoint1, Coord spoint2, Coord point) {
 
-		double dx = spoint2.getLongitude() - spoint1.getLongitude();
-		double dy = spoint2.getLatitude() - spoint1.getLatitude();
-
+		double dx = spoint2.getHighPrecLon() - spoint1.getHighPrecLon();
+		double dy = spoint2.getHighPrecLat() - spoint1.getHighPrecLat();
+		
 		if ((dx == 0) && (dy == 0)) {
 			return 0;
 		}
 
-		return ((point.getLongitude() - spoint1.getLongitude()) * dx + (point
-				.getLatitude() - spoint1.getLatitude()) * dy)
+		return ((point.getHighPrecLon() - spoint1.getHighPrecLon()) * dx + (point
+				.getHighPrecLat() - spoint1.getHighPrecLat()) * dy)
 				/ (dx * dx + dy * dy);
 
 	}
Index: src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(revision 3390)
+++ src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java	(working copy)
@@ -1989,6 +1989,7 @@
 		}
 	}
 
+	@Override
 	public Boolean getDriveOnLeft(){
 		assert roads == null : "getDriveOnLeft() should be called after end()";
 		return driveOnLeft;
Index: src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java	(revision 3390)
+++ src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java	(working copy)
@@ -84,13 +84,6 @@
 			usedTags.add("cycleway:left");
 			usedTags.add("cycleway:right");
 		}
-		
-		// add addr:street and addr:housenumber if housenumber search is enabled
-		if (props.getProperty("housenumbers", false)) {
-			usedTags.add("addr:street");
-			usedTags.add("addr:housenumber");
-		}
-	
 		return true;
 	}
 
Index: src/uk/me/parabola/mkgmap/reader/osm/HousenumberHooks.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/HousenumberHooks.java	(revision 0)
+++ src/uk/me/parabola/mkgmap/reader/osm/HousenumberHooks.java	(working copy)
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+package uk.me.parabola.mkgmap.reader.osm;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import uk.me.parabola.imgfmt.app.Coord;
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
+
+/**
+ * Collect data from ways with addr:interpolation tag.
+ * @author GerdP
+ *  
+ */
+public class HousenumberHooks extends OsmReadingHooksAdaptor {
+	private static final Logger log = Logger.getLogger(HousenumberHooks.class);
+	
+	private ElementSaver saver;
+	private Node currentNodeInWay;
+	private final List<Node> nodes = new ArrayList<>();
+	private boolean clearNodes;
+	
+	public final static short addrStreetTagKey = TagDict.getInstance().xlate("addr:street");
+	public final static short addrHousenumberTagKey = TagDict.getInstance().xlate("addr:housenumber");
+	public final static short addrInterpolationTagKey = TagDict.getInstance().xlate("addr:interpolation");
+	
+	@Override
+	public boolean init(ElementSaver saver, EnhancedProperties props) {
+		this.saver = saver;
+		if (props.getProperty("addr-interpolation", true) == false)
+			return false;
+		return (props.getProperty("housenumbers", false));
+	}
+
+	@Override
+	public Set<String> getUsedTags() {
+		HashSet<String> usedTags = new HashSet<>();
+		usedTags.add("addr:street");
+		usedTags.add("addr:housenumber");
+		usedTags.add("addr:interpolation");
+		return usedTags;
+	}
+	
+	@Override
+	public void onCoordAddedToWay(Way way, long id, Coord co) {
+		if (clearNodes){
+			nodes.clear();
+			clearNodes = false;
+		}
+		currentNodeInWay = saver.getNode(id);
+		if (currentNodeInWay == null)
+			return;
+		if (currentNodeInWay.getTag(addrStreetTagKey) == null
+				|| currentNodeInWay.getTag(addrHousenumberTagKey) == null)
+			return;
+		// this node might be part of a way that has the addr:interpolation tag
+		nodes.add(currentNodeInWay);
+	}
+
+	@Override
+	public void onAddWay(Way way) {
+		clearNodes = true; // make sure that the list is cleared with the next coord
+		String ai = way.getTag(addrInterpolationTagKey);
+		if (ai == null)
+			return;
+		if (nodes.size() < 2){
+			log.info(way.toBrowseURL(),"tag addr:interpolation="+ai, "is ignored, found less than two valid nodes.");
+			return;
+		}
+		switch (ai) {
+		case "odd":
+		case "even":
+		case "all":
+		case "1":
+		case "2":
+			break;
+		default:
+			log.info(way.toBrowseURL(),"tag addr:interpolation="+ai, "is ignored");
+			return;
+		}
+		
+		StringBuilder sb = new StringBuilder();
+		int num = nodes.size();
+		for (int i = 0; i < num; i++) {
+			Node n = nodes.get(i);
+			String id = String.valueOf(n.getId());
+			sb.append(id);
+			if (i + 1 < num)
+				sb.append(",");
+			n.addTag("mkgmap:part-of-interpolation", "1");
+		}
+		way.addTag("mkgmap:node-ids", sb.toString());
+	}
+
+	@Override
+	public void end() {
+		nodes.clear();
+	}
+}
Index: src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(revision 3390)
+++ src/uk/me/parabola/mkgmap/reader/osm/OsmMapDataSource.java	(working copy)
@@ -60,6 +60,7 @@
 			new HighwayHooks(),
 			new LocationHook(),
 			new POIGeneratorHook(),
+			new HousenumberHooks(),
 	};
 	protected OsmConverter converter;
 	private final Set<String> usedTags = new HashSet<String>();
