summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2020-03-10 00:28:04 +0200
committerzmiao <miao.zhao@mapbox.com>2020-03-10 12:12:00 +0200
commit518b8ce36a5ad72b16d056ebc0318551f8a1964d (patch)
tree85e16a4f6d14cc8e485146cea068ef46234d724d
parent8a5a610c88891fa7fb305c04854bb0543e81c531 (diff)
downloadqtlocation-mapboxgl-518b8ce36a5ad72b16d056ebc0318551f8a1964d.tar.gz
Add unit test for geometry buffer
-rw-r--r--include/mbgl/util/geometry_buffer.hpp9
-rw-r--r--platform/glfw/glfw_view.cpp21
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/util/geometry_buffer.test.cpp191
m---------vendor/cheap-ruler-cpp0
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