mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-30 06:50:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			803 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			803 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <scwx/gr/placefile.hpp>
 | |
| #include <scwx/gr/color.hpp>
 | |
| #include <scwx/util/logger.hpp>
 | |
| #include <scwx/util/streams.hpp>
 | |
| #include <scwx/util/strings.hpp>
 | |
| 
 | |
| #include <fstream>
 | |
| #include <sstream>
 | |
| #include <unordered_map>
 | |
| 
 | |
| #include <boost/algorithm/string.hpp>
 | |
| 
 | |
| using namespace units::literals;
 | |
| 
 | |
| namespace scwx
 | |
| {
 | |
| namespace gr
 | |
| {
 | |
| 
 | |
| static const std::string logPrefix_ {"scwx::gr::placefile"};
 | |
| static const auto        logger_ = util::Logger::Create(logPrefix_);
 | |
| 
 | |
| enum class DrawingStatement
 | |
| {
 | |
|    Standard,
 | |
|    Line,
 | |
|    Triangles,
 | |
|    Image,
 | |
|    Polygon
 | |
| };
 | |
| 
 | |
| class Placefile::Impl
 | |
| {
 | |
| public:
 | |
|    explicit Impl() = default;
 | |
|    ~Impl()         = default;
 | |
| 
 | |
|    struct Object
 | |
|    {
 | |
|       double x_ {};
 | |
|       double y_ {};
 | |
|    };
 | |
| 
 | |
|    void ParseLocation(const std::string& latitudeToken,
 | |
|                       const std::string& longitudeToken,
 | |
|                       double&            latitude,
 | |
|                       double&            longitude,
 | |
|                       double&            x,
 | |
|                       double&            y);
 | |
|    void ProcessElement(const std::string& line);
 | |
|    void ProcessElementEnd();
 | |
|    void ProcessLine(const std::string& line);
 | |
| 
 | |
|    static void ProcessEscapeCharacters(std::string& s);
 | |
|    static void TrimQuotes(std::string& s);
 | |
| 
 | |
|    std::string          name_ {};
 | |
|    std::string          title_ {};
 | |
|    std::chrono::seconds refresh_ {-1};
 | |
| 
 | |
|    // Parsing state
 | |
|    units::length::nautical_miles<double> threshold_ {999.0_nmi};
 | |
|    boost::gil::rgba8_pixel_t             color_ {255, 255, 255, 255};
 | |
|    ColorMode                             colorMode_ {ColorMode::RGBA};
 | |
|    std::vector<Object>                   objectStack_ {};
 | |
|    DrawingStatement          currentStatement_ {DrawingStatement::Standard};
 | |
|    std::shared_ptr<DrawItem> currentDrawItem_ {nullptr};
 | |
|    std::vector<PolygonDrawItem::Element> currentPolygonContour_ {};
 | |
| 
 | |
|    // References
 | |
|    std::unordered_map<std::size_t, std::shared_ptr<IconFile>> iconFiles_ {};
 | |
|    std::unordered_map<std::size_t, std::shared_ptr<Font>>     fonts_ {};
 | |
| 
 | |
|    std::vector<std::shared_ptr<DrawItem>> drawItems_ {};
 | |
| };
 | |
| 
 | |
| Placefile::Placefile() : p(std::make_unique<Impl>()) {}
 | |
| Placefile::~Placefile() = default;
 | |
| 
 | |
| Placefile::Placefile(Placefile&&) noexcept            = default;
 | |
| Placefile& Placefile::operator=(Placefile&&) noexcept = default;
 | |
| 
 | |
| bool Placefile::IsValid() const
 | |
| {
 | |
|    return p->drawItems_.size() > 0;
 | |
| }
 | |
| 
 | |
| std::vector<std::shared_ptr<Placefile::DrawItem>> Placefile::GetDrawItems()
 | |
| {
 | |
|    return p->drawItems_;
 | |
| }
 | |
| 
 | |
| std::vector<std::shared_ptr<const Placefile::IconFile>> Placefile::icon_files()
 | |
| {
 | |
|    std::vector<std::shared_ptr<const Placefile::IconFile>> iconFiles {};
 | |
|    iconFiles.reserve(p->iconFiles_.size());
 | |
| 
 | |
|    std::transform(p->iconFiles_.begin(),
 | |
|                   p->iconFiles_.end(),
 | |
|                   std::back_inserter(iconFiles),
 | |
|                   [](auto& iconFile) { return iconFile.second; });
 | |
| 
 | |
|    return iconFiles;
 | |
| }
 | |
| 
 | |
| std::string Placefile::name() const
 | |
| {
 | |
|    return p->name_;
 | |
| }
 | |
| 
 | |
| std::string Placefile::title() const
 | |
| {
 | |
|    return p->title_;
 | |
| }
 | |
| 
 | |
| std::unordered_map<std::size_t, std::shared_ptr<Placefile::Font>>
 | |
| Placefile::fonts()
 | |
| {
 | |
|    return p->fonts_;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<Placefile::Font> Placefile::font(std::size_t i)
 | |
| {
 | |
|    auto it = p->fonts_.find(i);
 | |
|    if (it != p->fonts_.cend())
 | |
|    {
 | |
|       return it->second;
 | |
|    }
 | |
|    return nullptr;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<Placefile> Placefile::Load(const std::string& filename)
 | |
| {
 | |
|    logger_->debug("Loading placefile: {}", filename);
 | |
|    std::ifstream f(filename, std::ios_base::in);
 | |
|    return Load(filename, f);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<Placefile> Placefile::Load(const std::string& name,
 | |
|                                            std::istream&      is)
 | |
| {
 | |
|    std::shared_ptr<Placefile> placefile = std::make_shared<Placefile>();
 | |
| 
 | |
|    placefile->p->name_ = name;
 | |
| 
 | |
|    std::string line;
 | |
|    while (scwx::util::getline(is, line))
 | |
|    {
 | |
|       // Find position of comment (;)
 | |
|       bool inQuotes = false;
 | |
|       for (std::size_t i = 0; i < line.size(); ++i)
 | |
|       {
 | |
|          if (!inQuotes && line[i] == ';')
 | |
|          {
 | |
|             // Remove comment
 | |
|             line.erase(i);
 | |
|             break;
 | |
|          }
 | |
|          else if (line[i] == '"')
 | |
|          {
 | |
|             // Toggle quote state
 | |
|             inQuotes = !inQuotes;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       // Remove extra spacing from line
 | |
|       boost::trim(line);
 | |
| 
 | |
|       if (line.size() >= 1)
 | |
|       {
 | |
|          try
 | |
|          {
 | |
|             switch (placefile->p->currentStatement_)
 | |
|             {
 | |
|             case DrawingStatement::Standard:
 | |
|                placefile->p->ProcessLine(line);
 | |
|                break;
 | |
| 
 | |
|             case DrawingStatement::Line:
 | |
|             case DrawingStatement::Triangles:
 | |
|             case DrawingStatement::Image:
 | |
|             case DrawingStatement::Polygon:
 | |
|                if (boost::istarts_with(line, "End:"))
 | |
|                {
 | |
|                   placefile->p->ProcessElementEnd();
 | |
| 
 | |
|                   placefile->p->currentStatement_ = DrawingStatement::Standard;
 | |
|                   placefile->p->currentDrawItem_  = nullptr;
 | |
|                }
 | |
|                else if (placefile->p->currentDrawItem_ != nullptr)
 | |
|                {
 | |
|                   placefile->p->ProcessElement(line);
 | |
|                }
 | |
|                break;
 | |
|             }
 | |
|          }
 | |
|          catch (const std::exception&)
 | |
|          {
 | |
|             logger_->warn("Could not parse line: {}", line);
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    return placefile;
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::ProcessLine(const std::string& line)
 | |
| {
 | |
|    static const std::string titleKey_ {"Title:"};
 | |
|    static const std::string thresholdKey_ {"Threshold:"};
 | |
|    static const std::string timeRangeKey_ {"TimeRange:"};
 | |
|    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;
 | |
| 
 | |
|    // When tokenizing, add one additional delimiter to discard unexpected
 | |
|    // parameters (where appropriate)
 | |
| 
 | |
|    if (boost::istarts_with(line, titleKey_))
 | |
|    {
 | |
|       // Title: title
 | |
|       title_ = line.substr(titleKey_.size());
 | |
|       boost::trim(title_);
 | |
|    }
 | |
|    else if (boost::istarts_with(line, thresholdKey_))
 | |
|    {
 | |
|       // Threshold: nautical_miles
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {" "}, thresholdKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 1)
 | |
|       {
 | |
|          threshold_ =
 | |
|             units::length::nautical_miles<double>(std::stod(tokenList[0]));
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, timeRangeKey_))
 | |
|    {
 | |
|       // TimeRange: start_time end_time
 | |
| 
 | |
|       // TODO
 | |
|    }
 | |
|    else if (boost::istarts_with(line, hsluvKey_))
 | |
|    {
 | |
|       // HSLuv: value
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {" "}, hsluvKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 1)
 | |
|       {
 | |
|          if (boost::iequals(tokenList[0], "true"))
 | |
|          {
 | |
|             colorMode_ = ColorMode::HSLuv;
 | |
|          }
 | |
|          else
 | |
|          {
 | |
|             colorMode_ = ColorMode::RGBA;
 | |
|          }
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, colorKey_))
 | |
|    {
 | |
|       // Color: red green blue [alpha]
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {" ", " ", " ", " "}, colorKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 3)
 | |
|       {
 | |
|          color_ = ParseColor(tokenList, 0, colorMode_);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, refreshKey_))
 | |
|    {
 | |
|       // Refresh: minutes
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {" "}, refreshKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 1)
 | |
|       {
 | |
|          refresh_ = std::chrono::minutes {std::stoi(tokenList[0])};
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, refreshSecondsKey_))
 | |
|    {
 | |
|       // RefreshSeconds: seconds
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {" "}, refreshSecondsKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 1)
 | |
|       {
 | |
|          refresh_ = std::chrono::seconds {std::stoi(tokenList[0])};
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, placeKey_))
 | |
|    {
 | |
|       // Place: latitude, longitude, string with spaces
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ","}, placeKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 3)
 | |
|       {
 | |
|          std::shared_ptr<TextDrawItem> di = std::make_shared<TextDrawItem>();
 | |
| 
 | |
|          di->threshold_ = threshold_;
 | |
|          di->color_     = color_;
 | |
| 
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        di->latitude_,
 | |
|                        di->longitude_,
 | |
|                        di->x_,
 | |
|                        di->y_);
 | |
| 
 | |
|          ProcessEscapeCharacters(tokenList[2]);
 | |
|          di->text_.swap(tokenList[2]);
 | |
| 
 | |
|          drawItems_.emplace_back(std::move(di));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Place statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, iconFileKey_))
 | |
|    {
 | |
|       // IconFile: fileNumber, iconWidth, iconHeight, hotX, hotY, fileName
 | |
|       std::vector<std::string> tokenList = util::ParseTokens(
 | |
|          line, {",", ",", ",", ",", ","}, iconFileKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 6)
 | |
|       {
 | |
|          std::shared_ptr<IconFile> iconFile = std::make_shared<IconFile>();
 | |
| 
 | |
|          iconFile->fileNumber_ = std::stoul(tokenList[0]);
 | |
|          iconFile->iconWidth_  = std::stoul(tokenList[1]);
 | |
|          iconFile->iconHeight_ = std::stoul(tokenList[2]);
 | |
|          iconFile->hotX_       = std::stoul(tokenList[3]);
 | |
|          iconFile->hotY_       = std::stoul(tokenList[4]);
 | |
| 
 | |
|          TrimQuotes(tokenList[5]);
 | |
|          iconFile->filename_.swap(tokenList[5]);
 | |
| 
 | |
|          iconFiles_.insert_or_assign(iconFile->fileNumber_, iconFile);
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("IconFile statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, iconKey_))
 | |
|    {
 | |
|       // Icon: lat, lon, angle, fileNumber, iconNumber, hoverText
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ",", ",", ",", ","}, iconKey_.size());
 | |
| 
 | |
|       std::shared_ptr<IconDrawItem> di = nullptr;
 | |
| 
 | |
|       if (tokenList.size() >= 5)
 | |
|       {
 | |
|          di = std::make_shared<IconDrawItem>();
 | |
| 
 | |
|          di->threshold_ = threshold_;
 | |
| 
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        di->latitude_,
 | |
|                        di->longitude_,
 | |
|                        di->x_,
 | |
|                        di->y_);
 | |
| 
 | |
|          di->angle_ = units::angle::degrees<double>(std::stod(tokenList[2]));
 | |
| 
 | |
|          di->fileNumber_ = std::stoul(tokenList[3]);
 | |
|          di->iconNumber_ = std::stoul(tokenList[4]);
 | |
|       }
 | |
|       if (tokenList.size() >= 6)
 | |
|       {
 | |
|          ProcessEscapeCharacters(tokenList[5]);
 | |
|          TrimQuotes(tokenList[5]);
 | |
|          di->hoverText_.swap(tokenList[5]);
 | |
|       }
 | |
| 
 | |
|       if (di != nullptr)
 | |
|       {
 | |
|          drawItems_.emplace_back(std::move(di));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Icon statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, fontKey_))
 | |
|    {
 | |
|       // Font: fontNumber, pixels, flags, "face"
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ",", ",", ","}, fontKey_.size());
 | |
| 
 | |
|       if (tokenList.size() >= 4)
 | |
|       {
 | |
|          std::shared_ptr<Font> font = std::make_shared<Font>();
 | |
| 
 | |
|          font->fontNumber_ = std::stoul(tokenList[0]);
 | |
|          font->pixels_     = std::stoul(tokenList[1]);
 | |
|          font->flags_      = std::stoi(tokenList[2]);
 | |
| 
 | |
|          TrimQuotes(tokenList[3]);
 | |
|          font->face_.swap(tokenList[3]);
 | |
| 
 | |
|          fonts_.insert_or_assign(font->fontNumber_, font);
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Font statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, textKey_))
 | |
|    {
 | |
|       // Text: lat, lon, fontNumber, "string", "hover"
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ",", ",", ",", ","}, textKey_.size());
 | |
| 
 | |
|       std::shared_ptr<TextDrawItem> di = nullptr;
 | |
| 
 | |
|       if (tokenList.size() >= 4)
 | |
|       {
 | |
|          di = std::make_shared<TextDrawItem>();
 | |
| 
 | |
|          di->threshold_ = threshold_;
 | |
|          di->color_     = color_;
 | |
| 
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        di->latitude_,
 | |
|                        di->longitude_,
 | |
|                        di->x_,
 | |
|                        di->y_);
 | |
| 
 | |
|          di->fontNumber_ = std::stoul(tokenList[2]);
 | |
| 
 | |
|          ProcessEscapeCharacters(tokenList[3]);
 | |
|          TrimQuotes(tokenList[3]);
 | |
|          di->text_.swap(tokenList[3]);
 | |
|       }
 | |
|       if (tokenList.size() >= 5)
 | |
|       {
 | |
|          ProcessEscapeCharacters(tokenList[4]);
 | |
|          TrimQuotes(tokenList[4]);
 | |
|          di->hoverText_.swap(tokenList[4]);
 | |
|       }
 | |
| 
 | |
|       if (di != nullptr)
 | |
|       {
 | |
|          drawItems_.emplace_back(std::move(di));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Text statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, objectKey_))
 | |
|    {
 | |
|       // Object: lat, lon
 | |
|       //    ...
 | |
|       // End:
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ","}, objectKey_.size());
 | |
| 
 | |
|       double latitude {};
 | |
|       double longitude {};
 | |
| 
 | |
|       if (tokenList.size() >= 2)
 | |
|       {
 | |
|          latitude  = std::stod(tokenList[0]);
 | |
|          longitude = std::stod(tokenList[1]);
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Object statement malformed: {}", line);
 | |
|       }
 | |
| 
 | |
|       objectStack_.emplace_back(Object {latitude, longitude});
 | |
|    }
 | |
|    else if (boost::istarts_with(line, endKey_))
 | |
|    {
 | |
|       // Object End
 | |
|       if (!objectStack_.empty())
 | |
|       {
 | |
|          objectStack_.pop_back();
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, lineKey_))
 | |
|    {
 | |
|       // Line: width, flags [, hover_text]
 | |
|       //    lat, lon
 | |
|       //    ...
 | |
|       // End:
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ","}, lineKey_.size());
 | |
| 
 | |
|       currentStatement_ = DrawingStatement::Line;
 | |
| 
 | |
|       std::shared_ptr<LineDrawItem> di = nullptr;
 | |
| 
 | |
|       if (tokenList.size() >= 2)
 | |
|       {
 | |
|          di = std::make_shared<LineDrawItem>();
 | |
| 
 | |
|          di->threshold_ = threshold_;
 | |
|          di->color_     = color_;
 | |
| 
 | |
|          di->width_ = std::stoul(tokenList[0]);
 | |
|          di->flags_ = std::stoul(tokenList[1]);
 | |
|       }
 | |
|       if (tokenList.size() >= 3)
 | |
|       {
 | |
|          ProcessEscapeCharacters(tokenList[2]);
 | |
|          TrimQuotes(tokenList[2]);
 | |
|          di->hoverText_.swap(tokenList[2]);
 | |
|       }
 | |
| 
 | |
|       if (di != nullptr)
 | |
|       {
 | |
|          currentDrawItem_ = di;
 | |
|          drawItems_.emplace_back(std::move(di));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Line statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (boost::istarts_with(line, trianglesKey_))
 | |
|    {
 | |
|       // Triangles:
 | |
|       //    lat, lon [, r, g, b [,a]]
 | |
|       //    ...
 | |
|       // End:
 | |
|       currentStatement_ = DrawingStatement::Triangles;
 | |
| 
 | |
|       std::shared_ptr<TrianglesDrawItem> di =
 | |
|          std::make_shared<TrianglesDrawItem>();
 | |
| 
 | |
|       di->threshold_ = threshold_;
 | |
|       di->color_     = color_;
 | |
| 
 | |
|       currentDrawItem_ = di;
 | |
|       drawItems_.emplace_back(std::move(di));
 | |
|    }
 | |
|    else if (boost::istarts_with(line, imageKey_))
 | |
|    {
 | |
|       // Image: image_file
 | |
|       //    lat, lon, Tu [, Tv ]
 | |
|       //    ...
 | |
|       // End:
 | |
|       currentStatement_ = DrawingStatement::Image;
 | |
| 
 | |
|       // TODO
 | |
|    }
 | |
|    else if (boost::istarts_with(line, polygonKey_))
 | |
|    {
 | |
|       // Polygon:
 | |
|       //    lat1, lon1 [, r, g, b [,a]] ; start of the first contour
 | |
|       //    ...
 | |
|       //    lat1, lon1                  ; repeating the first point closes the
 | |
|       //                                ; contour
 | |
|       //
 | |
|       //    lat2, lon2                  ; next point starts a new contour
 | |
|       //    ...
 | |
|       //    lat2, lon2                  ; and repeating it ends the contour
 | |
|       // End:
 | |
|       currentStatement_ = DrawingStatement::Polygon;
 | |
| 
 | |
|       std::shared_ptr<PolygonDrawItem> di = std::make_shared<PolygonDrawItem>();
 | |
| 
 | |
|       di->threshold_ = threshold_;
 | |
|       di->color_     = color_;
 | |
| 
 | |
|       currentDrawItem_ = di;
 | |
|       drawItems_.emplace_back(std::move(di));
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       logger_->warn("Unknown statement: {}", line);
 | |
|    }
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::ProcessElement(const std::string& line)
 | |
| {
 | |
|    if (currentStatement_ == DrawingStatement::Line)
 | |
|    {
 | |
|       // Line: width, flags [, hover_text]
 | |
|       //    lat, lon
 | |
|       //    ...
 | |
|       // End:
 | |
|       std::vector<std::string> tokenList = util::ParseTokens(line, {",", ","});
 | |
| 
 | |
|       if (tokenList.size() >= 2)
 | |
|       {
 | |
|          LineDrawItem::Element element;
 | |
| 
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        element.latitude_,
 | |
|                        element.longitude_,
 | |
|                        element.x_,
 | |
|                        element.y_);
 | |
| 
 | |
|          std::static_pointer_cast<LineDrawItem>(currentDrawItem_)
 | |
|             ->elements_.emplace_back(std::move(element));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Line sub-statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (currentStatement_ == DrawingStatement::Triangles)
 | |
|    {
 | |
|       // Triangles:
 | |
|       //    lat, lon [, r, g, b [,a]]
 | |
|       //    ...
 | |
|       // End:
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ",", ",", ",", ",", ","});
 | |
| 
 | |
|       TrianglesDrawItem::Element element;
 | |
| 
 | |
|       if (tokenList.size() >= 5)
 | |
|       {
 | |
|          element.color_ = ParseColor(tokenList, 2, colorMode_);
 | |
|       }
 | |
| 
 | |
|       if (tokenList.size() >= 2)
 | |
|       {
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        element.latitude_,
 | |
|                        element.longitude_,
 | |
|                        element.x_,
 | |
|                        element.y_);
 | |
| 
 | |
|          std::static_pointer_cast<TrianglesDrawItem>(currentDrawItem_)
 | |
|             ->elements_.emplace_back(std::move(element));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Triangles sub-statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
|    else if (currentStatement_ == DrawingStatement::Polygon)
 | |
|    {
 | |
|       // Polygon:
 | |
|       //    lat1, lon1 [, r, g, b [,a]] ; start of the first contour
 | |
|       //    ...
 | |
|       //    lat1, lon1                  ; repeating the first point closes the
 | |
|       //                                ; contour
 | |
|       //
 | |
|       //    lat2, lon2                  ; next point starts a new contour
 | |
|       //    ...
 | |
|       //    lat2, lon2                  ; and repeating it ends the contour
 | |
|       // End:
 | |
|       std::vector<std::string> tokenList =
 | |
|          util::ParseTokens(line, {",", ",", ",", ",", ",", ","});
 | |
| 
 | |
|       PolygonDrawItem::Element element;
 | |
| 
 | |
|       if (tokenList.size() >= 5)
 | |
|       {
 | |
|          element.color_ = ParseColor(tokenList, 2, colorMode_);
 | |
|       }
 | |
| 
 | |
|       if (tokenList.size() >= 2)
 | |
|       {
 | |
|          ParseLocation(tokenList[0],
 | |
|                        tokenList[1],
 | |
|                        element.latitude_,
 | |
|                        element.longitude_,
 | |
|                        element.x_,
 | |
|                        element.y_);
 | |
| 
 | |
|          currentPolygonContour_.emplace_back(std::move(element));
 | |
| 
 | |
|          if (currentPolygonContour_.size() >= 2)
 | |
|          {
 | |
|             auto& first = currentPolygonContour_.front();
 | |
|             auto& last  = currentPolygonContour_.back();
 | |
| 
 | |
|             // Repeating the first point closes the contour
 | |
|             if (first.latitude_ == last.latitude_ &&
 | |
|                 first.longitude_ == last.longitude_ && //
 | |
|                 first.x_ == last.x_ &&                 //
 | |
|                 first.y_ == last.y_)
 | |
|             {
 | |
|                auto& contours =
 | |
|                   std::static_pointer_cast<PolygonDrawItem>(currentDrawItem_)
 | |
|                      ->contours_;
 | |
| 
 | |
|                auto& newContour = contours.emplace_back(
 | |
|                   std::vector<PolygonDrawItem::Element> {});
 | |
|                newContour.swap(currentPolygonContour_);
 | |
|             }
 | |
|          }
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          logger_->warn("Polygon sub-statement malformed: {}", line);
 | |
|       }
 | |
|    }
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::ProcessElementEnd()
 | |
| {
 | |
|    if (currentStatement_ == DrawingStatement::Polygon)
 | |
|    {
 | |
|       auto di = std::static_pointer_cast<PolygonDrawItem>(currentDrawItem_);
 | |
| 
 | |
|       // Complete the current contour when ending the Polygon statement
 | |
|       if (!currentPolygonContour_.empty())
 | |
|       {
 | |
|          auto& contours = di->contours_;
 | |
| 
 | |
|          auto& newContour =
 | |
|             contours.emplace_back(std::vector<PolygonDrawItem::Element> {});
 | |
|          newContour.swap(currentPolygonContour_);
 | |
|       }
 | |
| 
 | |
|       if (!di->contours_.empty())
 | |
|       {
 | |
|          std::vector<common::Coordinate> coordinates {};
 | |
|          std::transform(di->contours_[0].cbegin(),
 | |
|                         di->contours_[0].cend(),
 | |
|                         std::back_inserter(coordinates),
 | |
|                         [](auto& element) {
 | |
|                            return common::Coordinate {element.latitude_,
 | |
|                                                       element.longitude_};
 | |
|                         });
 | |
|          di->center_ = GetCentroid(coordinates);
 | |
|       }
 | |
|    }
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::ParseLocation(const std::string& latitudeToken,
 | |
|                                     const std::string& longitudeToken,
 | |
|                                     double&            latitude,
 | |
|                                     double&            longitude,
 | |
|                                     double&            x,
 | |
|                                     double&            y)
 | |
| {
 | |
|    if (objectStack_.empty())
 | |
|    {
 | |
|       // If an Object statement is not currently open, parse latitude and
 | |
|       // longitude tokens as-is
 | |
|       latitude  = std::stod(latitudeToken);
 | |
|       longitude = std::stod(longitudeToken);
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       // If an Object statement is open, the latitude and longitude are from the
 | |
|       // outermost Object
 | |
|       latitude  = objectStack_[0].x_;
 | |
|       longitude = objectStack_[0].y_;
 | |
| 
 | |
|       // The latitude and longitude tokens are interpreted as x, y offsets
 | |
|       x = std::stod(latitudeToken);
 | |
|       y = std::stod(longitudeToken);
 | |
| 
 | |
|       // If there are inner Object statements open, treat these as x, y offsets
 | |
|       for (std::size_t i = 1; i < objectStack_.size(); i++)
 | |
|       {
 | |
|          x += objectStack_[i].x_;
 | |
|          y += objectStack_[i].y_;
 | |
|       }
 | |
|    }
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::ProcessEscapeCharacters(std::string& s)
 | |
| {
 | |
|    boost::replace_all(s, "\\n", "\n");
 | |
| }
 | |
| 
 | |
| void Placefile::Impl::TrimQuotes(std::string& s)
 | |
| {
 | |
|    if (s.size() >= 2 && s.front() == '"' && s.back() == '"')
 | |
|    {
 | |
|       s.erase(s.size() - 1);
 | |
|       s.erase(0, 1);
 | |
|    }
 | |
| }
 | |
| 
 | |
| } // namespace gr
 | |
| } // namespace scwx
 | 
