From 17b30b1938fb08dca12afb2cde2247a49bfa80c2 Mon Sep 17 00:00:00 2001
From: Sergi Hernandez Juan <shernand@iri.upc.edu>
Date: Tue, 19 Jan 2021 10:48:33 +0100
Subject: [PATCH] Added some functions: * to get the pose at a given distance
 in the link. * to get the curvature at a given distance in the link. * to get
 the closest node, node index and pose to a given point. Added the link
 reference update to the clone(),get_sub_lane(), update_start_node() and
 update_end_node() functions.

---
 include/opendrive_lane.h |  16 ++--
 src/opendrive_lane.cpp   | 162 +++++++++++++++++++++++++++++++--------
 2 files changed, 142 insertions(+), 36 deletions(-)

diff --git a/include/opendrive_lane.h b/include/opendrive_lane.h
index 4208f4c..10b9977 100644
--- a/include/opendrive_lane.h
+++ b/include/opendrive_lane.h
@@ -36,7 +36,7 @@ class COpendriveLane
     COpendriveLane(const COpendriveLane &object);
     void load(const ::lane &lane_info,COpendriveRoadSegment *segment);
     void link_neightbour_lane(COpendriveLane *lane);
-    void link_lane(COpendriveLane *lane,opendrive_mark_t mark,bool from_start,bool to_start);
+    void link_lane(COpendriveLane *lane,opendrive_mark_t mark,bool from_start,bool to_start,bool belongs_to_lane);
     void add_next_lane(COpendriveLane *lane);
     void add_prev_lane(COpendriveLane *lane);
     void remove_lane(COpendriveLane *lane);
@@ -56,10 +56,10 @@ class COpendriveLane
     bool updated(lane_up_ref_t &refs);
     void update_references(segment_up_ref_t &segment_refs,lane_up_ref_t &lane_refs,node_up_ref_t &node_refs);
     void clean_references(segment_up_ref_t &segment_refs,lane_up_ref_t &lane_refs,node_up_ref_t &node_refs);
-    void update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *start=NULL);
-    void update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *end=NULL);
-    COpendriveLane *get_sub_lane(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *start=NULL,TOpendriveWorldPose *end=NULL);
-    COpendriveLane *clone(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref);
+    void update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *start=NULL);
+    void update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *end=NULL);
+    COpendriveLane *get_sub_lane(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *start=NULL,TOpendriveWorldPose *end=NULL);
+    COpendriveLane *clone(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref);
   public:
     unsigned int get_num_nodes(void) const;
     const COpendriveRoadNode &get_node(unsigned int index) const;
@@ -77,9 +77,13 @@ class COpendriveLane
     TOpendriveWorldPose get_start_pose(void) const;
     TOpendriveWorldPose get_end_pose(void) const;
     double get_length(void) const;
+    TOpendriveWorldPose get_pose_at(double length);
+    double get_curvature_at(double length);    
     TOpendriveLocalPose transform_to_local_pose(TOpendriveTrackPose &track) const;
     TOpendriveWorldPose transform_to_world_pose(TOpendriveTrackPose &track) const;
-    unsigned int get_closest_node_index(TOpendriveWorldPose &pose,double angle_tol=0.1);
+    const COpendriveRoadNode &get_closest_node(TOpendriveWorldPose &pose,double &distance,double angle_tol=0.1) const;
+    unsigned int get_closest_node_index(TOpendriveWorldPose &pose,double &distance,double angle_tol=0.1) const;
+    double get_closest_pose(TOpendriveWorldPose &pose,TOpendriveWorldPose &closest_pose,double angle_tol=0.1) const;
     friend std::ostream& operator<<(std::ostream& out, COpendriveLane &lane);
     ~COpendriveLane();
 };
diff --git a/src/opendrive_lane.cpp b/src/opendrive_lane.cpp
index 57f9d9e..8e2f245 100644
--- a/src/opendrive_lane.cpp
+++ b/src/opendrive_lane.cpp
@@ -52,12 +52,9 @@ void COpendriveLane::link_neightbour_lane(COpendriveLane *lane)
       min_num_nodes=lane->get_num_nodes();
     for(unsigned int i=0;i<min_num_nodes-1;i++)
     {
-      this->nodes[i]->add_link(lane->nodes[i+1],this->left_mark,this->segment,this);
-      lane->nodes[i]->add_link(this->nodes[i+1],this->left_mark,this->segment,lane);
-      if(this->id>0)// left lanes
-        this->right_mark=lane->left_mark;
-      else// right lanes
-        this->left_mark=lane->right_mark;
+      this->nodes[i]->add_link(lane->nodes[i+1],lane->right_mark,this->segment,NULL);
+      lane->nodes[i]->add_link(this->nodes[i+1],lane->right_mark,this->segment,NULL);
+      this->left_mark=lane->right_mark;
     } 
     if(min_num_nodes>0)
     {
@@ -67,7 +64,7 @@ void COpendriveLane::link_neightbour_lane(COpendriveLane *lane)
   }
 }
 
-void COpendriveLane::link_lane(COpendriveLane *lane,opendrive_mark_t mark,bool from_start, bool to_start)
+void COpendriveLane::link_lane(COpendriveLane *lane,opendrive_mark_t mark,bool from_start, bool to_start,bool belongs_to_lane)
 {
   COpendriveRoadNode *start,*end;
   std::stringstream error;
@@ -118,7 +115,10 @@ void COpendriveLane::link_lane(COpendriveLane *lane,opendrive_mark_t mark,bool f
             end=lane->nodes[0];
         }
       }
-      start->add_link(end,mark,this->segment,this);
+      if(belongs_to_lane)
+        start->add_link(end,mark,this->segment,this);
+      else
+        start->add_link(end,mark,this->segment,NULL);
       // link lane
       this->add_next_lane(lane);
       lane->add_prev_lane(this);
@@ -350,22 +350,26 @@ void COpendriveLane::clean_references(segment_up_ref_t &segment_refs,lane_up_ref
   }
 }
 
-void COpendriveLane::update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *start)
+void COpendriveLane::update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *start)
 {
   std::vector<COpendriveRoadNode *>::iterator it;
   segment_up_ref_t new_segment_ref;
   unsigned int start_node_index,i;
+  TOpendriveWorldPose start_pose;
   COpendriveRoadNode *new_node;
   std::stringstream error;
+  double distance;
   
   if(start==NULL)
     return;
-  start_node_index=this->get_closest_node_index(*start,3.14159);
+  start_node_index=this->get_closest_node_index(*start,distance,3.14159);
   if(start_node_index==(unsigned int)-1)
   {
     error << "No node close to (x=" << start->x << ",y=" << start->y << ",heading=" << start->heading << ") in lane " << this->id << " of segment " << this->segment->get_name();
     throw CException(_HERE_,error.str());
   }
+  // get the actual start position on the lane
+  this->get_closest_pose(*start,start_pose,3.14159);
   // eliminate all the node before the start one
   for(it=this->nodes.begin(),i=0;it!=this->nodes.end();i++)
   {
@@ -382,7 +386,7 @@ void COpendriveLane::update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t
     {
       if(!(*it)->updated(new_node_ref))
       {
-        new_node=new COpendriveRoadNode(**it);
+        new_node=(*it)->clone(new_link_ref);
         new_node_ref[*it]=new_node;
       }
       it++;
@@ -390,25 +394,28 @@ void COpendriveLane::update_start_node(node_up_ref_t &new_node_ref,lane_up_ref_t
   }
   this->prev.clear();// it is no longer connected to any previous lane
   this->update_references(new_segment_ref,new_lane_ref,new_node_ref);
-  this->nodes[0]->update_start_pose(this,start);
+  this->nodes[0]->update_start_pose(this,start_pose,distance);
 }
 
-void COpendriveLane::update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *end)
+void COpendriveLane::update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *end)
 {
   std::vector<COpendriveRoadNode *>::iterator it;
   segment_up_ref_t new_segment_ref;
   unsigned int end_node_index,i;
+  TOpendriveWorldPose end_pose;
   COpendriveRoadNode *new_node;
   std::stringstream error;
+  double distance;
     
   if(end==NULL)
     return;
-  end_node_index=this->get_closest_node_index(*end,3.14159);
+  end_node_index=this->get_closest_node_index(*end,distance,3.14159);
   if(end_node_index==(unsigned int)-1)
   {
     error << "No node close to (x=" << end->x << ",y=" << end->y << ",heading=" << end->heading << ") in lane " << this->id << " of segment " << this->segment->get_name();
     throw CException(_HERE_,error.str());
   }
+  this->get_closest_pose(*end,end_pose,3.14159);
   // eliminate all the node before the start one
   for(it=this->nodes.begin(),i=0;it!=this->nodes.end();i++)
   {
@@ -425,7 +432,7 @@ void COpendriveLane::update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &
     {
       if(!(*it)->updated(new_node_ref))
       {
-        new_node=new COpendriveRoadNode(**it);
+        new_node=(*it)->clone(new_link_ref);
         new_node_ref[*it]=new_node;
       }
       it++;
@@ -433,32 +440,32 @@ void COpendriveLane::update_end_node(node_up_ref_t &new_node_ref,lane_up_ref_t &
   }
   this->next.clear();// it is no longer connected to any next lane
   this->update_references(new_segment_ref,new_lane_ref,new_node_ref);
-  this->nodes[this->nodes.size()-1]->update_end_pose(this,end);
+  this->nodes[this->nodes.size()-1]->update_end_pose(this,end_pose,distance);
 }
 
-COpendriveLane *COpendriveLane::get_sub_lane(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,TOpendriveWorldPose *start,TOpendriveWorldPose *end)
+COpendriveLane *COpendriveLane::get_sub_lane(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref,TOpendriveWorldPose *start,TOpendriveWorldPose *end)
 {
   COpendriveLane *new_lane;
 
   if(start==NULL && end==NULL)
-    return this->clone(new_node_ref,new_lane_ref);
+    return this->clone(new_node_ref,new_lane_ref,new_link_ref);
   new_lane=new COpendriveLane(*this);
   new_lane_ref[this]=new_lane;
   if(this->id<0)
   {
-    new_lane->update_start_node(new_node_ref,new_lane_ref,start);
-    new_lane->update_end_node(new_node_ref,new_lane_ref,end);
+    new_lane->update_start_node(new_node_ref,new_lane_ref,new_link_ref,start);
+    new_lane->update_end_node(new_node_ref,new_lane_ref,new_link_ref,end);
   }
   else
   {
-    new_lane->update_start_node(new_node_ref,new_lane_ref,end);
-    new_lane->update_end_node(new_node_ref,new_lane_ref,start);
+    new_lane->update_start_node(new_node_ref,new_lane_ref,new_link_ref,end);
+    new_lane->update_end_node(new_node_ref,new_lane_ref,new_link_ref,start);
   }
 
   return new_lane;
 }
 
-COpendriveLane *COpendriveLane::clone(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref)
+COpendriveLane *COpendriveLane::clone(node_up_ref_t &new_node_ref,lane_up_ref_t &new_lane_ref,link_up_ref_t &new_link_ref)
 {
   COpendriveLane *new_lane;
   std::vector<COpendriveRoadNode *>::iterator it;
@@ -469,7 +476,7 @@ COpendriveLane *COpendriveLane::clone(node_up_ref_t &new_node_ref,lane_up_ref_t
   new_lane_ref[this]=new_lane;
   for(it=new_lane->nodes.begin();it!=new_lane->nodes.end();it++)
   {
-    new_node=new COpendriveRoadNode(**it);
+    new_node=(*it)->clone(new_link_ref);
     new_node_ref[*it]=new_node;
   }
   this->update_references(new_segment_ref,new_lane_ref,new_node_ref);
@@ -546,10 +553,7 @@ void COpendriveLane::load(const ::lane &lane_info,COpendriveRoadSegment *segment
     else
       mark=OD_MARK_NONE;
   }
-  if(this->id<0)//right lanes
-    this->right_mark=mark;
-  else
-    this->left_mark=mark;
+  this->right_mark=mark;
 
   this->set_parent_segment(segment);
 }
@@ -707,6 +711,54 @@ double COpendriveLane::get_length(void) const
   return length;
 }
 
+TOpendriveWorldPose COpendriveLane::get_pose_at(double length)
+{
+  TOpendriveWorldPose world_pose={0};
+
+  for(unsigned int i=0;i<this->nodes.size();i++)
+  {
+    for(unsigned int j=0;j<this->nodes[i]->get_num_links();j++)
+    {
+      if(&this->nodes[i]->get_link(j).get_parent_lane()==this)
+      {
+        if((length-this->nodes[i]->links[j]->get_length())<0.0)
+        {
+          world_pose=this->nodes[i]->links[j]->get_pose_at(length);
+          return world_pose;
+        }
+        else
+          length-=this->nodes[i]->links[j]->get_length();
+      }
+    }
+  }
+
+  return world_pose;
+}
+
+double COpendriveLane::get_curvature_at(double length)
+{
+  double curvature;
+
+  for(unsigned int i=0;i<this->nodes.size();i++)
+  {
+    for(unsigned int j=0;j<this->nodes[i]->get_num_links();j++)
+    {
+      if(&this->nodes[i]->get_link(j).get_parent_lane()==this)
+      {
+        if((length-this->nodes[i]->links[j]->get_length())<0.0)
+        {
+          curvature=this->nodes[i]->links[j]->get_curvature_at(length);
+          return curvature;
+        }
+        else
+          length-=this->nodes[i]->links[j]->get_length();
+      }
+    }
+  }
+
+  return 0.0;
+}
+
 TOpendriveLocalPose COpendriveLane::transform_to_local_pose(TOpendriveTrackPose &track) const
 {
   TOpendriveTrackPose pose;
@@ -725,7 +777,7 @@ TOpendriveWorldPose COpendriveLane::transform_to_world_pose(TOpendriveTrackPose
   return this->segment->transform_to_world_pose(pose);
 }
 
-unsigned int COpendriveLane::get_closest_node_index(TOpendriveWorldPose &pose,double angle_tol)
+unsigned int COpendriveLane::get_closest_node_index(TOpendriveWorldPose &pose,double &distance,double angle_tol) const
 {
   double dist,min_dist=std::numeric_limits<double>::max();
   TOpendriveWorldPose found_pose;
@@ -733,7 +785,7 @@ unsigned int COpendriveLane::get_closest_node_index(TOpendriveWorldPose &pose,do
 
   for(unsigned int i=0;i<this->nodes.size();i++)
   {
-    this->nodes[i]->get_closest_pose(pose,found_pose,angle_tol);
+    this->nodes[i]->get_closest_pose(pose,found_pose,true,angle_tol);
     dist=sqrt(pow(found_pose.x-pose.x,2.0)+pow(found_pose.y-pose.y,2.0));
     if(dist<min_dist)
     {
@@ -742,9 +794,59 @@ unsigned int COpendriveLane::get_closest_node_index(TOpendriveWorldPose &pose,do
     }
   }
 
+  distance=min_dist;
   return closest_index;
 }
 
+const COpendriveRoadNode &COpendriveLane::get_closest_node(TOpendriveWorldPose &pose,double &distance,double angle_tol) const
+{
+  unsigned int closest_index;
+
+  closest_index=this->get_closest_node_index(pose,distance,angle_tol);
+  if(closest_index==(unsigned int)-1)
+    throw CException(_HERE_,"Impossible to find the closest node");
+  return *this->nodes[closest_index];
+}
+
+double COpendriveLane::get_closest_pose(TOpendriveWorldPose &pose,TOpendriveWorldPose &closest_pose,double angle_tol) const
+{
+  double dist,min_dist=std::numeric_limits<double>::max(),distance;
+  TOpendriveWorldPose found_pose;
+  unsigned int closest_index=-1;
+  COpendriveLink *link;
+
+  closest_pose.x=std::numeric_limits<double>::max();
+  closest_pose.y=std::numeric_limits<double>::max();
+  closest_pose.heading=std::numeric_limits<double>::max();
+  for(unsigned int i=0;i<this->nodes.size();i++)
+  {
+    this->nodes[i]->get_closest_pose(pose,found_pose,true,angle_tol);
+    dist=sqrt(pow(found_pose.x-pose.x,2.0)+pow(found_pose.y-pose.y,2.0));
+    if(dist<min_dist)
+    {
+      min_dist=dist;
+      closest_index=i;
+      closest_pose=found_pose;
+    }
+  }
+
+  distance=0.0;
+  for(unsigned int i=0;i<this->nodes.size();i++)
+  {
+    if(i<closest_index)
+    {
+      link=this->nodes[i]->get_link_with(this->nodes[i+1]);
+      if(link!=NULL)
+        distance+=link->get_length();
+    }
+    else
+      break;
+  }
+  distance+=min_dist;
+
+  return distance;
+}
+
 std::ostream& operator<<(std::ostream& out, COpendriveLane &lane)
 {
   out << "  Lane: " << lane.id << " (offset: " << lane.offset << ")" << std::endl;
-- 
GitLab