Skip to content

Commit 98bfbb9

Browse files
committed
fix: enhance dissolve and add unit test
1 parent f33dc4b commit 98bfbb9

File tree

3 files changed

+184
-187
lines changed

3 files changed

+184
-187
lines changed

extensions/test/algorithms/dissolve.cpp

Lines changed: 42 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Boost.Geometry (aka GGL, Generic Geometry Library)
22
// Unit Test
33

4-
// Copyright (c) 2010-2017 Barend Gehrels, Amsterdam, the Netherlands.
4+
// Copyright (c) 2010-2025 Barend Gehrels, Amsterdam, the Netherlands.
55

66
// This file was modified by Oracle on 2022.
77
// Modifications copyright (c) 2022, Oracle and/or its affiliates.
@@ -11,8 +11,11 @@
1111
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
1212
// http://www.boost.org/LICENSE_1_0.txt)
1313

14-
//#define BOOST_GEOMETRY_DEBUG_ENRICH
15-
//#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
14+
15+
#if defined(TEST_WITH_GEOJSON)
16+
#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
17+
#define BOOST_GEOMETRY_DEBUG_IDENTIFIER
18+
#endif
1619

1720
#include <geometry_test_common.hpp>
1821

@@ -33,18 +36,10 @@
3336
#include <boost/geometry/strategies/strategies.hpp>
3437

3538
#include <boost/geometry/io/wkt/wkt.hpp>
36-
#include <boost/geometry/multi/io/wkt/wkt.hpp>
37-
38-
#include <boost/geometry/multi/algorithms/for_each.hpp>
3939

40-
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
41-
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
42-
43-
44-
#if defined(TEST_WITH_SVG)
45-
# include <boost/geometry/io/svg/svg_mapper.hpp>
46-
# include <boost/geometry/io/svg/write_svg_multi.hpp>
47-
# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
40+
#if defined(TEST_WITH_GEOJSON)
41+
#include <boost/geometry/extensions/gis/io/geojson/geojson_writer.hpp>
42+
#include "dissolve_geojson_visitor.hpp"
4843
#endif
4944

5045

@@ -176,135 +171,6 @@ namespace
176171
std::string const dissolve_ticket10713 = "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))";
177172
}
178173

179-
180-
#if defined(TEST_WITH_SVG)
181-
template <typename Mapper>
182-
struct map_visitor
183-
{
184-
map_visitor(Mapper& mapper)
185-
: m_mapper(mapper)
186-
{}
187-
188-
template <typename Turns>
189-
void visit_turns(int phase, Turns const& turns)
190-
{
191-
typedef typename boost::range_value<Turns>::type turn_type;
192-
std::size_t index = 0;
193-
for (turn_type const& turn : turns)
194-
{
195-
switch (phase)
196-
{
197-
case 2 : // after self_turns and enrich
198-
if (turn.discarded)
199-
{
200-
m_mapper.map(turn.point, "fill:rgb(255,128,0);", 2);
201-
}
202-
else
203-
{
204-
m_mapper.map(turn.point, "fill:rgb(255,128,0);"
205-
"stroke:rgb(0,0,0);stroke-width:1", 4);
206-
}
207-
break;
208-
case 3 : // after enrich/traverse
209-
label_turn(index, turn, -2, "fill:rgb(0,0,128);");
210-
break;
211-
}
212-
index++;
213-
}
214-
}
215-
216-
template <typename Clusters, typename Turns>
217-
void visit_clusters(Clusters const& , Turns const& ) {}
218-
219-
template <typename Turns, typename Turn, typename Operation>
220-
void visit_traverse(Turns const& , Turn const& , Operation const& , char const*)
221-
{}
222-
223-
template <typename Turns, typename Turn, typename Operation>
224-
void visit_traverse_reject(Turns const& , Turn const& , Operation const& ,
225-
bg::detail::overlay::traverse_error_type )
226-
{}
227-
228-
template <typename Rings>
229-
void visit_generated_rings(Rings const& rings)
230-
{
231-
typedef typename boost::range_value<Rings>::type ring_type;
232-
for (ring_type const& ring : rings)
233-
{
234-
double const area = bg::area(ring);
235-
std::string const color = area < 0 ? "rgb(255,0,0)" : "rgb(0,0,255)";
236-
std::string const style = "stroke:" + color
237-
+ ";stroke-width:0.1;fill-opacity:0.1;fill:" + color;
238-
m_mapper.map(ring, style);
239-
}
240-
}
241-
242-
243-
private :
244-
245-
template <typename Turn>
246-
bool label_operation(Turn const& turn, std::size_t index, std::ostream& os)
247-
{
248-
os << bg::operation_char(turn.operations[index].operation);
249-
bool result = false;
250-
if (! turn.discarded)
251-
{
252-
os << "->" << turn.operations[index].enriched.get_next_turn_index();
253-
result = true;
254-
}
255-
if (turn.operations[index].enriched.prefer_start)
256-
{
257-
os << "$";
258-
}
259-
if (! turn.operations[index].enriched.startable)
260-
{
261-
os << "@";
262-
}
263-
264-
return result;
265-
}
266-
267-
template <typename Turn>
268-
void label_turn(std::size_t index, Turn const& turn, int y_offset, std::string const& color)
269-
{
270-
std::ostringstream out;
271-
out << index;
272-
if (turn.cluster_id != -1)
273-
{
274-
out << " c=" << turn.cluster_id << " ";
275-
}
276-
bool lab1 = label_operation(turn, 0, out);
277-
out << " / ";
278-
bool lab2 = label_operation(turn, 1, out);
279-
if (turn.discarded)
280-
{
281-
out << "!";
282-
}
283-
284-
std::string font8 = "font-family:Arial;font-size:6px";
285-
std::string font6 = "font-family:Arial;font-size:4px";
286-
std::string style = color + ";" + font8;
287-
if (turn.discarded)
288-
{
289-
style = "fill:rgb(92,92,92);" + font6;
290-
}
291-
else if (turn.cluster_id != -1)
292-
{
293-
style = color + ";" + font8;
294-
}
295-
else if (! lab1 || ! lab2)
296-
{
297-
style = color + ";" + font6;
298-
}
299-
300-
m_mapper.text(turn.point, out.str(), style, 5, y_offset, 6);
301-
}
302-
303-
Mapper& m_mapper;
304-
};
305-
306-
#endif
307-
308174
//! Unittest settings
309175
struct ut_settings
310176
{
@@ -334,6 +200,17 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry,
334200
std::size_t expected_point_count,
335201
ut_settings const& settings)
336202
{
203+
#if defined(TEST_WITH_GEOJSON)
204+
std::ostringstream filename;
205+
// For QGis, it is usually convenient to always write to the same geojson file.
206+
filename << "/tmp/"
207+
// << caseid << "_"
208+
<< "dissolve.geojson";
209+
std::ofstream geojson_file(filename.str().c_str());
210+
211+
boost::geometry::geojson_writer writer(geojson_file);
212+
#endif
213+
337214
using coordinate_type = typename bg::coordinate_type<Geometry>::type;
338215

339216
static const bool is_line = bg::geometry_id<GeometryOut>::type::value == 2;
@@ -350,59 +227,21 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry,
350227
Geometry, Geometry
351228
>::type;
352229

353-
using rescale_policy_type = typename bg::rescale_policy_type
354-
<
355-
typename bg::point_type<Geometry>::type
356-
>::type;
357-
358-
rescale_policy_type robust_policy
359-
= bg::get_rescale_policy<rescale_policy_type>(geometry);
360-
361-
// This will optionally also create SVG with turn-debug information
362230
strategy_type strategy;
363231

364-
365-
#if ! defined(TEST_WITH_SVG)
366-
bg::detail::overlay::overlay_null_visitor visitor;
232+
#if defined(TEST_WITH_GEOJSON)
233+
geojson_visitor visitor(writer);
367234
#else
368-
std::ostringstream filename;
369-
filename << "dissolve_" << caseid << "_"
370-
<< string_from_type<coordinate_type>::name()
371-
<< ".svg";
372-
373-
std::ofstream svg(filename.str().c_str());
374-
375-
using mapper_type = bg::svg_mapper
376-
<
377-
typename bg::point_type<Geometry>::type
378-
>;
379-
380-
mapper_type mapper(svg, 500, 500);
381-
mapper.add(geometry);
382-
383-
mapper.map(geometry, "fill-opacity:0.5;fill:rgb(153,204,0);"
384-
"stroke:rgb(153,204,0);stroke-width:2;fill-rule:nonzero;");
385-
386-
map_visitor<mapper_type> visitor(mapper);
387-
#endif
235+
bg::detail::overlay::overlay_null_visitor visitor;
236+
#endif
388237

389238
bg::dispatch::dissolve
390239
<
391240
Geometry,
392241
GeometryOut,
393242
false
394-
>::apply(geometry, robust_policy, std::back_inserter(dissolved1),
243+
>::apply(geometry, std::back_inserter(dissolved1),
395244
strategy, visitor);
396-
397-
#if defined(TEST_WITH_SVG)
398-
for (GeometryOut& dissolved : dissolved1)
399-
{
400-
mapper.map(dissolved, "fill-opacity:0.1;fill:rgb(255,0,0);"
401-
"stroke-opacity:0.4;stroke:rgb(255,0,255);stroke-width:3;"
402-
"fill-rule:nonzero;");
403-
}
404-
#endif
405-
406245
}
407246

408247
if (settings.test_validity)
@@ -482,8 +321,19 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry,
482321
BOOST_CHECK_MESSAGE(wkt1 == wkt3, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt3);
483322
}
484323
}
485-
}
486324

325+
#if defined(TEST_WITH_GEOJSON)
326+
writer.feature(geometry);
327+
writer.add_property("type", "input");
328+
329+
for (const auto& polygon : dissolved3)
330+
{
331+
writer.feature(polygon);
332+
writer.add_property("type", "dissolved");
333+
}
334+
#endif
335+
336+
}
487337

488338
template <typename Geometry, typename GeometryOut>
489339
void test_one(std::string caseid, std::string const& wkt,
@@ -505,6 +355,8 @@ void test_one(std::string caseid, std::string const& wkt,
505355
expected_clip_count, expected_hole_count, expected_point_count,
506356
settings);
507357

358+
#ifdef BOOST_GEOMETRY_TEST_REVERSE
359+
508360
// Verify if reversed version is indeed identical (it should, because each
509361
// ring is now corrected within dissolve itself
510362
bg::reverse(geometry);
@@ -514,6 +366,7 @@ void test_one(std::string caseid, std::string const& wkt,
514366
expected_area,
515367
expected_clip_count, expected_hole_count, expected_point_count,
516368
settings);
369+
#endif
517370

518371
#ifdef BOOST_GEOMETRY_TEST_MULTI_PERMUTATIONS
519372
// Test different combinations of a multi-polygon
@@ -652,7 +505,9 @@ void test_all(ut_settings const& settings_for_sensitive_cases)
652505

653506
int test_main(int, char* [])
654507
{
508+
#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
655509
test_all<bg::model::d2::point_xy<float>, true >(ut_settings(0.01));
510+
#endif
656511
test_all<bg::model::d2::point_xy<double>, true >(ut_settings());
657512
// Counter clockwise input does not work correctly in all cases, it is
658513
// partly a problem of the test itself

0 commit comments

Comments
 (0)