summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2020-04-28 20:03:46 +0300
committerzmiao <miao.zhao@mapbox.com>2020-04-30 02:31:15 +0300
commit70fbc97e589604a9f34b86274cefd2da8cdc570a (patch)
tree114487b503fa596c7ad4887262bc09c5b4935ff6
parent0fa485092ec2eff32d2c9c9ad93fbf8586acee06 (diff)
downloadqtlocation-mapboxgl-70fbc97e589604a9f34b86274cefd2da8cdc570a.tar.gz
[test] Distance expression: Update unit tests
-rw-r--r--src/mbgl/style/expression/distance.cpp25
-rw-r--r--test/fixtures/geometry_data/multi_point_3.geojson75
-rw-r--r--test/fixtures/geometry_data/multi_polygon_1.geojson132
-rw-r--r--test/fixtures/geometry_data/multi_polygon_2.geojson81
-rw-r--r--test/style/property_expression.test.cpp189
5 files changed, 456 insertions, 46 deletions
diff --git a/src/mbgl/style/expression/distance.cpp b/src/mbgl/style/expression/distance.cpp
index c839e764fa..18db0e06f8 100644
--- a/src/mbgl/style/expression/distance.cpp
+++ b/src/mbgl/style/expression/distance.cpp
@@ -270,6 +270,9 @@ struct Comparator {
// The priority queue will ensure the top element would always be the pair that has the biggest distance
using DistQueue = std::priority_queue<DistPair, std::deque<DistPair>, Comparator>;
+// Divide and conqure, the time complexity is O(n*lgn), faster than Brute force O(n*n)
+// Most of the time, use index for in-place processing.
+
double lineToPolygonDistance(const mapbox::geometry::line_string<double>& line,
const mapbox::geometry::polygon<double>& polygon,
mapbox::cheap_ruler::CheapRuler& ruler,
@@ -278,6 +281,7 @@ double lineToPolygonDistance(const mapbox::geometry::line_string<double>& line,
DistQueue distQueue;
distQueue.push(std::forward_as_tuple(0, IndexRange(0, line.size() - 1), IndexRange(0, 0)));
+ const auto polyBBox = getBBox(polygon);
while (!distQueue.empty()) {
auto distPair = distQueue.top();
distQueue.pop();
@@ -293,9 +297,9 @@ double lineToPolygonDistance(const mapbox::geometry::line_string<double>& line,
} else {
auto newRangesA = splitRange(range, true /*isLine*/);
const auto updateQueue =
- [&distQueue, &miniDist, &ruler, &line, &polygon](mbgl::optional<IndexRange>& rangeA) {
+ [&distQueue, &miniDist, &ruler, &line, &polyBBox](mbgl::optional<IndexRange>& rangeA) {
if (!rangeA) return;
- auto tempDist = bboxToBBoxDistance(getBBox(line, *rangeA), getBBox(polygon), ruler);
+ auto tempDist = bboxToBBoxDistance(getBBox(line, *rangeA), polyBBox, ruler);
// Insert new pair to the queue if the bbox distance is less or equal to miniDist,
// The pair with biggest distance will be at the top
if (tempDist <= miniDist)
@@ -316,6 +320,7 @@ double pointsToPolygonDistance(const mapbox::geometry::multi_point<double>& poin
DistQueue distQueue;
distQueue.push(std::forward_as_tuple(0, IndexRange(0, points.size() - 1), IndexRange(0, 0)));
+ const auto polyBBox = getBBox(polygon);
while (!distQueue.empty()) {
auto distPair = distQueue.top();
distQueue.pop();
@@ -330,11 +335,11 @@ double pointsToPolygonDistance(const mapbox::geometry::multi_point<double>& poin
if (miniDist == 0.0) return 0.0;
}
} else {
- auto newRangesA = splitRange(range, true /*isLine*/);
+ auto newRangesA = splitRange(range, false /*isLine*/);
const auto updateQueue =
- [&distQueue, &miniDist, &ruler, &points, &polygon](mbgl::optional<IndexRange>& rangeA) {
+ [&distQueue, &miniDist, &ruler, &points, &polyBBox](mbgl::optional<IndexRange>& rangeA) {
if (!rangeA) return;
- auto tempDist = bboxToBBoxDistance(getBBox(points, *rangeA), getBBox(polygon), ruler);
+ auto tempDist = bboxToBBoxDistance(getBBox(points, *rangeA), polyBBox, ruler);
// Insert new pair to the queue if the bbox distance is less or equal to miniDist,
// The pair with biggest distance will be at the top
if (tempDist <= miniDist)
@@ -347,8 +352,6 @@ double pointsToPolygonDistance(const mapbox::geometry::multi_point<double>& poin
return miniDist;
}
-// Divide and conqure, the time complexity is O(n*lgn), faster than Brute force O(n*n)
-// Use index for in-place processing.
double lineToLineDistance(const mapbox::geometry::line_string<double>& line1,
const mapbox::geometry::multi_point<double>& line2,
mapbox::cheap_ruler::CheapRuler& ruler,
@@ -391,8 +394,6 @@ double lineToLineDistance(const mapbox::geometry::line_string<double>& line1,
return miniDist;
}
-// Divide and conqure, the time complexity is O(n*lgn), faster than Brute force O(n*n)
-// Use index for in-place processing.
double pointsToPointsDistance(const mapbox::geometry::multi_point<double>& pointSet1,
const mapbox::geometry::multi_point<double>& pointSet2,
mapbox::cheap_ruler::CheapRuler& ruler) {
@@ -436,8 +437,6 @@ double pointsToPointsDistance(const mapbox::geometry::multi_point<double>& point
return miniDist;
}
-// Divide and conqure, the time complexity is O(n*lgn), faster than Brute force O(n*n)
-// Most of the time, use index for in-place processing.
double pointsToLineDistance(const mapbox::geometry::multi_point<double>& points,
const mapbox::geometry::line_string<double>& line,
mapbox::cheap_ruler::CheapRuler& ruler) {
@@ -653,7 +652,7 @@ double calculateDistance(const GeometryTileFeature& feature,
optional<GeoJSON> parseValue(const style::conversion::Convertible& value, style::expression::ParsingContext& ctx) {
if (isArray(value)) {
- // object value, quoted with ["Distance", GeoJSONObj]
+ // object value, quoted with ["distance", GeoJSONObj]
auto length = arrayLength(value);
if (length != 2) {
ctx.error("'distance' expression requires one argument, but found " +
@@ -672,7 +671,7 @@ optional<GeoJSON> parseValue(const style::conversion::Convertible& value, style:
ctx.error(error.message);
}
}
- ctx.error("'distance' expression needs to be an array with format [\"Distance\", GeoJSONObj].");
+ ctx.error("'distance' expression needs to be an array with format [\"distance\", GeoJSONObj].");
return nullopt;
}
diff --git a/test/fixtures/geometry_data/multi_point_3.geojson b/test/fixtures/geometry_data/multi_point_3.geojson
new file mode 100644
index 0000000000..3d486cbbf4
--- /dev/null
+++ b/test/fixtures/geometry_data/multi_point_3.geojson
@@ -0,0 +1,75 @@
+{
+ "type": "Feature",
+ "properties": {
+ "marker-color": "#e0d96a"
+ },
+ "geometry": {
+ "type": "MultiPoint",
+ "coordinates": [
+ [
+ 24.941325187683105,
+ 60.169503396855
+ ],
+ [
+ 24.943127632141113,
+ 60.16984495711831
+ ],
+ [
+ 24.94493007659912,
+ 60.17101904344053
+ ],
+ [
+ 24.947762489318848,
+ 60.16753935642882
+ ],
+ [
+ 24.9479341506958,
+ 60.16907644153227
+ ],
+ [
+ 24.946002960205078,
+ 60.16901239775532
+ ],
+ [
+ 24.944028854370117,
+ 60.16824386269413
+ ],
+ [
+ 24.9459171295166,
+ 60.16756070532545
+ ],
+ [
+ 24.940338134765625,
+ 60.168286559558055
+ ],
+ [
+ 24.94248390197754,
+ 60.1678168910033
+ ],
+ [
+ 24.944286346435547,
+ 60.16696293097573
+ ],
+ [
+ 24.946260452270508,
+ 60.16619434797435
+ ],
+ [
+ 24.941411018371582,
+ 60.16619434797435
+ ],
+ [
+ 24.943749904632565,
+ 60.16782756536319
+ ],
+ [
+ 24.942612648010254,
+ 60.16908711548297
+ ],
+ [
+ 24.944608211517334,
+ 60.16952474447549
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/geometry_data/multi_polygon_1.geojson b/test/fixtures/geometry_data/multi_polygon_1.geojson
new file mode 100644
index 0000000000..0a804ce38d
--- /dev/null
+++ b/test/fixtures/geometry_data/multi_polygon_1.geojson
@@ -0,0 +1,132 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#e0d96a",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#c6ec00",
+ "fill-opacity": 0.5
+ },
+ "geometry": {
+ "type": "MultiPolygon",
+ "coordinates": [
+ [
+ [
+ [
+ 24.93870735168457,
+ 60.16429416283512
+ ],
+ [
+ 24.936389923095703,
+ 60.16205223008669
+ ],
+ [
+ 24.940123558044434,
+ 60.16250062887229
+ ],
+ [
+ 24.943857192993164,
+ 60.16333335324268
+ ],
+ [
+ 24.943127632141113,
+ 60.16465712803696
+ ],
+ [
+ 24.94123935699463,
+ 60.16595949958244
+ ],
+ [
+ 24.936561584472656,
+ 60.16561789892518
+ ],
+ [
+ 24.93450164794922,
+ 60.16435821580911
+ ],
+ [
+ 24.935274124145508,
+ 60.163568220403924
+ ],
+ [
+ 24.93870735168457,
+ 60.16429416283512
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 24.95166778564453,
+ 60.174306261926034
+ ],
+ [
+ 24.949607849121094,
+ 60.172918839697616
+ ],
+ [
+ 24.952783584594727,
+ 60.17144597351834
+ ],
+ [
+ 24.95595932006836,
+ 60.17217174191747
+ ],
+ [
+ 24.956517219543457,
+ 60.1742635728851
+ ],
+ [
+ 24.954586029052734,
+ 60.175736312737385
+ ],
+ [
+ 24.950637817382812,
+ 60.175245406790246
+ ],
+ [
+ 24.9514102935791,
+ 60.174348950911465
+ ],
+ [
+ 24.95166778564453,
+ 60.174306261926034
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 24.930424690246582,
+ 60.17413550542946
+ ],
+ [
+ 24.92875099182129,
+ 60.171830205844614
+ ],
+ [
+ 24.936904907226562,
+ 60.1703145966823
+ ],
+ [
+ 24.937891960144043,
+ 60.172150396016946
+ ],
+ [
+ 24.937891960144043,
+ 60.173922058560485
+ ],
+ [
+ 24.930424690246582,
+ 60.17413550542946
+ ]
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/test/fixtures/geometry_data/multi_polygon_2.geojson b/test/fixtures/geometry_data/multi_polygon_2.geojson
new file mode 100644
index 0000000000..ea7723754d
--- /dev/null
+++ b/test/fixtures/geometry_data/multi_polygon_2.geojson
@@ -0,0 +1,81 @@
+{
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "MultiPolygon",
+ "coordinates": [
+ [
+ [
+ [
+ 24.94171142578125,
+ 60.16777419352904
+ ],
+ [
+ 24.948577880859375,
+ 60.16777419352904
+ ],
+ [
+ 24.948577880859375,
+ 60.170122472217564
+ ],
+ [
+ 24.94171142578125,
+ 60.170122472217564
+ ],
+ [
+ 24.94171142578125,
+ 60.16777419352904
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 24.951581954956055,
+ 60.169503396855
+ ],
+ [
+ 24.955186843872067,
+ 60.169503396855
+ ],
+ [
+ 24.955186843872067,
+ 60.17052806699205
+ ],
+ [
+ 24.951581954956055,
+ 60.17052806699205
+ ],
+ [
+ 24.951581954956055,
+ 60.169503396855
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 24.944243431091305,
+ 60.16301307713581
+ ],
+ [
+ 24.94716167449951,
+ 60.16301307713581
+ ],
+ [
+ 24.94716167449951,
+ 60.16412335429406
+ ],
+ [
+ 24.944243431091305,
+ 60.16412335429406
+ ],
+ [
+ 24.944243431091305,
+ 60.16301307713581
+ ]
+ ]
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/style/property_expression.test.cpp b/test/style/property_expression.test.cpp
index 924b0f91f6..aecbec18e3 100644
--- a/test/style/property_expression.test.cpp
+++ b/test/style/property_expression.test.cpp
@@ -468,11 +468,12 @@ TEST(PropertyExpression, WithinExpressionAntiMeridian) {
TEST(PropertyExpression, DistanceExpression) {
static const double invalidResult = std::numeric_limits<double>::quiet_NaN();
static const CanonicalTileID canonicalTileID(15, 18653, 9484);
+
// Parsing error with invalid geojson source
{
// geoJSON source must be Point or LineString.
const std::string invalidGeoSource = R"({
- "type": "Polygon",
+ "type": "Unknown",
"coordinates": [[[0, 0], [0, 5], [5, 5], [5, 0], [0, 0]]]
})";
std::stringstream ss;
@@ -481,48 +482,18 @@ TEST(PropertyExpression, DistanceExpression) {
ASSERT_FALSE(expression);
}
- // Parsing error with invalid unit
+ // Parsing error with extra arguments
{
const std::string invalidGeoSource = R"({
"type": "Point",
"coordinates": [0, 0],
})";
std::stringstream ss;
- ss << std::string(R"(["distance", )") << invalidGeoSource << std::string(R"(, "InvalidUnits"])");
+ ss << std::string(R"(["distance", )") << invalidGeoSource << std::string(R"(, "ExtraArgument"])");
auto expression = createExpression(ss.str().c_str());
ASSERT_FALSE(expression);
}
- // Evaluation test with non-supported geometry type
- {
- const std::string pointGeoSource = R"({
- "type": "Point",
- "coordinates":
- [
- 24.938492774963375,
- 60.16980226227959
- ]
- })";
- std::stringstream ss;
- ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"( ])");
- auto expression = createExpression(ss.str().c_str());
- ASSERT_TRUE(expression);
- PropertyExpression<double> propExpr(std::move(expression));
-
- Polygon<double> testRing{{{24.937505722045895, 60.16815846879986},
- {24.940595626831055, 60.16815846879986},
- {24.940595626831055, 60.169268572114106},
- {24.937505722045895, 60.169268572114106},
- {24.937505722045895, 60.16815846879986}}};
- auto geoPoly = convertGeometry(testRing, canonicalTileID);
- StubGeometryTileFeature polyFeature(FeatureType::Polygon, geoPoly);
-
- // return evaluation error
- auto evaluatedResult =
- propExpr.evaluate(EvaluationContext(&polyFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
- EXPECT_TRUE(std::isnan(evaluatedResult));
- }
-
// Evaluation test with Point to Point distance by using different units
{
const std::string pointGeoSource = R"({
@@ -752,4 +723,156 @@ TEST(PropertyExpression, DistanceExpression) {
EvaluationContext(&multiLineFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
EXPECT_NEAR(384.109, evaluatedResult, 0.01);
}
+
+ // Evaluation test with MultiPoint to MultiPolygon distance
+ {
+ const auto multiPolygon = mbgl::util::read_file("test/fixtures/geometry_data/multi_polygon_1.geojson");
+ std::stringstream ss;
+ ss << std::string(R"(["distance", )") << multiPolygon << std::string(R"( ])");
+ auto expression = createExpression(ss.str().c_str());
+ ASSERT_TRUE(expression);
+ PropertyExpression<double> propExpr(std::move(expression));
+
+ const auto multiPointFeature1 = getFeature("multi_point_3.geojson", FeatureType::Point, canonicalTileID);
+ auto evaluatedResult = propExpr.evaluate(
+ EvaluationContext(&multiPointFeature1).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(27.917, evaluatedResult, 0.01);
+
+ const auto multiPointFeature2 = getFeature("multi_point_2.geojson", FeatureType::Point, canonicalTileID);
+ evaluatedResult = propExpr.evaluate(
+ EvaluationContext(&multiPointFeature2).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+ }
+
+ // Evaluation test with MultiLine to MultiPolygon distance
+ {
+ const auto multiPolygon = mbgl::util::read_file("test/fixtures/geometry_data/multi_polygon_1.geojson");
+ std::stringstream ss;
+ ss << std::string(R"(["distance", )") << multiPolygon << std::string(R"( ])");
+ auto expression = createExpression(ss.str().c_str());
+ ASSERT_TRUE(expression);
+ PropertyExpression<double> propExpr(std::move(expression));
+
+ const auto multiLineFeature1 =
+ getFeature("multi_line_string_1.geojson", FeatureType::LineString, canonicalTileID);
+ auto evaluatedResult = propExpr.evaluate(
+ EvaluationContext(&multiLineFeature1).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(193.450, evaluatedResult, 0.01);
+
+ const auto multiLineFeature2 =
+ getFeature("multi_line_string_2.geojson", FeatureType::LineString, canonicalTileID);
+ evaluatedResult = propExpr.evaluate(EvaluationContext(&multiLineFeature2).withCanonicalTileID(&canonicalTileID),
+ invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+ }
+
+ // Evaluation test with Polygon to MultiPolygon distance
+ {
+ const auto multiPolygon = mbgl::util::read_file("test/fixtures/geometry_data/multi_polygon_1.geojson");
+ std::stringstream ss;
+ ss << std::string(R"(["distance", )") << multiPolygon << std::string(R"( ])");
+ auto expression = createExpression(ss.str().c_str());
+ ASSERT_TRUE(expression);
+ PropertyExpression<double> propExpr(std::move(expression));
+
+ Polygon<double> polygon{{{24.94171142578125, 60.16777419352904},
+ {24.948577880859375, 60.16777419352904},
+ {24.948577880859375, 60.170122472217564},
+ {24.94171142578125, 60.170122472217564},
+ {24.94171142578125, 60.16777419352904}}};
+ auto geoPolygon = convertGeometry(polygon, canonicalTileID);
+ StubGeometryTileFeature polygonFeature(FeatureType::Polygon, geoPolygon);
+
+ auto evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&polygonFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(203.876, evaluatedResult, 0.01);
+ }
+
+ // Evaluation test with MultiPolygon to MultiPolygon distance
+ {
+ const auto multiPolygon = mbgl::util::read_file("test/fixtures/geometry_data/multi_polygon_1.geojson");
+ std::stringstream ss;
+ ss << std::string(R"(["distance", )") << multiPolygon << std::string(R"( ])");
+ auto expression = createExpression(ss.str().c_str());
+ ASSERT_TRUE(expression);
+ PropertyExpression<double> propExpr(std::move(expression));
+
+ const auto multiPolygonFeature = getFeature("multi_polygon_2.geojson", FeatureType::Polygon, canonicalTileID);
+ auto evaluatedResult = propExpr.evaluate(
+ EvaluationContext(&multiPolygonFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(41.632, evaluatedResult, 0.01);
+ }
+
+ // Evaluation test with LineString/Polygon to Polygon distance with special cases
+ {
+ const std::string multiPolygon = R"({
+ "type": "Polygon",
+ "coordinates": [[[24.937891960144043, 60.16730451765011],
+ [24.94892120361328, 60.16730451765011],
+ [24.94892120361328, 60.17010112498546],
+ [24.937891960144043, 60.17010112498546],
+ [24.937891960144043, 60.16730451765011]]]})";
+ std::stringstream ss;
+ ss << std::string(R"(["distance", )") << multiPolygon << std::string(R"( ])");
+ auto expression = createExpression(ss.str().c_str());
+ ASSERT_TRUE(expression);
+ PropertyExpression<double> propExpr(std::move(expression));
+
+ // polygon1 is fully inside polygon
+ Polygon<double> polygon1{{{24.941797256469727, 60.16850004304534},
+ {24.94235515594482, 60.16850004304534},
+ {24.94235515594482, 60.169439353910285},
+ {24.941797256469727, 60.169439353910285},
+ {24.941797256469727, 60.16850004304534}}};
+ auto geoPolygon1 = convertGeometry(polygon1, canonicalTileID);
+ StubGeometryTileFeature polygonFeature1(FeatureType::Polygon, geoPolygon1);
+
+ auto evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&polygonFeature1).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+
+ // polygon2's line is intersecting polygon, but no point is inside polygon
+ Polygon<double> polygon2{{{24.943385124206543, 60.166066249059554},
+ {24.94596004486084, 60.166066249059554},
+ {24.94596004486084, 60.1714246271462},
+ {24.943385124206543, 60.1714246271462},
+ {24.943385124206543, 60.166066249059554}}};
+ auto geoPolygon2 = convertGeometry(polygon2, canonicalTileID);
+ StubGeometryTileFeature polygonFeature2(FeatureType::Polygon, geoPolygon2);
+
+ evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&polygonFeature2).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+
+ // one of polygon3's point is inside polygon
+ Polygon<double> polygon3{{{24.94797706604004, 60.1694607015724},
+ {24.952354431152344, 60.1694607015724},
+ {24.952354431152344, 60.17082692309542},
+ {24.94797706604004, 60.17082692309542},
+ {24.94797706604004, 60.1694607015724}}};
+ auto geoPolygon3 = convertGeometry(polygon3, canonicalTileID);
+ StubGeometryTileFeature polygonFeature3(FeatureType::Polygon, geoPolygon3);
+
+ evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&polygonFeature3).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+
+ // lineString1 is within polygon
+ LineString<double> lineString1{{24.947097301483154, 60.16921520262075}, {24.947311878204346, 60.1680197032483}};
+ auto geoLineString1 = convertGeometry(lineString1, canonicalTileID);
+ StubGeometryTileFeature lineFeature1(FeatureType::LineString, geoLineString1);
+
+ evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&lineFeature1).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+
+ // lineString2 is intersecting the polygon but its points are outside the polygon
+ LineString<double> lineString2{{24.93999481201172, 60.17116846959888}, {24.940037727355957, 60.16542574699484}};
+ auto geoLineString2 = convertGeometry(lineString2, canonicalTileID);
+ StubGeometryTileFeature lineFeature2(FeatureType::LineString, geoLineString2);
+
+ evaluatedResult =
+ propExpr.evaluate(EvaluationContext(&lineFeature2).withCanonicalTileID(&canonicalTileID), invalidResult);
+ EXPECT_NEAR(0.0, evaluatedResult, 0.01);
+ }
}