Skip to content

Commit d964441

Browse files
authored
Merge pull request #13 from osrd-project/various_improvements
Various improvements
2 parents 0e9ff05 + cc91dd1 commit d964441

File tree

3 files changed

+93
-83
lines changed

3 files changed

+93
-83
lines changed

schema/lrs.fbs

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ table Connection {
6262
endpoint:Endpoint = null;
6363
}
6464

65-
/// A traversal is a path in a network.
65+
/// A traversal is a path in a network.
6666
/// Traversals may be used to model roads, railway tracks, railway lines or trips.
6767
/// Traversals are defined as a sequence of segment and direction pairs.
6868
enum Direction : byte { Increasing = 1, Decreasing = 2 }
@@ -124,7 +124,7 @@ table LinearReferencingMethod {
124124
/// And that LRM is the reference for the two other traversals corresponding to each direction
125125
used_on:[TraversalRef];
126126
anchor_indices:[uint64] (required);
127-
distances:[uint64] (required);
127+
distances:[double] (required);
128128
/// The unit used to measure the distance between anchors
129129
distance_unit:DistanceUnit = Meters;
130130
/// The unit used to express measures relative to anchors (12+230)

src/curves.rs

+85-75
Original file line numberDiff line numberDiff line change
@@ -9,94 +9,89 @@ use geo::prelude::*;
99
use geo::{coord, Line, LineString, Point, Rect};
1010
use thiserror::Error;
1111

12-
/// A curve is the fundamental building block for an LRM
13-
/// It provides basic primitives to locate/project points on it
14-
/// A curve can be part of a larger curve (e.g. for optimisation purpurses and have better bounding boxes)
15-
/// The curve can be implemented
12+
/// A `Curve` is the fundamental building block for an LRM.
13+
/// It provides basic primitives to locate/project points on it.
14+
/// A `Curve` can be part of a larger `Curve` (e.g. for optimisation purposes and to have better bounding boxes).
15+
/// The `Curve` can be implemented.
1616
pub trait Curve {
17-
/// Project the point to the closest position on the curve
18-
/// Will fail if the curve is invalid (e.g. no points on it)
19-
/// or if the point is to far away
20-
/// If the curve is a piece of a larger curve (start_offset > 0)
21-
/// then the distance_along_curve if from the whole curve, not just the current piece
17+
/// Builds a new `Curve` from a [LineString].
18+
/// `max_extent` is the maximum distance that is considered to be “on the curve”.
19+
/// `max_extent` plays a role in the bounding box.
20+
fn new(geom: LineString, max_extent: f64) -> Self;
21+
22+
/// Projects the [Point] to the closest position on the `Curve`.
23+
/// Will fail if the `Curve` is invalid (e.g. no `Point` on it)
24+
/// or if the `Point` is too far away.
25+
/// If the `Curve` is a piece of a larger `Curve` (`start_offset > 0`)
26+
/// then the `distance_along_curve` if from the whole `Curve`, not just the current piece.
2227
fn project(&self, point: Point) -> Result<CurveProjection, CurveError>;
2328

24-
/// Returns the geographical position of a point on the curve
25-
/// Will return an error if the CurveProjection is not on this Curve
26-
fn resolve(&self, projection: &CurveProjection) -> Result<Point, CurveError>;
29+
/// Returns the geographical position of a [Point] on the `Curve`.
30+
/// Will return an error if the `CurveProjection` is not on this `Curve`.
31+
fn resolve(&self, projection: CurveProjection) -> Result<Point, CurveError>;
2732

28-
/// Bounding box of the curve with a buffer of `max_extent`
33+
/// Bounding box of the `Curve` with a buffer of `max_extent`.
2934
fn bbox(&self) -> Rect;
3035

31-
/// The length of the curve
36+
/// The length of the `Curve`.
3237
fn length(&self) -> f64;
3338

34-
/// Computes the normal at a given offset on the curve
35-
/// Will return an error if the curve is invalid or the offset is outside of the curve
36-
/// Points to the positive side (left)
39+
/// Computes the normal at a given offset on the `Curve`.
40+
/// Will return an error if the `Curve` is invalid or the offset is outside of the `Curve`.
41+
/// Points to the positive side (left).
3742
fn get_normal(&self, offset: f64) -> Result<(f64, f64), CurveError>;
3843

39-
/// Returns the point where the curve and the segment intersect
40-
/// If the segment intersects the curve multiple times, an intersection is chosen randomly
41-
/// When the segment is colinear with the curve it is ignored
44+
/// Returns the [Point] where the `Curve` and the segment intersect.
45+
/// If the segment intersects the `Curve` multiple times, an intersection is chosen randomly.
46+
/// When the segment is colinear with the `Curve` it is ignored.
4247
fn intersect_segment(&self, segment: Line) -> Option<Point>;
4348

44-
/// Is the geometry valid. Depending on the representation
45-
/// It must have at least two coordinates
46-
/// If there are exactly two coordinates, they must be different
49+
/// Is the geometry valid. Depending on the representation.
50+
/// It must have at least two coordinates.
51+
/// If there are exactly two coordinates, they must be different.
4752
fn is_valid(&self) -> bool;
53+
54+
/// How far from the `Curve` could be considered to be still on the `Curve`.
55+
fn max_extent(&self) -> f64;
4856
}
4957

50-
/// Errors when manipulating the curves
51-
#[derive(Error, Debug)]
58+
/// Errors when manipulating the [Curve] objects.
59+
#[derive(Error, Debug, PartialEq)]
5260
pub enum CurveError {
53-
/// Depeding on the curve implementation the condition of validity might differ
61+
/// The condition of validity might differ depending on the [Curve] implementation.
5462
#[error("the curve geometry is not valid")]
5563
InvalidGeometry,
56-
/// At least a coordinate is non a finite number (NaN, inifinite)
64+
/// At least one coordinate is non a finite number (`NaN`, `infinite`).
5765
#[error("the coordinates are not finite")]
5866
NotFiniteCoordinates,
59-
/// A considered point is not on the curve
67+
/// The considered [Point] is not on the [Curve].
6068
#[error("the point is not on the curve")]
6169
NotOnTheCurve,
6270
}
6371

6472
/// Implementation based on [LineString]:
65-
/// the curve is a string of continous [Line]
66-
/// The coordinates are reprensented by f64.
67-
/// That means a precison of about 1_000_000th of a mm for a curve that spans around the Earth
73+
/// the [Curve] is a string of continous [Line].
74+
/// The coordinates are reprensented by `f64`.
75+
/// That means a precison of about 1_000_000th of a mm for a `Curve` that spans around the Earth.
6876
pub struct LineStringCurve {
69-
/// When curve might be a piece of a longer curve
70-
/// then the start_offset allows to know how fare along the longer curve we are
77+
/// When a [Curve] might be a piece of a longer `Curve`
78+
/// then the `start_offset` allows to know how fare along the longer `Curve` we are.
7179
pub start_offset: f64,
7280

73-
/// The max distance that is considered of being part of the curve
74-
/// It is used to compute the bounding box
81+
/// The max distance that is considered of being part of the [Curve].
82+
/// It is used to compute the bounding box.
7583
pub max_extent: f64,
7684

77-
/// The coordinates are considered as being planar
78-
/// All distance and length computations are in units of those coordinates
85+
/// The coordinates are considered to be planar.
86+
/// All distance and length calculations are expressed in the same units as coordinates.
7987
pub geom: LineString,
8088

8189
length: f64,
8290
}
8391

8492
impl LineStringCurve {
85-
/// Build a new curve from a LineString
86-
/// max_extent is the maximum distance that is considered to be “on the curve”
87-
/// max_extent plays a role in the bounding box
88-
pub fn new(geom: LineString, max_extent: f64) -> Self {
89-
let length = geom.euclidean_length();
90-
Self {
91-
start_offset: 0.,
92-
max_extent,
93-
geom,
94-
length,
95-
}
96-
}
97-
98-
/// Splits the LineString into smaller curves of at most `max_len` length
99-
/// If the initial geometry is invalid, it returns an empty vector
93+
/// Splits the [LineString] into smaller [Curve] objects of at most `max_len` length.
94+
/// If the initial geometry is invalid, it returns an empty vector.
10095
pub fn new_fragmented(geom: LineString, max_len: f64, max_extent: f64) -> Vec<Self> {
10196
let n = (geom.euclidean_length() / max_len).ceil() as usize;
10297
geom.line_segmentize(n)
@@ -112,6 +107,16 @@ impl LineStringCurve {
112107
}
113108

114109
impl Curve for LineStringCurve {
110+
fn new(geom: LineString, max_extent: f64) -> Self {
111+
let length = geom.euclidean_length();
112+
Self {
113+
start_offset: 0.,
114+
max_extent,
115+
geom,
116+
length,
117+
}
118+
}
119+
115120
fn project(&self, point: Point) -> Result<CurveProjection, CurveError> {
116121
if !self.is_valid() {
117122
return Err(CurveError::InvalidGeometry);
@@ -128,7 +133,7 @@ impl Curve for LineStringCurve {
128133
Orientation::Clockwise => 1.,
129134
_ => -1.,
130135
};
131-
let offset = (point.euclidean_distance(&self.geom) * sign) as isize;
136+
let offset = point.euclidean_distance(&self.geom) * sign;
132137

133138
Ok(CurveProjection {
134139
distance_along_curve,
@@ -139,7 +144,7 @@ impl Curve for LineStringCurve {
139144
}
140145
}
141146

142-
fn resolve(&self, projection: &CurveProjection) -> Result<Point, CurveError> {
147+
fn resolve(&self, projection: CurveProjection) -> Result<Point, CurveError> {
143148
let fraction = (projection.distance_along_curve - self.start_offset) / self.length();
144149
if !(0. ..=1.).contains(&fraction) || fraction.is_nan() {
145150
Err(CurveError::NotOnTheCurve)
@@ -182,17 +187,17 @@ impl Curve for LineStringCurve {
182187
}
183188

184189
fn get_normal(&self, offset: f64) -> Result<(f64, f64), CurveError> {
185-
// We find the point where the normal is computed
186-
let point = self.resolve(&CurveProjection {
190+
// We find the Point where the normal is computed
191+
let point = self.resolve(CurveProjection {
187192
distance_along_curve: offset,
188-
offset: 0,
193+
offset: 0.,
189194
})?;
190195

191-
// We find the line where the point is located
196+
// We find the line where the Point is located
192197
// This line will be used to construct the normal:
193198
// - we translate it so that it starts at `0,0`
194-
// - we rotated it by 90°
195-
// - we scale it in order to be a unit vector
199+
// - we rotate it by 90°
200+
// - we scale it in order to become a unit vector
196201
let line = self
197202
.geom
198203
.lines_iter()
@@ -211,18 +216,23 @@ impl Curve for LineStringCurve {
211216
fn is_valid(&self) -> bool {
212217
self.geom.coords_count() >= 2 && (self.geom.coords_count() > 2 || !self.geom.is_closed())
213218
}
219+
220+
fn max_extent(&self) -> f64 {
221+
self.max_extent
222+
}
214223
}
215224

216-
/// Represents a point in space projected on the curve
225+
/// Represents a [Point] in space projected on the [Curve]
226+
#[derive(Clone, Copy)]
217227
pub struct CurveProjection {
218-
/// How far from the curve start is located the point
219-
/// If the curve is part of a larger curve, start_offset is strictly positive
220-
/// and the start_offset will be considered
228+
/// How far from the [Curve] start is located the [Point]
229+
/// If the `Curve` is part of a larger `Curve`, `start_offset` is strictly positive
230+
/// and the `start_offset` will be considered
221231
pub distance_along_curve: f64,
222-
/// How far is the point from the curve (euclidian distance)
223-
/// It is positive if the point is located on the left of the curve
224-
/// and negative if the point is on the right
225-
pub offset: isize,
232+
/// How far is the [Point] from the [Curve] (euclidian distance)
233+
/// It is positive if the `Point` is located on the left of the `Curve`
234+
/// and negative if the `Point` is on the right
235+
pub offset: f64,
226236
}
227237

228238
#[cfg(test)]
@@ -244,11 +254,11 @@ mod tests {
244254

245255
let projected = c.project(point! {x: 1., y: 1.}).unwrap();
246256
assert_eq!(1., projected.distance_along_curve);
247-
assert_eq!(1, projected.offset);
257+
assert_eq!(1., projected.offset);
248258

249259
let projected = c.project(point! {x: 1., y: -1.}).unwrap();
250260
assert_eq!(1., projected.distance_along_curve);
251-
assert_eq!(-1, projected.offset);
261+
assert_eq!(-1., projected.offset);
252262

253263
c.start_offset = 1.;
254264
let projected = c.project(point! {x: 1., y: -1.}).unwrap();
@@ -261,18 +271,18 @@ mod tests {
261271

262272
let mut projection = CurveProjection {
263273
distance_along_curve: 1.,
264-
offset: 0,
274+
offset: 0.,
265275
};
266-
let p = c.resolve(&projection).unwrap();
276+
let p = c.resolve(projection).unwrap();
267277
assert_eq!(p.x(), 1.);
268278
assert_eq!(p.y(), 0.);
269279

270280
c.start_offset = 1.;
271-
let p = c.resolve(&projection).unwrap();
281+
let p = c.resolve(projection).unwrap();
272282
assert_eq!(p.x(), 0.);
273283

274284
projection.distance_along_curve = 4.;
275-
assert!(c.resolve(&projection).is_err());
285+
assert!(c.resolve(projection).is_err());
276286
}
277287

278288
#[test]
@@ -296,7 +306,7 @@ mod tests {
296306
let segment = Line::new(coord! {x: 10., y: 10.}, coord! {x:20., y: 10.});
297307
assert!(c.intersect_segment(segment).is_none());
298308

299-
// Colinear
309+
// Collinear
300310
let segment = Line::new(coord! {x: 0., y:0.,}, coord! {x: 1., y:0.});
301311
assert!(c.intersect_segment(segment).is_none());
302312

src/lrs_generated.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub const ENUM_VALUES_DIRECTION: [Direction; 2] = [
105105
Direction::Decreasing,
106106
];
107107

108-
/// A traversal is a path in a network.
108+
/// A traversal is a path in a network.
109109
/// Traversals may be used to model roads, railway tracks, railway lines or trips.
110110
/// Traversals are defined as a sequence of segment and direction pairs.
111111
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
@@ -1992,11 +1992,11 @@ impl<'a> LinearReferencingMethod<'a> {
19921992
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u64>>>(LinearReferencingMethod::VT_ANCHOR_INDICES, None).unwrap()}
19931993
}
19941994
#[inline]
1995-
pub fn distances(&self) -> flatbuffers::Vector<'a, u64> {
1995+
pub fn distances(&self) -> flatbuffers::Vector<'a, f64> {
19961996
// Safety:
19971997
// Created from valid Table for this object
19981998
// which contains a valid value in this slot
1999-
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u64>>>(LinearReferencingMethod::VT_DISTANCES, None).unwrap()}
1999+
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, f64>>>(LinearReferencingMethod::VT_DISTANCES, None).unwrap()}
20002000
}
20012001
/// The unit used to measure the distance between anchors
20022002
#[inline]
@@ -2028,7 +2028,7 @@ impl flatbuffers::Verifiable for LinearReferencingMethod<'_> {
20282028
.visit_field::<TraversalRef>("traversal_index", Self::VT_TRAVERSAL_INDEX, false)?
20292029
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, TraversalRef>>>("used_on", Self::VT_USED_ON, false)?
20302030
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u64>>>("anchor_indices", Self::VT_ANCHOR_INDICES, true)?
2031-
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, u64>>>("distances", Self::VT_DISTANCES, true)?
2031+
.visit_field::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'_, f64>>>("distances", Self::VT_DISTANCES, true)?
20322032
.visit_field::<DistanceUnit>("distance_unit", Self::VT_DISTANCE_UNIT, false)?
20332033
.visit_field::<DistanceUnit>("measure_unit", Self::VT_MEASURE_UNIT, false)?
20342034
.finish();
@@ -2041,7 +2041,7 @@ pub struct LinearReferencingMethodArgs<'a> {
20412041
pub traversal_index: Option<&'a TraversalRef>,
20422042
pub used_on: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, TraversalRef>>>,
20432043
pub anchor_indices: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, u64>>>,
2044-
pub distances: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, u64>>>,
2044+
pub distances: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a, f64>>>,
20452045
pub distance_unit: DistanceUnit,
20462046
pub measure_unit: DistanceUnit,
20472047
}
@@ -2087,7 +2087,7 @@ impl<'a: 'b, 'b> LinearReferencingMethodBuilder<'a, 'b> {
20872087
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(LinearReferencingMethod::VT_ANCHOR_INDICES, anchor_indices);
20882088
}
20892089
#[inline]
2090-
pub fn add_distances(&mut self, distances: flatbuffers::WIPOffset<flatbuffers::Vector<'b , u64>>) {
2090+
pub fn add_distances(&mut self, distances: flatbuffers::WIPOffset<flatbuffers::Vector<'b , f64>>) {
20912091
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(LinearReferencingMethod::VT_DISTANCES, distances);
20922092
}
20932093
#[inline]

0 commit comments

Comments
 (0)