vrp_cli/extensions/analyze/
clusters.rs

1#[cfg(test)]
2#[path = "../../../tests/unit/extensions/analyze/clusters_test.rs"]
3mod clusters_test;
4
5use std::io::{BufReader, BufWriter, Read};
6use std::sync::Arc;
7use vrp_core::construction::clustering::dbscan::create_job_clusters;
8use vrp_core::models::common::Timestamp;
9use vrp_core::models::problem::{get_job_locations, JobIdDimension};
10use vrp_core::models::Problem;
11use vrp_core::prelude::{Float, GenericResult};
12use vrp_pragmatic::format::problem::{deserialize_matrix, deserialize_problem, PragmaticProblem};
13use vrp_pragmatic::format::solution::serialize_named_locations_as_geojson;
14use vrp_pragmatic::format::{CoordIndexExtraProperty, MultiFormatError};
15
16/// Gets job clusters.
17pub fn get_clusters<F: Read>(
18    problem_reader: BufReader<F>,
19    matrices_readers: Option<Vec<BufReader<F>>>,
20    min_points: Option<usize>,
21    epsilon: Option<Float>,
22) -> GenericResult<String> {
23    let problem = Arc::new(get_core_problem(problem_reader, matrices_readers).map_err(|errs| errs.to_string())?);
24
25    let coord_index = problem.extras.get_coord_index().expect("cannot find coord index");
26    let coord_index = coord_index.as_ref();
27
28    let clusters = create_job_clusters(problem.jobs.all(), &problem.fleet, min_points, epsilon, |profile, job| {
29        problem.jobs.neighbors(profile, job, Timestamp::default())
30    })?;
31
32    let locations = clusters
33        .iter()
34        .enumerate()
35        .flat_map(|(cluster_idx, jobs)| {
36            jobs.iter()
37                .filter_map(move |job| {
38                    job.dimens().get_job_id().cloned().map(|job_id| {
39                        get_job_locations(job)
40                            .flatten()
41                            .filter_map(move |l_idx| coord_index.get_by_idx(l_idx))
42                            .map(move |location| (job_id.clone(), location, cluster_idx))
43                    })
44                })
45                .flatten()
46        })
47        .collect::<Vec<_>>();
48
49    let mut writer = BufWriter::new(Vec::new());
50
51    serialize_named_locations_as_geojson(locations.as_slice(), &mut writer)
52        .map_err(|err| format!("cannot write named locations as geojson: '{err}'"))?;
53
54    let bytes = writer.into_inner().map_err(|err| format!("{err}"))?;
55    let result = String::from_utf8(bytes).map_err(|err| format!("{err}"))?;
56
57    Ok(result)
58}
59
60fn get_core_problem<F: Read>(
61    problem_reader: BufReader<F>,
62    matrices_readers: Option<Vec<BufReader<F>>>,
63) -> Result<Problem, MultiFormatError> {
64    let problem = deserialize_problem(problem_reader)?;
65
66    let matrices = matrices_readers.map(|matrices| {
67        matrices.into_iter().map(|file| deserialize_matrix(BufReader::new(file))).collect::<Result<Vec<_>, _>>()
68    });
69
70    let matrices = if let Some(matrices) = matrices { Some(matrices?) } else { None };
71
72    (problem, matrices).read_pragmatic()
73}