annotation_collection.cpp
Go to the documentation of this file.
00001 /*
00002  * annotation_collection.cpp
00003  *
00004  *  Created on: May 7, 2014
00005  *      Author: jorge
00006  */
00007 
00008 #include <ros/ros.h>
00009 #include <world_canvas_msgs/GetAnnotations.h>
00010 #include <world_canvas_msgs/GetAnnotationsData.h>
00011 #include <world_canvas_msgs/PubAnnotationsData.h>
00012 #include <world_canvas_msgs/DeleteAnnotations.h>
00013 #include <world_canvas_msgs/SaveAnnotationsData.h>
00014 #include <visualization_msgs/MarkerArray.h>
00015 
00016 #include "world_canvas_client_cpp/annotation_collection.hpp"
00017 
00018 
00019 namespace wcf
00020 {
00021 
00022 AnnotationCollection::AnnotationCollection(const std::string& world,
00023                                            const std::string& srv_namespace)
00024   : AnnotationCollection(FilterCriteria(world), srv_namespace)
00025 {
00026 }
00027 
00028 AnnotationCollection::AnnotationCollection(const FilterCriteria& criteria,
00029                                            const std::string& srv_namespace)
00030   : WorldCanvasClient(srv_namespace), filter(criteria)
00031 {
00032   // Filter parameters provided, so don't wait more to retrieve annotations!
00033   this->filterBy(criteria);
00034 }
00035 
00036 AnnotationCollection::~AnnotationCollection()
00037 {
00038 }
00039 
00040 
00041 bool AnnotationCollection::filterBy(const FilterCriteria& criteria)
00042 {
00043   this->filter = criteria;
00044 
00045   try
00046   {
00047     ros::ServiceClient client =
00048         this->getServiceHandle<world_canvas_msgs::GetAnnotations>("get_annotations");
00049 
00050     ROS_INFO("Getting annotations for world %s and additional filter criteria",
00051              this->filter.getWorld().c_str());
00052     world_canvas_msgs::GetAnnotations srv;
00053     srv.request.world         = this->filter.getWorld();
00054     srv.request.ids           = this->filter.getUuids();
00055     srv.request.names         = this->filter.getNames();
00056     srv.request.types         = this->filter.getTypes();
00057     srv.request.keywords      = this->filter.getKeywords();
00058     srv.request.relationships = this->filter.getRelationships();
00059     if (client.call(srv))
00060     {
00061       if (srv.response.result == true)
00062       {
00063         if (srv.response.annotations.size() > 0)
00064         {
00065           ROS_INFO("%lu annotations found", srv.response.annotations.size());
00066         }
00067         else
00068         {
00069           ROS_INFO("No annotations found for world %s with the given search criteria",
00070                    this->filter.getWorld().c_str());
00071         }
00072         this->annotations = srv.response.annotations;
00073         return true;
00074       }
00075       else
00076       {
00077         ROS_ERROR("Server reported an error: %s", srv.response.message.c_str());
00078         return false;
00079       }
00080     }
00081     else
00082     {
00083       ROS_ERROR("Failed to call get_annotations service");
00084       return false;
00085     }
00086   }
00087   catch (const ros::Exception& e)
00088   {
00089     ROS_ERROR("ROS exception caught: %s", e.what());
00090     return false;
00091   }
00092 }
00093 
00094 bool AnnotationCollection::load()
00095 {
00096   // Retrieve annotations with current filter parameters
00097   return this->filterBy(this->filter);
00098 }
00099 
00100 bool AnnotationCollection::loadData()
00101 {
00102   if (this->annotations.size() == 0)
00103   {
00104     ROS_ERROR("No annotations retrieved. Nothing to load!");
00105     return false;
00106   }
00107 
00108   try
00109   {
00110     ros::ServiceClient client =
00111         this->getServiceHandle<world_canvas_msgs::GetAnnotationsData>("get_annotations_data");
00112 
00113     // Request from server the data for annotations previously retrieved; note that we send data
00114     // uuids, that identify the data associated to the annotation instead of the annotation itself
00115     ROS_INFO("Loading data for the %lu retrieved annotations", this->annotations.size());
00116     world_canvas_msgs::GetAnnotationsData srv;
00117     srv.request.annotation_ids = this->getAnnotsDataIDs();
00118     if (client.call(srv))
00119     {
00120       if (srv.response.result == true)
00121       {
00122         if (srv.response.data.size() > 0)
00123         {
00124           ROS_INFO("%lu annotations data found", srv.response.data.size());
00125         }
00126         else
00127         {
00128           ROS_INFO("No data found for the %lu retrieved annotations", this->annotations.size());
00129         }
00130         this->annots_data = srv.response.data;
00131         return true;
00132       }
00133       else
00134       {
00135         ROS_ERROR("Server reported an error: %s", srv.response.message.c_str());
00136         return false;
00137       }
00138     }
00139     else
00140     {
00141       ROS_ERROR("Failed to call get_annotations_data service");
00142       return false;
00143     }
00144   }
00145   catch (const ros::Exception& e)
00146   {
00147     ROS_ERROR("ROS exception caught: %s", e.what());
00148     return false;
00149   }
00150 }
00151 
00152 bool AnnotationCollection::save()
00153 {
00154   try
00155   {
00156     ros::ServiceClient client =
00157         this->getServiceHandle<world_canvas_msgs::SaveAnnotationsData>("save_annotations_data");
00158 
00159     // Request server to save current annotations list, with its data
00160     ROS_INFO("Requesting server to save annotations");
00161     world_canvas_msgs::SaveAnnotationsData srv;
00162   //  srv.request.annotations = this->annotations;
00163   //  srv.request.data        = this->annots_data;
00164 
00165     // This brittle saving procedure requires parallelly ordered annotations and data vectors
00166     // As this don't need to be the case, we must short them; but we need a better saving procedure (TODO)
00167     for (unsigned int i = 0; i < this->annotations.size(); i++)
00168     {
00169       for (unsigned int j = 0; j < this->annots_data.size(); j++)
00170       {
00171         if (this->annots_data[j].id.uuid == this->annotations[i].data_id.uuid)
00172         {
00173           ROS_DEBUG("Add annotation for saving with uuid '%s'", unique_id::toHexString(this->annotations[i].id).c_str());
00174           ROS_DEBUG("Add annot. data for saving with uuid '%s'", unique_id::toHexString(this->annots_data[j].id).c_str());
00175           srv.request.annotations.push_back(this->annotations[i]);
00176           srv.request.data.push_back(this->annots_data[j]);
00177           break;
00178         }
00179       }
00180     }
00181 
00182     // Do at least a rudimentary check
00183     if (! (this->annotations.size() == this->annots_data.size() == srv.request.annotations.size() == srv.request.data.size()))
00184     {
00185       ROS_ERROR("Incoherent annotation and data sizes: %lu != %lu != %lu != %lu",
00186                 this->annotations.size(), this->annots_data.size(), srv.request.annotations.size(), srv.request.data.size());
00187     }
00188 
00189     bool result = false;
00190     if (client.call(srv))
00191     {
00192       if (srv.response.result == true)
00193       {
00194         result = true;
00195       }
00196       else
00197       {
00198         ROS_ERROR("Server reported an error: %s", srv.response.message.c_str());
00199       }
00200     }
00201     else
00202     {
00203       ROS_ERROR("Failed to call save_annotations_data service");
00204     }
00205 
00206     if (result == true)
00207       saved = true;
00208 
00209     return result && saveDeletes();
00210   }
00211   catch (const ros::Exception& e)
00212   {
00213     ROS_ERROR("ROS exception caught: %s", e.what());
00214     return false;
00215   }
00216 }
00217 
00218 bool AnnotationCollection::saveDeletes()
00219 {
00220   // We remove from database the annotations doomed by delete method, if any
00221   if (annots_to_delete.size() == 0)
00222     return true;
00223 
00224   try
00225   {
00226     ros::ServiceClient client =
00227         this->getServiceHandle<world_canvas_msgs::DeleteAnnotations>("delete_annotations");
00228 
00229     // Request server to save current annotations list, with its data
00230     ROS_INFO("Requesting server to delete annotations");
00231     world_canvas_msgs::DeleteAnnotations srv;
00232     srv.request.annotations = annots_to_delete;
00233     if (client.call(srv))
00234     {
00235       if (srv.response.result == true)
00236       {
00237         return true;
00238       }
00239       else
00240       {
00241         ROS_ERROR("Server reported an error: %s", srv.response.message.c_str());
00242         return false;
00243       }
00244     }
00245     else
00246     {
00247       ROS_ERROR("Failed to call delete_annotations service");
00248       return false;
00249     }
00250   }
00251   catch (const ros::Exception& e)
00252   {
00253     ROS_ERROR("ROS exception caught: %s", e.what());
00254     return false;
00255   }
00256 }
00257 
00258 bool AnnotationCollection::add(const world_canvas_msgs::Annotation& annotation,
00259                                const world_canvas_msgs::AnnotationData& annot_data)
00260 {
00261   if (annotation.data_id.uuid != annot_data.id.uuid)
00262   {
00263     ROS_ERROR("Incoherent annotation and data uuids '%s' != '%s'",
00264               unique_id::toHexString(annotation.id).c_str(), unique_id::toHexString(annot_data.id).c_str());
00265     return false;
00266   }
00267 
00268   for (unsigned int i = 0; i < this->annotations.size(); i++)
00269   {
00270     if (this->annotations[i].id.uuid == annotation.id.uuid)
00271     {
00272       ROS_ERROR("Duplicated annotation with uuid '%s'", unique_id::toHexString(annotation.id).c_str());
00273       return false;
00274     }
00275   }
00276 
00277   for (unsigned int i = 0; i < this->annots_data.size(); i++)
00278   {
00279     if (this->annots_data[i].id.uuid == annot_data.id.uuid)
00280     {
00281       ROS_ERROR("Duplicated annotation data with uuid '%s'", unique_id::toHexString(annot_data.id).c_str());
00282       return false;
00283     }
00284   }
00285 
00286   this->annotations.push_back(annotation);
00287   this->annots_data.push_back(annot_data);
00288 
00289   // Re-publish annotations' visual markers to reflect the incorporation
00290   this->publishMarkers("annotation_markers");
00291 
00292   saved = false;
00293 
00294   return true;
00295 }
00296 
00297 bool AnnotationCollection::update(const world_canvas_msgs::Annotation& annotation,
00298                                   const world_canvas_msgs::AnnotationData& annot_data)
00299 {
00300   if (annotation.data_id.uuid != annot_data.id.uuid)
00301   {
00302     ROS_ERROR("Incoherent annotation and data uuids '%s' != '%s'",
00303               unique_id::toHexString(annotation.id).c_str(), unique_id::toHexString(annot_data.id).c_str());
00304     return false;
00305   }
00306 
00307   bool found = false;
00308   for (unsigned int i = 0; i < this->annotations.size(); i++)
00309   {
00310     if (this->annotations[i].id.uuid == annotation.id.uuid)
00311     {
00312       this->annotations[i] = annotation;
00313       found = true;
00314       break;
00315     }
00316   }
00317 
00318   if (found == false)
00319   {
00320     ROS_ERROR("Annotation uuid '%s' not found", unique_id::toHexString(annotation.id).c_str());
00321     return false;
00322   }
00323 
00324   found = false;
00325   for (unsigned int i = 0; i < this->annots_data.size(); i++)
00326   {
00327     if (this->annots_data[i].id.uuid == annot_data.id.uuid)
00328     {
00329       this->annots_data[i] = annot_data;
00330       found = true;
00331       break;
00332     }
00333   }
00334 
00335   if (found == false)
00336   {
00337     ROS_ERROR("Annotation data uuid '%s' not found", unique_id::toHexString(annot_data.id).c_str());
00338     return false;
00339   }
00340 
00341   // Re-publish annotations' visual markers to reflect changes
00342   this->publishMarkers("annotation_markers");
00343 
00344   saved = false;
00345 
00346   return true;
00347 }
00348 
00349 bool AnnotationCollection::remove(const uuid_msgs::UniqueID& id)
00350 {
00351   for (unsigned int i = 0; i < this->annotations.size(); i++)
00352   {
00353     if (this->annotations[i].id.uuid == id.uuid)
00354     {
00355       ROS_DEBUG("Annotation '%s' found", unique_id::toHexString(id).c_str());
00356 
00357       for (unsigned int j = 0; j < this->annots_data.size(); j++)
00358       {
00359         if (this->annots_data[j].id.uuid == this->annotations[i].data_id.uuid)
00360         {
00361           annots_to_delete.push_back(this->annotations[i]);
00362           saved = false;
00363 
00364           ROS_DEBUG("Removed annotation with uuid '%s'", unique_id::toHexString(this->annotations[i].id).c_str());
00365           ROS_DEBUG("Removed annot. data with uuid '%s'", unique_id::toHexString(this->annots_data[j].id).c_str());
00366           this->annotations.erase(this->annotations.begin() + i);
00367           this->annots_data.erase(this->annots_data.begin() + j);
00368 
00369           // Re-publish annotations' visual markers to reflect the leave
00370           this->publishMarkers("annotation_markers");
00371 
00372           return true;
00373         }
00374       }
00375 
00376       ROS_ERROR("No data found for annotation '%s' (data uuid is '%s')", unique_id::toHexString(id).c_str(),
00377                 unique_id::toHexString(this->annotations[i].data_id).c_str());
00378       return false;
00379     }
00380   }
00381 
00382   ROS_WARN("Annotation '%s' not found", unique_id::toHexString(id).c_str());
00383   return false;
00384 }
00385 
00386 bool AnnotationCollection::clearMarkers(const std::string& topic)
00387 {
00388   visualization_msgs::MarkerArray markers_array;
00389   visualization_msgs::Marker delete_all;
00390   delete_all.header.frame_id = "/map";  // TODO  OK? seems to work, but I always have /map as ref
00391   delete_all.action = 3; // visualization_msgs::Marker::DELETEALL is commented but it works!
00392   markers_array.markers.push_back(delete_all);
00393 
00394   // Check if the given topic has been already advertised for single or multiple markers publishing
00395   if (endsWith(marker_pub.getTopic(), topic) == true)
00396   {
00397     marker_pub.publish(markers_array);
00398     return true;
00399   }
00400 
00401   if (endsWith(markers_pub.getTopic(), topic) == true)
00402   {
00403     markers_pub.publish(markers_array);
00404     return true;
00405   }
00406 
00407   // Topic not yet advertised; do it!
00408   markers_pub = nh.advertise <visualization_msgs::MarkerArray> (topic, 1, true);
00409   markers_pub.publish(markers_array);
00410   return true;
00411 }
00412 
00413 bool AnnotationCollection::publishMarkers(const std::string& topic, bool clear_existing)
00414 {
00415   if (clear_existing == true)
00416     clearMarkers(topic);
00417 
00418   if (this->annotations.size() == 0)
00419   {
00420     ROS_ERROR("No annotations retrieved. Nothing to publish!");
00421     return false;
00422   }
00423 
00424   if (endsWith(markers_pub.getTopic(), topic) == false)
00425   {
00426     // Advertise a topic for retrieved annotations' visualization markers
00427     markers_pub = nh.advertise <visualization_msgs::MarkerArray> (topic, 1, true);
00428   }
00429 
00430   // Process retrieved data to build markers lists
00431   visualization_msgs::MarkerArray markers_array;
00432   for (unsigned int i = 0; i < this->annotations.size(); i++)
00433   {
00434     markers_array.markers.push_back(makeMarker(i, this->annotations[i]));
00435     markers_array.markers.push_back(makeLabel(markers_array.markers.back()));
00436   }
00437 
00438   markers_pub.publish(markers_array);
00439   return true;
00440 }
00441 
00442 bool AnnotationCollection::publishMarker(const std::string& topic, int marker_id,
00443                                          const world_canvas_msgs::Annotation& ann,
00444                                          bool clear_existing)
00445 {
00446   if (endsWith(marker_pub.getTopic(), topic) == false)
00447   {
00448     // Advertise a topic to publish a visual marker for the given annotation
00449     // Use a different publisher from the one created on publishMarkers so both can be used in parallel
00450     marker_pub = nh.advertise <visualization_msgs::MarkerArray> (topic, 1, true);
00451   }
00452 
00453   visualization_msgs::MarkerArray markers_array;
00454 
00455   if (clear_existing == true)
00456     clearMarkers(topic);
00457 
00458   markers_array.markers.push_back(makeMarker(marker_id, ann));
00459   markers_array.markers.push_back(makeLabel(markers_array.markers.back()));
00460   marker_pub.publish(markers_array);
00461 
00462   return true;
00463 }
00464 
00465 visualization_msgs::Marker AnnotationCollection::makeMarker(int id, const world_canvas_msgs::Annotation& ann)
00466 {
00467   std::stringstream name; name << ann.name << " [" << ann.type << "]";
00468 
00469   visualization_msgs::Marker marker;
00470   marker.header.frame_id = ann.pose.header.frame_id;
00471   marker.header.stamp = ros::Time::now();
00472   marker.scale = ann.size;
00473   marker.color = ann.color;
00474   marker.ns = name.str();
00475   marker.id = id;
00476   marker.pose = ann.pose.pose.pose;
00477   marker.type = ann.shape;
00478   marker.action = visualization_msgs::Marker::ADD;
00479 
00480   return marker;
00481 }
00482 
00483 visualization_msgs::Marker AnnotationCollection::makeLabel(const visualization_msgs::Marker& marker)
00484 {
00485   visualization_msgs::Marker label = marker;
00486   label.id = marker.id + 1000000;  // marker id must be unique
00487   label.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
00488   label.pose.position.z = marker.pose.position.z + marker.scale.z/2.0 + 0.1; // just above the visual
00489   label.text = marker.ns != " []" ? marker.ns : "";
00490   label.scale.x = label.scale.y = label.scale.z = 0.12;
00491   label.color = marker.color;
00492 
00493   return label;
00494 }
00495 
00496 
00497 bool AnnotationCollection::publish(const std::string& topic_name, const std::string& topic_type,
00498                                    bool by_server, bool as_list)
00499 {
00500   if (this->annotations.size() == 0)
00501   {
00502     ROS_ERROR("No annotations retrieved. Nothing to publish!");
00503     return false;
00504   }
00505 
00506   std::string common_tt = topic_type;
00507 
00508   if (common_tt.empty() == true)
00509   {
00510     if (as_list == true)
00511     {
00512       ROS_ERROR("Topic type argument is mandatory if as_list is true");
00513       return false;
00514     }
00515     else
00516     {
00517       // Take annotations type and verify that it's the same within the
00518       // collection (as we will publish all of them in the same topic)
00519       for (unsigned int i = 0; i < this->annotations.size(); i++)
00520       {
00521         if ((common_tt.empty() == false) && (common_tt != annotations[i].type))
00522         {
00523           ROS_ERROR("Cannot publish annotations of different types (%s, %s)",
00524                     common_tt.c_str(), annotations[i].type.c_str());
00525           return false;
00526         }
00527         common_tt = annotations[i].type;
00528       }
00529     }
00530   }
00531 
00532   try
00533   {
00534     if (by_server == true)
00535     {
00536       ros::ServiceClient client =
00537           this->getServiceHandle<world_canvas_msgs::PubAnnotationsData>("pub_annotations_data");
00538 
00539       // Request server to publish the annotations previously retrieved; note that we send the data
00540       // uuids, that identify the data associated to the annotation instead of the annotation itself
00541       ROS_INFO("Requesting server to publish annotations of type '%s'", common_tt.c_str());
00542       world_canvas_msgs::PubAnnotationsData srv;
00543       if (as_list == true)
00544       {
00545         // We try to publish all annotations if the user request to do so as a list, but...
00546         srv.request.annotation_ids = this->getAnnotsDataIDs();
00547       }
00548       else
00549       {
00550         // we take only annotations of the given type if we will publish them one by one, as we will
00551         // publish all of them in the same topic
00552         // TODO: this is quite confusing: https://github.com/corot/world_canvas_libs/issues/24
00553         for (unsigned int i = 0; i < this->annotations.size(); i++)
00554         {
00555           if (annotations[i].type == common_tt)
00556             srv.request.annotation_ids.push_back(annotations[i].data_id);
00557         }
00558       }
00559       srv.request.topic_name = topic_name;
00560       srv.request.topic_type = common_tt;
00561       srv.request.pub_as_list = as_list;
00562       if (client.call(srv))
00563       {
00564         if (srv.response.result == true)
00565         {
00566           return true;
00567         }
00568         else
00569         {
00570           ROS_ERROR("Server reported an error: %s", srv.response.message.c_str());
00571           return false;
00572         }
00573       }
00574       else
00575       {
00576         ROS_ERROR("Failed to call pub_annotations_data service");
00577         return false;
00578       }
00579     }
00580     else
00581     {
00582       // TODO: we cannot publish here without the messages class, as we did with Python. Maybe I can
00583       // use templates, as the user of this class knows the message class. Or I can even make this a
00584       // template class, assuming annotations collections have a uniform type.
00585       // See https://github.com/corot/world_canvas/issues/5 for details
00586       ROS_ERROR("Publish by client not implemented!");
00587       return false;
00588     }
00589   }
00590   catch (const ros::Exception& e)
00591   {
00592     ROS_ERROR("ROS exception caught: %s", e.what());
00593     return false;
00594   }
00595 }
00596 
00597 bool AnnotationCollection::hasAnnotation(const UniqueIDmsg& id)
00598 {
00599   for (unsigned int i = 0; i < annotations.size(); i++)
00600   {
00601     if (annotations[i].id.uuid == id.uuid)
00602       return true;
00603   }
00604   return false;
00605 }
00606 
00607 const world_canvas_msgs::Annotation& AnnotationCollection::getAnnotation(const UniqueIDmsg& id)
00608 {
00609   for (unsigned int i = 0; i < annotations.size(); i++)
00610   {
00611     if (annotations[i].id.uuid == id.uuid)
00612       return annotations[i];
00613   }
00614   throw ros::Exception("Uuid not found: " + unique_id::toHexString(id));
00615 }
00616 
00617 std::vector<world_canvas_msgs::Annotation>
00618 AnnotationCollection::getAnnotations(const std::string& name)
00619 {
00620   std::vector<world_canvas_msgs::Annotation> result;
00621   for (unsigned int i = 0; i < annotations.size(); i++)
00622   {
00623     if (annotations[i].name == name)
00624       result.push_back(annotations[i]);
00625   }
00626   return result;
00627 }
00628 
00629 std::vector<UniqueIDmsg> AnnotationCollection::getAnnotationIDs()
00630 {
00631   std::vector<UniqueIDmsg> uuids(annotations.size());
00632   for (unsigned int i = 0; i < annotations.size(); i++)
00633   {
00634     uuids[i] = annotations[i].id;
00635   }
00636   return uuids;
00637 }
00638 
00639 std::vector<UniqueIDmsg> AnnotationCollection::getAnnotsDataIDs()
00640 {
00641   std::vector<UniqueIDmsg> uuids(annotations.size());
00642   for (unsigned int i = 0; i < annotations.size(); i++)
00643   {
00644     uuids[i] = annotations[i].data_id;
00645   }
00646   return uuids;
00647 }
00648 
00649 const world_canvas_msgs::AnnotationData&
00650 AnnotationCollection::getData(const world_canvas_msgs::Annotation& ann)
00651 {
00652   for (unsigned int i = 0; i < this->annots_data.size(); i++)
00653   {
00654     if (this->annots_data[i].id.uuid == ann.data_id.uuid)
00655     {
00656       return this->annots_data[i];
00657     }
00658   }
00659 
00660   throw ros::Exception("Data uuid not found: " + unique_id::toHexString(ann.data_id));
00661 }
00662 
00663 } // namespace wcf


world_canvas_client_cpp
Author(s): Jorge Santos
autogenerated on Thu Jun 6 2019 18:32:38