File: buffer_linestring_geo.cpp

package info (click to toggle)
boost1.90 1.90.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 593,120 kB
  • sloc: cpp: 4,190,908; xml: 196,648; python: 34,618; ansic: 23,145; asm: 5,468; sh: 3,774; makefile: 1,161; perl: 1,020; sql: 728; ruby: 676; yacc: 478; java: 77; lisp: 24; csh: 6
file content (185 lines) | stat: -rw-r--r-- 8,685 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Boost.Geometry
// Unit Test

// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include "test_buffer_geo.hpp"
#include "aimes_cases.hpp"

// Road with one segment from west to east
static std::string const simplex = "LINESTRING(10.3965628 63.4276786,10.3953134 63.4299634)";

// Road from west to east and from south to north
static std::string const road = "LINESTRING(10.3966569 63.4276957,10.3998059 63.4279182,10.4003964 63.4288424)";

// Road with first a sharp angle to the right, then a rectangular angle to the left, then two obtuse angles
static std::string const sharp = "LINESTRING(10.3939684 63.4255808, 10.3948159 63.4263661, 10.3947354 63.4255918, 10.3950209 63.4255588, 10.3960570 63.4255423, 10.3973017 63.4255570)";

// Linestring forwards and backwards (close to 180 corner)
static std::string const opposite = "LINESTRING(10.4000988 63.4283885,10.4029694 63.4280726,10.4006988 63.4283397)";

namespace
{
    constexpr int points_per_circle = 360;
}

template <typename Formula, bool Clockwise, typename PointType>
void test_linestring()
{
    using linestring = bg::model::linestring<PointType>;
    using polygon = bg::model::polygon<PointType, Clockwise>;

    // Because areas can change significantly when another formula is used,
    // use a high tolerance.
    ut_settings settings(0.5);

#if defined(BOOST_GEOMETRY_TEST_FAILURES)
    bool const thomas_skip = false;
#else
    // TODO: some cases are missing one or more turns
    bool const thomas_skip = std::is_same<Formula, bg::strategy::thomas>::value;
#endif

    bg::strategies::buffer::geographic<Formula> strategy;
    bg::strategy::buffer::geographic_side_straight<Formula> side;
    bg::strategy::buffer::geographic_join_miter<Formula> join_miter;
    bg::strategy::buffer::geographic_join_miter<Formula> join_miter25(2.5);
    bg::strategy::buffer::geographic_join_round<Formula> join_round(points_per_circle);
    bg::strategy::buffer::geographic_end_round<Formula> end_round(points_per_circle);
    bg::strategy::buffer::geographic_point_circle<Formula> circle(points_per_circle);
    bg::strategy::buffer::end_flat end_flat;

    test_one_geo<linestring, polygon>("simplex_5_8", simplex, strategy, side, circle, join_round, end_flat, 2622.0, 5.0, settings);
    test_one_geo<linestring, polygon>("road_5_flat", road, strategy, side, circle, join_round, end_flat, 2644.0, 5.0, settings);
    test_one_geo<linestring, polygon>("road_5_25_round", road, strategy, side, circle, join_round, end_round, 2016.0, 5.0, settings, 2.5);
    test_one_geo<linestring, polygon>("sharp_5_round", sharp, strategy, side, circle, join_round, end_round, 3090.0, 5.0, settings);
    test_one_geo<linestring, polygon>("sharp_5_miter", sharp, strategy, side, circle, join_miter, end_round, 3181.0, 5.0, settings);
    test_one_geo<linestring, polygon>("sharp_5_miter25", sharp, strategy, side, circle, join_miter25, end_round, 3121.0, 5.0, settings);

#if defined(BOOST_GEOMETRY_TEST_FAILURES)
    if (! BOOST_GEOMETRY_CONDITION(thomas_skip))
    {
        // Misses an intersection point when using thomas
        test_one_geo<linestring, polygon>("opposite", opposite, strategy, side, circle, join_round, end_round, 1658.0, 5.0, settings);
    }
#endif

    {
        auto specific = settings;
        specific.fraction_buffered_points_too_close = 0.3;
        test_one_geo<linestring, polygon>("opposite_miter", opposite, strategy, side, circle, join_miter, end_flat, 1705.0, 5.0, specific);
        test_one_geo<linestring, polygon>("opposite_miter25", opposite, strategy, side, circle, join_miter25, end_flat, 1642.0, 5.0, specific);
    }
}

template <typename Formula, bool Clockwise, typename PointType>
void test_linestring_aimes()
{
    using coordinate_type = bg::coordinate_type_t<PointType>;
    using linestring = bg::model::linestring<PointType>;
    using polygon = bg::model::polygon<PointType, Clockwise>;

    constexpr coordinate_type distance_meters = 25.0;

    // Because areas can change significantly when another formula is used,
    // use a high tolerance.
    ut_settings settings(0.5);
    settings.test_area = false;

    bg::strategies::buffer::geographic<Formula> strategy;
    bg::strategy::buffer::geographic_side_straight<Formula> side;
    bg::strategy::buffer::geographic_join_miter<Formula> join_miter;
    bg::strategy::buffer::geographic_join_miter<Formula> join_miter25(2.5);
    bg::strategy::buffer::geographic_join_round<Formula> join_round(points_per_circle);
    bg::strategy::buffer::geographic_end_round<Formula> end_round(points_per_circle);
    bg::strategy::buffer::geographic_point_circle<Formula> circle(points_per_circle);
    bg::strategy::buffer::end_flat end_flat;

    // There are cases where some estimations are not met.
    // If this needs to be changed, be sure to
    // inspect the result visually on SVG or CSV (QGis).

    // Cases which are curved and have some artefacts (same as for cartesian)
    std::set<int> const cases_with_artefacts{119, 142};
    // Cases which are curved such that the min area is smaller than expected.
    std::set<int> const curved_cases_min_area{86, 181};
    // Cases which are curved such that the max area is larger than expected.
    std::set<int> const curved_cases_max_area{5, 95, 119, 142};

#if defined(BOOST_GEOMETRY_TEST_FAILURES)
    std::set<int> const skip_cases_round_round{};
    std::set<int> const skip_cases_round_flat{};
    std::set<int> const skip_cases_miter_flat{};
#else
    // Cases where the algorithm is still failing.
    // It might differ in debug/release mode.
    // It might also depend on the compiler and on the operating system.
    std::set<int> const skip_cases_round_round{17, 22, 38, 67, 75, 109, 163, 181, 143, 196};
    std::set<int> const skip_cases_round_flat{17, 22, 38, 67, 75, 103, 109, 196};
    std::set<int> const skip_cases_miter_flat{17, 18, 22, 38, 67, 75, 103, 109, 196};
#endif

    for (auto const& enumerated : bg::util::enumerate(testcases_aimes))
    {
        auto const index = enumerated.index;
        auto const& test_case = enumerated.value;

        settings.multiplier_min_area
            = curved_cases_min_area.count(index) > 0 ? 0.85
            : ut_settings().multiplier_min_area;

        settings.multiplier_max_area
            = curved_cases_max_area.count(index) > 0 ? 1.2
            : ut_settings().multiplier_max_area;

        settings.fraction_buffered_points_too_close
            = cases_with_artefacts.count(index) > 0 ? 0.20
            : ut_settings().fraction_buffered_points_too_close;

        // With miter/flat, the artefacts are more pronounced and there can
        // be more points close than expected.
        auto settings_miter_flat = settings;
        settings_miter_flat.fraction_buffered_points_too_close *= 4.0;

        // With rounded cases, both rounded ends overap, adapt the expected area
        auto settings_round_round = settings;
        settings_round_round.multiplier_min_area = settings.multiplier_min_area;

        std::string const caseid = "aimes_" + std::to_string(index);
        if (skip_cases_round_round.count(index) == 0)
        {
            test_one_geo<linestring, polygon>(caseid + "_rr", test_case,
                strategy, side, circle, join_round, end_round, -1, distance_meters, settings_round_round);
        }
        if (skip_cases_round_flat.count(index) == 0)
        {
            test_one_geo<linestring, polygon>(caseid + "_rf", test_case,
                strategy, side, circle, join_round, end_flat, -1, distance_meters, settings);
        }
        if (skip_cases_miter_flat.count(index) == 0)
        {
            test_one_geo<linestring, polygon>(caseid + "_mf", test_case,
                strategy, side, circle, join_miter, end_flat, -1, distance_meters, settings_miter_flat);
        }
    }
}

int test_main(int, char* [])
{
    BoostGeometryWriteTestConfiguration();

    test_linestring<bg::strategy::andoyer, true, bg::model::point<default_test_type, 2, bg::cs::geographic<bg::degree> > >();
    test_linestring<bg::strategy::thomas, true, bg::model::point<default_test_type, 2, bg::cs::geographic<bg::degree> > >();

    test_linestring_aimes<bg::strategy::andoyer, true, bg::model::point<default_test_type, 2, bg::cs::geographic<bg::degree> > >();

#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
    test_linestring<bg::strategy::andoyer, true, bg::model::point<long double, 2, bg::cs::geographic<bg::degree> > >();
#endif

    return 0;
}