Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "liblrs"
version = "0.4.3"
version = "0.5.0"
edition = "2024"
description = "Library to manipulate linear referencing systems"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "liblrs_python"
description = "Python bindings for liblrs: a library to work with linear referencing systems"
version = "0.4.3"
version = "0.5.0"
edition = "2024"
license = "MIT"
repository = "https://github.com/OpenRailAssociation/liblrs/"
Expand Down
5 changes: 4 additions & 1 deletion python/liblrs_python.pyi

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "liblrs_python"
version = "0.4.3"
version = "0.5.0"
requires-python = ">=3.12"
dependencies = ["pip>=25.2"]

Expand Down
63 changes: 60 additions & 3 deletions python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn liblrs_python(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<AnchorOnLrm>()?;
m.add_class::<SegmentOfTraversal>()?;
m.add_class::<Builder>()?;
m.add_class::<DataIssueReporter>()?;
Ok(())
}

Expand Down Expand Up @@ -576,15 +577,22 @@ impl Builder {
/// Read the topology from an OpenStreetMap source
///
/// It reads the nodes, segments and traversals.
pub fn read_from_osm(
pub fn read_from_osm<'py>(
&mut self,
input_osm_file: PathBuf,
lrm_tag: String,
required: Vec<(String, String)>,
to_reject: Vec<(String, String)>,
reporter: Bound<'py, DataIssueReporter>,
) {
self.inner
.read_from_osm(&input_osm_file, &lrm_tag, required, to_reject)
let mut reporter = PythonDataIssueReporter(reporter);
self.inner.read_from_osm(
&input_osm_file,
&lrm_tag,
required,
to_reject,
Some(&mut reporter),
)
}

/// Save the lrs to a file
Expand Down Expand Up @@ -656,3 +664,52 @@ impl Builder {
}

define_stub_info_gatherer!(stub_info);

#[gen_stub_pyclass]
#[pyclass(subclass)]
struct DataIssueReporter {}

#[pymethods]
impl DataIssueReporter {
#[new]
fn new() -> Self {
Self {}
}
}

#[allow(unused_variables)]
#[gen_stub_pymethods]
impl liblrs::DataIssueReporter for DataIssueReporter {
fn report_ignoring_traversal_edges(
&mut self,
traversal_ref: &str,
ignored_count: usize,
total_count: usize,
first_node: i64,
last_node: i64,
) {
}
}

struct PythonDataIssueReporter<'a>(Bound<'a, DataIssueReporter>);
impl liblrs::DataIssueReporter for PythonDataIssueReporter<'_> {
fn report_ignoring_traversal_edges(
&mut self,
traversal_ref: &str,
ignored_count: usize,
total_count: usize,
first_node: i64,
last_node: i64,
) {
let _ = self.0.call_method1(
"report_ignoring_traversal_edges",
(
traversal_ref,
ignored_count,
total_count,
first_node,
last_node,
),
);
}
}
2 changes: 1 addition & 1 deletion python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::lrs::Properties;
use crate::lrs_ext::ExtLrs;
use crate::lrs_generated::{self, *};
use crate::osm_helpers::sort_edges;
use crate::properties;
use crate::{DataIssueReporter, properties};

/// The linear position of an [`Anchor`] doesn’t always match the measured distance.
/// For example if a road was transformed into a bypass, resulting in a longer road,
Expand Down Expand Up @@ -436,7 +436,10 @@ impl<'fbb> Builder<'fbb> {
lrm_tag: &str,
required: Vec<(String, String)>,
to_reject: Vec<(String, String)>,
reporter: Option<&mut dyn DataIssueReporter>,
) {
let mut default_reporter = ();
let reporter = reporter.unwrap_or(&mut default_reporter);
let mut reader = osm4routing::Reader::new().merge_ways().read_tag(lrm_tag);

for (key, value) in required.iter() {
Expand Down Expand Up @@ -479,7 +482,7 @@ impl<'fbb> Builder<'fbb> {

// Sort the traversals
for (srv_ref, edges) in traversals.into_iter() {
let segments: Vec<_> = sort_edges(edges, &srv_ref)
let segments: Vec<_> = sort_edges(edges, &srv_ref, reporter)
.into_iter()
.map(|(edge, reversed)| SegmentOfTraversal {
segment_index: edges_map[&edge.id],
Expand Down
1 change: 1 addition & 0 deletions src/geometry_from_osm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fn main() {
&cli_args.lrm_tag,
required,
to_reject,
Some(&mut liblrs::LoggingDataIssueReporter),
);

builder.save(
Expand Down
40 changes: 40 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,46 @@ pub mod lrs_ext;
#[deny(missing_docs)]
pub mod builder;

pub trait DataIssueReporter {
fn report_ignoring_traversal_edges(
&mut self,
traversal_ref: &str,
ignored_count: usize,
total_count: usize,
first_node: i64,
last_node: i64,
);
}

pub struct LoggingDataIssueReporter;

impl DataIssueReporter for LoggingDataIssueReporter {
fn report_ignoring_traversal_edges(
&mut self,
traversal_ref: &str,
ignored_count: usize,
total_count: usize,
first_node: i64,
last_node: i64,
) {
println!(
"[WARN] on traversal {traversal_ref}, ignoring {ignored_count} edges out of {total_count}. Sorted from {first_node} to {last_node}"
);
}
}

impl DataIssueReporter for () {
fn report_ignoring_traversal_edges(
&mut self,
_traversal_ref: &str,
_ignored_count: usize,
_total_count: usize,
_first_node: i64,
_last_node: i64,
) {
}
}

#[test]
fn read_and_write_lrs() {
use builder::*;
Expand Down
2 changes: 1 addition & 1 deletion src/lrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ mod tests {
use approx::assert_relative_eq;
use geo::line_string;

use crate::{curves::PlanarLineStringCurve, properties};
use crate::curves::PlanarLineStringCurve;

use super::*;

Expand Down
27 changes: 19 additions & 8 deletions src/osm_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

use osm4routing::Edge;

use crate::DataIssueReporter;

/// When sorting the edges, each candidate is tested to see if they match and if they need to be reversed.
#[derive(PartialEq, Eq, Debug)]
enum Candidate {
Expand Down Expand Up @@ -79,7 +81,11 @@ fn sort_iteration(
/// The traversals are identified by a tag that is used on many ways.
/// We try to build the longest continous chain of ways, but the is no guarantee to succeed.
/// The ways might not share nodes or they might represent a tree.
pub fn sort_edges(edges: Vec<Edge>, traversal_ref: &str) -> Vec<(Edge, bool)> {
pub fn sort_edges(
edges: Vec<Edge>,
traversal_ref: &str,
reporter: &mut dyn DataIssueReporter,
) -> Vec<(Edge, bool)> {
let (to_insert, sorted) = sort_iteration(edges, vec![]);

// Print some stats about edges that could not be matched
Expand All @@ -98,10 +104,7 @@ pub fn sort_edges(edges: Vec<Edge>, traversal_ref: &str) -> Vec<(Edge, bool)> {
} else {
last_edge.0.target
};
println!(
"[WARN] on traversal {traversal_ref}, ignoring {ignored} edges out of {total}. Sorted from {} to {}",
first.0, last.0
);
reporter.report_ignoring_traversal_edges(traversal_ref, ignored, total, first.0, last.0);
}

sorted
Expand Down Expand Up @@ -170,7 +173,7 @@ pub mod tests {
fn sort_edges_simple() {
let e = edge(0, 1);

let sorted = sort_edges(vec![e.clone()], "");
let sorted = sort_edges(vec![e.clone()], "", &mut crate::LoggingDataIssueReporter);
assert_eq!(sorted[0].0, e);
assert!(!sorted[0].1);
}
Expand All @@ -180,7 +183,11 @@ pub mod tests {
let e1 = edge(0, 1);
let e2 = edge(1, 2);

let sorted = sort_edges(vec![e1.clone(), e2.clone()], "");
let sorted = sort_edges(
vec![e1.clone(), e2.clone()],
"",
&mut crate::LoggingDataIssueReporter,
);
assert_eq!(sorted[0].0, e1);
assert_eq!(sorted[1].0, e2);
assert!(!sorted[0].1);
Expand All @@ -192,7 +199,11 @@ pub mod tests {
let e1 = edge(1, 0);
let e2 = edge(1, 2);

let sorted = sort_edges(vec![e1.clone(), e2.clone()], "");
let sorted = sort_edges(
vec![e1.clone(), e2.clone()],
"",
&mut crate::LoggingDataIssueReporter,
);
assert_eq!(sorted[0].0, e1);
assert_eq!(sorted[1].0, e2);
assert!(sorted[0].1);
Expand Down
2 changes: 1 addition & 1 deletion wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "liblrs-wasm"
version = "0.4.3"
version = "0.5.0"
edition = "2024"

[lib]
Expand Down
4 changes: 2 additions & 2 deletions wasm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"text-encoding": "0.7.0"
},
"name": "liblrs",
"version": "0.4.3",
"version": "0.5.0",
"repository": {
"type": "git",
"url": "git+https://github.com/OpenRailAssociation/liblrs.git"
Expand Down
Loading