mirror of
https://github.com/boostorg/geometry.git
synced 2026-01-31 20:22:09 +00:00
Merge branch 'develop' into feature/dissolve
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
|
||||
[*Improvements]
|
||||
|
||||
* Add distance for geographic PointLike/AnyGeometry.
|
||||
|
||||
[*Solved issues]
|
||||
|
||||
* [@https://svn.boost.org/trac10/ticket/12503 12503] Validity of complex polygon
|
||||
@@ -33,6 +35,7 @@
|
||||
* Fixes in validity of union/intersection/difference which were sometimes invalid. In most cases, results are valid now.
|
||||
* Fixes in validity of buffer which were sometimes invalid. In most cases, results are valid now.
|
||||
* Fixes in results of union/intersection/difference which could be incorrect in very complex cases
|
||||
* Fixes in set and relational operations for non-cartesian coordinate systems.
|
||||
|
||||
[/=================]
|
||||
[heading Boost 1.65]
|
||||
|
||||
@@ -412,7 +412,7 @@ inline void buffer_point(Point const& point, Collection& collection,
|
||||
DistanceStrategy const& distance_strategy,
|
||||
PointStrategy const& point_strategy)
|
||||
{
|
||||
collection.start_new_ring();
|
||||
collection.start_new_ring(false);
|
||||
std::vector<OutputPointType> range_out;
|
||||
point_strategy.apply(point, distance_strategy, range_out);
|
||||
collection.add_piece(geometry::strategy::buffer::buffered_point, range_out, false);
|
||||
@@ -627,7 +627,7 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
|
||||
RobustPolicy const& robust_policy,
|
||||
Strategy const& strategy) // side strategy
|
||||
{
|
||||
collection.start_new_ring();
|
||||
collection.start_new_ring(distance.negative());
|
||||
geometry::strategy::buffer::result_code const code
|
||||
= buffer_inserter_ring<RingInput, RingOutput>::apply(ring,
|
||||
collection, distance,
|
||||
@@ -745,7 +745,7 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon>
|
||||
std::size_t n = boost::size(simplified);
|
||||
if (n > 1)
|
||||
{
|
||||
collection.start_new_ring();
|
||||
collection.start_new_ring(false);
|
||||
output_point_type first_p1;
|
||||
code = iterate(collection,
|
||||
boost::begin(simplified), boost::end(simplified),
|
||||
@@ -819,7 +819,13 @@ private:
|
||||
{
|
||||
for (Iterator it = begin; it != end; ++it)
|
||||
{
|
||||
collection.start_new_ring();
|
||||
// For exterior rings, it deflates if distance is negative.
|
||||
// For interior rings, it is vice versa
|
||||
bool const deflate = is_interior
|
||||
? ! distance.negative()
|
||||
: distance.negative();
|
||||
|
||||
collection.start_new_ring(deflate);
|
||||
geometry::strategy::buffer::result_code const code
|
||||
= policy::apply(*it, collection, distance, side_strategy,
|
||||
join_strategy, end_strategy, point_strategy,
|
||||
@@ -881,7 +887,7 @@ public:
|
||||
Strategy const& strategy) // side strategy
|
||||
{
|
||||
{
|
||||
collection.start_new_ring();
|
||||
collection.start_new_ring(distance.negative());
|
||||
|
||||
geometry::strategy::buffer::result_code const code
|
||||
= policy::apply(exterior_ring(polygon), collection,
|
||||
|
||||
@@ -220,6 +220,7 @@ struct buffered_piece_collection
|
||||
bool is_flat_start;
|
||||
bool is_flat_end;
|
||||
|
||||
bool is_deflated;
|
||||
bool is_convex;
|
||||
bool is_monotonic_increasing[2]; // 0=x, 1=y
|
||||
bool is_monotonic_decreasing[2]; // 0=x, 1=y
|
||||
@@ -249,6 +250,7 @@ struct buffered_piece_collection
|
||||
, offsetted_count(-1)
|
||||
, is_flat_start(false)
|
||||
, is_flat_end(false)
|
||||
, is_deflated(false)
|
||||
, is_convex(false)
|
||||
, robust_min_comparable_radius(0)
|
||||
, robust_max_comparable_radius(0)
|
||||
@@ -300,6 +302,8 @@ struct buffered_piece_collection
|
||||
piece_vector_type m_pieces;
|
||||
turn_vector_type m_turns;
|
||||
signed_size_type m_first_piece_index;
|
||||
bool m_deflate;
|
||||
bool m_has_deflated;
|
||||
|
||||
buffered_ring_collection<buffered_ring<Ring> > offsetted_rings; // indexed by multi_index
|
||||
std::vector<robust_original> robust_originals; // robust representation of the original(s)
|
||||
@@ -338,6 +342,8 @@ struct buffered_piece_collection
|
||||
buffered_piece_collection(IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
: m_first_piece_index(-1)
|
||||
, m_deflate(false)
|
||||
, m_has_deflated(false)
|
||||
, m_intersection_strategy(intersection_strategy)
|
||||
, m_side_strategy(intersection_strategy.get_side_strategy())
|
||||
, m_area_strategy(intersection_strategy.template get_area_strategy<point_type>())
|
||||
@@ -529,6 +535,90 @@ struct buffered_piece_collection
|
||||
}
|
||||
}
|
||||
|
||||
struct deflate_properties
|
||||
{
|
||||
bool has_inflated;
|
||||
std::size_t count;
|
||||
|
||||
inline deflate_properties()
|
||||
: has_inflated(false)
|
||||
, count(0u)
|
||||
{}
|
||||
};
|
||||
|
||||
inline void discard_turns_for_deflate()
|
||||
{
|
||||
// Deflate cases should have at least 3 points PER deflated original
|
||||
// to form a correct triangle
|
||||
|
||||
// But if there are intersections between a deflated ring and another
|
||||
// ring, it is all accepted
|
||||
|
||||
// In deflate most turns are i/u by nature, but u/u is also possible
|
||||
|
||||
std::map<signed_size_type, deflate_properties> properties;
|
||||
|
||||
for (typename boost::range_iterator<turn_vector_type const>::type it =
|
||||
boost::begin(m_turns); it != boost::end(m_turns); ++it)
|
||||
{
|
||||
const buffer_turn_info_type& turn = *it;
|
||||
if (turn.location == location_ok)
|
||||
{
|
||||
const buffer_turn_operation_type& op0 = turn.operations[0];
|
||||
const buffer_turn_operation_type& op1 = turn.operations[1];
|
||||
|
||||
if (! m_pieces[op0.seg_id.piece_index].is_deflated
|
||||
|| ! m_pieces[op1.seg_id.piece_index].is_deflated)
|
||||
{
|
||||
properties[op0.seg_id.multi_index].has_inflated = true;
|
||||
properties[op1.seg_id.multi_index].has_inflated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// It is deflated, update counts
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
const buffer_turn_operation_type& op = turn.operations[i];
|
||||
if (op.operation == detail::overlay::operation_union
|
||||
|| op.operation == detail::overlay::operation_continue)
|
||||
{
|
||||
properties[op.seg_id.multi_index].count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (typename boost::range_iterator<turn_vector_type>::type it =
|
||||
boost::begin(m_turns); it != boost::end(m_turns); ++it)
|
||||
{
|
||||
buffer_turn_info_type& turn = *it;
|
||||
|
||||
if (turn.location == location_ok)
|
||||
{
|
||||
const buffer_turn_operation_type& op0 = turn.operations[0];
|
||||
const buffer_turn_operation_type& op1 = turn.operations[1];
|
||||
signed_size_type const multi0 = op0.seg_id.multi_index;
|
||||
signed_size_type const multi1 = op1.seg_id.multi_index;
|
||||
|
||||
if (multi0 == multi1)
|
||||
{
|
||||
const deflate_properties& prop = properties[multi0];
|
||||
if (! prop.has_inflated && prop.count < 3)
|
||||
{
|
||||
// Property is not inflated
|
||||
// Not enough points, this might be caused by <float> where
|
||||
// detection turn-in-original failed because of numeric errors
|
||||
turn.location = location_discard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Two different (possibly deflated) rings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DistanceStrategy>
|
||||
inline void check_remaining_points(DistanceStrategy const& distance_strategy)
|
||||
{
|
||||
@@ -554,7 +644,7 @@ struct buffered_piece_collection
|
||||
{
|
||||
if (deflate && turn.count_in_original <= 0)
|
||||
{
|
||||
// For deflate: it is not in original, discard
|
||||
// For deflate/negative buffers: it is not in original, discard
|
||||
turn.location = location_discard;
|
||||
}
|
||||
else if (! deflate && turn.count_in_original > 0)
|
||||
@@ -564,6 +654,12 @@ struct buffered_piece_collection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_has_deflated)
|
||||
{
|
||||
// Either strategy was negative, or there were interior rings
|
||||
discard_turns_for_deflate();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool assert_indices_in_robust_rings() const
|
||||
@@ -832,7 +928,7 @@ struct buffered_piece_collection
|
||||
}
|
||||
}
|
||||
|
||||
inline void start_new_ring()
|
||||
inline void start_new_ring(bool deflate)
|
||||
{
|
||||
signed_size_type const n = static_cast<signed_size_type>(offsetted_rings.size());
|
||||
current_segment_id.source_index = 0;
|
||||
@@ -844,6 +940,13 @@ struct buffered_piece_collection
|
||||
current_robust_ring.clear();
|
||||
|
||||
m_first_piece_index = static_cast<signed_size_type>(boost::size(m_pieces));
|
||||
m_deflate = deflate;
|
||||
if (deflate)
|
||||
{
|
||||
// Pieces contain either deflated exterior rings, or inflated
|
||||
// interior rings which are effectively deflated too
|
||||
m_has_deflated = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void abort_ring()
|
||||
@@ -978,6 +1081,7 @@ struct buffered_piece_collection
|
||||
piece pc;
|
||||
pc.type = type;
|
||||
pc.index = static_cast<signed_size_type>(boost::size(m_pieces));
|
||||
pc.is_deflated = m_deflate;
|
||||
|
||||
current_segment_id.piece_index = pc.index;
|
||||
|
||||
|
||||
@@ -169,9 +169,8 @@ public:
|
||||
{
|
||||
CT const sigma2 = S_sigma - sigma1;
|
||||
//theta2 = asin(cos(sigma2)) <=> sin_theta0 = 1
|
||||
// calculate absolute value because the geodesic is reversed
|
||||
// if azimuth is pi and the result is altered below
|
||||
CT const tan_theta2 = math::abs(cos(sigma2) / sin(sigma2));
|
||||
// NOTE: cos(sigma2) defines the sign of tan_theta2
|
||||
CT const tan_theta2 = cos(sigma2) / math::abs(sin(sigma2));
|
||||
result.lat2 = atan(tan_theta2 / one_minus_f);
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ public:
|
||||
{
|
||||
result.lat2 = -result.lat2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ public :
|
||||
return negative() ? -1 : 1;
|
||||
}
|
||||
|
||||
//! Returns true if distance is negative
|
||||
//! Returns true if distance is negative (aka deflate)
|
||||
inline bool negative() const
|
||||
{
|
||||
return m_distance < 0;
|
||||
|
||||
@@ -133,7 +133,6 @@ void test_one(std::string const& caseid, std::string const& wkt, double expected
|
||||
// Test with a high tolerance, even a difference of 1000 is only ~1.0e-6%
|
||||
|
||||
settings.tolerance = 10000.0;
|
||||
settings.check_self_intersections = false;
|
||||
|
||||
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
|
||||
// in case robustness policies are changed, areas should be adapted
|
||||
|
||||
@@ -251,14 +251,11 @@ void test_all()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
ut_settings settings(1.0e-10);
|
||||
// Check on validity, with high precision because areas are all very small
|
||||
ut_settings settings(1.0e-10, true);
|
||||
|
||||
ut_settings settings_wo_self = settings;
|
||||
settings_wo_self.check_self_intersections = false;
|
||||
|
||||
test_one<linestring, polygon>("aimes120", aimes120, join_miter, end_flat, 1.62669948622351512e-08, 0.000018, settings_wo_self);
|
||||
test_one<linestring, polygon>("aimes120", aimes120, join_miter, end_flat, 1.62669948622351512e-08, 0.000018, settings);
|
||||
test_one<linestring, polygon>("aimes120", aimes120, join_round, end_round, 1.72842078427493107e-08, 0.000018, settings);
|
||||
|
||||
test_one<linestring, polygon>("aimes167", aimes167, join_miter, end_flat, 1.88900628472765675e-09, 0.000018, settings);
|
||||
|
||||
@@ -468,10 +468,8 @@ void test_aimes()
|
||||
bg::strategy::buffer::end_flat end_flat;
|
||||
bg::strategy::buffer::end_round end_round(100);
|
||||
|
||||
ut_settings settings(1.0e-10, true, false);
|
||||
|
||||
// Aimes tested originally with 0.000018 degrees (around 2 m)
|
||||
std::size_t self_ip_count = 0;
|
||||
ut_settings settings(1.0e-10);
|
||||
|
||||
int expectation_index = 0;
|
||||
for (int width = 18; width <= 36; width += 18, expectation_index += 2)
|
||||
@@ -483,17 +481,17 @@ void test_aimes()
|
||||
try
|
||||
{
|
||||
name << "aimes_" << i << "_" << width;
|
||||
test_one_and_count_ips<linestring, polygon>
|
||||
test_one<linestring, polygon>
|
||||
(
|
||||
name.str(), testcases[i], join_miter, end_flat,
|
||||
expectations[i][expectation_index],
|
||||
aimes_width, self_ip_count, settings
|
||||
aimes_width, settings
|
||||
);
|
||||
test_one_and_count_ips<linestring, polygon>
|
||||
test_one<linestring, polygon>
|
||||
(
|
||||
name.str(), testcases[i], join_round, end_round,
|
||||
expectations[i][expectation_index + 1],
|
||||
aimes_width, self_ip_count, settings
|
||||
aimes_width, settings
|
||||
);
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
@@ -502,12 +500,6 @@ void test_aimes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
self_ip_count == 0,
|
||||
"There are self-intersections: " << self_ip_count
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -125,36 +125,31 @@ void test_all()
|
||||
test_one<multi_linestring_type, polygon>("mikado4_small", mikado4, join_round32, end_flat, 1930.785, 10.0);
|
||||
}
|
||||
|
||||
{
|
||||
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
|
||||
// Coordinates in one linestring vary so much that
|
||||
// length = geometry::math::sqrt(dx * dx + dy * dy); returns a value of inf for length
|
||||
// That geometry is skipped for the buffer
|
||||
// SQL Server reports area 2117753600 (for b)
|
||||
// PostGIS reports 2087335072 (for b)
|
||||
// BG (2) reports 794569660 (for a/b) which apparently misses parts
|
||||
// old value was 927681870 (for a/b) which also misses parts
|
||||
// (2: since selecting other IP at end points or when segment b is smaller than a)
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_04_10a", mysql_2015_04_10a, join_round32, end_round32, 1063005187.214, 0.98);
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_04_10b", mysql_2015_04_10b, join_round32, end_round32, 1063005187.214, 0.98);
|
||||
// Coordinates in one linestring vary so much that
|
||||
// length = geometry::math::sqrt(dx * dx + dy * dy); returns a value of inf for length
|
||||
// That geometry is skipped for the buffer
|
||||
// SQL Server reports area 2117753600 (for b)
|
||||
// PostGIS reports 2087335072 (for b)
|
||||
// BG (2017) reports 1063005187 (for a/b) which apparently misses parts
|
||||
// BG (2015) reported 794569660 (for a/b)
|
||||
// BG (earlier) reported 927681870 (for a/b)
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_04_10a",
|
||||
mysql_2015_04_10a, join_round32, end_round32,
|
||||
ut_settings::ignore_area(), 0.98, ut_settings::assertions_only());
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_04_10b",
|
||||
mysql_2015_04_10b, join_round32, end_round32,
|
||||
ut_settings::ignore_area(), 0.98, ut_settings::assertions_only());
|
||||
#endif
|
||||
|
||||
{
|
||||
// Two other cases with inf for length calculation (tolerance quite high
|
||||
// because the output area is quite high and varies between gcc/clang)
|
||||
|
||||
// On MinGW this still causes invalid output, for case a:
|
||||
// multiline_mysql_2015_09_08a_d_round_round output is self-intersecting.
|
||||
// multiline_mysql_2015_09_08a_d_round_round is not valid
|
||||
// On PowerPC also case b is reported as invalid
|
||||
|
||||
ut_settings settings(1.0e12, false, false);
|
||||
|
||||
// Two other cases with <inf> for length calculation
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_09_08a",
|
||||
mysql_2015_09_08a, join_round32, end_round32,
|
||||
5.12436196736438764e+19, 4051744443.0, settings);
|
||||
mysql_2015_09_08a, join_round32, end_round32,
|
||||
ut_settings::ignore_area(), 4051744443.0, ut_settings::assertions_only());
|
||||
test_one<multi_linestring_type, polygon>("mysql_2015_09_08b",
|
||||
mysql_2015_09_08b, join_round32, end_round32,
|
||||
1.32832149026508268e+19, 2061380362.0, settings);
|
||||
mysql_2015_09_08b, join_round32, end_round32,
|
||||
ut_settings::ignore_area(), 2061380362.0, ut_settings::assertions_only());
|
||||
}
|
||||
|
||||
// Generates first no interior, then one touching point (no interior),
|
||||
|
||||
@@ -464,8 +464,8 @@ void test_all()
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u2", rt_u2, join_round, end_flat, 138.8001, 1.0);
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u3", rt_u3, join_round, end_flat, 133.4526, 1.0);
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u4", rt_u4, join_round, end_flat, 126.9268, 1.0);
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u5", rt_u5, join_round, end_flat, 78.4906, 1.0, ut_settings::ignore_validity());
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u6", rt_u6, join_round, end_flat, 115.4461, 1.0, ut_settings::ignore_validity());
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u5", rt_u5, join_round, end_flat, 78.4906, 1.0);
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u6", rt_u6, join_round, end_flat, 115.4461, 1.0);
|
||||
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u7", rt_u7, join_miter, end_flat, 42.6421, 1.0);
|
||||
test_one<multi_polygon_type, polygon_type>("rt_u7", rt_u7, join_round, end_flat, 35.6233, 1.0);
|
||||
@@ -503,15 +503,16 @@ void test_all()
|
||||
neighbouring_with_holes,
|
||||
join_round32, end_round32, 0.0, -10.0);
|
||||
|
||||
// On MinGW the output is invalid
|
||||
// Check cases with extreme coordinates on assertions
|
||||
test_one<multi_polygon_type, polygon_type>("mysql_report_2015_07_05_1",
|
||||
mysql_report_2015_07_05_1,
|
||||
join_round32, end_round32, 6.04454566324708726e+23, 5526.0,
|
||||
ut_settings(1e+020, false, false));
|
||||
join_round32, end_round32, ut_settings::ignore_area(), 5526.0,
|
||||
ut_settings::assertions_only());
|
||||
|
||||
test_one<multi_polygon_type, polygon_type>("mysql_report_2015_07_05_2",
|
||||
mysql_report_2015_07_05_2,
|
||||
join_round32, end_round32, 0.0, 948189399.0);
|
||||
join_round32, end_round32, ut_settings::ignore_area(), 948189399.0,
|
||||
ut_settings::assertions_only());
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
|
||||
@@ -114,6 +114,10 @@ static std::string const italy_part1
|
||||
static std::string const nl_part1
|
||||
= "POLYGON ((471871.966884626832325 6683515.683521211147308,470695.876464393688366 6681756.129975977353752,469211.542374156008009 6679924.978601523675025,464542.357652322971262 6674631.078279769048095,463243.59315323777264 6673494.345109832473099,459502.033748187706806 6670774.304660517722368,452204.484529234410729 6666030.372161027044058,439990.287360413349234 6659313.515823394991457,434547.988775020290632 6657783.034025833010674,433867.715366783668287 6657685.311832630075514,433063.65468478546245 6657783.034025833010674,423725.285241116478574 6659758.338574352674186,422581.143514745577704 6660350.880751021206379,422333.791606202081311 6660844.461689073592424,421993.599242338153999 6662622.453031159006059,421993.599242338153999 6663363.130126810632646,422612.090333183819894 6667314.244697011075914,422241.062470370030496 6667759.324870104901493,421096.80942450783914 6668303.522556710988283,408449.802075482031796 6673245.655735924839973,401646.845354124438018 6675273.665065255947411,400842.895991614612285 6675372.844085373915732,400255.351719210040756 6675224.699206517077982,392370.258227849320974 6672455.311684883199632,391968.283546594379004 6672009.975794731639326,391875.554410762328189 6671219.573235900141299,391937.336728153284639 6670527.121663697995245,392122.906319305824582 6669933.841785561293364,392339.311409408226609 6667957.501854715868831,392308.475910458364524 6665733.178529324010015,391937.336728153284639 6665289.631341196596622,387793.802641845482867 6664449.731981083750725,385814.7647345236619 6664202.740090588107705,384268.648326894966885 6664300.5401631873101,382846.319193030707538 6664646.406163938343525,382289.610419573145919 6664943.560305980034173,377186.502322628628463 6668957.888622742146254,376352.608017096004914 6669834.728738470934331,376197.985244384559337 6670428.001423852518201,375548.658654586179182 6676313.056885857135057,375243.086652359867003 6687692.866469252854586,379228.324422756850254 6689778.692146780900657,391782.713955441897269 6694388.125634397380054,393885.427817036863416 6694487.537080916576087,395215.139134563156404 6694091.147844122722745,405171.999669074953999 6689084.665672835893929,414263.128523688821588 6684478.40333267301321,415778.298112876422238 6683439.398042757064104,416396.677884233708028 6683192.009851422160864,419025.042381353967357 6682597.809754087589681,429909.63955213711597 6681508.792763692326844,430497.183824544539675 6681656.873437026515603,440979.806314075656701 6686955.330480101518333,467325.344922157470137 6687797.546342735178769,468129.294284664443694 6687698.216345063410699,468747.785375512961764 6687450.699095219373703,469211.542374156008009 6687054.651439971290529,469489.952420631831046 6686707.835750493220985,471871.966884626832325 6683515.683521211147308))";
|
||||
|
||||
static std::string const erode_triangle ="POLYGON((-262.6446228027343700 -261.9999389648437500, -261.0000000000000000 -262.0000000000000000, -261.0000915527343700 -262.5285644531250000, -262.6446228027343700 -261.9999389648437500))";
|
||||
|
||||
static std::string const forming_uu_a ="POLYGON((0 0,0 10,5 6,10 10,10 0,5 4,0 0))";
|
||||
static std::string const forming_uu_b ="POLYGON((0 0,0 10,5 6,10 10,10 0,5 4,0 0),(1.25 2.5, 4.25 5,1.25 7.5,1.25 5.5,2.25 5.0,1.25 4.5,1.25 2.5))";
|
||||
|
||||
// Ticket 10398, fails at next distances ( /10.0 ):
|
||||
// #1: 5,25,84
|
||||
@@ -168,6 +172,20 @@ static std::string const mysql_report_2015_07_05_4
|
||||
static std::string const mysql_report_2015_07_05_5
|
||||
= "POLYGON((9 3,-8193 8.38861e+06,-2 -4,9 3),(-10 -2,32268 -2557,1.72036e+308 5.67867e+307,4.91634e+307 1.41031e+308,-2.68435e+08 -19,-10 -2),(-5 4,9.50167e+307 1.05883e+308,-422 -25737,-5 4))";
|
||||
|
||||
// Versions without interiors
|
||||
static std::string const mysql_report_2015_07_05_0_wi
|
||||
= "POLYGON((0 0,0 30000,30000 20000,25000 0,0 0))";
|
||||
static std::string const mysql_report_2015_07_05_1_wi
|
||||
= "POLYGON((9192 27876,128 4.5036e+15,-1 5,3 9,9192 27876))";
|
||||
static std::string const mysql_report_2015_07_05_2_wi
|
||||
= "POLYGON((-15 -15,1.31128e+308 2.47964e+307,-20 -9,-15 -15))";
|
||||
static std::string const mysql_report_2015_07_05_3_wi
|
||||
= "POLYGON((-2.68435e+08 -12,27090 -14130,5.76461e+17 5.49756e+11,-2.68435e+08 -12))";
|
||||
static std::string const mysql_report_2015_07_05_4_wi
|
||||
= "POLYGON((2.19902e+12 524287,1.13649e+308 1.36464e+308,-10 -19,2.19902e+12 524287))";
|
||||
static std::string const mysql_report_2015_07_05_5_wi
|
||||
= "POLYGON((9 3,-8193 8.38861e+06,-2 -4,9 3))";
|
||||
|
||||
class buffer_custom_side_strategy
|
||||
{
|
||||
public :
|
||||
@@ -509,7 +527,6 @@ void test_all()
|
||||
|
||||
{
|
||||
ut_settings settings;
|
||||
settings.check_self_intersections = false;
|
||||
settings.test_validity = false;
|
||||
|
||||
// Tickets
|
||||
@@ -582,27 +599,51 @@ void test_all()
|
||||
join_round32, end_round32, 64.0, -1.0);
|
||||
|
||||
{
|
||||
// These extreme testcases, containing huge coordinate differences
|
||||
// and huge buffer distances, are to verify assertions.
|
||||
// No assertions should be raised.
|
||||
|
||||
// The buffers themselves are most often wrong. Versions
|
||||
// without interior rings might be smaller (or have no output)
|
||||
// then their versions with interior rings.
|
||||
|
||||
// Therefore all checks on area, validity, self-intersections
|
||||
// or having output at all are omitted.
|
||||
|
||||
// Details (for versions with interior rings)
|
||||
// Case 3 is reported as invalid
|
||||
// On MinGW, also case 2 and 4 are reported as invalid
|
||||
// On PowerPC, also case 1 is reported as invalid
|
||||
|
||||
ut_settings settings(1.0e+20, false, false);
|
||||
ut_settings settings = ut_settings::assertions_only();
|
||||
const double no_area = ut_settings::ignore_area();
|
||||
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_0", mysql_report_2015_07_05_0,
|
||||
join_round32, end_round32, 700643542.242915988, 6.0);
|
||||
join_round32, end_round32, no_area, 6.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_1", mysql_report_2015_07_05_1,
|
||||
join_round32, end_round32, 2.07548405999982264e+19, 6.0, settings);
|
||||
|
||||
join_round32, end_round32, no_area, 6.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_2", mysql_report_2015_07_05_2,
|
||||
join_round32, end_round32, 9.48681585720922691e+23, 549755813889.0, settings);
|
||||
join_round32, end_round32, no_area, 549755813889.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_3", mysql_report_2015_07_05_3,
|
||||
join_round32, end_round32, 6.10005339242509925e+22, 49316.0, settings);
|
||||
join_round32, end_round32, no_area, 49316.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_4", mysql_report_2015_07_05_4,
|
||||
join_round32, end_round32, 4.25405937213774089e+23, 1479986.0, settings);
|
||||
|
||||
join_round32, end_round32, no_area, 1479986.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_5", mysql_report_2015_07_05_5,
|
||||
join_round32, end_round32, 644489321051.62439, 38141.0,
|
||||
ut_settings(100000.0, true, false));
|
||||
join_round32, end_round32, no_area, 38141.0, settings);
|
||||
|
||||
// Versions without interior rings (area should be smaller but still no checks)
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_0_wi", mysql_report_2015_07_05_0_wi,
|
||||
join_round32, end_round32, no_area, 6.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_1_wi", mysql_report_2015_07_05_1_wi,
|
||||
join_round32, end_round32, no_area, 6.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_2_wi", mysql_report_2015_07_05_2_wi,
|
||||
join_round32, end_round32, no_area, 549755813889.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_3_wi", mysql_report_2015_07_05_3_wi,
|
||||
join_round32, end_round32, no_area, 49316.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_4_wi", mysql_report_2015_07_05_4_wi,
|
||||
join_round32, end_round32, no_area, 1479986.0, settings);
|
||||
test_one<polygon_type, polygon_type>("mysql_report_2015_07_05_5_wi", mysql_report_2015_07_05_5_wi,
|
||||
join_round32, end_round32, no_area, 38141.0, settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,7 +728,42 @@ void test_all()
|
||||
join_round(49), end_flat, distance(1.0), custom_side_strategy, point_strategy,
|
||||
31.1087);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Clockwise, typename P>
|
||||
void test_deflate_special_cases()
|
||||
{
|
||||
typedef bg::model::polygon<P, Clockwise, true> polygon_type;
|
||||
|
||||
bg::strategy::buffer::join_miter join_miter(5);
|
||||
bg::strategy::buffer::join_round join_round(8);
|
||||
bg::strategy::buffer::end_flat end_flat;
|
||||
|
||||
// This case fails for <float> because there is an IP formed at the border of the original
|
||||
test_one<polygon_type, polygon_type>("erode_50", erode_triangle, join_miter, end_flat, 0, 0, 0.0, -0.50);
|
||||
test_one<polygon_type, polygon_type>("erode_40", erode_triangle, join_miter, end_flat, 0, 0, 0.0, -0.40);
|
||||
test_one<polygon_type, polygon_type>("erode_60", erode_triangle, join_miter, end_flat, 0, 0, 0.0, -0.60);
|
||||
|
||||
// This case generates a uu-turn in deflate, at 1.0
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_95", forming_uu_a, join_round, end_flat, 1, 0, 23.0516, -0.95);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_10", forming_uu_a, join_round, end_flat, 2, 0, 21.4606, -1.0);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_15", forming_uu_a, join_round, end_flat, 2, 0, 8.8272, -1.5);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_20", forming_uu_a, join_round, end_flat, 2, 0, 1.7588, -2.0);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_21", forming_uu_a, join_round, end_flat, 2, 0, 0.9944, -2.1);
|
||||
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_25", forming_uu_b, join_round, end_flat, 1, 1, 38.4064, -0.25);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_50", forming_uu_b, join_round, end_flat, 1, 1, 24.4551, -0.50);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_95", forming_uu_b, join_round, end_flat, 1, 0, 11.5009, -0.95);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_10", forming_uu_b, join_round, end_flat, 1, 0, 10.7152, -1.0);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_15", forming_uu_b, join_round, end_flat, 1, 0, 4.4136, -1.5);
|
||||
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_25", forming_uu_b, join_round, end_flat, 1, 1, 67.7139, 0.25);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_50", forming_uu_b, join_round, end_flat, 1, 1, 82.0260, 0.50);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_70", forming_uu_b, join_round, end_flat, 1, 3, 93.0760, 0.70);
|
||||
|
||||
// From here on a/b have the same result
|
||||
test_one<polygon_type, polygon_type>("forming_uu_a_10", forming_uu_a, join_round, end_flat, 1, 0, 108.0959, 1.0);
|
||||
test_one<polygon_type, polygon_type>("forming_uu_b_10", forming_uu_b, join_round, end_flat, 1, 0, 108.0959, 1.0);
|
||||
}
|
||||
|
||||
template
|
||||
@@ -727,6 +803,10 @@ int test_main(int, char* [])
|
||||
test_all<true, dpoint>();
|
||||
test_all<false, dpoint>();
|
||||
|
||||
typedef bg::model::point<float, 2, bg::cs::cartesian> fpoint;
|
||||
test_deflate_special_cases<true, fpoint>();
|
||||
test_deflate_special_cases<true, dpoint>();
|
||||
|
||||
#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
|
||||
|
||||
test_mixed<dpoint, dpoint, false, false, true, true>();
|
||||
|
||||
@@ -98,13 +98,13 @@ template<> struct EndTestProperties<boost::geometry::strategy::buffer::end_flat>
|
||||
struct ut_settings
|
||||
{
|
||||
double tolerance;
|
||||
bool check_self_intersections;
|
||||
bool test_validity;
|
||||
bool test_area;
|
||||
|
||||
explicit ut_settings(double tol = 0.01, bool val = true, bool self = true)
|
||||
explicit ut_settings(double tol = 0.01, bool val = true)
|
||||
: tolerance(tol)
|
||||
, check_self_intersections(self)
|
||||
, test_validity(val)
|
||||
, test_area(true)
|
||||
{}
|
||||
|
||||
static inline ut_settings ignore_validity()
|
||||
@@ -114,31 +114,17 @@ struct ut_settings
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline ut_settings assertions_only()
|
||||
{
|
||||
ut_settings result;
|
||||
result.test_validity = false;
|
||||
result.test_area = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline double ignore_area() { return 9999.9; }
|
||||
};
|
||||
|
||||
template <typename Geometry, typename Strategy, typename RescalePolicy>
|
||||
inline std::size_t count_self_ips(Geometry const& geometry,
|
||||
Strategy const& strategy,
|
||||
RescalePolicy const& rescale_policy)
|
||||
{
|
||||
typedef typename bg::point_type<Geometry>::type point_type;
|
||||
typedef bg::detail::overlay::turn_info
|
||||
<
|
||||
point_type,
|
||||
typename bg::segment_ratio_type<point_type, RescalePolicy>::type
|
||||
> turn_info;
|
||||
|
||||
std::vector<turn_info> turns;
|
||||
|
||||
bg::detail::self_get_turn_points::no_interrupt_policy policy;
|
||||
bg::self_turns
|
||||
<
|
||||
bg::detail::overlay::assign_null_policy
|
||||
>(geometry, strategy, rescale_policy, turns, policy);
|
||||
|
||||
return turns.size();
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename GeometryOut,
|
||||
@@ -158,8 +144,7 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
int expected_count,
|
||||
int expected_holes_count,
|
||||
double expected_area,
|
||||
ut_settings const& settings = ut_settings(),
|
||||
std::size_t* self_ip_count = NULL)
|
||||
ut_settings const& settings)
|
||||
{
|
||||
namespace bg = boost::geometry;
|
||||
|
||||
@@ -266,9 +251,6 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
buffer_mapper.map_input_output(mapper, geometry, buffered, distance_strategy.negative());
|
||||
#endif
|
||||
|
||||
|
||||
typename bg::default_area_result<GeometryOut>::type area = bg::area(buffered);
|
||||
|
||||
//Uncomment to create simple CSV to compare/use in tests - adapt precision if necessary
|
||||
//std::cout << complete.str() << "," << std::fixed << std::setprecision(0) << area << std::endl;
|
||||
//return;
|
||||
@@ -279,19 +261,19 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
! bg::is_empty(buffered),
|
||||
complete.str() << " output is empty (unexpected)."
|
||||
);
|
||||
if (settings.test_area)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
! bg::is_empty(buffered),
|
||||
complete.str() << " output is empty (unexpected)."
|
||||
);
|
||||
}
|
||||
|
||||
bg::model::box<point_type> envelope_output;
|
||||
bg::assign_values(envelope_output, 0, 0, 1, 1);
|
||||
bg::envelope(buffered, envelope_output);
|
||||
|
||||
rescale_policy_type rescale_policy_output
|
||||
= bg::get_rescale_policy<rescale_policy_type>(envelope_output);
|
||||
|
||||
// std::cout << caseid << std::endl;
|
||||
// std::cout << "INPUT: " << bg::wkt(geometry) << std::endl;
|
||||
// std::cout << "OUTPUT: " << area << std::endl;
|
||||
@@ -321,8 +303,9 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
);
|
||||
}
|
||||
|
||||
if (expected_area > -0.1)
|
||||
if (settings.test_area)
|
||||
{
|
||||
typename bg::default_area_result<GeometryOut>::type area = bg::area(buffered);
|
||||
double const difference = area - expected_area;
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
@@ -336,31 +319,6 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
<< std::setprecision(3)
|
||||
<< " , " << 100.0 * (difference / expected_area) << "%"
|
||||
);
|
||||
|
||||
if (settings.check_self_intersections)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
bool has_self_ips = bg::detail::overlay::has_self_intersections(
|
||||
buffered, strategy, rescale_policy_output, false);
|
||||
// Be sure resulting polygon does not contain
|
||||
// self-intersections
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
! has_self_ips,
|
||||
complete.str() << " output is self-intersecting. "
|
||||
);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE
|
||||
(
|
||||
false,
|
||||
"Exception in checking self-intersections"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.test_validity && ! bg::is_valid(buffered))
|
||||
@@ -385,25 +343,11 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
// self_ips NYI here
|
||||
}
|
||||
#elif defined(TEST_WITH_SVG)
|
||||
rescale_policy_type rescale_policy_output
|
||||
= bg::get_rescale_policy<rescale_policy_type>(envelope_output);
|
||||
buffer_mapper.map_self_ips(mapper, buffered, strategy, rescale_policy_output);
|
||||
#endif
|
||||
|
||||
// Check for self-intersections
|
||||
if (self_ip_count != NULL)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
if (bg::detail::overlay::has_self_intersections(buffered,
|
||||
strategy, rescale_policy_output, false))
|
||||
{
|
||||
count = count_self_ips(buffered, strategy, rescale_policy_output);
|
||||
}
|
||||
|
||||
*self_ip_count += count;
|
||||
if (count > 0)
|
||||
{
|
||||
std::cout << complete.str() << " " << count << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
@@ -423,12 +367,11 @@ void test_buffer(std::string const& caseid, Geometry const& geometry,
|
||||
SideStrategy const& side_strategy,
|
||||
PointStrategy const& point_strategy,
|
||||
double expected_area,
|
||||
ut_settings const& settings = ut_settings(),
|
||||
std::size_t* self_ip_count = NULL)
|
||||
ut_settings const& settings = ut_settings())
|
||||
{
|
||||
test_buffer<GeometryOut>(caseid, geometry,
|
||||
join_strategy, end_strategy, distance_strategy, side_strategy, point_strategy,
|
||||
-1, -1, expected_area, settings, self_ip_count);
|
||||
-1, -1, expected_area, settings);
|
||||
}
|
||||
|
||||
#ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
|
||||
@@ -482,7 +425,7 @@ void test_one(std::string const& caseid, std::string const& wkt,
|
||||
join_strategy, end_strategy,
|
||||
distance_strategy, side_strategy, circle_strategy,
|
||||
expected_count, expected_holes_count, expected_area,
|
||||
settings, NULL);
|
||||
settings);
|
||||
|
||||
#if !defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) && defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE)
|
||||
|
||||
@@ -500,7 +443,7 @@ void test_one(std::string const& caseid, std::string const& wkt,
|
||||
join_strategy, end_strategy,
|
||||
sym_distance_strategy, side_strategy, circle_strategy,
|
||||
expected_count, expected_holes_count, expected_area,
|
||||
settings, NULL);
|
||||
settings);
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -524,40 +467,6 @@ void test_one(std::string const& caseid, std::string const& wkt,
|
||||
distance_left, settings, distance_right);
|
||||
}
|
||||
|
||||
// Version (currently for the Aimes test) counting self-ip's instead of checking
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename GeometryOut,
|
||||
typename JoinStrategy,
|
||||
typename EndStrategy
|
||||
>
|
||||
void test_one_and_count_ips(std::string const& caseid, std::string const& wkt,
|
||||
JoinStrategy const& join_strategy, EndStrategy const& end_strategy,
|
||||
double expected_area,
|
||||
double distance_left,
|
||||
std::size_t& self_ip_count,
|
||||
ut_settings const& settings)
|
||||
{
|
||||
namespace bg = boost::geometry;
|
||||
Geometry g;
|
||||
bg::read_wkt(wkt, g);
|
||||
bg::correct(g);
|
||||
|
||||
bg::strategy::buffer::distance_asymmetric
|
||||
<
|
||||
typename bg::coordinate_type<Geometry>::type
|
||||
> distance_strategy(distance_left, distance_left);
|
||||
|
||||
bg::strategy::buffer::point_circle circle_strategy(88);
|
||||
bg::strategy::buffer::side_straight side_strategy;
|
||||
test_buffer<GeometryOut>(caseid, g,
|
||||
join_strategy, end_strategy,
|
||||
distance_strategy, side_strategy, circle_strategy,
|
||||
expected_area,
|
||||
settings, &self_ip_count);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
@@ -587,9 +496,7 @@ void test_with_custom_strategies(std::string const& caseid,
|
||||
(caseid, g,
|
||||
join_strategy, end_strategy,
|
||||
distance_strategy, side_strategy, point_strategy,
|
||||
expected_area, settings, NULL);
|
||||
expected_area, settings);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -87,8 +87,8 @@ bool verify(std::string const& caseid, MultiPolygon const& mp, MultiPolygon cons
|
||||
bool result = true;
|
||||
|
||||
// Area of buffer must be larger than of original polygon
|
||||
BOOST_AUTO(area_mp, bg::area(mp));
|
||||
BOOST_AUTO(area_buf, bg::area(buffer));
|
||||
double area_mp = bg::area(mp);
|
||||
double area_buf = bg::area(buffer);
|
||||
|
||||
if (area_buf < area_mp)
|
||||
{
|
||||
@@ -109,6 +109,16 @@ bool verify(std::string const& caseid, MultiPolygon const& mp, MultiPolygon cons
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
std::string message;
|
||||
if (! bg::is_valid(buffer, message))
|
||||
{
|
||||
std::cout << "Buffer is not valid: " << message << std::endl;
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool svg = settings.svg;
|
||||
bool wkt = settings.wkt;
|
||||
if (! result)
|
||||
@@ -204,7 +214,7 @@ bool test_buffer(MultiPolygon& result, int& index,
|
||||
bg::strategy::buffer::end_round end_strategy;
|
||||
bg::strategy::buffer::point_circle point_strategy;
|
||||
bg::strategy::buffer::side_straight side_strategy;
|
||||
bg::strategy::buffer::join_round join_round_strategy(100); // Compatible with unit tests
|
||||
bg::strategy::buffer::join_round join_round_strategy(32); // Compatible with MySQL
|
||||
bg::strategy::buffer::join_miter join_miter_strategy;
|
||||
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user