diff options
author | zmiao <miao.zhao@mapbox.com> | 2020-03-10 00:28:04 +0200 |
---|---|---|
committer | zmiao <miao.zhao@mapbox.com> | 2020-03-10 12:12:00 +0200 |
commit | 518b8ce36a5ad72b16d056ebc0318551f8a1964d (patch) | |
tree | 85e16a4f6d14cc8e485146cea068ef46234d724d | |
parent | 8a5a610c88891fa7fb305c04854bb0543e81c531 (diff) | |
download | qtlocation-mapboxgl-518b8ce36a5ad72b16d056ebc0318551f8a1964d.tar.gz |
Add unit test for geometry buffer
-rw-r--r-- | include/mbgl/util/geometry_buffer.hpp | 9 | ||||
-rw-r--r-- | platform/glfw/glfw_view.cpp | 21 | ||||
-rw-r--r-- | test/CMakeLists.txt | 3 | ||||
-rw-r--r-- | test/util/geometry_buffer.test.cpp | 191 | ||||
m--------- | vendor/cheap-ruler-cpp | 0 |
5 files changed, 208 insertions, 16 deletions
diff --git a/include/mbgl/util/geometry_buffer.hpp b/include/mbgl/util/geometry_buffer.hpp index f26b13e03c..b42e6c81ed 100644 --- a/include/mbgl/util/geometry_buffer.hpp +++ b/include/mbgl/util/geometry_buffer.hpp @@ -9,12 +9,12 @@ #include <rapidjson/stringbuffer.h> #include <mbgl/util/geometry.hpp> -#include <mbgl/util/variant.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/variant.hpp> -#include <mapbox/geometry/geometry.hpp> #include <mapbox/geojson_impl.hpp> +#include <mapbox/geometry/geometry.hpp> #include <cstdio> #include <fstream> @@ -138,7 +138,7 @@ std::string getGeoStringBuffer(const mapbox::geometry::geometry<double> geometry json.AddMember("type", "Feature", allocator); JSValue v; v.SetObject(); - json.AddMember("properties",v, allocator); + json.AddMember("properties", v, allocator); json.AddMember("geometry", mapbox::geojson::convert(geometry, allocator), allocator); json.Accept(writer); @@ -271,7 +271,8 @@ public: return {}; } - static std::string geoJSONFromGeometryBuffer(const mapbox::geometry::multi_polygon<double>& geometryBuffer, bool singleLine = false) { + static std::string geoJSONFromGeometryBuffer(const mapbox::geometry::multi_polygon<double>& geometryBuffer, + bool singleLine = false) { return getGeoStringBuffer(geometryBuffer, singleLine); } diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 4de64003af..2bf21c28d8 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -1,8 +1,8 @@ #include "glfw_view.hpp" #include "glfw_backend.hpp" #include "glfw_renderer_frontend.hpp" -#include "ny_route.hpp" #include "helsinki_route.hpp" +#include "ny_route.hpp" #include "test_writer.hpp" #include <mbgl/annotation/annotation.hpp> @@ -22,10 +22,10 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry_buffer.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/string.hpp> -#include <mbgl/util/geometry_buffer.hpp> #include <mapbox/cheap_ruler.hpp> #include <mapbox/geometry.hpp> @@ -469,22 +469,19 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, using namespace mbgl::style::expression::dsl; static mapbox::geojson::geojson route{mapbox::geojson::parse(mbgl::platform::glfw::helsinkiRoute)}; - const auto& geometry = route.match( - [](const mapbox::geometry::geometry<double>& geometrySet) { + const auto &geometry = route.match( + [](const mapbox::geometry::geometry<double> &geometrySet) { return mbgl::Feature(geometrySet).geometry; }, - [](const mapbox::feature::feature<double>& feature) { - return mbgl::Feature(feature).geometry; - }, - [](const mapbox::feature::feature_collection<double>& features) { + [](const mapbox::feature::feature<double> &feature) { return mbgl::Feature(feature).geometry; }, + [](const mapbox::feature::feature_collection<double> &features) { return mbgl::Feature(features.front()).geometry; }, - [](const auto&) { - return mapbox::geometry::empty(); - }); + [](const auto &) { return mapbox::geometry::empty(); }); assert(geometry != mapbox::geometry::empty()); - // Generate route boundary based on the radius (50 meter here) and points per circle(used for configuring line joints/ends and circles) + // Generate route boundary based on the radius (50 meter here) and points per circle(used for configuring + // line joints/ends and circles) const std::string boundary = mbgl::GeometryBuffer(geometry, 50.0, 10).getGeoJSONBuffer(); // Add a new layer to show the generated boundary diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0248582c61..30f205c96a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -83,6 +83,7 @@ add_library( ${PROJECT_SOURCE_DIR}/test/util/async_task.test.cpp ${PROJECT_SOURCE_DIR}/test/util/dtoa.test.cpp ${PROJECT_SOURCE_DIR}/test/util/geo.test.cpp + ${PROJECT_SOURCE_DIR}/test/util/geometry_buffer.test.cpp ${PROJECT_SOURCE_DIR}/test/util/grid_index.test.cpp ${PROJECT_SOURCE_DIR}/test/util/http_timeout.test.cpp ${PROJECT_SOURCE_DIR}/test/util/image.test.cpp @@ -203,6 +204,7 @@ endif() # Needed for testing private classes get_target_property(MBGL_CORE_PRIVATE_LIBRARIES mbgl-core LINK_LIBRARIES) +include(${PROJECT_SOURCE_DIR}/vendor/cheap-ruler-cpp.cmake) target_link_libraries( mbgl-test @@ -211,6 +213,7 @@ target_link_libraries( Mapbox::Base::Extras::args Mapbox::Base::pixelmatch-cpp mbgl-compiler-options + mbgl-vendor-cheap-ruler-cpp PUBLIC mbgl-core ) diff --git a/test/util/geometry_buffer.test.cpp b/test/util/geometry_buffer.test.cpp new file mode 100644 index 0000000000..a1ea8ec366 --- /dev/null +++ b/test/util/geometry_buffer.test.cpp @@ -0,0 +1,191 @@ +#include <mbgl/test/util.hpp> + +#include <mapbox/geojson_impl.hpp> +#include <mapbox/geometry/geometry.hpp> +#include <mbgl/util/geometry_buffer.hpp> + +#include <mapbox/cheap_ruler.hpp> +#include <mbgl/util/feature.hpp> + +#include <string> + +#include <boost/geometry/geometries/geometries.hpp> +#include <boost/geometry/geometries/register/point.hpp> +#include <boost/geometry/geometry.hpp> + +using namespace mbgl; + +namespace { +mapbox::geometry::geometry<double> getGeometry(const std::string& geo) { + mapbox::geojson::geojson geojson{mapbox::geojson::parse(geo)}; + return geojson.match( + [](const mapbox::geometry::geometry<double>& geometrySet) { return mbgl::Feature(geometrySet).geometry; }, + [](const mapbox::feature::feature<double>& feature) { return mbgl::Feature(feature).geometry; }, + [](const mapbox::feature::feature_collection<double>& features) { + return mbgl::Feature(features.front()).geometry; + }, + [](const auto&) { return mapbox::geometry::empty(); }); +} + +} // namespace + +TEST(GeometryBuffer, BufferForPoint) { + static const std::string point = R"data({ + "type": "Point", + "coordinates": [1, 1] + })data"; + + const auto geometry = getGeometry(point); + ASSERT_NE(geometry, mapbox::geometry::empty()); + // Generate a polygon buffer with radius 50 meters and 10 points + const auto geoPolygons = mbgl::GeometryBuffer(geometry, 500.0, 10).getGeometryBuffer(); + + // Check result: only one polygon with one ring + ASSERT_TRUE(geoPolygons); + ASSERT_EQ(1, geoPolygons->size()); + const auto& polygon = (*geoPolygons)[0]; + ASSERT_EQ(1, polygon.size()); + const auto& ring = polygon[0]; + ASSERT_EQ(11, ring.size()); + + mapbox::cheap_ruler::CheapRuler ruler(1.0, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + const mapbox::geometry::point<double> center(1, 1); + for (size_t i = 0; i < ring.size(); i++) { + auto actualDistance = ruler.distance(center, ring[i]); + // Allow 2.5 meter's error, which is 5 meters per 1km + EXPECT_NEAR(500.0, actualDistance, 2.5); + } +} + +TEST(GeometryBuffer, BufferForPoints) { + static const std::string points = R"data({ + "type": "MultiPoint", + "coordinates": [[60, 50], [60.3, 50.2]] + })data"; + + const auto geometry = getGeometry(points); + ASSERT_NE(geometry, mapbox::geometry::empty()); + // Generate a polygon buffer with radius 10 meters and 5 points for circle + const auto geoPolygons = mbgl::GeometryBuffer(geometry, 10.0, 5).getGeometryBuffer(); + + // Check result: 2 polygons with one ring in each + ASSERT_TRUE(geoPolygons); + ASSERT_EQ(2, geoPolygons->size()); + + const auto& polygon1 = (*geoPolygons)[0]; + ASSERT_EQ(1, polygon1.size()); + const auto& ring1 = polygon1[0]; + ASSERT_EQ(6, ring1.size()); + mapbox::cheap_ruler::CheapRuler ruler1(50.0, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + const mapbox::geometry::point<double> center1(60, 50); + for (size_t i = 0; i < ring1.size(); i++) { + auto actualDistance = ruler1.distance(center1, ring1[i]); + // Allow 0.5 meter's error + EXPECT_NEAR(10.0, actualDistance, 0.05); + } + const auto& polygon2 = (*geoPolygons)[1]; + ASSERT_EQ(1, polygon2.size()); + const auto& ring2 = polygon2[0]; + ASSERT_EQ(6, ring2.size()); + mapbox::cheap_ruler::CheapRuler ruler2(50.2, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + const mapbox::geometry::point<double> center2(60.3, 50.2); + for (size_t i = 0; i < ring2.size(); i++) { + auto actualDistance = ruler2.distance(center2, ring2[i]); + // Allow 0.05 meter's error, which is 5 meters per 1km + EXPECT_NEAR(10.0, actualDistance, 0.05); + } +} + +TEST(GeometryBuffer, BufferForLine) { + static const std::string line = R"data({ + "type": "LineString", + "coordinates": [[ + 24.93696391582489, + 60.16926456940512 + ], + [ + 24.938240647315975, + 60.16974755943638 + ]] + })data"; + + const auto geometry = getGeometry(line); + ASSERT_NE(geometry, mapbox::geometry::empty()); + // Generate a polygon buffer with radius 50 meters and 5 points for circle + const auto geoPolygons = mbgl::GeometryBuffer(geometry, 100.0, 5).getGeometryBuffer(); + + // Check result: only one polygon with one ring + ASSERT_TRUE(geoPolygons); + ASSERT_EQ(1, geoPolygons->size()); + + const auto& polygon1 = (*geoPolygons)[0]; + ASSERT_EQ(1, polygon1.size()); + const auto& ring1 = polygon1[0]; + ASSERT_EQ(10, ring1.size()); + const mapbox::geometry::point<double> a(24.93696391582489, 60.16926456940512), + b(24.938240647315975, 60.16974755943638); + mapbox::cheap_ruler::CheapRuler ruler2(a.y, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + for (size_t i = 0; i < ring1.size(); i++) { + auto actualDistance = ruler2.distanceToLineSegment(ring1[i], a, b); + // Allow 0.5 meter's error, which is 5 meters per 1km + EXPECT_NEAR(100.0, actualDistance, 0.5); + } +} + +TEST(GeometryBuffer, BufferForLines) { + static const std::string lines = R"data({ + "type": "MultiLineString", + "coordinates": [[[ + 24.93696391582489, + 60.16926456940512 + ], + [ + 24.938240647315975, + 60.16974755943638 + ]], + [[ + 24.93122935295105, + 60.167383242200444 + ], + [ + 24.931269586086273, + 60.16739658532588 + ]] + ] + })data"; + + const auto geometry = getGeometry(lines); + ASSERT_NE(geometry, mapbox::geometry::empty()); + // Generate a polygon buffer with radius 50 meters and 15 points for circle + const auto geoPolygons = mbgl::GeometryBuffer(geometry, 50.0, 15).getGeometryBuffer(); + + // Check result: 2 polygons with one ring in each + ASSERT_TRUE(geoPolygons); + ASSERT_EQ(2, geoPolygons->size()); + + const auto& polygon1 = (*geoPolygons)[0]; + ASSERT_EQ(1, polygon1.size()); + const auto& ring1 = polygon1[0]; + ASSERT_EQ(20, ring1.size()); + const mapbox::geometry::point<double> a1(24.93696391582489, 60.16926456940512), + b1(24.938240647315975, 60.16974755943638); + mapbox::cheap_ruler::CheapRuler ruler1(a1.y, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + for (size_t i = 0; i < ring1.size(); i++) { + auto actualDistance = ruler1.distanceToLineSegment(ring1[i], a1, b1); + // Allow 0.25 meter's error, which is 5 meters per 1km + EXPECT_NEAR(50.0, actualDistance, 0.25); + } + + const auto& polygon2 = (*geoPolygons)[1]; + ASSERT_EQ(1, polygon2.size()); + const auto& ring2 = polygon2[0]; + ASSERT_EQ(20, ring2.size()); + const mapbox::geometry::point<double> a2(24.93122935295105, 60.167383242200444), + b2(24.931269586086273, 60.16739658532588); + mapbox::cheap_ruler::CheapRuler ruler2(a1.y, mapbox::cheap_ruler::CheapRuler::Unit::Meters); + for (size_t i = 0; i < ring2.size(); i++) { + auto actualDistance = ruler2.distanceToLineSegment(ring2[i], a2, b2); + // Allow 0.25 meter's error, which is 5 meters per 1km + EXPECT_NEAR(50.0, actualDistance, 0.25); + } +} diff --git a/vendor/cheap-ruler-cpp b/vendor/cheap-ruler-cpp -Subproject 98cbe70dab74c5b77f139013ea5b3623d3ba3dc +Subproject a65a1438c70bc2433abda383e3bd64d98cc50d0 |