1use crate::error::{AlgorithmError, Result};
8use oxigdal_core::vector::{Coordinate, LineString};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct NodeId(pub usize);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub struct EdgeId(pub usize);
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum GraphType {
22 Directed,
24 Undirected,
26}
27
28#[derive(Debug, Clone)]
30pub struct EdgeWeight {
31 pub distance: f64,
33 pub time: f64,
35 pub monetary: f64,
37 pub custom: HashMap<String, f64>,
39}
40
41impl EdgeWeight {
42 pub fn from_distance(distance: f64) -> Self {
44 Self {
45 distance,
46 time: 0.0,
47 monetary: 0.0,
48 custom: HashMap::new(),
49 }
50 }
51
52 pub fn from_distance_time(distance: f64, time: f64) -> Self {
54 Self {
55 distance,
56 time,
57 monetary: 0.0,
58 custom: HashMap::new(),
59 }
60 }
61
62 pub fn new(distance: f64, time: f64, monetary: f64) -> Self {
64 Self {
65 distance,
66 time,
67 monetary,
68 custom: HashMap::new(),
69 }
70 }
71
72 pub fn with_custom(mut self, name: String, value: f64) -> Self {
74 self.custom.insert(name, value);
75 self
76 }
77
78 pub fn weighted_cost(&self, criteria: &HashMap<String, f64>) -> f64 {
83 let mut total = 0.0;
84 if let Some(&w) = criteria.get("distance") {
85 total += self.distance * w;
86 }
87 if let Some(&w) = criteria.get("time") {
88 total += self.time * w;
89 }
90 if let Some(&w) = criteria.get("monetary") {
91 total += self.monetary * w;
92 }
93 for (name, &value) in &self.custom {
94 if let Some(&w) = criteria.get(name.as_str()) {
95 total += value * w;
96 }
97 }
98 total
99 }
100
101 pub fn primary(&self) -> f64 {
103 self.distance
104 }
105}
106
107impl Default for EdgeWeight {
108 fn default() -> Self {
109 Self {
110 distance: 0.0,
111 time: 0.0,
112 monetary: 0.0,
113 custom: HashMap::new(),
114 }
115 }
116}
117
118#[derive(Debug, Clone)]
120pub struct Node {
121 pub id: NodeId,
123 pub coordinate: Coordinate,
125 pub edges: Vec<EdgeId>,
127 pub attributes: HashMap<String, String>,
129}
130
131impl Node {
132 pub fn new(id: NodeId, coordinate: Coordinate) -> Self {
134 Self {
135 id,
136 coordinate,
137 edges: Vec::new(),
138 attributes: HashMap::new(),
139 }
140 }
141
142 pub fn add_attribute(&mut self, key: String, value: String) {
144 self.attributes.insert(key, value);
145 }
146
147 pub fn get_attribute(&self, key: &str) -> Option<&String> {
149 self.attributes.get(key)
150 }
151
152 pub fn degree(&self) -> usize {
154 self.edges.len()
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
160pub enum RoadClass {
161 Motorway,
163 Trunk,
165 Primary,
167 Secondary,
169 Tertiary,
171 Residential,
173 Service,
175 Path,
177 Unclassified,
179}
180
181#[derive(Debug, Clone)]
183pub struct Edge {
184 pub id: EdgeId,
186 pub source: NodeId,
188 pub target: NodeId,
190 pub weight: f64,
192 pub multi_weight: EdgeWeight,
194 pub geometry: Option<LineString>,
196 pub bidirectional: bool,
198 pub speed_limit: Option<f64>,
200 pub road_class: Option<RoadClass>,
202 pub attributes: HashMap<String, String>,
204}
205
206impl Edge {
207 pub fn new(id: EdgeId, source: NodeId, target: NodeId, weight: f64) -> Self {
209 Self {
210 id,
211 source,
212 target,
213 weight,
214 multi_weight: EdgeWeight::from_distance(weight),
215 geometry: None,
216 bidirectional: false,
217 speed_limit: None,
218 road_class: None,
219 attributes: HashMap::new(),
220 }
221 }
222
223 pub fn with_multi_weight(
225 id: EdgeId,
226 source: NodeId,
227 target: NodeId,
228 multi_weight: EdgeWeight,
229 ) -> Self {
230 let weight = multi_weight.primary();
231 Self {
232 id,
233 source,
234 target,
235 weight,
236 multi_weight,
237 geometry: None,
238 bidirectional: false,
239 speed_limit: None,
240 road_class: None,
241 attributes: HashMap::new(),
242 }
243 }
244
245 pub fn with_geometry(mut self, geometry: LineString) -> Self {
247 self.geometry = Some(geometry);
248 self
249 }
250
251 pub fn bidirectional(mut self) -> Self {
253 self.bidirectional = true;
254 self
255 }
256
257 pub fn with_speed_limit(mut self, speed_limit: f64) -> Self {
259 self.speed_limit = Some(speed_limit);
260 self
261 }
262
263 pub fn with_road_class(mut self, road_class: RoadClass) -> Self {
265 self.road_class = Some(road_class);
266 self
267 }
268
269 pub fn add_attribute(&mut self, key: String, value: String) {
271 self.attributes.insert(key, value);
272 }
273
274 pub fn get_attribute(&self, key: &str) -> Option<&String> {
276 self.attributes.get(key)
277 }
278
279 pub fn travel_time(&self) -> Option<f64> {
281 if let (Some(geom), Some(speed)) = (&self.geometry, self.speed_limit) {
282 let length = compute_linestring_length(geom);
283 Some((length / 1000.0) / speed) } else {
285 None
286 }
287 }
288
289 pub fn other_node(&self, node: NodeId) -> Option<NodeId> {
291 if node == self.source {
292 Some(self.target)
293 } else if node == self.target {
294 Some(self.source)
295 } else {
296 None
297 }
298 }
299
300 pub fn is_self_loop(&self) -> bool {
302 self.source == self.target
303 }
304}
305
306fn compute_linestring_length(linestring: &LineString) -> f64 {
308 let coords = &linestring.coords;
309 let mut length = 0.0;
310
311 for i in 0..coords.len().saturating_sub(1) {
312 let dx = coords[i + 1].x - coords[i].x;
313 let dy = coords[i + 1].y - coords[i].y;
314 length += (dx * dx + dy * dy).sqrt();
315 }
316
317 length
318}
319
320#[derive(Debug, Clone)]
322pub struct GraphMetrics {
323 pub num_nodes: usize,
325 pub num_edges: usize,
327 pub avg_degree: f64,
329 pub max_degree: usize,
331 pub min_degree: usize,
333 pub density: f64,
335 pub num_components: usize,
337 pub total_weight: f64,
339 pub avg_weight: f64,
341 pub min_weight: f64,
343 pub max_weight: f64,
345 pub graph_type: GraphType,
347}
348
349#[derive(Debug, Clone)]
351pub struct Graph {
352 nodes: HashMap<NodeId, Node>,
354 edges: HashMap<EdgeId, Edge>,
356 adjacency: HashMap<NodeId, Vec<EdgeId>>,
358 reverse_adjacency: HashMap<NodeId, Vec<EdgeId>>,
360 next_node_id: usize,
362 next_edge_id: usize,
364 graph_type: GraphType,
366}
367
368impl Graph {
369 pub fn new() -> Self {
371 Self {
372 nodes: HashMap::new(),
373 edges: HashMap::new(),
374 adjacency: HashMap::new(),
375 reverse_adjacency: HashMap::new(),
376 next_node_id: 0,
377 next_edge_id: 0,
378 graph_type: GraphType::Directed,
379 }
380 }
381
382 pub fn with_type(graph_type: GraphType) -> Self {
384 Self {
385 nodes: HashMap::new(),
386 edges: HashMap::new(),
387 adjacency: HashMap::new(),
388 reverse_adjacency: HashMap::new(),
389 next_node_id: 0,
390 next_edge_id: 0,
391 graph_type,
392 }
393 }
394
395 pub fn graph_type(&self) -> GraphType {
397 self.graph_type
398 }
399
400 pub fn add_node(&mut self, coordinate: Coordinate) -> NodeId {
402 let id = NodeId(self.next_node_id);
403 self.next_node_id = self.next_node_id.wrapping_add(1);
404
405 let node = Node::new(id, coordinate);
406 self.nodes.insert(id, node);
407 self.adjacency.insert(id, Vec::new());
408 self.reverse_adjacency.insert(id, Vec::new());
409
410 id
411 }
412
413 pub fn add_node_with_id(&mut self, id: NodeId, coordinate: Coordinate) -> Result<NodeId> {
415 if self.nodes.contains_key(&id) {
416 return Err(AlgorithmError::InvalidGeometry(format!(
417 "Node {:?} already exists",
418 id
419 )));
420 }
421
422 let node = Node::new(id, coordinate);
423 self.nodes.insert(id, node);
424 self.adjacency.insert(id, Vec::new());
425 self.reverse_adjacency.insert(id, Vec::new());
426
427 if id.0 >= self.next_node_id {
428 self.next_node_id = id.0.wrapping_add(1);
429 }
430
431 Ok(id)
432 }
433
434 pub fn add_edge(&mut self, source: NodeId, target: NodeId, weight: f64) -> Result<EdgeId> {
436 if !self.nodes.contains_key(&source) {
437 return Err(AlgorithmError::InvalidGeometry(format!(
438 "Source node {:?} not found",
439 source
440 )));
441 }
442
443 if !self.nodes.contains_key(&target) {
444 return Err(AlgorithmError::InvalidGeometry(format!(
445 "Target node {:?} not found",
446 target
447 )));
448 }
449
450 let id = EdgeId(self.next_edge_id);
451 self.next_edge_id = self.next_edge_id.wrapping_add(1);
452
453 let edge = Edge::new(id, source, target, weight);
454 self.edges.insert(id, edge);
455
456 self.adjacency
457 .get_mut(&source)
458 .ok_or_else(|| AlgorithmError::InvalidGeometry("Source node not found".to_string()))?
459 .push(id);
460
461 self.reverse_adjacency
462 .get_mut(&target)
463 .ok_or_else(|| AlgorithmError::InvalidGeometry("Target node not found".to_string()))?
464 .push(id);
465
466 if self.graph_type == GraphType::Undirected && source != target {
468 self.adjacency
469 .get_mut(&target)
470 .ok_or_else(|| {
471 AlgorithmError::InvalidGeometry("Target node not found".to_string())
472 })?
473 .push(id);
474
475 self.reverse_adjacency
476 .get_mut(&source)
477 .ok_or_else(|| {
478 AlgorithmError::InvalidGeometry("Source node not found".to_string())
479 })?
480 .push(id);
481 }
482
483 if let Some(node) = self.nodes.get_mut(&source) {
484 node.edges.push(id);
485 }
486
487 if source != target {
488 if let Some(node) = self.nodes.get_mut(&target) {
489 node.edges.push(id);
490 }
491 }
492
493 Ok(id)
494 }
495
496 pub fn add_weighted_edge(
498 &mut self,
499 source: NodeId,
500 target: NodeId,
501 multi_weight: EdgeWeight,
502 ) -> Result<EdgeId> {
503 if !self.nodes.contains_key(&source) {
504 return Err(AlgorithmError::InvalidGeometry(format!(
505 "Source node {:?} not found",
506 source
507 )));
508 }
509
510 if !self.nodes.contains_key(&target) {
511 return Err(AlgorithmError::InvalidGeometry(format!(
512 "Target node {:?} not found",
513 target
514 )));
515 }
516
517 let id = EdgeId(self.next_edge_id);
518 self.next_edge_id = self.next_edge_id.wrapping_add(1);
519
520 let edge = Edge::with_multi_weight(id, source, target, multi_weight);
521 self.edges.insert(id, edge);
522
523 self.adjacency
524 .get_mut(&source)
525 .ok_or_else(|| AlgorithmError::InvalidGeometry("Source node not found".to_string()))?
526 .push(id);
527
528 self.reverse_adjacency
529 .get_mut(&target)
530 .ok_or_else(|| AlgorithmError::InvalidGeometry("Target node not found".to_string()))?
531 .push(id);
532
533 if self.graph_type == GraphType::Undirected && source != target {
534 self.adjacency
535 .get_mut(&target)
536 .ok_or_else(|| {
537 AlgorithmError::InvalidGeometry("Target node not found".to_string())
538 })?
539 .push(id);
540
541 self.reverse_adjacency
542 .get_mut(&source)
543 .ok_or_else(|| {
544 AlgorithmError::InvalidGeometry("Source node not found".to_string())
545 })?
546 .push(id);
547 }
548
549 if let Some(node) = self.nodes.get_mut(&source) {
550 node.edges.push(id);
551 }
552
553 if source != target {
554 if let Some(node) = self.nodes.get_mut(&target) {
555 node.edges.push(id);
556 }
557 }
558
559 Ok(id)
560 }
561
562 pub fn remove_edge(&mut self, edge_id: EdgeId) -> Result<Edge> {
564 let edge = self.edges.remove(&edge_id).ok_or_else(|| {
565 AlgorithmError::InvalidGeometry(format!("Edge {:?} not found", edge_id))
566 })?;
567
568 if let Some(adj) = self.adjacency.get_mut(&edge.source) {
569 adj.retain(|&e| e != edge_id);
570 }
571 if let Some(radj) = self.reverse_adjacency.get_mut(&edge.target) {
572 radj.retain(|&e| e != edge_id);
573 }
574
575 if self.graph_type == GraphType::Undirected && edge.source != edge.target {
576 if let Some(adj) = self.adjacency.get_mut(&edge.target) {
577 adj.retain(|&e| e != edge_id);
578 }
579 if let Some(radj) = self.reverse_adjacency.get_mut(&edge.source) {
580 radj.retain(|&e| e != edge_id);
581 }
582 }
583
584 if let Some(node) = self.nodes.get_mut(&edge.source) {
585 node.edges.retain(|&e| e != edge_id);
586 }
587 if let Some(node) = self.nodes.get_mut(&edge.target) {
588 node.edges.retain(|&e| e != edge_id);
589 }
590
591 Ok(edge)
592 }
593
594 pub fn remove_node(&mut self, node_id: NodeId) -> Result<Node> {
596 let node = self.nodes.get(&node_id).ok_or_else(|| {
597 AlgorithmError::InvalidGeometry(format!("Node {:?} not found", node_id))
598 })?;
599
600 let incident_edges: Vec<EdgeId> = node.edges.clone();
601
602 for edge_id in incident_edges {
603 let _ = self.remove_edge(edge_id);
604 }
605
606 self.adjacency.remove(&node_id);
607 self.reverse_adjacency.remove(&node_id);
608
609 let node = self.nodes.remove(&node_id).ok_or_else(|| {
610 AlgorithmError::InvalidGeometry(format!("Node {:?} not found", node_id))
611 })?;
612
613 Ok(node)
614 }
615
616 pub(crate) fn remove_node_unchecked(&mut self, node_id: NodeId) {
618 self.nodes.remove(&node_id);
619 self.adjacency.remove(&node_id);
620 self.reverse_adjacency.remove(&node_id);
621 }
622
623 pub fn get_node(&self, id: NodeId) -> Option<&Node> {
625 self.nodes.get(&id)
626 }
627
628 pub fn get_node_mut(&mut self, id: NodeId) -> Option<&mut Node> {
630 self.nodes.get_mut(&id)
631 }
632
633 pub fn get_edge(&self, id: EdgeId) -> Option<&Edge> {
635 self.edges.get(&id)
636 }
637
638 pub fn get_edge_mut(&mut self, id: EdgeId) -> Option<&mut Edge> {
640 self.edges.get_mut(&id)
641 }
642
643 pub fn outgoing_edges(&self, node: NodeId) -> &[EdgeId] {
645 self.adjacency
646 .get(&node)
647 .map(|v| v.as_slice())
648 .unwrap_or(&[])
649 }
650
651 pub fn incoming_edges(&self, node: NodeId) -> &[EdgeId] {
653 self.reverse_adjacency
654 .get(&node)
655 .map(|v| v.as_slice())
656 .unwrap_or(&[])
657 }
658
659 pub fn neighbors(&self, node: NodeId) -> Vec<NodeId> {
661 self.outgoing_edges(node)
662 .iter()
663 .filter_map(|edge_id| {
664 self.edges.get(edge_id).and_then(|e| {
665 if self.graph_type == GraphType::Undirected {
666 e.other_node(node)
667 } else {
668 Some(e.target)
669 }
670 })
671 })
672 .collect()
673 }
674
675 pub fn neighbors_with_edges(&self, node: NodeId) -> Vec<(NodeId, EdgeId, f64)> {
677 self.outgoing_edges(node)
678 .iter()
679 .filter_map(|&edge_id| {
680 self.edges.get(&edge_id).and_then(|e| {
681 let neighbor = if self.graph_type == GraphType::Undirected {
682 e.other_node(node)
683 } else {
684 Some(e.target)
685 };
686 neighbor.map(|n| (n, edge_id, e.weight))
687 })
688 })
689 .collect()
690 }
691
692 pub fn num_nodes(&self) -> usize {
694 self.nodes.len()
695 }
696
697 pub fn num_edges(&self) -> usize {
699 self.edges.len()
700 }
701
702 pub fn is_empty(&self) -> bool {
704 self.nodes.is_empty()
705 }
706
707 pub fn node_ids(&self) -> Vec<NodeId> {
709 self.nodes.keys().copied().collect()
710 }
711
712 pub fn edge_ids(&self) -> Vec<EdgeId> {
714 self.edges.keys().copied().collect()
715 }
716
717 pub fn has_node(&self, id: NodeId) -> bool {
719 self.nodes.contains_key(&id)
720 }
721
722 pub fn has_edge(&self, id: EdgeId) -> bool {
724 self.edges.contains_key(&id)
725 }
726
727 pub fn find_edge(&self, source: NodeId, target: NodeId) -> Option<EdgeId> {
729 self.outgoing_edges(source).iter().find_map(|&edge_id| {
730 self.edges.get(&edge_id).and_then(|e| {
731 if e.target == target
732 || (self.graph_type == GraphType::Undirected && e.source == target)
733 {
734 Some(edge_id)
735 } else {
736 None
737 }
738 })
739 })
740 }
741
742 pub fn find_edges(&self, source: NodeId, target: NodeId) -> Vec<EdgeId> {
744 self.outgoing_edges(source)
745 .iter()
746 .filter_map(|&edge_id| {
747 self.edges.get(&edge_id).and_then(|e| {
748 if e.target == target
749 || (self.graph_type == GraphType::Undirected && e.source == target)
750 {
751 Some(edge_id)
752 } else {
753 None
754 }
755 })
756 })
757 .collect()
758 }
759
760 pub fn nearest_node(&self, coord: &Coordinate) -> Option<NodeId> {
762 self.nodes
763 .iter()
764 .min_by(|(_, a), (_, b)| {
765 let dist_a = Self::euclidean_distance(&a.coordinate, coord);
766 let dist_b = Self::euclidean_distance(&b.coordinate, coord);
767 dist_a
768 .partial_cmp(&dist_b)
769 .unwrap_or(std::cmp::Ordering::Equal)
770 })
771 .map(|(id, _)| *id)
772 }
773
774 pub fn k_nearest_nodes(&self, coord: &Coordinate, k: usize) -> Vec<(NodeId, f64)> {
776 let mut node_dists: Vec<(NodeId, f64)> = self
777 .nodes
778 .iter()
779 .map(|(id, node)| (*id, Self::euclidean_distance(&node.coordinate, coord)))
780 .collect();
781
782 node_dists.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
783 node_dists.truncate(k);
784 node_dists
785 }
786
787 fn euclidean_distance(a: &Coordinate, b: &Coordinate) -> f64 {
789 let dx = a.x - b.x;
790 let dy = a.y - b.y;
791 (dx * dx + dy * dy).sqrt()
792 }
793
794 pub fn in_degree(&self, node: NodeId) -> usize {
796 self.incoming_edges(node).len()
797 }
798
799 pub fn out_degree(&self, node: NodeId) -> usize {
801 self.outgoing_edges(node).len()
802 }
803
804 pub fn degree(&self, node: NodeId) -> usize {
806 if self.graph_type == GraphType::Undirected {
807 self.outgoing_edges(node).len()
808 } else {
809 self.in_degree(node) + self.out_degree(node)
810 }
811 }
812
813 pub fn metrics(&self) -> GraphMetrics {
815 let mut total_degree = 0;
816 let mut max_degree = 0;
817 let mut min_degree = usize::MAX;
818 let mut total_weight = 0.0;
819 let mut min_weight = f64::INFINITY;
820 let mut max_weight = f64::NEG_INFINITY;
821
822 for node_id in self.nodes.keys() {
823 let deg = self.degree(*node_id);
824 total_degree += deg;
825 max_degree = max_degree.max(deg);
826 min_degree = min_degree.min(deg);
827 }
828
829 for edge in self.edges.values() {
830 total_weight += edge.weight;
831 min_weight = min_weight.min(edge.weight);
832 max_weight = max_weight.max(edge.weight);
833 }
834
835 if self.nodes.is_empty() {
836 min_degree = 0;
837 }
838 if self.edges.is_empty() {
839 min_weight = 0.0;
840 max_weight = 0.0;
841 }
842
843 let avg_degree = if self.num_nodes() > 0 {
844 total_degree as f64 / self.num_nodes() as f64
845 } else {
846 0.0
847 };
848
849 let avg_weight = if self.num_edges() > 0 {
850 total_weight / self.num_edges() as f64
851 } else {
852 0.0
853 };
854
855 let components = self.connected_components();
856 let density = if self.num_nodes() > 1 {
857 let max_edges = if self.graph_type == GraphType::Directed {
858 self.num_nodes() * (self.num_nodes() - 1)
859 } else {
860 self.num_nodes() * (self.num_nodes() - 1) / 2
861 };
862 if max_edges > 0 {
863 self.num_edges() as f64 / max_edges as f64
864 } else {
865 0.0
866 }
867 } else {
868 0.0
869 };
870
871 GraphMetrics {
872 num_nodes: self.num_nodes(),
873 num_edges: self.num_edges(),
874 avg_degree,
875 max_degree,
876 min_degree,
877 density,
878 num_components: components.len(),
879 total_weight,
880 avg_weight,
881 min_weight,
882 max_weight,
883 graph_type: self.graph_type,
884 }
885 }
886
887 pub fn subgraph(&self, node_ids: &[NodeId]) -> Result<Graph> {
889 let node_set: std::collections::HashSet<NodeId> = node_ids.iter().copied().collect();
890 let mut sub = Graph::with_type(self.graph_type);
891
892 for &node_id in &node_set {
893 let node = self.nodes.get(&node_id).ok_or_else(|| {
894 AlgorithmError::InvalidGeometry(format!("Node {:?} not found", node_id))
895 })?;
896 sub.add_node_with_id(node_id, node.coordinate)?;
897 }
898
899 for edge in self.edges.values() {
900 if node_set.contains(&edge.source) && node_set.contains(&edge.target) {
901 let _ = sub.add_edge(edge.source, edge.target, edge.weight);
902 }
903 }
904
905 Ok(sub)
906 }
907
908 pub fn reverse(&self) -> Graph {
910 let mut reversed = Graph::with_type(self.graph_type);
911
912 for (id, node) in &self.nodes {
913 let _ = reversed.add_node_with_id(*id, node.coordinate);
914 }
915
916 for edge in self.edges.values() {
917 let _ = reversed.add_edge(edge.target, edge.source, edge.weight);
918 }
919
920 reversed
921 }
922
923 pub fn nodes_iter(&self) -> impl Iterator<Item = (&NodeId, &Node)> {
925 self.nodes.iter()
926 }
927
928 pub fn edges_iter(&self) -> impl Iterator<Item = (&EdgeId, &Edge)> {
930 self.edges.iter()
931 }
932
933 pub fn sorted_node_ids(&self) -> Vec<NodeId> {
935 let mut ids: Vec<NodeId> = self.nodes.keys().copied().collect();
936 ids.sort();
937 ids
938 }
939}
940
941impl Default for Graph {
942 fn default() -> Self {
943 Self::new()
944 }
945}
946
947pub struct GraphBuilder {
949 graph: Graph,
950 tolerance: f64,
951 node_index: Vec<(Coordinate, NodeId)>,
953}
954
955impl GraphBuilder {
956 pub fn new() -> Self {
958 Self {
959 graph: Graph::new(),
960 tolerance: 1e-6,
961 node_index: Vec::new(),
962 }
963 }
964
965 pub fn with_graph_type(graph_type: GraphType) -> Self {
967 Self {
968 graph: Graph::with_type(graph_type),
969 tolerance: 1e-6,
970 node_index: Vec::new(),
971 }
972 }
973
974 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
976 self.tolerance = tolerance;
977 self
978 }
979
980 pub fn add_linestring(
982 &mut self,
983 linestring: &LineString,
984 weight_fn: impl Fn(f64) -> f64,
985 ) -> Result<Vec<EdgeId>> {
986 let coords = &linestring.coords;
987 if coords.len() < 2 {
988 return Err(AlgorithmError::InvalidGeometry(
989 "Linestring must have at least 2 coordinates".to_string(),
990 ));
991 }
992
993 let mut edge_ids = Vec::new();
994 let mut prev_node = self.find_or_create_node(coords[0]);
995
996 for i in 1..coords.len() {
997 let curr_node = self.find_or_create_node(coords[i]);
998 let dx = coords[i].x - coords[i - 1].x;
999 let dy = coords[i].y - coords[i - 1].y;
1000 let length = (dx * dx + dy * dy).sqrt();
1001 let weight = weight_fn(length);
1002 let edge_id = self.graph.add_edge(prev_node, curr_node, weight)?;
1003 edge_ids.push(edge_id);
1004 prev_node = curr_node;
1005 }
1006
1007 Ok(edge_ids)
1008 }
1009
1010 pub fn add_linestring_weighted(
1012 &mut self,
1013 linestring: &LineString,
1014 weight_fn: impl Fn(f64) -> EdgeWeight,
1015 ) -> Result<Vec<EdgeId>> {
1016 let coords = &linestring.coords;
1017 if coords.len() < 2 {
1018 return Err(AlgorithmError::InvalidGeometry(
1019 "Linestring must have at least 2 coordinates".to_string(),
1020 ));
1021 }
1022
1023 let mut edge_ids = Vec::new();
1024 let mut prev_node = self.find_or_create_node(coords[0]);
1025
1026 for i in 1..coords.len() {
1027 let curr_node = self.find_or_create_node(coords[i]);
1028 let dx = coords[i].x - coords[i - 1].x;
1029 let dy = coords[i].y - coords[i - 1].y;
1030 let length = (dx * dx + dy * dy).sqrt();
1031 let multi_weight = weight_fn(length);
1032 let edge_id = self
1033 .graph
1034 .add_weighted_edge(prev_node, curr_node, multi_weight)?;
1035 edge_ids.push(edge_id);
1036 prev_node = curr_node;
1037 }
1038
1039 Ok(edge_ids)
1040 }
1041
1042 fn find_or_create_node(&mut self, coord: Coordinate) -> NodeId {
1044 for (existing_coord, node_id) in &self.node_index {
1045 let dx = existing_coord.x - coord.x;
1046 let dy = existing_coord.y - coord.y;
1047 if (dx * dx + dy * dy).sqrt() < self.tolerance {
1048 return *node_id;
1049 }
1050 }
1051 let node_id = self.graph.add_node(coord);
1052 self.node_index.push((coord, node_id));
1053 node_id
1054 }
1055
1056 pub fn build(self) -> Graph {
1058 self.graph
1059 }
1060
1061 pub fn build_validated(self) -> Result<Graph> {
1063 let graph = self.graph;
1064 graph.validate()?;
1065 Ok(graph)
1066 }
1067}
1068
1069impl Default for GraphBuilder {
1070 fn default() -> Self {
1071 Self::new()
1072 }
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078
1079 #[test]
1080 fn test_create_graph() {
1081 let mut graph = Graph::new();
1082 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1083 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1084 assert_eq!(graph.num_nodes(), 2);
1085 let edge = graph.add_edge(n1, n2, 1.0);
1086 assert!(edge.is_ok());
1087 assert_eq!(graph.num_edges(), 1);
1088 }
1089
1090 #[test]
1091 fn test_undirected_graph() {
1092 let mut graph = Graph::with_type(GraphType::Undirected);
1093 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1094 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1095 let _ = graph.add_edge(n1, n2, 1.0);
1096 assert!(graph.neighbors(n1).contains(&n2));
1097 assert!(graph.neighbors(n2).contains(&n1));
1098 }
1099
1100 #[test]
1101 fn test_directed_graph_one_way() {
1102 let mut graph = Graph::with_type(GraphType::Directed);
1103 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1104 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1105 let _ = graph.add_edge(n1, n2, 1.0);
1106 assert!(graph.neighbors(n1).contains(&n2));
1107 assert!(graph.neighbors(n2).is_empty());
1108 }
1109
1110 #[test]
1111 fn test_adjacency() {
1112 let mut graph = Graph::new();
1113 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1114 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1115 let n3 = graph.add_node(Coordinate::new_2d(2.0, 0.0));
1116 let _ = graph.add_edge(n1, n2, 1.0).expect("add edge");
1117 let _ = graph.add_edge(n1, n3, 2.0).expect("add edge");
1118 let neighbors = graph.neighbors(n1);
1119 assert_eq!(neighbors.len(), 2);
1120 }
1121
1122 #[test]
1123 fn test_nearest_node() {
1124 let mut graph = Graph::new();
1125 let _n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1126 let n2 = graph.add_node(Coordinate::new_2d(5.0, 0.0));
1127 let _n3 = graph.add_node(Coordinate::new_2d(10.0, 0.0));
1128 assert_eq!(graph.nearest_node(&Coordinate::new_2d(4.5, 0.0)), Some(n2));
1129 }
1130
1131 #[test]
1132 fn test_k_nearest_nodes() {
1133 let mut graph = Graph::new();
1134 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1135 let n2 = graph.add_node(Coordinate::new_2d(5.0, 0.0));
1136 let _n3 = graph.add_node(Coordinate::new_2d(10.0, 0.0));
1137 let nearest = graph.k_nearest_nodes(&Coordinate::new_2d(1.0, 0.0), 2);
1138 assert_eq!(nearest.len(), 2);
1139 assert_eq!(nearest[0].0, n1);
1140 assert_eq!(nearest[1].0, n2);
1141 }
1142
1143 #[test]
1144 fn test_graph_builder() {
1145 let coords = vec![
1146 Coordinate::new_2d(0.0, 0.0),
1147 Coordinate::new_2d(1.0, 0.0),
1148 Coordinate::new_2d(2.0, 0.0),
1149 ];
1150 let linestring = LineString::new(coords).expect("linestring");
1151 let mut builder = GraphBuilder::new();
1152 let edges = builder.add_linestring(&linestring, |length| length);
1153 assert!(edges.is_ok());
1154 let graph = builder.build();
1155 assert_eq!(graph.num_nodes(), 3);
1156 assert_eq!(graph.num_edges(), 2);
1157 }
1158
1159 #[test]
1160 fn test_graph_metrics() {
1161 let mut graph = Graph::new();
1162 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1163 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1164 let n3 = graph.add_node(Coordinate::new_2d(2.0, 0.0));
1165 let _ = graph.add_edge(n1, n2, 1.0);
1166 let _ = graph.add_edge(n2, n3, 1.0);
1167 let metrics = graph.metrics();
1168 assert_eq!(metrics.num_nodes, 3);
1169 assert_eq!(metrics.num_edges, 2);
1170 assert_eq!(metrics.total_weight, 2.0);
1171 }
1172
1173 #[test]
1174 fn test_edge_travel_time() {
1175 let coords = vec![
1176 Coordinate::new_2d(0.0, 0.0),
1177 Coordinate::new_2d(1000.0, 0.0),
1178 ];
1179 let linestring = LineString::new(coords).expect("linestring");
1180 let edge = Edge::new(EdgeId(0), NodeId(0), NodeId(1), 1.0)
1181 .with_geometry(linestring)
1182 .with_speed_limit(60.0);
1183 let time = edge.travel_time().expect("travel time");
1184 assert!((time - 1.0 / 60.0).abs() < 1e-6);
1185 }
1186
1187 #[test]
1188 fn test_remove_edge() {
1189 let mut graph = Graph::new();
1190 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1191 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1192 let e1 = graph.add_edge(n1, n2, 1.0).expect("add edge");
1193 assert!(graph.remove_edge(e1).is_ok());
1194 assert_eq!(graph.num_edges(), 0);
1195 }
1196
1197 #[test]
1198 fn test_remove_node() {
1199 let mut graph = Graph::new();
1200 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1201 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1202 let n3 = graph.add_node(Coordinate::new_2d(2.0, 0.0));
1203 let _ = graph.add_edge(n1, n2, 1.0);
1204 let _ = graph.add_edge(n2, n3, 1.0);
1205 assert!(graph.remove_node(n2).is_ok());
1206 assert_eq!(graph.num_nodes(), 2);
1207 assert_eq!(graph.num_edges(), 0);
1208 }
1209
1210 #[test]
1211 fn test_edge_weight_multi_criteria() {
1212 let weight = EdgeWeight::new(100.0, 60.0, 5.0).with_custom("elevation".to_string(), 10.0);
1213 let mut criteria = HashMap::new();
1214 criteria.insert("distance".to_string(), 1.0);
1215 criteria.insert("time".to_string(), 2.0);
1216 criteria.insert("monetary".to_string(), 0.5);
1217 criteria.insert("elevation".to_string(), 0.3);
1218 let cost = weight.weighted_cost(&criteria);
1219 assert!((cost - 225.5).abs() < 1e-10);
1220 }
1221
1222 #[test]
1223 fn test_subgraph() {
1224 let mut graph = Graph::new();
1225 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1226 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1227 let n3 = graph.add_node(Coordinate::new_2d(2.0, 0.0));
1228 let _ = graph.add_edge(n1, n2, 1.0);
1229 let _ = graph.add_edge(n2, n3, 1.0);
1230 let sub = graph.subgraph(&[n1, n2]).expect("subgraph");
1231 assert_eq!(sub.num_nodes(), 2);
1232 assert_eq!(sub.num_edges(), 1);
1233 }
1234
1235 #[test]
1236 fn test_graph_reverse() {
1237 let mut graph = Graph::with_type(GraphType::Directed);
1238 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1239 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1240 let _ = graph.add_edge(n1, n2, 1.0);
1241 let reversed = graph.reverse();
1242 assert!(reversed.neighbors(n2).contains(&n1));
1243 assert!(reversed.neighbors(n1).is_empty());
1244 }
1245
1246 #[test]
1247 fn test_find_edge() {
1248 let mut graph = Graph::new();
1249 let n1 = graph.add_node(Coordinate::new_2d(0.0, 0.0));
1250 let n2 = graph.add_node(Coordinate::new_2d(1.0, 0.0));
1251 let e1 = graph.add_edge(n1, n2, 1.0).expect("add edge");
1252 assert_eq!(graph.find_edge(n1, n2), Some(e1));
1253 assert_eq!(graph.find_edge(n2, n1), None);
1254 }
1255
1256 #[test]
1257 fn test_builder_weighted() {
1258 let coords = vec![Coordinate::new_2d(0.0, 0.0), Coordinate::new_2d(1.0, 0.0)];
1259 let linestring = LineString::new(coords).expect("linestring");
1260 let mut builder = GraphBuilder::new();
1261 let edges = builder.add_linestring_weighted(&linestring, |len| {
1262 EdgeWeight::from_distance_time(len, len / 50.0)
1263 });
1264 assert!(edges.is_ok());
1265 assert_eq!(builder.build().num_edges(), 1);
1266 }
1267}