31 if (qualified ==
nullptr)
35 std::string name(qualified);
36 const auto colon = name.rfind(
':');
37 return colon == std::string::npos ? name : name.substr(colon + 1);
41 const tinyxml2::XMLElement * parent,
const std::string & wanted)
43 for (
const tinyxml2::XMLElement * child = parent->FirstChildElement(); child !=
nullptr;
44 child = child->NextSiblingElement())
57 if (element ==
nullptr)
62 if (
localName(element->Name()) ==
"LineString")
65 if (coords !=
nullptr && coords->GetText() !=
nullptr)
71 for (
const tinyxml2::XMLElement * child = element->FirstChildElement(); child !=
nullptr;
72 child = child->NextSiblingElement())
84bool parseCoordinates(
const std::string & text, std::vector<GeoPoint> & out, std::string & error)
86 std::istringstream tokens(text);
88 while (tokens >> token)
90 std::vector<std::string> fields;
92 std::istringstream fieldStream(token);
93 while (std::getline(fieldStream, field,
','))
95 fields.push_back(field);
98 if (fields.size() < 2)
100 error =
"coordinate tuple '" + token +
"' has fewer than 2 fields";
107 p.
lon = std::stod(fields[0]);
108 p.
lat = std::stod(fields[1]);
109 p.
alt = fields.size() >= 3 && !fields[2].empty() ? std::stod(fields[2]) : 0.0;
112 catch (
const std::exception & e)
114 error =
"coordinate tuple '" + token +
"' is not numeric: " + e.what();
121 error =
"LineString has no coordinate tuples";
127std::vector<GeoPoint>
parseDocument(tinyxml2::XMLDocument & doc, std::string & error)
129 std::vector<GeoPoint> points;
131 if (coords ==
nullptr)
133 error =
"no LineString/coordinates element found";
152 RCLCPP_INFO(
getLogger(),
"CpKmlMissionLoader: ready (first LineString backbone only)");
156 const std::string & xml, std::string & error)
158 tinyxml2::XMLDocument doc;
159 if (doc.Parse(xml.c_str(), xml.size()) != tinyxml2::XML_SUCCESS)
161 error = std::string(
"XML parse error: ") + (doc.ErrorStr() ? doc.ErrorStr() :
"unknown");
164 return parseDocument(doc, error);
171 tinyxml2::XMLDocument doc;
172 const tinyxml2::XMLError status = doc.LoadFile(absolutePath.c_str());
173 if (status != tinyxml2::XML_SUCCESS)
175 result.
error = std::string(
"cannot load '") + absolutePath +
176 "': " + (doc.ErrorStr() ? doc.ErrorStr() :
"unknown error");
177 RCLCPP_WARN(
getLogger(),
"CpKmlMissionLoader: %s", result.
error.c_str());
182 std::vector<GeoPoint> points = parseDocument(doc, error);
185 result.
error =
"'" + absolutePath +
"': " + error;
186 RCLCPP_WARN(
getLogger(),
"CpKmlMissionLoader: %s", result.
error.c_str());
191 std::lock_guard<std::mutex> lock(
mutex_);
200 "CpKmlMissionLoader: loaded %zu backbone points from '%s' (first %.6f,%.6f last %.6f,%.6f)",
201 points.size(), absolutePath.c_str(), points.front().lat, points.front().lon, points.back().lat,
208 std::lock_guard<std::mutex> lock(
mutex_);
215 std::lock_guard<std::mutex> lock(
mutex_);
221 std::lock_guard<std::mutex> lock(
mutex_);
227 std::lock_guard<std::mutex> lock(
mutex_);
233 std::lock_guard<std::mutex> lock(
mutex_);
std::vector< GeoPoint > getMission() const
void setMission(std::vector< GeoPoint > points, const std::string &source)
std::string getSource() const
void onInitialize() override
KmlLoadResult loadFile(const std::string &absolutePath)
virtual ~CpKmlMissionLoader()
std::vector< GeoPoint > mission_
static std::vector< GeoPoint > parseKmlString(const std::string &xml, std::string &error)
rclcpp::Logger getLogger() const
const tinyxml2::XMLElement * findFirstLineStringCoordinates(const tinyxml2::XMLElement *element)
std::string localName(const char *qualified)
const tinyxml2::XMLElement * firstChildByLocalName(const tinyxml2::XMLElement *parent, const std::string &wanted)
std::vector< GeoPoint > parseDocument(tinyxml2::XMLDocument &doc, std::string &error)
bool parseCoordinates(const std::string &text, std::vector< GeoPoint > &out, std::string &error)