1
- use num_traits:: Float ;
1
+ use geo_types:: LineString ;
2
+ use num_traits:: Bounded ;
2
3
3
- use crate :: {
4
- algorithm:: { centroid:: Centroid , rotate:: Rotate , BoundingRect , CoordsIter } ,
5
- Area , ConvexHull , CoordFloat , GeoFloat , GeoNum , LinesIter , Polygon ,
6
- } ;
4
+ use crate :: { algorithm:: CoordsIter , ConvexHull , CoordFloat , GeoFloat , GeoNum , Polygon } ;
7
5
/// Return the minimum bounding rectangle(MBR) of geometry
8
6
/// reference: <https://en.wikipedia.org/wiki/Minimum_bounding_box>
9
7
/// minimum rotated rect is the rectangle that can enclose all points given
@@ -20,11 +18,11 @@ use crate::{
20
18
/// assert_eq!(
21
19
/// mbr.exterior(),
22
20
/// &LineString::from(vec![
23
- /// (1.7000000000000006 , 24.6 ),
24
- /// (1.4501458363715918, 30.446587428904767 ),
25
- /// (14.4 , 31.0 ),
26
- /// (14.649854163628408, 25.153412571095235 ),
27
- /// (1.7000000000000006 , 24.6 ),
21
+ /// (1.7000000000000004 , 24.600000000000005 ),
22
+ /// (14.649854163628412, 25.153412571095238 ),
23
+ /// (14.400000000000002 , 31.000000000000007 ),
24
+ /// (1.4501458363715916, 30.446587428904774 ),
25
+ /// (1.7000000000000004 , 24.600000000000005 ),
28
26
/// ])
29
27
/// );
30
28
/// ```
@@ -41,24 +39,74 @@ where
41
39
type Scalar = T ;
42
40
43
41
fn minimum_rotated_rect ( & self ) -> Option < Polygon < Self :: Scalar > > {
44
- let convex_poly = ConvexHull :: convex_hull ( self ) ;
45
- let mut min_area: T = Float :: max_value ( ) ;
46
- let mut min_angle: T = T :: zero ( ) ;
47
- let mut rect_poly: Option < Polygon < T > > = None ;
48
- let rotate_point = convex_poly. centroid ( ) ;
49
- for line in convex_poly. exterior ( ) . lines_iter ( ) {
50
- let ( ci, cii) = line. points ( ) ;
51
- let angle = ( cii. y ( ) - ci. y ( ) ) . atan2 ( cii. x ( ) - ci. x ( ) ) . to_degrees ( ) ;
52
- let rotated_poly = Rotate :: rotate_around_point ( & convex_poly, -angle, rotate_point?) ;
53
- let tmp_poly = rotated_poly. bounding_rect ( ) ?. to_polygon ( ) ;
54
- let area = tmp_poly. unsigned_area ( ) ;
42
+ let hull = ConvexHull :: convex_hull ( self ) ;
43
+
44
+ // We take unit vectors along and perpendicular to each edge, then use
45
+ // them to build a rotation matrix and apply it to the convex hull,
46
+ // keeping track of the best AABB found.
47
+ //
48
+ // See also the discussion at
49
+ // https://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points/22934
50
+ let mut min_area = <T as Bounded >:: max_value ( ) ;
51
+ let mut best_edge = None ;
52
+ let ( mut best_min_x, mut best_max_x) = ( T :: zero ( ) , T :: zero ( ) ) ;
53
+ let ( mut best_min_y, mut best_max_y) = ( T :: zero ( ) , T :: zero ( ) ) ;
54
+ for edge in hull. exterior ( ) . lines ( ) {
55
+ let ( dx, dy) = edge. delta ( ) . x_y ( ) ;
56
+ let norm = dx. hypot ( dy) ;
57
+ if norm. is_zero ( ) {
58
+ continue ;
59
+ }
60
+ let edge = ( dx / norm, dy / norm) ;
61
+
62
+ let ( mut min_x, mut max_x) = ( <T as Bounded >:: max_value ( ) , <T as Bounded >:: min_value ( ) ) ;
63
+ let ( mut min_y, mut max_y) = ( <T as Bounded >:: max_value ( ) , <T as Bounded >:: min_value ( ) ) ;
64
+ for point in hull. exterior ( ) . points ( ) {
65
+ let x = point. y ( ) * edge. 1 + point. x ( ) * edge. 0 ;
66
+ let y = point. y ( ) * edge. 0 - point. x ( ) * edge. 1 ;
67
+
68
+ min_x = min_x. min ( x) ;
69
+ max_x = max_x. max ( x) ;
70
+ min_y = min_y. min ( y) ;
71
+ max_y = max_y. max ( y) ;
72
+ }
73
+
74
+ let area = ( max_y - min_y) * ( max_x - min_x) ;
55
75
if area < min_area {
56
76
min_area = area;
57
- min_angle = angle;
58
- rect_poly = Some ( tmp_poly) ;
77
+ best_edge = Some ( edge) ;
78
+ best_min_x = min_x;
79
+ best_max_x = max_x;
80
+ best_min_y = min_y;
81
+ best_max_y = max_y;
59
82
}
60
83
}
61
- Some ( rect_poly?. rotate_around_point ( min_angle, rotate_point?) )
84
+
85
+ if let Some ( e) = best_edge {
86
+ let p1 = (
87
+ best_min_x * e. 0 - best_min_y * e. 1 ,
88
+ best_min_x * e. 1 + best_min_y * e. 0 ,
89
+ ) ;
90
+ let p2 = (
91
+ best_max_x * e. 0 - best_min_y * e. 1 ,
92
+ best_max_x * e. 1 + best_min_y * e. 0 ,
93
+ ) ;
94
+ let p3 = (
95
+ best_max_x * e. 0 - best_max_y * e. 1 ,
96
+ best_max_x * e. 1 + best_max_y * e. 0 ,
97
+ ) ;
98
+ let p4 = (
99
+ best_min_x * e. 0 - best_max_y * e. 1 ,
100
+ best_min_x * e. 1 + best_max_y * e. 0 ,
101
+ ) ;
102
+ let rectangle = Polygon :: new (
103
+ LineString ( vec ! [ p1. into( ) , p2. into( ) , p3. into( ) , p4. into( ) , p1. into( ) ] ) ,
104
+ vec ! [ ] ,
105
+ ) ;
106
+ Some ( Polygon :: from ( rectangle) )
107
+ } else {
108
+ None
109
+ }
62
110
}
63
111
}
64
112
@@ -75,11 +123,11 @@ mod test {
75
123
assert_eq ! (
76
124
mbr. exterior( ) ,
77
125
& LineString :: from( vec![
78
- ( 1.7000000000000006 , 24.6 ) ,
79
- ( 1.4501458363715918 , 30.446587428904767 ) ,
80
- ( 14.4 , 31.0 ) ,
81
- ( 14.649854163628408 , 25.153412571095235 ) ,
82
- ( 1.7000000000000006 , 24.6 ) ,
126
+ ( 1.7000000000000004 , 24.600000000000005 ) ,
127
+ ( 14.649854163628412 , 25.153412571095238 ) ,
128
+ ( 14.400000000000002 , 31.000000000000007 ) ,
129
+ ( 1.4501458363715916 , 30.446587428904774 ) ,
130
+ ( 1.7000000000000004 , 24.600000000000005 ) ,
83
131
] )
84
132
) ;
85
133
}
@@ -90,11 +138,11 @@ mod test {
90
138
assert_eq ! (
91
139
mbr. exterior( ) ,
92
140
& LineString :: from( vec![
93
- ( 1.7000000000000006 , 24.6 ) ,
94
- ( 1.4501458363715918 , 30.446587428904767 ) ,
95
- ( 14.4 , 31.0 ) ,
96
- ( 14.649854163628408 , 25.153412571095235 ) ,
97
- ( 1.7000000000000006 , 24.6 ) ,
141
+ ( 1.7000000000000004 , 24.600000000000005 ) ,
142
+ ( 14.649854163628412 , 25.153412571095238 ) ,
143
+ ( 14.400000000000002 , 31.000000000000007 ) ,
144
+ ( 1.4501458363715916 , 30.446587428904774 ) ,
145
+ ( 1.7000000000000004 , 24.600000000000005 ) ,
98
146
] )
99
147
) ;
100
148
}
0 commit comments