Update placefile parsing to use custom tokenizer instead of regex

This commit is contained in:
Dan Paulat 2023-07-17 21:59:30 -05:00
parent c99e24f2c1
commit fed94e0b7f

View file

@ -2,14 +2,13 @@
#include <scwx/gr/color.hpp> #include <scwx/gr/color.hpp>
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <scwx/util/streams.hpp> #include <scwx/util/streams.hpp>
#include <scwx/util/strings.hpp>
#include <fstream> #include <fstream>
#include <regex>
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <boost/units/base_units/metric/nautical_mile.hpp> #include <boost/units/base_units/metric/nautical_mile.hpp>
#include <boost/units/quantity.hpp> #include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp> #include <boost/units/systems/si/length.hpp>
@ -45,11 +44,11 @@ public:
struct DrawItem struct DrawItem
{ {
boost::units::quantity<boost::units::si::length> threshold_ {};
}; };
struct PlaceDrawItem : DrawItem struct PlaceDrawItem : DrawItem
{ {
boost::units::quantity<boost::units::si::length> threshold_ {};
boost::gil::rgba8_pixel_t color_ {}; boost::gil::rgba8_pixel_t color_ {};
double latitude_ {}; double latitude_ {};
double longitude_ {}; double longitude_ {};
@ -64,8 +63,7 @@ public:
double& longitude, double& longitude,
double& x, double& x,
double& y); double& y);
void ProcessLine(const std::string& line, void ProcessLine(const std::string& line);
const std::vector<std::string>& tokenList);
std::chrono::seconds refresh_ {-1}; std::chrono::seconds refresh_ {-1};
@ -129,30 +127,21 @@ std::shared_ptr<Placefile> Placefile::Load(std::istream& is)
// Remove extra spacing from line // Remove extra spacing from line
boost::trim(line); boost::trim(line);
boost::char_separator<char> delimiter(", "); if (line.size() >= 1)
boost::tokenizer tokens(line, delimiter);
std::vector<std::string> tokenList;
for (auto& token : tokens)
{
tokenList.push_back(token);
}
if (tokenList.size() >= 1)
{ {
try try
{ {
switch (placefile->p->currentStatement_) switch (placefile->p->currentStatement_)
{ {
case DrawingStatement::Standard: case DrawingStatement::Standard:
placefile->p->ProcessLine(line, tokenList); placefile->p->ProcessLine(line);
break; break;
case DrawingStatement::Line: case DrawingStatement::Line:
case DrawingStatement::Triangles: case DrawingStatement::Triangles:
case DrawingStatement::Image: case DrawingStatement::Image:
case DrawingStatement::Polygon: case DrawingStatement::Polygon:
if (boost::iequals(tokenList[0], "End:")) if (boost::istarts_with(line, "End:"))
{ {
placefile->p->currentStatement_ = DrawingStatement::Standard; placefile->p->currentStatement_ = DrawingStatement::Standard;
} }
@ -169,28 +158,53 @@ std::shared_ptr<Placefile> Placefile::Load(std::istream& is)
return placefile; return placefile;
} }
void Placefile::Impl::ProcessLine(const std::string& line, void Placefile::Impl::ProcessLine(const std::string& line)
const std::vector<std::string>& tokenList)
{ {
static const std::string thresholdKey_ {"Threshold:"};
static const std::string hsluvKey_ {"HSLuv:"};
static const std::string colorKey_ {"Color:"};
static const std::string refreshKey_ {"Refresh:"};
static const std::string refreshSecondsKey_ {"RefreshSeconds:"};
static const std::string placeKey_ {"Place:"};
static const std::string iconFileKey_ {"IconFile:"};
static const std::string iconKey_ {"Icon:"};
static const std::string fontKey_ {"Font:"};
static const std::string textKey_ {"Text:"};
static const std::string objectKey_ {"Object:"};
static const std::string endKey_ {"End:"};
static const std::string lineKey_ {"Line:"};
static const std::string trianglesKey_ {"Triangles:"};
static const std::string imageKey_ {"Image:"};
static const std::string polygonKey_ {"Polygon:"};
currentStatement_ = DrawingStatement::Standard; currentStatement_ = DrawingStatement::Standard;
if (boost::iequals(tokenList[0], "Threshold:")) // When tokenizing, add one additional delimiter to discard unexpected
// parameters (where appropriate)
if (boost::istarts_with(line, thresholdKey_))
{ {
// Threshold: nautical_miles // Threshold: nautical_miles
if (tokenList.size() >= 2) std::vector<std::string> tokenList =
util::ParseTokens(line, {" "}, thresholdKey_.size());
if (tokenList.size() >= 1)
{ {
threshold_ = threshold_ =
static_cast<boost::units::quantity<boost::units::si::length>>( static_cast<boost::units::quantity<boost::units::si::length>>(
std::stod(tokenList[1]) * std::stod(tokenList[0]) *
boost::units::metric::nautical_mile_base_unit::unit_type()); boost::units::metric::nautical_mile_base_unit::unit_type());
} }
} }
else if (boost::iequals(tokenList[0], "HSLuv:")) else if (boost::istarts_with(line, hsluvKey_))
{ {
// HSLuv: value // HSLuv: value
if (tokenList.size() >= 2) std::vector<std::string> tokenList =
util::ParseTokens(line, {" "}, hsluvKey_.size());
if (tokenList.size() >= 1)
{ {
if (boost::iequals(tokenList[1], "true")) if (boost::iequals(tokenList[0], "true"))
{ {
colorMode_ = ColorMode::HSLuv; colorMode_ = ColorMode::HSLuv;
} }
@ -200,52 +214,60 @@ void Placefile::Impl::ProcessLine(const std::string& line,
} }
} }
} }
else if (boost::iequals(tokenList[0], "Color:")) else if (boost::istarts_with(line, colorKey_))
{ {
// Color: red green blue // Color: red green blue [alpha]
if (tokenList.size() >= 2) std::vector<std::string> tokenList =
util::ParseTokens(line, {" ", " ", " ", " "}, colorKey_.size());
if (tokenList.size() >= 3)
{ {
color_ = ParseColor(tokenList, 1, colorMode_); color_ = ParseColor(tokenList, 0, colorMode_);
} }
} }
else if (boost::iequals(tokenList[0], "Refresh:")) else if (boost::istarts_with(line, refreshKey_))
{ {
// Refresh: minutes // Refresh: minutes
if (tokenList.size() >= 2) std::vector<std::string> tokenList =
util::ParseTokens(line, {" "}, refreshKey_.size());
if (tokenList.size() >= 1)
{ {
refresh_ = std::chrono::minutes {std::stoi(tokenList[1])}; refresh_ = std::chrono::minutes {std::stoi(tokenList[0])};
} }
} }
else if (boost::iequals(tokenList[0], "RefreshSeconds:")) else if (boost::istarts_with(line, refreshSecondsKey_))
{ {
// RefreshSeconds: seconds // RefreshSeconds: seconds
if (tokenList.size() >= 2) std::vector<std::string> tokenList =
util::ParseTokens(line, {" "}, refreshSecondsKey_.size());
if (tokenList.size() >= 1)
{ {
refresh_ = std::chrono::seconds {std::stoi(tokenList[1])}; refresh_ = std::chrono::seconds {std::stoi(tokenList[0])};
} }
} }
else if (boost::iequals(tokenList[0], "Place:")) else if (boost::istarts_with(line, placeKey_))
{ {
// Place: latitude, longitude, string with spaces // Place: latitude, longitude, string with spaces
std::regex re {"Place:\\s*([+\\-0-9\\.]+),\\s*([+\\-0-9\\.]+),\\s*(.+)"}; std::vector<std::string> tokenList =
std::smatch match; util::ParseTokens(line, {",", ","}, placeKey_.size());
std::regex_match(line, match, re);
if (match.size() >= 4) if (tokenList.size() >= 3)
{ {
std::shared_ptr<PlaceDrawItem> di = std::make_shared<PlaceDrawItem>(); std::shared_ptr<PlaceDrawItem> di = std::make_shared<PlaceDrawItem>();
di->threshold_ = threshold_; di->threshold_ = threshold_;
di->color_ = color_; di->color_ = color_;
ParseLocation(match[1].str(), ParseLocation(tokenList[0],
match[2].str(), tokenList[1],
di->latitude_, di->latitude_,
di->longitude_, di->longitude_,
di->x_, di->x_,
di->y_); di->y_);
di->text_ = match[3].str(); di->text_.swap(tokenList[2]);
drawItems_.emplace_back(std::move(di)); drawItems_.emplace_back(std::move(di));
} }
@ -254,46 +276,45 @@ void Placefile::Impl::ProcessLine(const std::string& line,
logger_->warn("Place statement malformed: {}", line); logger_->warn("Place statement malformed: {}", line);
} }
} }
else if (boost::iequals(tokenList[0], "IconFile:")) else if (boost::istarts_with(line, iconFileKey_))
{ {
// IconFile: fileNumber, iconWidth, iconHeight, hotX, hotY, fileName // IconFile: fileNumber, iconWidth, iconHeight, hotX, hotY, fileName
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Icon:")) else if (boost::istarts_with(line, iconKey_))
{ {
// Icon: lat, lon, angle, fileNumber, iconNumber, hoverText // Icon: lat, lon, angle, fileNumber, iconNumber, hoverText
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Font:")) else if (boost::istarts_with(line, fontKey_))
{ {
// Font: fontNumber, pixels, flags, "face" // Font: fontNumber, pixels, flags, "face"
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Text:")) else if (boost::istarts_with(line, textKey_))
{ {
// Text: lat, lon, fontNumber, "string", "hover" // Text: lat, lon, fontNumber, "string", "hover"
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Object:")) else if (boost::istarts_with(line, objectKey_))
{ {
// Object: lat, lon // Object: lat, lon
// ... // ...
// End: // End:
std::regex re {"Object:\\s*([+\\-0-9\\.]+),\\s*([+\\-0-9\\.]+)"}; std::vector<std::string> tokenList =
std::smatch match; util::ParseTokens(line, {",", ","}, objectKey_.size());
std::regex_match(line, match, re);
double latitude {}; double latitude {};
double longitude {}; double longitude {};
if (match.size() >= 3) if (tokenList.size() >= 2)
{ {
latitude = std::stod(match[1].str()); latitude = std::stod(tokenList[0]);
longitude = std::stod(match[2].str()); longitude = std::stod(tokenList[1]);
} }
else else
{ {
@ -302,7 +323,7 @@ void Placefile::Impl::ProcessLine(const std::string& line,
objectStack_.emplace_back(Object {latitude, longitude}); objectStack_.emplace_back(Object {latitude, longitude});
} }
else if (boost::iequals(tokenList[0], "End:")) else if (boost::istarts_with(line, endKey_))
{ {
// Object End // Object End
if (!objectStack_.empty()) if (!objectStack_.empty())
@ -314,7 +335,7 @@ void Placefile::Impl::ProcessLine(const std::string& line,
logger_->warn("End found without Object"); logger_->warn("End found without Object");
} }
} }
else if (boost::iequals(tokenList[0], "Line:")) else if (boost::istarts_with(line, lineKey_))
{ {
// Line: width, flags [, hover_text] // Line: width, flags [, hover_text]
// lat, lon // lat, lon
@ -324,7 +345,7 @@ void Placefile::Impl::ProcessLine(const std::string& line,
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Triangles:")) else if (boost::istarts_with(line, trianglesKey_))
{ {
// Triangles: // Triangles:
// lat, lon [, r, g, b [,a]] // lat, lon [, r, g, b [,a]]
@ -334,7 +355,7 @@ void Placefile::Impl::ProcessLine(const std::string& line,
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Image:")) else if (boost::istarts_with(line, imageKey_))
{ {
// Image: image_file // Image: image_file
// lat, lon, Tu [, Tv ] // lat, lon, Tu [, Tv ]
@ -344,7 +365,7 @@ void Placefile::Impl::ProcessLine(const std::string& line,
// TODO // TODO
} }
else if (boost::iequals(tokenList[0], "Polygon:")) else if (boost::istarts_with(line, polygonKey_))
{ {
// Polygon: // Polygon:
// lat1, lon1 [, r, g, b [,a]] ; start of the first contour // lat1, lon1 [, r, g, b [,a]] ; start of the first contour
@ -360,6 +381,10 @@ void Placefile::Impl::ProcessLine(const std::string& line,
// TODO // TODO
} }
else
{
logger_->warn("Unknown statement: {}", line);
}
} }
void Placefile::Impl::ParseLocation(const std::string& latitudeToken, void Placefile::Impl::ParseLocation(const std::string& latitudeToken,