gdal/autotest/cpp/test_marching_squares_square.cpp

726 строки
25 KiB
C++

/******************************************************************************
* $Id$
*
* Project: GDAL algorithms
* Purpose: Tests for the marching squares algorithm
* Author: Hugo Mercier, <hugo dot mercier at oslandia dot com>
*
******************************************************************************
* Copyright (c) 2018, Hugo Mercier, <hugo dot mercier at oslandia dot com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "gdal_unit_test.h"
#include "gdal_alg.h"
#include "marching_squares/square.h"
#include "marching_squares/level_generator.h"
#include <vector>
#include <map>
#include <fstream>
#include "gtest_include.h"
namespace marching_squares
{
struct Writer
{
typedef std::pair<Point, Point> Segment;
void addSegment(int levelIdx, const Point &first, const Point &second)
{
contours[levelIdx].push_back(Segment(first, second));
}
void addBorderSegment(int levelIdx, const Point &first, const Point &second)
{
borders[levelIdx].push_back(Segment(first, second));
}
std::map<int, std::vector<Segment>> contours;
std::map<int, std::vector<Segment>> borders;
const bool polygonize = true;
};
} // namespace marching_squares
namespace
{
using namespace marching_squares;
// Common fixture with test data
struct test_ms_square : public ::testing::Test
{
};
// Dummy test
TEST_F(test_ms_square, dummy)
{
{
const double levels[] = {0, 4};
FixedLevelRangeIterator levelGenerator(levels, 2);
auto r = levelGenerator.range(0, 5.0);
auto b = r.begin();
EXPECT_EQ((*b).first, 1);
EXPECT_EQ((*b).second, 4.0);
auto e = r.end();
EXPECT_EQ((*e).first, 2);
EXPECT_EQ((*e).second, Inf);
}
{
IntervalLevelRangeIterator levelGenerator(0, 4);
auto r = levelGenerator.range(0, 5.0);
auto b = r.begin();
EXPECT_EQ((*b).first, 1);
EXPECT_EQ((*b).second, 4.0);
auto e = r.end();
EXPECT_EQ((*e).first, 2);
EXPECT_EQ((*e).second, 8.0);
}
{
IntervalLevelRangeIterator levelGenerator(0, 10);
auto r = levelGenerator.range(-18, 5.0);
auto b = r.begin();
EXPECT_EQ((*b).first, -1);
EXPECT_EQ((*b).second, -10.0);
auto e = r.end();
EXPECT_EQ((*e).first, 1);
EXPECT_EQ((*e).second, 10.0);
}
{
ExponentialLevelRangeIterator levelGenerator(2);
auto r = levelGenerator.range(0, 5.0);
auto b = r.begin();
EXPECT_EQ((*b).first, 1);
EXPECT_EQ((*b).second, 1.0);
++b;
EXPECT_EQ((*b).first, 2);
EXPECT_EQ((*b).second, 2.0);
++b;
EXPECT_EQ((*b).first, 3);
EXPECT_EQ((*b).second, 4.0);
auto e = r.end();
EXPECT_EQ((*e).first, 4);
EXPECT_EQ((*e).second, 8.0);
}
}
TEST_F(test_ms_square, only_zero)
{
// Square with only 0, level = 0.1
Square square(ValuedPoint(0, 1, 0), ValuedPoint(1, 1, 0),
ValuedPoint(0, 0, 0), ValuedPoint(1, 0, 0));
Square::Segments segments(square.segments(.1));
//
// 0 0
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 0 0
EXPECT_EQ(segments.size(), size_t(0));
}
TEST_F(test_ms_square, only_one)
{
// Square with only 1, level = 0.1
Square square(ValuedPoint(0, 1, 1), ValuedPoint(1, 1, 1),
ValuedPoint(0, 0, 1), ValuedPoint(1, 0, 1));
//
// 1 1
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 1 1
Square::Segments segments(square.segments(.1));
EXPECT_EQ(segments.size(), size_t(0));
}
TEST_F(test_ms_square, only_zero_level_1)
{
// Square with only 1, level = 1.0
Square square(ValuedPoint(0, 1, 1), ValuedPoint(1, 1, 1),
ValuedPoint(0, 0, 1), ValuedPoint(1, 0, 1));
//
// 1 1
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 1 1
Square::Segments segments(square.segments(1.0));
EXPECT_EQ(segments.size(), size_t(0));
}
TEST_F(test_ms_square, one_segment)
{
// Square with one segment, level = 0.1
Square square(ValuedPoint(0, 1, 1), ValuedPoint(1, 1, 0),
ValuedPoint(0, 0, 0), ValuedPoint(1, 0, 0));
//
// 0 0
// +------------------+
// | |
// | |
// | |
// | |
// | |
// o |
// | \ |
// +---o--------------+
// 1 0
Square::Segments segments(square.segments(.1));
EXPECT_EQ(segments.size(), size_t(1));
EXPECT_TRUE(segments[0].first == Point(.9, 1));
EXPECT_TRUE(segments[0].second == Point(0, .1));
}
TEST_F(test_ms_square, fudge_test_1)
{
// Fudge test 1
Square square(ValuedPoint(0, 1, 0), ValuedPoint(1, 1, 1),
ValuedPoint(0, 0, 1), ValuedPoint(1, 0, 1));
//
// 0 1
// +------------------o
// | __/|
// | __/ |
// | __/ |
// | _/ |
// | __/ |
// | __/ |
// |/ |
// o------------------+
// 1 1
// (0,0)
{
Square::Segments segments(square.segments(0.0));
EXPECT_EQ(segments.size(), size_t(0));
}
{
Square::Segments segments(square.segments(1.0));
EXPECT_EQ(segments.size(), size_t(1));
EXPECT_NEAR(segments[0].first.x, 0.0, 0.001);
EXPECT_NEAR(segments[0].first.y, 0.0, 0.001);
EXPECT_NEAR(segments[0].second.x, 1.0, 0.001);
EXPECT_NEAR(segments[0].second.y, 1.0, 0.001);
}
}
TEST_F(test_ms_square, fudge_test_2)
{
// Fudge test 2
Square square(ValuedPoint(0, 1, 1), ValuedPoint(1, 1, 0),
ValuedPoint(0, 0, 0), ValuedPoint(1, 0, 0));
//
// 1 0
// +o-----------------+
// o+ |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 0 0
// (0,0)
{
Square::Segments segments(square.segments(1.0));
EXPECT_EQ(segments.size(), 1);
EXPECT_NEAR(segments[0].first.x, 0.0, 0.001);
EXPECT_NEAR(segments[0].first.y, 1.0, 0.001);
EXPECT_NEAR(segments[0].second.x, 0.0, 0.001);
EXPECT_NEAR(segments[0].second.y, 1.0, 0.001);
}
{
Square::Segments segments(square.segments(0.0));
EXPECT_EQ(segments.size(), 0);
}
}
TEST_F(test_ms_square, nan)
{
// A square with NaN
const Square square(ValuedPoint(2.500000, 1.500000, 224.990005),
ValuedPoint(3.500000, 1.500000, NaN),
ValuedPoint(2.500000, 2.500000, 225.029999),
ValuedPoint(3.500000, 2.500000, 224.770004));
//
// 224.990005 NaN
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 225.029999 224.770004
const Square ul(square.upperLeftSquare());
const Square ll(square.lowerLeftSquare());
// upper left and lower left squares
//
// 224.990005 224.990005 NaN
// +--------+---------+
// | | |
// | | |
// | | |
// +--------+ 224.930002
// 225.010002 | |
// | | |
// | 224.900001 |
// +--------+---------+
// 225.029999 224.770004
EXPECT_NEAR(ul.lowerLeft.value, 225.010002, 0.000001);
EXPECT_NEAR(ul.lowerRight.value, 224.930002, 0.000001);
EXPECT_NEAR(ul.upperRight.value, 224.990005, 0.000001);
EXPECT_NEAR(ll.lowerRight.value, 224.900001, 0.000001);
EXPECT_EQ(ul.lowerLeft.x, ll.upperLeft.x);
EXPECT_EQ(ul.lowerLeft.y, ll.upperLeft.y);
EXPECT_EQ(ul.lowerLeft.value, ll.upperLeft.value);
EXPECT_EQ(ul.lowerRight.x, ll.upperRight.x);
EXPECT_EQ(ul.lowerRight.y, ll.upperRight.y);
EXPECT_EQ(ul.lowerRight.value, ll.upperRight.value);
const Square::Segments segments_up(ul.segments(225));
const Square::Segments segments_down(ll.segments(225));
// segments on 225
//
// 224.990005 224.990005 NaN
// <--------<---------+
// | | |
// o_ | |
// | \ | |
// >--o-----< 224.930002
// 225.01|002 | |
// | \ | |
// | |224.900001 |
// >---o----<---------+
// 225.029999 224.770004
EXPECT_EQ(segments_up.size(), 1);
EXPECT_EQ(segments_down.size(), 1);
// the two segments have a point in common
EXPECT_EQ(segments_up[0].second, segments_down[0].first);
}
TEST_F(test_ms_square, border_test_1)
{
// Border test 1
const Square square(ValuedPoint(0.5, 0.5, NaN), ValuedPoint(1.5, 0.5, NaN),
ValuedPoint(0.5, 1.5, 272.87),
ValuedPoint(1.5, 1.5, 272.93));
//
// NaN NaN
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 272.87 272.93
const Square ll(square.lowerLeftSquare());
const Square lr(square.lowerRightSquare());
//
// NaN NaN
// +------------------+
// | |
// | |
// 272.87 272.90000 272.93
// +--------+---------+
// | | |
// | | |
// | | |
// +--------+---------+
// 272.87 272.90000 272.93
Square::Segments segments_l(ll.segments(272.9));
Square::Segments segments_r(lr.segments(272.9));
// the level falls exactly on corners
// thanks to the fudge, each corner should be shifted away a bit
//
// NaN NaN
// +------------------+
// | |
// | |
// 272.87 272.90000 272.93
// <-------o>--------->
// | :| |
// | :| |
// | :| |
// <-------o>--------->
// 272.87 272.90000 272.93
EXPECT_EQ(segments_l.size(), size_t(1));
EXPECT_EQ(segments_r.size(), size_t(0));
}
TEST_F(test_ms_square, multiple_levels)
{
// Multiple levels
const Square square(
ValuedPoint(0.5, 1.5, 272.99), ValuedPoint(1.5, 1.5, NaN),
ValuedPoint(0.5, 0.5, 273.03), ValuedPoint(1.5, 0.5, 272.9));
//
// 272.99 NaN
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 273.03 272.90
const Square ul(square.upperLeftSquare());
//
// 272.99 272.99 NaN
// +---------+--------+
// | | |
// | | |
// | | |
// +---------+ |
// 273.01 272.97 |
// | |
// | |
// +------------------+
// 273.03 272.90
EXPECT_TRUE((std::fabs(ul.lowerLeft.value - 273.01) < 0.01));
EXPECT_TRUE((std::fabs(ul.lowerRight.value - 272.97) < 0.01));
EXPECT_TRUE((std::fabs(ul.upperRight.value - 272.99) < 0.01));
// We have a NaN value on the right, we should then have a right border
EXPECT_TRUE((ul.borders == Square::RIGHT_BORDER));
Writer writer;
// levels starting at min and increasing by 0.1
IntervalLevelRangeIterator levelGenerator(0, .1);
ul.process(levelGenerator, writer);
// we only have a contour when level = 273.0
// (0.5, 1.5) (1.5, 1.5)
// 272.99 272.99 NaN
// +---------+--------+
// | || |
// o || |
// |\ || |
// +-o-------+ |
// 273.01 272.97 |
// | |
// | |
// +------------------+
// 273.03 272.90
// (0.5, 0.5) (1.5, 0.5)
EXPECT_TRUE((writer.contours.size() == 2));
EXPECT_TRUE((writer.borders.size() == 1));
EXPECT_TRUE((writer.contours.find(2730) != writer.contours.end()));
EXPECT_TRUE((writer.contours.find(2731) != writer.contours.end()));
EXPECT_TRUE((writer.borders.find(2730) != writer.borders.end()));
// we have one segment border on the right
EXPECT_TRUE((writer.borders[2730].size() == 1));
EXPECT_TRUE((writer.contours[2730].size() == 1));
EXPECT_TRUE((writer.contours[2731].size() == 1));
}
TEST_F(test_ms_square, border_test_3)
{
// Border test 3
Square square(ValuedPoint(0, 0, 10), ValuedPoint(1, 0, 5),
ValuedPoint(0, 1, NaN), ValuedPoint(1, 1, 4));
// level value = 7
// 10 7.5 5
// +---------+--------+
// | | |
// | _o |
// | _/ | |
// 10 +====o====+ 6.33 |
// | |
// | |
// | |
// +------------------+
// NaN 4
const Square ul(square.upperLeftSquare());
// "Lower left value",
EXPECT_TRUE((std::fabs(ul.lowerLeft.value - 10.00) < 0.01));
// "Lower right value",
EXPECT_TRUE((std::fabs(ul.lowerRight.value - 6.33) < 0.01));
// "Upper right value",
EXPECT_TRUE((std::fabs(ul.upperRight.value - 7.50) < 0.01));
// We have a NaN value on the right, we should then have a right border
// "We have the lower border",
EXPECT_TRUE(ul.borders == Square::LOWER_BORDER);
{
// ... with a level interval
Writer writer;
IntervalLevelRangeIterator levelGenerator(7, 5);
ul.process(levelGenerator, writer);
// we have one contour at 7 and 12
// and two borders: one, at 7 and the second at >7 (12)
// "We have 2 borders",
EXPECT_EQ(writer.borders.size(), size_t(2));
// "We have 2 contours",
EXPECT_EQ(writer.contours.size(), size_t(2));
// "Border at 0",
EXPECT_TRUE(writer.borders.find(0) != writer.borders.end());
// "Border at 1",
EXPECT_TRUE(writer.borders.find(1) != writer.borders.end());
// "No contour at 0",
EXPECT_TRUE(writer.contours.find(0) != writer.contours.end());
// and we have one contour and 2 borders
// "1 contour at 0",
EXPECT_EQ(writer.contours[0].size(), size_t(1));
// "1 border at 0",
EXPECT_EQ(writer.borders[0].size(), size_t(1));
// "1 border at 1",
EXPECT_EQ(writer.borders[1].size(), size_t(1));
// the border at 7.0 is around 0.5, 0.5
// "Border at 1 is around 0.5, 0.5",
EXPECT_TRUE((writer.borders[0][0].first.x == 0.5 &&
writer.borders[0][0].first.y == 0.5) ||
(writer.borders[0][0].second.x == 0.5 &&
writer.borders[0][0].second.y == 0.5));
// the border at 12.0 is around 0, 0.5
// "Border at 1 is around 0, 0.5",
EXPECT_TRUE((writer.borders[1][0].first.x == 0.0 &&
writer.borders[1][0].first.y == 0.5) ||
(writer.borders[1][0].second.x == 0.0 &&
writer.borders[1][0].second.y == 0.5));
}
// test with a fixed set of levels
{
Writer writer;
std::vector<double> levels = {7.0};
FixedLevelRangeIterator levelGenerator(&levels[0], 1);
ul.process(levelGenerator, writer);
// we have one contour at 7 and 12
// and two borders: one, at 7 and the second at >7 (12)
// "We have 2 borders",
EXPECT_EQ(writer.borders.size(), size_t(2));
// "We have 2 contours",
EXPECT_EQ(writer.contours.size(), size_t(2));
// "Border at 0",
EXPECT_TRUE(writer.borders.find(0) != writer.borders.end());
// "Border at 1",
EXPECT_TRUE(writer.borders.find(1) != writer.borders.end());
// "No contour at 0",
EXPECT_TRUE(writer.contours.find(0) != writer.contours.end());
// and we have one contour and 2 borders
// "1 contour at 0",
EXPECT_EQ(writer.contours[0].size(), size_t(1));
// "1 border at 0",
EXPECT_EQ(writer.borders[0].size(), size_t(1));
// "1 border at 1",
EXPECT_EQ(writer.borders[1].size(), size_t(1));
// the border at 7.0 is around 0.5, 0.5
// "Border at 1 is around 0.5, 0.5",
EXPECT_TRUE((writer.borders[0][0].first.x == 0.5 &&
writer.borders[0][0].first.y == 0.5) ||
(writer.borders[0][0].second.x == 0.5 &&
writer.borders[0][0].second.y == 0.5));
// the border at 12.0 is around 0, 0.5
// "Border at 1 is around 0, 0.5",
EXPECT_TRUE((writer.borders[1][0].first.x == 0.0 &&
writer.borders[1][0].first.y == 0.5) ||
(writer.borders[1][0].second.x == 0.0 &&
writer.borders[1][0].second.y == 0.5));
}
}
TEST_F(test_ms_square, level_value_below_square_values)
{
// Test level value below square values
Square square(ValuedPoint(0, 0, 10), ValuedPoint(1, 0, 5),
ValuedPoint(0, 1, 8), ValuedPoint(1, 1, 4));
// level value = 2
// 10 5
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// 8 4
{
Writer writer;
std::vector<double> levels = {2.0};
FixedLevelRangeIterator levelGenerator(&levels[0], 1);
square.process(levelGenerator, writer);
EXPECT_TRUE((writer.borders.size() == 0));
EXPECT_TRUE((writer.contours.size() == 0));
}
}
TEST_F(test_ms_square, full_border_test_1)
{
// Full border test 1
Square square(ValuedPoint(-0.5, -0.5, NaN), ValuedPoint(0.5, -0.5, NaN),
ValuedPoint(-0.5, 0.5, NaN), ValuedPoint(0.5, 0.5, 5));
// level value = 0, 10
// NaN NaN
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// NaN 5
{
Writer writer;
IntervalLevelRangeIterator levelGenerator(0, 10.0);
square.process(levelGenerator, writer);
EXPECT_TRUE((writer.borders.size() == 1));
EXPECT_TRUE((writer.borders[1].size() == 2));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.0 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.0 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.5 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.5 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.5)));
}
}
TEST_F(test_ms_square, full_border_test_2)
{
// Full border test 2
Square square(ValuedPoint(-0.5, -0.5, NaN), ValuedPoint(0.5, -0.5, NaN),
ValuedPoint(-0.5, 0.5, NaN), ValuedPoint(0.5, 0.5, 5));
// level value = 5.0, 10.0
// NaN NaN
// +------------------+
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// +------------------+
// NaN 5
{
Writer writer;
IntervalLevelRangeIterator levelGenerator(5.0, 5.0);
square.process(levelGenerator, writer);
EXPECT_TRUE((writer.borders.size() == 1));
EXPECT_TRUE((writer.borders[1].size() == 2));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.0 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.0 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.5 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.5 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.5)));
}
{
Writer writer;
std::vector<double> levels = {5.0};
FixedLevelRangeIterator levelGenerator(&levels[0], 1);
square.process(levelGenerator, writer);
EXPECT_TRUE((writer.borders.size() == 1));
EXPECT_TRUE((writer.borders[1].size() == 2));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.0 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.0 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][0].first.x == 0.5 &&
writer.borders[1][0].first.y == 0.0) ||
(writer.borders[1][0].second.x == 0.5 &&
writer.borders[1][0].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.0)));
EXPECT_TRUE(((writer.borders[1][1].first.x == 0.0 &&
writer.borders[1][1].first.y == 0.0) ||
(writer.borders[1][1].second.x == 0.0 &&
writer.borders[1][1].second.y == 0.5)));
}
}
} // namespace