From fed94e0b7fc3f77c8ea50a80098fa3ce04bc4791 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 17 Jul 2023 21:59:30 -0500 Subject: [PATCH] Update placefile parsing to use custom tokenizer instead of regex --- wxdata/source/scwx/gr/placefile.cpp | 155 ++++++++++++++++------------ 1 file changed, 90 insertions(+), 65 deletions(-) diff --git a/wxdata/source/scwx/gr/placefile.cpp b/wxdata/source/scwx/gr/placefile.cpp index 4c4fdc7f..e6fb1a6f 100644 --- a/wxdata/source/scwx/gr/placefile.cpp +++ b/wxdata/source/scwx/gr/placefile.cpp @@ -2,14 +2,13 @@ #include #include #include +#include #include -#include #include #include #include -#include #include #include #include @@ -45,17 +44,17 @@ public: struct DrawItem { + boost::units::quantity threshold_ {}; }; struct PlaceDrawItem : DrawItem { - boost::units::quantity threshold_ {}; - boost::gil::rgba8_pixel_t color_ {}; - double latitude_ {}; - double longitude_ {}; - double x_ {}; - double y_ {}; - std::string text_ {}; + boost::gil::rgba8_pixel_t color_ {}; + double latitude_ {}; + double longitude_ {}; + double x_ {}; + double y_ {}; + std::string text_ {}; }; void ParseLocation(const std::string& latitudeToken, @@ -64,8 +63,7 @@ public: double& longitude, double& x, double& y); - void ProcessLine(const std::string& line, - const std::vector& tokenList); + void ProcessLine(const std::string& line); std::chrono::seconds refresh_ {-1}; @@ -129,30 +127,21 @@ std::shared_ptr Placefile::Load(std::istream& is) // Remove extra spacing from line boost::trim(line); - boost::char_separator delimiter(", "); - boost::tokenizer tokens(line, delimiter); - std::vector tokenList; - - for (auto& token : tokens) - { - tokenList.push_back(token); - } - - if (tokenList.size() >= 1) + if (line.size() >= 1) { try { switch (placefile->p->currentStatement_) { case DrawingStatement::Standard: - placefile->p->ProcessLine(line, tokenList); + placefile->p->ProcessLine(line); break; case DrawingStatement::Line: case DrawingStatement::Triangles: case DrawingStatement::Image: case DrawingStatement::Polygon: - if (boost::iequals(tokenList[0], "End:")) + if (boost::istarts_with(line, "End:")) { placefile->p->currentStatement_ = DrawingStatement::Standard; } @@ -169,28 +158,53 @@ std::shared_ptr Placefile::Load(std::istream& is) return placefile; } -void Placefile::Impl::ProcessLine(const std::string& line, - const std::vector& tokenList) +void Placefile::Impl::ProcessLine(const std::string& line) { + 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; - 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 - if (tokenList.size() >= 2) + std::vector tokenList = + util::ParseTokens(line, {" "}, thresholdKey_.size()); + + if (tokenList.size() >= 1) { threshold_ = static_cast>( - std::stod(tokenList[1]) * + std::stod(tokenList[0]) * 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 - if (tokenList.size() >= 2) + std::vector 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; } @@ -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 - if (tokenList.size() >= 2) + // Color: red green blue [alpha] + std::vector 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 - if (tokenList.size() >= 2) + std::vector 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 - if (tokenList.size() >= 2) + std::vector 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 - std::regex re {"Place:\\s*([+\\-0-9\\.]+),\\s*([+\\-0-9\\.]+),\\s*(.+)"}; - std::smatch match; - std::regex_match(line, match, re); + std::vector tokenList = + util::ParseTokens(line, {",", ","}, placeKey_.size()); - if (match.size() >= 4) + if (tokenList.size() >= 3) { std::shared_ptr di = std::make_shared(); di->threshold_ = threshold_; di->color_ = color_; - ParseLocation(match[1].str(), - match[2].str(), + ParseLocation(tokenList[0], + tokenList[1], di->latitude_, di->longitude_, di->x_, di->y_); - di->text_ = match[3].str(); + di->text_.swap(tokenList[2]); drawItems_.emplace_back(std::move(di)); } @@ -254,46 +276,45 @@ void Placefile::Impl::ProcessLine(const std::string& 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 // TODO } - else if (boost::iequals(tokenList[0], "Icon:")) + else if (boost::istarts_with(line, iconKey_)) { // Icon: lat, lon, angle, fileNumber, iconNumber, hoverText // TODO } - else if (boost::iequals(tokenList[0], "Font:")) + else if (boost::istarts_with(line, fontKey_)) { // Font: fontNumber, pixels, flags, "face" // TODO } - else if (boost::iequals(tokenList[0], "Text:")) + else if (boost::istarts_with(line, textKey_)) { // Text: lat, lon, fontNumber, "string", "hover" // TODO } - else if (boost::iequals(tokenList[0], "Object:")) + else if (boost::istarts_with(line, objectKey_)) { // Object: lat, lon // ... // End: - std::regex re {"Object:\\s*([+\\-0-9\\.]+),\\s*([+\\-0-9\\.]+)"}; - std::smatch match; - std::regex_match(line, match, re); + std::vector tokenList = + util::ParseTokens(line, {",", ","}, objectKey_.size()); double latitude {}; double longitude {}; - if (match.size() >= 3) + if (tokenList.size() >= 2) { - latitude = std::stod(match[1].str()); - longitude = std::stod(match[2].str()); + latitude = std::stod(tokenList[0]); + longitude = std::stod(tokenList[1]); } else { @@ -302,7 +323,7 @@ void Placefile::Impl::ProcessLine(const std::string& line, objectStack_.emplace_back(Object {latitude, longitude}); } - else if (boost::iequals(tokenList[0], "End:")) + else if (boost::istarts_with(line, endKey_)) { // Object End if (!objectStack_.empty()) @@ -314,7 +335,7 @@ void Placefile::Impl::ProcessLine(const std::string& line, 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] // lat, lon @@ -324,7 +345,7 @@ void Placefile::Impl::ProcessLine(const std::string& line, // TODO } - else if (boost::iequals(tokenList[0], "Triangles:")) + else if (boost::istarts_with(line, trianglesKey_)) { // Triangles: // lat, lon [, r, g, b [,a]] @@ -334,7 +355,7 @@ void Placefile::Impl::ProcessLine(const std::string& line, // TODO } - else if (boost::iequals(tokenList[0], "Image:")) + else if (boost::istarts_with(line, imageKey_)) { // Image: image_file // lat, lon, Tu [, Tv ] @@ -344,7 +365,7 @@ void Placefile::Impl::ProcessLine(const std::string& line, // TODO } - else if (boost::iequals(tokenList[0], "Polygon:")) + else if (boost::istarts_with(line, polygonKey_)) { // Polygon: // lat1, lon1 [, r, g, b [,a]] ; start of the first contour @@ -360,6 +381,10 @@ void Placefile::Impl::ProcessLine(const std::string& line, // TODO } + else + { + logger_->warn("Unknown statement: {}", line); + } } void Placefile::Impl::ParseLocation(const std::string& latitudeToken,