Index: doc/styles/rules.txt
===================================================================
--- doc/styles/rules.txt	(revision 4453)
+++ doc/styles/rules.txt	(working copy)
@@ -286,21 +286,26 @@
 +true+ if the element is in polygon(s) having the specified +tag+=+value+ according to the +method+, +false+ otherwise.
 The methods available depend on the Style section:
 
-* points:
+. polygons:
+ +all+ - all of the closed Way is within the polygon(s).
+ +any+ - some is within.
+
+. points:
  +in+ - the Node is within a polygon.
  +in_or_on+ - it is within or on the edge.
  +on+ - it is on the edge.
 
-* lines:
+. lines:
  +all+ - part of the Way is within the polygon(s), none is outside; it might touch an edge.
- +all_in_or_on+ - none is outside. This is useful for the negative - is_in(...,all_in_or_on)=false - for processing a line that is outside the polgon(s).
+ +all_in_or_on+ - none is outside.
  +on+ - it runs along the edge.
  +any+ - part is within.
- +any_in_or_on+ - part is within or in the edge.
+ +none+ - part is outside, none is inside
 
-* polygons:
- +all+ - all of the closed Way is within the polygon(s).
- +any+ - some is within.
+A common case is a line outside the polygon that runs to the edge, joining a line that is inside.
+The method to match an outside line (+none+) allows part to be on the edge,
+likewise, the method to match an inside line (+all+) allows part to be on the edge.
+The method +all_in_or_on+ additionally matches lines are only on the edge of the polygon.
 
 |====
 
Index: src/uk/me/parabola/mkgmap/osmstyle/function/IsInFunction.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/function/IsInFunction.java	(revision 4453)
+++ src/uk/me/parabola/mkgmap/osmstyle/function/IsInFunction.java	(working copy)
@@ -43,19 +43,33 @@
 
 	private enum MethodArg {
 
-		//                                       can stop when: IN     ON     OUT
-		POINT_IN("in",                    FeatureKind.POINT,    true,  false, false),
-		POINT_IN_OR_ON("in_or_on",        FeatureKind.POINT,    true,  true,  false),
-		POINT_ON("on",                    FeatureKind.POINT,    false, true,  false),
+		//                                       can stop when: IN     ON     OUT    MERGE
+		POINT_IN("in",                    FeatureKind.POINT,    true,  false, false, true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn;} },
+		POINT_IN_OR_ON("in_or_on",        FeatureKind.POINT,    true,  true,  false, false)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn || hasOn;} },
+		POINT_ON("on",                    FeatureKind.POINT,    false, true,  false, true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasOn;} },
 
-		LINE_SOME_IN_NONE_OUT("all",      FeatureKind.POLYLINE, false, false, true),
-		LINE_ALL_IN_OR_ON("all_in_or_on", FeatureKind.POLYLINE, false, false, true),
-		LINE_ALL_ON("on",                 FeatureKind.POLYLINE, true,  false, true),
-		LINE_ANY_IN("any",                FeatureKind.POLYLINE, true,  false, false),
-		LINE_ANY_IN_OR_ON("any_in_or_on", FeatureKind.POLYLINE, true,  true,  false),
+		LINE_SOME_IN_NONE_OUT("all",      FeatureKind.POLYLINE, false, false, true,  true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn && !hasOut;} },
+		LINE_ALL_IN_OR_ON("all_in_or_on", FeatureKind.POLYLINE, false, false, true,  true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return !hasOut;} },
+		LINE_ALL_ON("on",                 FeatureKind.POLYLINE, true,  false, true,  true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return !(hasIn || hasOut);} },
+		LINE_ANY_IN("any",                FeatureKind.POLYLINE, true,  false, false, false)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn;} },
+//		LINE_ANY_IN_OR_ON("any_in_or_on", FeatureKind.POLYLINE, true,  false, false, true)
+//			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn || !hasOut;} },
+		LINE_NONE_IN_SOME_OUT("none",     FeatureKind.POLYLINE, true,  false, false, true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return !hasIn && hasOut;} },
+		
+		POLYGON_ALL("all",                FeatureKind.POLYGON,  false, false, true,  true)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return !hasOut;} },
+		POLYGON_ANY("any",                FeatureKind.POLYGON,  true,  false, false, false)
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn;} };
 
-		POLYGON_ALL("all",                FeatureKind.POLYGON,  false, false, true),
-		POLYGON_ANY("any",                FeatureKind.POLYGON,  true,  false, false);
+		public abstract boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut);
 
 		private final String methodName;
 		private final FeatureKind kind;
@@ -62,13 +76,15 @@
 		private final boolean stopIn;
 		private final boolean stopOn;
 		private final boolean stopOut;
+		private final boolean needMerge;
 
-		MethodArg(String methodName, FeatureKind kind, boolean stopIn, boolean stopOn, boolean stopOut) {
+		MethodArg(String methodName, FeatureKind kind, boolean stopIn, boolean stopOn, boolean stopOut, boolean needMerge) {
 			this.methodName = methodName;
 			this.kind = kind;
 			this.stopIn = stopIn;
 			this.stopOn = stopOn;
 			this.stopOut = stopOut;
+			this.needMerge = needMerge;
 		}
 
 		public String toString() {
@@ -88,6 +104,9 @@
 		public boolean canStopOut() {
 			return stopOut;
 		}
+		public boolean needMerge() {
+			return needMerge;
+		}
 	}
 
 	private class CanStopProcessing extends RuntimeException {};
@@ -134,7 +153,9 @@
 			}
 		} catch (CanStopProcessing e) {}
 		log.info("done", System.identityHashCode(this), hasIn, hasOn, hasOut);
-		return String.valueOf(mapHasFlagsAnswer());
+		if (!hasIn && !hasOn)
+			hasOut = true;
+		return String.valueOf(method.mapFlags(hasIn, hasOn, hasOut));
 	}
 
 /* don't have this for CachedFunction
@@ -169,7 +190,7 @@
 	private void setIn() {
 		log.info("setIn", hasIn, hasOn, hasOut);
 		hasIn = true;
-		if (method.canStopIn() || (hasOn && hasOut))
+		if (method.canStopIn() || hasOut)
 			throw new CanStopProcessing();
 	}
 
@@ -182,45 +203,20 @@
 	private void setOut() {
 		log.info("setOut", hasIn, hasOn, hasOut);
 		hasOut = true;
-		if (method.canStopOut() || (hasIn && hasOn))
+		if (method.canStopOut() || hasIn)
 			throw new CanStopProcessing();
 	}
 
 	private void setHasFromFlags(int flags) {
+		log.info("setFlags", flags);
+		if ((flags & IsInUtil.ON) != 0)
+			setOn();
 		if ((flags & IsInUtil.IN) != 0)
 			setIn();
-		if ((flags & IsInUtil.ON) != 0)
-			setOn();
 		if ((flags & IsInUtil.OUT) != 0)
 			setOut();
 	}
 
-	private boolean mapHasFlagsAnswer() {
-		switch (method) {
-		case POINT_IN:
-			return hasIn;
-		case POINT_IN_OR_ON:
-			return hasIn || hasOn;
-		case POINT_ON:
-			return hasOn;
-		case LINE_SOME_IN_NONE_OUT:
-			return hasIn && !hasOut;
-		case LINE_ALL_IN_OR_ON:
-			return !hasOut;
-		case LINE_ALL_ON:
-			return !(hasIn || hasOut);
-		case LINE_ANY_IN:
-			return hasIn;
-		case LINE_ANY_IN_OR_ON:
-			return hasIn || hasOn;
-		case POLYGON_ALL:
-			return !hasOut;
-		case POLYGON_ANY:
-			return hasIn;
-		}
-		return false;
-	}
-
 	private static boolean notInHole(Coord c, List<List<Coord>> holes) {
 		if (holes == null)
 			return true;
@@ -263,7 +259,7 @@
 		Area elementBbox = Area.getBBox(Collections.singletonList(c));
 		Set<Way> polygons = qt.get(elementBbox).stream().map(e -> (Way) e)
 				.collect(Collectors.toCollection(LinkedHashSet::new));
-		if ((method == MethodArg.POINT_IN || method == MethodArg.POINT_ON) && polygons.size() > 1) {
+		if (method.needMerge() && polygons.size() > 1) {
 			// need to merge shapes so that POI on shared boundary becomes IN rather than ON
 			List<List<Coord>> outers = new ArrayList<>();
 			List<List<Coord>> holes = new ArrayList<>();
@@ -290,9 +286,11 @@
 		doCommonTest(el);
 	}
 
-	private void checkHoles(List<Coord> polyLine, List<List<Coord>> holes, Area elementBbox) {
+	private boolean checkHoles(List<Coord> polyLine, List<List<Coord>> holes, Area elementBbox) {
+		boolean foundSomething = false;
 		for (List<Coord> hole : holes) {
-			int flags = IsInUtil.isLineInShape(kind, polyLine, hole, elementBbox);
+			int flags = IsInUtil.isLineInShape(polyLine, hole, elementBbox);
+			log.info("checkhole", flags);
 			if ((flags & IsInUtil.IN) != 0) {
 				setOut();
 				if ((flags & IsInUtil.ON) != 0)
@@ -299,49 +297,78 @@
 					setOn();
 				if ((flags & IsInUtil.OUT) != 0)
 					setIn();
+				return true;
+			} else if ((flags & IsInUtil.ON) != 0) {
+				setOn();
+				if ((flags & IsInUtil.OUT) != 0)
+					setIn();
+				foundSomething = true;
+			}
+		}
+		return foundSomething;
+	}
+
+	private void checkHoleInThis(List<Coord> polyLine, List<List<Coord>> holes, Area elementBbox) {
+		for (List<Coord> hole : holes) {
+			int flags = IsInUtil.isLineInShape(hole, polyLine, elementBbox);
+			log.info("holeInThis", flags);
+			if ((flags & IsInUtil.IN) != 0 ||
+			    (flags == IsInUtil.ON)) { // exactly on hole
+				setOut();
 				return;
 			}
 		}
 	}
-    /*
-make above function return boolean depending on it finding something.
-maybe should also respond to ON
 
-in below, delay setting the IN flag until have done the above, because the line could all be ON the inner.
-and use return from above to set IN, assume that when it finds something, it will set IN of OUT etc
-
-for POLY, still need a final check that, even if ON/IN, none of the holes is in this one, so
-we need another check (can we use above not-in-hole...) that takes a point from the hole and
-checks if it is the polyLine
-     */
 	private void doCommonTest(Element el) {
 		List<Coord> polyLine = ((Way)el).getPoints();
 		Area elementBbox = Area.getBBox(polyLine);
 		Set<Way> polygons = qt.get(elementBbox).stream().map(e -> (Way) e)
 				.collect(Collectors.toCollection(LinkedHashSet::new));
-		if ((method == MethodArg.LINE_SOME_IN_NONE_OUT ||
-		     method == MethodArg.LINE_ALL_IN_OR_ON ||
-		     method == MethodArg.LINE_ALL_ON ||
-		     method == MethodArg.POLYGON_ALL) && polygons.size() > 1) {
-			// ALL-like methods need to merge shapes
+		if (log.isInfoEnabled()) {
+			log.info("line", polyLine);
+			log.info(polygons.size(), "polygons");
+			for (Way polygon : polygons)
+				log.info("polygon", polygon.getPoints());
+		}
+		if (method.needMerge() && polygons.size() > 1) { // ALL-like methods need to merge shapes
 			List<List<Coord>> outers = new ArrayList<>();
 			List<List<Coord>> holes = new ArrayList<>();
 			IsInUtil.mergePolygons(polygons, outers, holes);
-			log.info("polyMerge", polygons.size(), outers.size(), holes.size());
+			if (log.isInfoEnabled()) {
+				log.info(outers.size(), "outers", holes.size(), "holes");
+				for (List<Coord> shape : outers)
+					log.info("outer", shape);
+				for (List<Coord> hole : holes)
+					log.info("hole", hole);
+			}
 			for (List<Coord> shape : outers) {
-				int flags = IsInUtil.isLineInShape(kind, polyLine, shape, elementBbox);
-				if ((flags & (IsInUtil.IN | IsInUtil.ON)) != 0) {
-				    	// this shape is the one to consider
-					setHasFromFlags(flags); // might set OUT and stop
-					if ((flags & IsInUtil.IN) != 0)
-						checkHoles(polyLine, holes, elementBbox);
+				int flags = IsInUtil.isLineInShape(polyLine, shape, elementBbox);
+				log.info("checkShape", flags);
+				if ((flags & IsInUtil.IN) != 0) { // this shape is the one to consider
+					if ((flags & IsInUtil.ON) != 0)
+						setOn();
+					if ((flags & IsInUtil.OUT) != 0)
+						setOut();
+					if (!checkHoles(polyLine, holes, elementBbox))
+						setIn();
+					if (!hasOut && kind == FeatureKind.POLYGON)
+						checkHoleInThis(polyLine, holes, elementBbox);
 					break;
+				} else if ((flags & IsInUtil.ON) != 0) { // might still be IN later one
+					setOn();
+					if ((flags & IsInUtil.OUT) != 0)
+						setOut();
+					else { // exactly on
+						if (kind == FeatureKind.POLYGON)
+							checkHoleInThis(polyLine, holes, elementBbox);
+						break; // hence can't be in another
+					}
 				}
 			}
-		} else { // an ANY-like method
-			log.info("poly1by1", polygons.size());
+		} else { // an ANY-like method or 1 polygon
 			for (Way polygon : polygons)
-				setHasFromFlags(IsInUtil.isLineInShape(kind, polyLine, polygon.getPoints(), elementBbox));
+				setHasFromFlags(IsInUtil.isLineInShape(polyLine, polygon.getPoints(), elementBbox));
 		}
 	}
 
Index: src/uk/me/parabola/util/IsInUtil.java
===================================================================
--- src/uk/me/parabola/util/IsInUtil.java	(revision 4453)
+++ src/uk/me/parabola/util/IsInUtil.java	(working copy)
@@ -25,7 +25,6 @@
 import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.mkgmap.reader.osm.Element;
-import uk.me.parabola.mkgmap.reader.osm.FeatureKind;
 import uk.me.parabola.mkgmap.reader.osm.Node;
 import uk.me.parabola.mkgmap.reader.osm.Way;
 
@@ -80,7 +79,7 @@
 	}
 	
 	
-	public static int isLineInShape(FeatureKind kind, List<Coord> lineToTest, List<Coord> shape, Area elementBbox) {
+	public static int isLineInShape(List<Coord> lineToTest, List<Coord> shape, Area elementBbox) {
 		final int n = lineToTest.size();
 		int status = isPointInShape(lineToTest.get(0), shape);
 		BitSet onBoundary = new BitSet();
@@ -185,7 +184,7 @@
 				status |= insidePolygon(pTest, false, shape) ? IN : OUT;
 				return status;
 			}
-			status |= checkAllOn(kind, lineToTest, shape);
+			status |= checkAllOn(lineToTest, shape);
 		}
 		return status;
 	}
@@ -193,12 +192,11 @@
 
 	/** 
 	 * Handle special case that all points of {@code lineToTest} are on the edge of shape
-	 * @param kind 
 	 * @param lineToTest
 	 * @param shape
 	 * @return
 	 */
-	private static int checkAllOn(FeatureKind kind, List<Coord> lineToTest, List<Coord> shape) {
+	private static int checkAllOn(List<Coord> lineToTest, List<Coord> shape) {
 		int n = lineToTest.size();
 		// all points are on boundary
 		for (int i = 0; i < n-1; i++) {
@@ -212,51 +210,6 @@
 					return resMidPoint;
 			}
 		}
-		
-		// if we get here we can assume that the line runs along the shape
-		if (kind == FeatureKind.POLYGON) {
-			// lineToTest is a polygon and all segments are on boundary
-			// find a node inside lineToTest and check if this point is in shape
-			
-			// find topmost node(s)  
-			int maxLat = Integer.MIN_VALUE;
-			List<SimpleEntry<Coord, Integer>> topNodes = new ArrayList<>();
-			for (int i = 0; i < lineToTest.size() - 1; i++) {
-				Coord c = lineToTest.get(i);
-				int latHp = c.getHighPrecLat();
-				if (latHp > maxLat) {
-					maxLat = latHp;
-					topNodes.clear();
-				} 
-				if (latHp >= maxLat) {
-					topNodes.add(new SimpleEntry<>(c,i));
-				}
-			}
-			
-			for (SimpleEntry<Coord, Integer> topNode : topNodes) {
-				int pos = topNode.getValue();
-				Coord top = topNode.getKey();
-				Coord prev = lineToTest.get(pos == 0 ? n - 2 : pos - 1);
-				Coord next = lineToTest.get(pos == n - 1 ? 1 : pos + 1);
-				double b1 = top.bearingTo(prev);
-				double b2 = top.bearingTo(next);
-				// b1 and b2 must be heading south or exactly east or west
-				// find heading of angle bisector
-				double bisectorBearing = (b1 + b2) / 2;
-				if (bisectorBearing > -90 && bisectorBearing < 90) {
-					// don't go north of top
-					bisectorBearing -= 180;
-					if (bisectorBearing < -180)
-						bisectorBearing += 360;
-				}
-				Coord pTest = topNode.getKey().destOnRhumLine(0.1, bisectorBearing);
-				// double check: the calculated point may not be inside the
-				// element
-				if (isPointInShape(pTest, lineToTest) == IN && isPointInShape(pTest, shape) == IN) {
-					return IN; // all ON and areas intersect
-				}
-			}
-		}
 		return ON;
 	}
 
Index: test/uk/me/parabola/util/IsInUtilTest.java
===================================================================
--- test/uk/me/parabola/util/IsInUtilTest.java	(revision 4453)
+++ test/uk/me/parabola/util/IsInUtilTest.java	(working copy)
@@ -41,103 +41,74 @@
 import uk.me.parabola.mkgmap.reader.osm.OsmMapDataSource;
 import uk.me.parabola.mkgmap.reader.osm.Way;
 
-import uk.me.parabola.mkgmap.osmstyle.function.IsInFunction;
+/*
+Source: test/resources/in/osm/is-in-samples.osm
+errors: test-reports/uk/me/parabola/util/62_IsInUtilTest-err.html
 
+b14 failed, expected: 4 got 5
+*/
+
 public class IsInUtilTest {
 
-	private static int calcInsideness(FeatureKind kind, Element el, Set<Way> polygons) {
-		int result = 0;  
-		if (polygons == null || polygons.isEmpty()) { 
-			return IsInUtil.OUT;
-		}
-		
-		Area elementBbox;
-		if (el instanceof Node) {
-			Coord c = ((Node) el).getLocation();
-			for (Way polygon : polygons) {
-				switch (IsInUtil.isPointInShape(c, polygon.getPoints())) {
-				case IsInUtil.IN:
-					return IsInUtil.IN;
-				case IsInUtil.ON:
-					result |= IsInUtil.ON;
-				default:
-				}
-			}
-			return result == 0 ? IsInUtil.OUT : IsInUtil.ON;
-		} else if (el instanceof Way) {
-			Way w = (Way) el;
-			if (w.isComplete()) {
-				elementBbox = Area.getBBox(w.getPoints());
-				if (polygons.size() > 1) {
-					// combine all polygons which intersect the bbox of the element if possible
-					Path2D.Double path = new Path2D.Double();
-					for (Way polygon : polygons) {
-						path.append(Java2DConverter.createPath2D(polygon.getPoints()), false);
-					}
-					java.awt.geom.Area polygonsArea = new java.awt.geom.Area(path);
-					List<List<Coord>> mergedShapes = Java2DConverter.areaToShapes(polygonsArea);
+	Area testSourceBbox = null;
 
-					// combination of polygons may contain holes. They are counter clockwise.
-					List<List<Coord>> holes = new ArrayList<>();
-					List<List<Coord>> outers = new ArrayList<>();
-					for (List<Coord> shape : mergedShapes) {
-						(Way.clockwise(shape) ? outers : holes).add(shape);
-					}
-					
-					// check if any outer intersects with the way
-					for (List<Coord> shape : outers) {
-						int tmpRes = IsInUtil.isLineInShape(kind, w.getPoints(), shape, elementBbox);
-						if (tmpRes != IsInUtil.OUT) {
-							result |= tmpRes;
-							if ((tmpRes & IsInUtil.IN) != 0) {
-								result = tmpRes;
-								break;
-							}
-						}
-					}
-					if ((result & IsInUtil.IN) != 0 && !holes.isEmpty()) {
-						// an outer ring matched
-						// check if any hole intersects with the way
-						for (List<Coord> hole : holes) {
-							int tmpRes = IsInUtil.isLineInShape(kind, w.getPoints(), hole, elementBbox);
-							if (tmpRes == IsInUtil.IN_ON_OUT)
-								return tmpRes;
-							if ((tmpRes & IsInUtil.IN) != 0) 
-								result = IsInUtil.OUT;
-							result |= tmpRes & IsInUtil.ON;
-							
-						}
-					}
-					
-				} else if (polygons.size() == 1) {
-					result = IsInUtil.isLineInShape(kind, w.getPoints(), (polygons.iterator().next()).getPoints(), elementBbox);
-				}
-			}
-		}
-		if (result == 0)
-			result = IsInUtil.OUT;
-		return result;
+	private static boolean invokeMethod(IsInFunction anInst, String method, FeatureKind kind, Element el) {
+		anInst.setParams(Arrays.asList("landuse", "residential", method), kind); // tag key/value don't matter
+		String rslt = anInst.calcImpl(el);
+		return "true".equals(rslt);
 	}
 
-	private static int dev_calcInsideness(FeatureKind kind, Element el, Set<Way> polygons) {
-		int result = 0;  
-		if (polygons == null || polygons.isEmpty()) { 
+	private int calcInsideness(FeatureKind kind, Element el, Set<Way> polygons) {
+		int result = 0;
+		if (polygons == null || polygons.isEmpty()) {
 			return IsInUtil.OUT;
 		}
-		Area tileBbox = null; // ??? Area.getBBox(el); maybe
 		IsInFunction anInst = new IsInFunction();
-		// [javac] /norbert/svn/branches/is-in/test/uk/me/parabola/util/IsInUtilTest.java:125: error: incompatible types: Set<Way> cannot be converted to Collection<Element>
-		//fix anInst.unitTestAugment(new ElementQuadTree(tileBbox, polygons));
-		anInst.setParams(Arrays.asList("landuse", "residential", "all"), kind);
-		String rslt = anInst.calcImpl(el);
-		/* TODO: %%%
-choose 3 methods depending on Kind whose combination will give us IN/ON/OUT 
-test the rslt string for "True" and set the bits in result as appropriate
-		*/
+		List<Element> matchingPolygons = new ArrayList<>();
+		for (Way polygon : polygons)
+			matchingPolygons.add(polygon);
+		anInst.unitTestAugment(new ElementQuadTree(testSourceBbox, matchingPolygons));
+		switch (kind) {
+		case POINT:
+			if (invokeMethod(anInst, "in_or_on", kind, el))
+				if (invokeMethod(anInst, "in", kind, el))
+					result = IsInUtil.IN;
+				else
+					result = IsInUtil.ON;
+			else
+				result = IsInUtil.OUT;
+			break;
+		case POLYLINE:
+			/* all=someInNoneOut, any=anyIn, none=someOutNoneIn
+a) IN        all allInOrOn    any
+b) IN ON     all allInOrOn    any
+c) IN ON OUT                  any
+d)    ON         allInOrOn on
+e)    ON OUT                      none
+f)       OUT                      none
+			*/
+			if (invokeMethod(anInst, "all", kind, el)) /*a,b*/
+				result = IsInUtil.IN | IsInUtil.ON; // methods won't say if also ON
+			else /*c,d,e,f*/ if (invokeMethod(anInst, "on", kind, el)) /*d*/
+				result = IsInUtil.ON;
+			else /*c,e,f*/ if (invokeMethod(anInst, "any", kind, el)) /*c*/
+				result = IsInUtil.IN | IsInUtil.ON | IsInUtil.OUT;
+			else /*e,f*/
+				result = IsInUtil.OUT | IsInUtil.ON; // methods won't say if also ON
+			break;
+		case POLYGON: // ON is meaningless for polygons
+			if (invokeMethod(anInst, "all", kind, el))
+				result = IsInUtil.IN;
+			else if (invokeMethod(anInst, "any", kind, el))
+				result = IsInUtil.IN | IsInUtil.OUT;
+			else
+				result = IsInUtil.OUT;
+			break;
+		}
 		return result;
 	}
- 
-	public static List<String> testWithVariants(FeatureKind kind, Element el, String name, Set<Way> polygons) {
+
+	public List<String> testWithVariants(FeatureKind kind, Element el, String name, Set<Way> polygons) {
 		List<String> errors = new ArrayList<>();
 		int res = calcInsideness(kind, el, polygons);
 		
@@ -144,6 +115,21 @@
 		String expectedVal = el.getTag("expected");
 		if (expectedVal != null && !"?".equals(expectedVal)) {
 			int expected = Integer.parseInt(expectedVal);
+
+/*
+Using the "method" interface to emulate the old version of IsInUtil.calcInsideness and try and deduce
+the IN/ON/OUT flags to compare with the 'expected' tag isn't quite possible:
+ For POLYGONs the ON flag is meaningless - it can be wholly or partially within
+ For LINEs, the methods don't distinguish between a line that is totally ON and one that is IN but touches the edge,
+  Similarly a line that is OUT and one that touches the edge.
+So here we adjust the expected value to match what can be tested.
+*/
+			if (kind == FeatureKind.POLYGON)
+				expected &= ~IsInUtil.ON;
+			else if (kind == FeatureKind.POLYLINE)
+				if (expected == IsInUtil.IN || expected == IsInUtil.OUT)
+					expected |= IsInUtil.ON;
+
 			if (expected != res) {
 				errors.add(name + " failed, expected: " + expected + " got "+ res);
 				return errors;
@@ -205,6 +191,7 @@
 		TestSource src = new TestSource();
 		src.config(new EnhancedProperties());
 		src.load(Args.TEST_RESOURCE_OSM + "is-in-samples.osm", false);
+		testSourceBbox = src.getElementSaver().getBoundingBox();
 		
 		ElementQuadTree qt = IsInFunction.buildTree(src.getElementSaver(), "landuse", "residential");
 		ArrayList<String> allErrors = new ArrayList<>();
