Hi Gerd
Patch attached that:
- rewords the sentence is the Style Manual and changes the
highlighting; I need to check the next build/download to see if this is
clearer.
- fixes polygon 'any' method to also return true if exactly ON.
- merge polygons for 'any' so that line on shared boundary is "in"
rather than "on".
- change the test driver to try all methods relevant to the element,
checking they return true/false as appropriate. I decided that, rather
than introducing a new tag saying which methods should match, it was
clearer to use the 'expected' tag value as a description of how the
element interacted with the polygons and generate the methods that
should match from this and the non-matching from a list if all methods.
Ticker
On Thu, 2020-02-20 at 16:45 +0000, Ticker Berkin wrote:
> Hi Gerd
> 
> I don't think the test data 'expected' values are wrong, it is just
> that they are more specific than the 'method' mechanism allows to be
> differentiated; eg a polygon can only be tested for ALL in or ANY in.
> 
> At the moment I feel you have a reluctance about the whole concept of
> the methods. Once the principle is accepted, I'll go through the test
> data and add, as another tag, the list of methods that should match
> the
> element, then change the test driver to check that these match and
> the
> other applicable methods don't.
> 
> Reg. b14: It isn't the stop-early code that causes the problems,
> isLineInShape is not giving the correct answer for a simple polygon
> produced by the MP cutter.
> 
> It would be quite easy to introduce some POLYGON 'on' methods, that
> match the outer, inner or either edge of a polygon, but maybe this
> could wait until there is a call for it.
> 
> Next mail:
> I'll change the sentence as you suggest.
> 
> Please can you commit the patch as it stands; it has a lot of good
> stuff in it. Then I can do the IsInUtilTest and test data changes as
> the next stage. It's also handy to see how the Style Manual looks
> after
> each build into the download area, because I don't know how to
> generate
> it and am just guessing at the formatting.
> 
> Thank you
> Ticker
> 
> On Thu, 2020-02-20 at 15:41 +0000, Gerd Petermann wrote:
> > Hi Ticker,
> > 
> > I see that you overwrite the expected value stored in the test data
> > in the unit test. Please don't do this. If you think that the
> > expected value in is-in-samples.osm
> > is wrong we should discuss the test data.
> > In my eyes b14 clearly has points on the edge (as it is part of the
> > edge) and is out.
> > 
> > If you think the expected results are correct but your new code
> > doesn't allow to test them because of the early stop code please
> > add
> > a new tag to each object or maybe create a new  file. The unit test
> > file is meant to document what the code does.
> > 
> > Gerd
Index: doc/styles/rules.txt
===================================================================
--- doc/styles/rules.txt	(revision 4456)
+++ doc/styles/rules.txt	(working copy)
@@ -283,29 +283,29 @@
 some element ids are changed and some have a faked id > 4611686018427387904.  
 
 |is_in(tag,value,method)   | x | x  |  |
-+true+ if the element is in polygon(s) having the specified +tag+=+value+ according to the +method+, +false+ otherwise.
++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:
 
-. polygons:
- +all+ - all of the closed Way is within the polygon(s).
- +any+ - some is within.
+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.
+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:
- +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.
- +on+ - it runs along the edge.
- +any+ - part is within.
- +none+ - part is outside, none is inside
+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.
+ *on* - it runs along the edge.
+ *any* - part is within.
+ *none* - part is outside, none is inside.
 
 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.
+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.
+Compared to *all*, the method *all_in_or_on* additionally matches lines which 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 4456)
+++ src/uk/me/parabola/mkgmap/osmstyle/function/IsInFunction.java	(working copy)
@@ -57,7 +57,7 @@
 			{ @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)
+		LINE_ANY_IN("any",                FeatureKind.POLYLINE, true,  false, false, true)
 			{ @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;} },
@@ -69,8 +69,27 @@
 //		POLYGON_ANY("any",                FeatureKind.POLYGON,  true,  false, false, false)
 // problem with test b14 on the cut polygons and isLineInShape that goes away when merged. TODO: investigate sometime
 		POLYGON_ANY("any",                FeatureKind.POLYGON,  true,  false, false, true)
-			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn;} };
+			{ @Override public boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut) {return hasIn || !hasOut;} };
 
+/* thoughts for ON methods for polyons and the hasOn flag
+
+possible methods:
+ on_outer / on
+ on_inner / hole
+ on_either
+ all_or_inner - to match, say building, even when cut out of area
+
+on_outer is ok, with just ON.
+on_inner would be logical to represent as ON|OUT
+but, at the moment, an outside line/poly touching an outer will also set this combination
+
+Could:
+ don't hasOn() when isLineInShape returns IN|ON|OUT (in setHasFromFlags)
+ other places where currently call hasOn(), test kind for poly and don't when in comb. with IN or OUT
+
+actually, would be safe not to call hasOn() even for POLYLINE, because none of the methods test it
+*/
+
 		public abstract boolean mapFlags(boolean hasIn, boolean hasOn, boolean hasOut);
 
 		private final String methodName;
Index: test/uk/me/parabola/util/IsInUtilTest.java
===================================================================
--- test/uk/me/parabola/util/IsInUtilTest.java	(revision 4456)
+++ test/uk/me/parabola/util/IsInUtilTest.java	(working copy)
@@ -27,6 +27,8 @@
 import java.util.Set;
 import java.util.Arrays;
 import java.util.stream.Collectors;
+import java.util.Map;
+import java.util.HashMap;
 
 import org.junit.Test;
 
@@ -44,8 +46,6 @@
 /*
 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 {
@@ -52,6 +52,46 @@
 
 	Area testSourceBbox = null;
 
+	private static final String allPointMethods = "in,in_or_on,on";
+	private static final String allLineMethods = "all,all_in_or_on,on,any,none";
+	private static final String allPolygonMethods = "all,any";
+
+	private static final Map<Integer, String> pointMethods = new HashMap<>();
+	private static final Map<Integer, String> lineMethods = new HashMap<>();
+	private static final Map<Integer, String> polygonMethods = new HashMap<>();
+
+	public IsInUtilTest() {
+		// set up the methods that should return true for the 'expected' value
+		pointMethods.put(1, "in,in_or_on");
+		pointMethods.put(2, "in_or_on,on");
+		pointMethods.put(4, "");
+
+/* all=someInNoneOut, any=anyIn, none=someOutNoneIn
+     1  2  4
+a 1) IN        all allInOrOn    any
+b 3) IN ON     all allInOrOn    any
+c 7) IN ON OUT                  any
+d 2)    ON         allInOrOn on
+e 6)    ON OUT                      none
+f 4)       OUT                      none
+*/
+		lineMethods.put(1, "all,all_in_or_on,any");
+		lineMethods.put(2, "all_in_or_on,on");
+		lineMethods.put(3, "all,all_in_or_on,any");
+		lineMethods.put(4, "none");
+		//lineMethods.put(5, "");
+		lineMethods.put(6, "none");
+		lineMethods.put(7, "any");
+
+		polygonMethods.put(1, "all,any");
+		polygonMethods.put(2, "all,any");
+		polygonMethods.put(3, "all,any");
+		polygonMethods.put(4, "");
+		//polygonMethods.put(5, "");
+		polygonMethods.put(6, "");
+		polygonMethods.put(7, "any");
+	}
+
 	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);
@@ -58,118 +98,93 @@
 		return "true".equals(rslt);
 	}
 
-	private int calcInsideness(FeatureKind kind, Element el, Set<Way> polygons) {
-		int result = 0;
-		if (polygons == null || polygons.isEmpty()) {
-			return IsInUtil.OUT;
-		}
+	public List<String> testWithVariants(FeatureKind kind, Element el, String name, Set<Way> polygons) {
+		List<String> errors = new ArrayList<>();
+
 		IsInFunction anInst = new IsInFunction();
 		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 List<String> testWithVariants(FeatureKind kind, Element el, String name, Set<Way> polygons) {
-		List<String> errors = new ArrayList<>();
-		int res = calcInsideness(kind, el, polygons);
 		
 		String expectedVal = el.getTag("expected");
 		if (expectedVal != null && !"?".equals(expectedVal)) {
 			int expected = Integer.parseInt(expectedVal);
+			String allMethods = "";
+			Map<Integer, String> methods = null;
+			switch (kind) {
+			case POINT:
+				allMethods = allPointMethods;
+				methods = pointMethods;
+				break;
+			case POLYLINE:
+				allMethods = allLineMethods;
+				methods = lineMethods;
+				break;
+			case POLYGON:
+				allMethods = allPolygonMethods;
+				methods = polygonMethods;
+				break;
+			}
+			if (!methods.containsKey(expected)) {
+				errors.add(name + " failed, no methods for expected: " + expectedVal);
+				return errors;
+			}
+			String[] trueMethods = methods.get(expected).split(",");
+			if (trueMethods[0].isEmpty())
+				trueMethods = new String[0];
+			List<String> falseMethods = new ArrayList<>();
+			for (String tstMethod : allMethods.split(",")) {
+				boolean inList = false;
+				for (String trueMethod : trueMethods)
+					if (tstMethod.equals(trueMethod)) {
+						inList = true;
+						break;
+					}
+				if (!inList)
+					falseMethods.add(tstMethod);
+			}
 
-/*
-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;
+			for (String tstMethod : trueMethods)
+				if (!invokeMethod(anInst, tstMethod, kind, el))
+					errors.add(name + " failed, expected: " + expectedVal + ". " + tstMethod + " should be true");
+			for (String tstMethod : falseMethods)
+				if (invokeMethod(anInst, tstMethod, kind, el))
+					errors.add(name + " failed, expected: " + expectedVal + ". " + tstMethod + " should be false");
 
-			if (expected != res) {
-				errors.add(name + " failed, expected: " + expected + " got "+ res);
+			if (!errors.isEmpty() || !(el instanceof Way))
 				return errors;
+			Way w2 = (Way) el.copy();
+			Collections.reverse(w2.getPoints());
+			for (String tstMethod : trueMethods)
+				if (!invokeMethod(anInst, tstMethod, kind, w2))
+					errors.add(name + " failed reversed, expected: " + expectedVal + ". " + tstMethod + " should be true");
+			for (String tstMethod : falseMethods)
+				if (invokeMethod(anInst, tstMethod, kind, w2))
+					errors.add(name + " failed reversed, expected: " + expectedVal + ". " + tstMethod + " should be false");
+
+			if (!errors.isEmpty() || !w2.hasIdenticalEndPoints())
+				return errors;
+			List<Coord> points = w2.getPoints();
+			for (int i = 1; i < w2.getPoints().size(); i++) {
+				points.remove(points.size() - 1);
+				Collections.rotate(points, 1);
+				points.add(points.get(0));
+				for (String tstMethod : trueMethods)
+					if (!invokeMethod(anInst, tstMethod, kind, w2))
+						errors.add(name + " failed rotated, expected: " + expectedVal + ". " + tstMethod + " should be true");
+				for (String tstMethod : falseMethods)
+					if (invokeMethod(anInst, tstMethod, kind, w2))
+						errors.add(name + " failed rotated, expected: " + expectedVal + ". " + tstMethod + " should be false");
 			}
-			if (el instanceof Way) {
-				Way w2 = (Way) el.copy();
-				Collections.reverse(w2.getPoints());
-				int res2 = calcInsideness(kind, w2, polygons);
-				if (expected != res2) {
-					errors.add(name + " failed reversed, expected: " + expected + " got " + res2);
-				}
-				if (w2.hasIdenticalEndPoints()) {
-					List<Coord> points = w2.getPoints();
-					for (int i = 1; i < w2.getPoints().size(); i++) {
-						points.remove(points.size() - 1);
-						Collections.rotate(points, 1);
-						points.add(points.get(0));
-						res2 = calcInsideness(kind, w2, polygons);
-						if (expected != res2) {
-							errors.add(name + " failed rotated " + i + " , expected: " + expected + " got " + res2);
-						}
-					}
-				}
-			}
 		}
 		return errors;
 	}
-	
-	/**
-	 * A very basic check that the size of all the sections has not changed.
-	 * This can be used to make sure that a change that is not expected to
-	 * change the output does not do so.
-	 *
-	 * The sizes will have to be always changed when the output does change
-	 * though.
-	 */
+
 	@Test
 	public void testBasic() throws FileNotFoundException {
 
-		// just loads the file 
+		// just loads the file
 		class TestSource extends OsmMapDataSource {
 			@Override
 			public Set<String> getUsedTags() {
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to