-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/uniform point distribution #618
base: develop
Are you sure you want to change the base?
Changes from 11 commits
0704d7e
0c46155
57632e1
a6bfe95
d906785
b9a3660
1156767
e981c46
361861c
f247803
f24d604
c899eb6
ec6adac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ project boost-geometry-examples-extensions | |
; | ||
|
||
build-project gis ; | ||
build-project random ; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Boost.Geometry (aka GGL, Generic Geometry Library) | ||
# | ||
# Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. | ||
# Copyright (c) 2008-2012 Bruno Lalande, Paris, France. | ||
# Copyright (c) 2009-2012 Mateusz Loskot, London, UK. | ||
|
||
# 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) | ||
|
||
|
||
project boost-geometry-example-extensions-random | ||
: # requirements | ||
; | ||
|
||
exe random_example : random_example.cpp ; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Boost.Geometry (aka GGL, Generic Geometry Library) | ||
|
||
// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. | ||
|
||
// 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) | ||
|
||
// Random Example - showing random sampling in various geometries | ||
|
||
#include <fstream> | ||
#include <random> | ||
#include <cmath> | ||
|
||
#include <boost/geometry.hpp> | ||
#include <boost/geometry/extensions/random/uniform_point_distribution.hpp> | ||
|
||
const int samples = 100; | ||
|
||
template<typename Distribution, typename Generator, typename Mapper> | ||
void draw_samples(Distribution& d, Generator& g, Mapper& m) { | ||
for (int i = 0 ; i < samples ; ++i) | ||
{ | ||
m.map(d(g), "fill-opacity:1;fill:rgb(255,0,0);stroke:rgb(255,0,0);stroke-width:1", 1); | ||
} | ||
} | ||
|
||
int main() | ||
{ | ||
namespace bg = boost::geometry; | ||
typedef bg::model::point<double, 2, bg::cs::cartesian> point; | ||
typedef bg::model::segment<point> segment; | ||
typedef bg::model::box<point> box; | ||
typedef bg::model::multi_point<point> multi_point; | ||
typedef bg::model::polygon<point> polygon; | ||
|
||
std::default_random_engine generator(1); | ||
polygon poly; | ||
boost::geometry::read_wkt("POLYGON((16 21,17.1226 17.5451,20.7553 17.5451,17.8164 15.4098,18.9389 11.9549,16 14.0902,13.0611 11.9549,14.1836 15.4098,11.2447 17.5451,14.8774 17.5451,16 21))", poly); | ||
box b(point(0, 0), point(10, 10)); | ||
segment s(point(11, 0), point(21, 10)); | ||
multi_point mp; | ||
for (double y = 11.0 ; y <= 21.0 ; y += 0.5) | ||
{ | ||
for(double x = 0.0 ; x <= 10.0 ; x += 0.5) | ||
{ | ||
bg::append(mp, point(x, y)); | ||
} | ||
} | ||
bg::random::uniform_point_distribution<box> box_dist(b); | ||
bg::random::uniform_point_distribution<multi_point> mp_dist(mp); | ||
bg::random::uniform_point_distribution<segment> seg_dist(s); | ||
bg::random::uniform_point_distribution<polygon> poly_dist(poly); | ||
std::ofstream svg("random.svg"); | ||
bg::svg_mapper<point> mapper(svg, 720, 720); | ||
mapper.add(poly); | ||
mapper.add(b); | ||
mapper.add(mp); | ||
mapper.add(s); | ||
mapper.add(box(point(0, 22), point(22, 35))); | ||
mapper.map(b, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2"); | ||
mapper.map(s, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2"); | ||
mapper.map(mp, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2", 2); | ||
mapper.map(poly, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2"); | ||
draw_samples(box_dist, generator, mapper); | ||
draw_samples(mp_dist, generator, mapper); | ||
draw_samples(seg_dist, generator, mapper); | ||
draw_samples(poly_dist, generator, mapper); | ||
|
||
typedef bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree>> point_spherical; | ||
typedef bg::model::box<point_spherical> box_spherical; | ||
box_spherical sb(point_spherical(0, 0), point_spherical(90, 90)); | ||
bg::random::uniform_point_distribution<box_spherical> sb_dist(sb); | ||
for (int i = 0 ; i < 10*samples ; ++i) | ||
{ | ||
point_spherical p = sb_dist(generator); | ||
double x = bg::get<0>(p) / 90 * 22; | ||
double y = 32 - bg::get<1>(p) / 90 * 10; | ||
mapper.map(point(x,y), "fill-opacity:1;fill:rgb(255,0,0);stroke:rgb(255,0,0);stroke-width:1", 1); | ||
} | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Boost.Geometry (aka GGL, Generic Geometry Library) | ||
# | ||
# 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) | ||
|
||
test-suite boost-geometry-extensions-random | ||
: | ||
[ run random.cpp ] | ||
; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Boost.Geometry (aka GGL, Generic Geometry Library) | ||
// Unit Test | ||
|
||
// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can ack gsoc (see other PR) |
||
|
||
// 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 <random> | ||
#include <vector> | ||
#include <sstream> | ||
|
||
#include <geometry_test_common.hpp> | ||
|
||
#include <boost/geometry.hpp> | ||
#include <boost/geometry/extensions/random/uniform_point_distribution.hpp> | ||
|
||
typedef bg::model::point<double, 2, bg::cs::cartesian> point2d_cart; | ||
typedef bg::model::point<double, 3, bg::cs::cartesian> point3d_cart; | ||
typedef bg::model::point<double, 2, bg::cs::geographic<bg::degree>> point2d_geog; | ||
|
||
void test_geographic() | ||
{ | ||
//We check whether the generated points lie roughly along | ||
//the great circle segment (<2 km distance on a sphere with | ||
//the radius of earth) and are distributed uniformly with respect | ||
//to great circle arc length. | ||
typedef bg::model::linestring<point2d_geog> linestring; | ||
linestring ls {{ 0.0, 0.0 }, { 45.0, 45.0 }, { 60.0, 60.0 }}; | ||
bg::random::uniform_point_distribution<linestring> l_dist(ls); | ||
std::mt19937 generator(0); | ||
int sample_count = 2000; | ||
int count_below_45 = 0; | ||
for (int i = 0 ; i < sample_count ; ++i) | ||
{ | ||
point2d_geog sample = l_dist(generator); | ||
BOOST_CHECK( bg::distance(sample, ls) < 2000 ); | ||
if(bg::get<0>(sample) < 45.0) count_below_45++; | ||
} | ||
double length_ratio = bg::distance(ls[0], ls[1]) / | ||
( bg::distance(ls[0], ls[1]) + bg::distance(ls[1], ls[2]) ); | ||
double sample_ratio = ((double) count_below_45) / sample_count; | ||
bool in_range = sample_ratio * 0.95 < length_ratio | ||
&& sample_ratio * 1.05 > length_ratio; | ||
BOOST_CHECK( in_range ); | ||
|
||
//We check whether the generated points lie in the spherical box | ||
//(which is actually a triangle in this case) and whether the latitude | ||
//is distributed as expected for uniform spherical distribution, using | ||
//known area ratios of spherical caps. | ||
typedef bg::model::box<point2d_geog> box; | ||
box b {{ 0.0, 0.0 }, { 90.0, 90.0 }}; | ||
bg::random::uniform_point_distribution<box> b_dist(b); | ||
int under_60 = 0; | ||
for (int i = 0 ; i < sample_count ; ++i) | ||
{ | ||
point2d_geog sample = b_dist(generator); | ||
BOOST_CHECK( bg::within(sample, b) ); | ||
if(bg::get<1>(sample) < 60.0) ++under_60; | ||
} | ||
BOOST_CHECK_GT(under_60, 0.5 * 0.95 * sample_count); | ||
BOOST_CHECK_LT(under_60, 0.5 * 1.05 * sample_count); | ||
} | ||
|
||
void test_polygon() | ||
{ | ||
//This test will test uniform sampling in polygon, which also checks | ||
//uniform sampling in boxes. We check whether two equal distributions | ||
//(copied using operator<< and operator>>) generate the same sequence | ||
//of points and whether those points are uniformly distributed with | ||
//respect to cartesian area. | ||
typedef bg::model::polygon<point2d_cart> polygon; | ||
polygon poly; | ||
bg::read_wkt( | ||
"POLYGON((16 21,17.1226 17.5451,20.7553 17.5451, 17.8164 15.4098,18.9389 11.9549,16 14.0902,13.0611 11.9549, 14.1836 15.4098,11.2447 17.5451,14.8774 17.5451,16 21))", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please reduce length |
||
poly); | ||
bg::random::uniform_point_distribution<polygon> poly_dist(poly); | ||
bg::random::uniform_point_distribution<polygon> poly_dist2; | ||
BOOST_CHECK( !(poly_dist == poly_dist2) ); | ||
std::stringstream ss; | ||
ss << poly_dist; | ||
ss >> poly_dist2; | ||
BOOST_CHECK( poly_dist == poly_dist2 ); | ||
std::mt19937 generator(0), generator2(0); | ||
for (int i = 0 ; i < 100 ; ++i) | ||
{ | ||
point2d_cart sample1 = poly_dist(generator); | ||
BOOST_CHECK( bg::equals(sample1, poly_dist2(generator2)) ); | ||
BOOST_CHECK( bg::within(sample1, poly) ); | ||
} | ||
std::vector<point2d_cart> randoms; | ||
const int uniformity_test_samples = 2000; | ||
for (int i = 0 ; i < uniformity_test_samples ; ++i) | ||
{ | ||
randoms.push_back(poly_dist(generator)); | ||
} | ||
typedef bg::model::box<point2d_cart> box; | ||
box env, lhalf; | ||
bg::envelope(poly, env); | ||
bg::set<bg::min_corner, 0>(lhalf, bg::get<bg::min_corner, 0>(env)); | ||
bg::set<bg::min_corner, 1>(lhalf, bg::get<bg::min_corner, 1>(env)); | ||
bg::set<bg::max_corner, 0>(lhalf, bg::get<bg::max_corner, 0>(env)); | ||
bg::set<bg::max_corner, 1>(lhalf, | ||
(bg::get<bg::max_corner, 1>(env) + bg::get<bg::min_corner, 1>(env)) / 2); | ||
std::vector<polygon> lower; | ||
bg::intersection(lhalf, poly, lower); | ||
double area_ratio = bg::area(lower[0])/bg::area(poly); | ||
int in_lower = 0; | ||
for (int i = 0 ; i < uniformity_test_samples ; ++i) | ||
{ | ||
if(bg::within(randoms[i], lhalf)) | ||
++in_lower; | ||
} | ||
double sample_ratio = ((double) in_lower ) / uniformity_test_samples; | ||
BOOST_CHECK_GT( sample_ratio * 1.05, area_ratio ); | ||
BOOST_CHECK_LT( sample_ratio * 0.95, area_ratio ); | ||
} | ||
|
||
void test_multipoint() | ||
{ | ||
typedef bg::model::multi_point<point3d_cart> multipoint; | ||
multipoint mp {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}}; | ||
int first = 0; | ||
bg::random::uniform_point_distribution<multipoint> mp_dist(mp); | ||
std::mt19937 generator(0); | ||
int sample_count = 1000; | ||
for (int i = 0 ; i < sample_count ; ++i) | ||
{ | ||
point3d_cart sample = mp_dist(generator); | ||
BOOST_CHECK( bg::within(sample, mp) ); | ||
if(bg::equals(sample, mp[0])) ++first; | ||
} | ||
BOOST_CHECK_GT(first * 1.05, sample_count / 3); | ||
BOOST_CHECK_LT(first * 0.95, sample_count / 3); | ||
} | ||
|
||
int test_main(int, char* []) | ||
{ | ||
test_polygon(); | ||
test_geographic(); | ||
test_multipoint(); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Boost.Geometry (aka GGL, Generic Geometry Library) | ||
|
||
// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. | ||
|
||
// 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) | ||
|
||
#ifndef BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_FAN_HPP | ||
#define BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_FAN_HPP | ||
|
||
#include <random> | ||
#include <vector> | ||
#include <cstdlib> | ||
#include <cmath> | ||
|
||
#include <boost/geometry/algorithms/equals.hpp> | ||
#include <boost/geometry/algorithms/transform.hpp> | ||
#include <boost/geometry/algorithms/within.hpp> | ||
|
||
namespace boost { namespace geometry | ||
{ | ||
|
||
namespace strategy { namespace uniform_point_distribution { | ||
|
||
// The following strategy is suitable for convex rings and polygons with | ||
// non-empty interior. | ||
template | ||
< | ||
typename Point, | ||
typename DomainGeometry, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is either a convex ring or convex polygon (they are actually the same but the can have different types) |
||
typename TriangleStrategy, | ||
typename SideStrategy //Actually, we need a triangle area strategy here. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to use already existing AreaStrategy here? Can this agnostic strategy (or algorithm) be used in noncartesian CS? Side strategy would not allow to calculate areas in general case as you noticed. |
||
> | ||
struct uniform_convex_fan | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this really a strategy (i.e. coordinate type specific) or an algorithm? To my point of view agnostic strategies are just algorithms |
||
{ | ||
private: | ||
std::vector<double> accumulated_areas; | ||
// It is hard to see a reason not to use double here. If a triangles | ||
// relative size is smaller than doubles epsilon, it is too unlikely to | ||
// realistically occur in a random sample anyway. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type should probably be taken from area strategy if that's what's being calculated here (not side) . |
||
public: | ||
uniform_convex_fan(DomainGeometry const& g) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. any clue for choosing that name? As far as I know, in geometry, fan is a collection of cones and thus fan makes no sense. Maybe I am missing some notation or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name was chosen based on the fact that this class implicitly uses the fan triangulation of convex polygons for sampling. Reconsidering that choice, the fact that it uses the fan triangulation may be an implementation detail and alluding to it may be unnecessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, so this is probably not the right name. The name should declare what this class is doing, e.g. |
||
{ | ||
accumulated_areas.push_back(0); | ||
for (int i = 2 ; i < g.size() ; ++i) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We put |
||
accumulated_areas.push_back( | ||
accumulated_areas.back() + | ||
std::abs(SideStrategy::template side_value<double, double>( | ||
*g.begin(), | ||
*(g.begin() + i - 1), | ||
*(g.begin() + i)))); | ||
} | ||
} | ||
bool equals(DomainGeometry const& l_domain, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not see why do you need this function? BG has functions for checking equality of geometries. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also you check all the possible linestrings of the convex polygons from a fixed point. What if the geometries are equal and they start at different vertices i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The notion of equality as defined by the C++ named requirement "RandomNumberDistribution" on which I based my design of random point distributions considers two distribution instances A, B equal if their parameters are equal and they will produce the same output given the same generators, i.e. from A == B and g1 == g2 it needs to follow that A(g1) == A(g2). Because of the way I generate samples for convex rings, two distributions will only produce the same output if the convex rings start from the same point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I see, I remember also our discussions on this topic. Please add a comment or document this for future reference |
||
DomainGeometry const& r_domain, | ||
uniform_convex_fan const& r_strategy) const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this strategy is unused There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have that parameter because the rhs strategy is used in equal-comparison for other strategies and so the code in uniform_point_distribution passes it. Would it be ok, if I just removed the name (r_strategy)? Or is their a way around the necessity for having all strategies take all values that other strategies could need for comparison? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think removing the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alternatively you can use |
||
{ | ||
if( l_domain.size() != r_domain.size() ) return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, I know it looks ugly. I personally would put Btw, @barendgehrels what do you think about allowing single-line returns without brackets? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for one line returns. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is |
||
for (int i = 0; i < l_domain.size(); ++i) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And then here |
||
if( !boost::geometry::equals(*(l_domain.begin() + i), | ||
*(r_domain.begin() + i))) | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So like that, though in this case it would make sense to have brakcets because it's hard to see the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, the |
||
} | ||
return true; | ||
} | ||
template<typename Gen> | ||
Point apply(Gen& g, DomainGeometry const& d) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is more clear to name it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also please be consistent; above |
||
{ | ||
std::uniform_real_distribution<double> dist(0, 1); | ||
double r = dist(g) * accumulated_areas.back(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot find it in guidelines but bg uses one initialization per variable as
@barendgehrels right? |
||
s = dist(g); | ||
std::size_t i = std::distance( | ||
accumulated_areas.begin(), | ||
std::lower_bound(accumulated_areas.begin(), | ||
accumulated_areas.end(), | ||
r)); | ||
return TriangleStrategy::template map | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why |
||
< | ||
double | ||
>(* d.begin(), | ||
*(d.begin() + i), | ||
*(d.begin() + i + 1), | ||
( r - accumulated_areas[ i - 1 ]) / | ||
( accumulated_areas[ i ] - accumulated_areas[ i - 1 ] ), | ||
s); | ||
} | ||
void reset(DomainGeometry const&) {}; | ||
}; | ||
|
||
}} // namespace strategy::uniform_point_distribution | ||
|
||
}} // namespace boost::geometry | ||
|
||
#endif // BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_FAN_HPP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please reduce line's length
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g.: