mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-30 23:30:04 +00:00 
			
		
		
		
	Color table implementation
This commit is contained in:
		
							parent
							
								
									6c25ce4230
								
							
						
					
					
						commit
						d734bc6a0a
					
				
					 12 changed files with 437 additions and 5 deletions
				
			
		
							
								
								
									
										313
									
								
								wxdata/source/scwx/common/color_table.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								wxdata/source/scwx/common/color_table.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,313 @@ | |||
| #include <scwx/common/color_table.hpp> | ||||
| 
 | ||||
| #include <cmath> | ||||
| #include <fstream> | ||||
| #include <limits> | ||||
| #include <map> | ||||
| #include <optional> | ||||
| #include <sstream> | ||||
| 
 | ||||
| #include <boost/gil.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| #include <hsluv.h> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace common | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ {"[scwx::common::color_table] "}; | ||||
| 
 | ||||
| enum class ColorMode | ||||
| { | ||||
|    RGBA, | ||||
|    HSLuv | ||||
| }; | ||||
| 
 | ||||
| static boost::gil::rgba8_pixel_t | ||||
| ParseColor(const std::vector<std::string>& tokenList, | ||||
|            size_t                          startIndex, | ||||
|            ColorMode                       colorMode, | ||||
|            bool                            hasAlpha = true); | ||||
| template<typename T> | ||||
| T RoundChannel(double value); | ||||
| template<typename T> | ||||
| T StringToDecimal(const std::string& str); | ||||
| 
 | ||||
| class ColorTableImpl | ||||
| { | ||||
| public: | ||||
|    explicit ColorTableImpl() : | ||||
|        product_ {}, | ||||
|        units_ {}, | ||||
|        scale_ {1.0f}, | ||||
|        offset_ {0.0f}, | ||||
|        step_ {10}, | ||||
|        rfColor_ {0, 0, 0, 0}, | ||||
|        colorMode_ {ColorMode::RGBA}, | ||||
|        colorMap_ {} {}; | ||||
|    ~ColorTableImpl() = default; | ||||
| 
 | ||||
|    std::string               product_; | ||||
|    std::string               units_; | ||||
|    float                     scale_; | ||||
|    float                     offset_; | ||||
|    long                      step_; | ||||
|    boost::gil::rgba8_pixel_t rfColor_; | ||||
|    ColorMode                 colorMode_; | ||||
| 
 | ||||
|    std::map<float, | ||||
|             std::pair<boost::gil::rgba8_pixel_t, | ||||
|                       std::optional<boost::gil::rgba8_pixel_t>>> | ||||
|       colorMap_; | ||||
| }; | ||||
| 
 | ||||
| ColorTable::ColorTable() : p(std::make_unique<ColorTableImpl>()) {} | ||||
| ColorTable::~ColorTable() = default; | ||||
| 
 | ||||
| ColorTable::ColorTable(ColorTable&&) noexcept = default; | ||||
| ColorTable& ColorTable::operator=(ColorTable&&) noexcept = default; | ||||
| 
 | ||||
| boost::gil::rgba8_pixel_t ColorTable::Color(float value) const | ||||
| { | ||||
|    boost::gil::rgba8_pixel_t color; | ||||
|    bool                      found = false; | ||||
| 
 | ||||
|    value = value * p->scale_ + p->offset_; | ||||
| 
 | ||||
|    auto prev = p->colorMap_.cbegin(); | ||||
|    for (auto it = p->colorMap_.cbegin(); it != p->colorMap_.cend(); ++it) | ||||
|    { | ||||
|       if (value < it->first) | ||||
|       { | ||||
|          if (it == p->colorMap_.cbegin()) | ||||
|          { | ||||
|             color = it->second.first; | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             // Interpolate
 | ||||
|             float key1 = prev->first; | ||||
|             float key2 = it->first; | ||||
| 
 | ||||
|             boost::gil::rgba8_pixel_t color1 = prev->second.first; | ||||
|             boost::gil::rgba8_pixel_t color2 = (prev->second.second) ? | ||||
|                                                   prev->second.second.value() : | ||||
|                                                   it->second.first; | ||||
| 
 | ||||
|             float t = (value - key1) / (key2 - key1); | ||||
|             color[0] = | ||||
|                RoundChannel<uint8_t>(std::lerp(color1[0], color2[0], t)); | ||||
|             color[1] = | ||||
|                RoundChannel<uint8_t>(std::lerp(color1[1], color2[1], t)); | ||||
|             color[2] = | ||||
|                RoundChannel<uint8_t>(std::lerp(color1[2], color2[2], t)); | ||||
|             color[3] = | ||||
|                RoundChannel<uint8_t>(std::lerp(color1[3], color2[3], t)); | ||||
|          } | ||||
| 
 | ||||
|          found = true; | ||||
|          break; | ||||
|       } | ||||
| 
 | ||||
|       prev = it; | ||||
|    } | ||||
| 
 | ||||
|    if (!found) | ||||
|    { | ||||
|       color = prev->second.first; | ||||
|    } | ||||
| 
 | ||||
|    return color; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<ColorTable> ColorTable::Load(const std::string& filename) | ||||
| { | ||||
|    BOOST_LOG_TRIVIAL(debug) | ||||
|       << logPrefix_ << "Loading color table: " << filename; | ||||
| 
 | ||||
|    std::shared_ptr<ColorTable> p = std::make_shared<ColorTable>(); | ||||
| 
 | ||||
|    std::ifstream f(filename, std::ios_base::in); | ||||
| 
 | ||||
|    std::string line; | ||||
|    while (std::getline(f, line)) | ||||
|    { | ||||
|       std::string              token; | ||||
|       std::istringstream       tokens(line); | ||||
|       std::vector<std::string> tokenList; | ||||
| 
 | ||||
|       while (tokens >> token) | ||||
|       { | ||||
|          if (token.find(';') != std::string::npos) | ||||
|          { | ||||
|             break; | ||||
|          } | ||||
| 
 | ||||
|          tokenList.push_back(std::move(token)); | ||||
|       } | ||||
| 
 | ||||
|       if (tokenList.size() >= 2) | ||||
|       { | ||||
|          try | ||||
|          { | ||||
|             p->ProcessLine(tokenList); | ||||
|          } | ||||
|          catch (const std::exception&) | ||||
|          { | ||||
|             BOOST_LOG_TRIVIAL(warning) | ||||
|                << logPrefix_ << "Could not parse line: " << line; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    return p; | ||||
| } | ||||
| 
 | ||||
| void ColorTable::ProcessLine(const std::vector<std::string>& tokenList) | ||||
| { | ||||
|    if (tokenList[0] == "Product:") | ||||
|    { | ||||
|       // Product: string
 | ||||
|       p->product_ = tokenList[1]; | ||||
|    } | ||||
|    else if (tokenList[0] == "Units:") | ||||
|    { | ||||
|       // Units: string
 | ||||
|       p->units_ = tokenList[1]; | ||||
|    } | ||||
|    else if (tokenList[0] == "Scale:") | ||||
|    { | ||||
|       // Scale: float
 | ||||
|       p->scale_ = std::stof(tokenList[1]); | ||||
|    } | ||||
|    else if (tokenList[0] == "Offset:") | ||||
|    { | ||||
|       // Offset: float
 | ||||
|       p->offset_ = std::stof(tokenList[1]); | ||||
|    } | ||||
|    else if (tokenList[0] == "Step:") | ||||
|    { | ||||
|       // Step: float
 | ||||
|       p->step_ = std::stof(tokenList[1]); | ||||
|    } | ||||
|    else if (tokenList[0] == "RF") | ||||
|    { | ||||
|       // RF: R G B [A]
 | ||||
|       p->rfColor_ = ParseColor(tokenList, 1, p->colorMode_); | ||||
|    } | ||||
|    else if (tokenList[0] == "Color:") | ||||
|    { | ||||
|       // Color: value R G B [R G B]
 | ||||
|       float key = std::stof(tokenList[1]); | ||||
| 
 | ||||
|       boost::gil::rgba8_pixel_t color1 = | ||||
|          ParseColor(tokenList, 2, p->colorMode_, false); | ||||
|       std::optional<boost::gil::rgba8_pixel_t> color2; | ||||
| 
 | ||||
|       if (tokenList.size() >= 8) | ||||
|       { | ||||
|          color2 = ParseColor(tokenList, 5, p->colorMode_, false); | ||||
|       } | ||||
| 
 | ||||
|       p->colorMap_[key] = std::make_pair(color1, color2); | ||||
|    } | ||||
|    else if (tokenList[0] == "Color4:") | ||||
|    { | ||||
|       // Color4: value R G B A [R G B A]
 | ||||
|       float key = std::stof(tokenList[1]); | ||||
| 
 | ||||
|       boost::gil::rgba8_pixel_t color1 = | ||||
|          ParseColor(tokenList, 2, p->colorMode_); | ||||
|       std::optional<boost::gil::rgba8_pixel_t> color2; | ||||
| 
 | ||||
|       if (tokenList.size() >= 10) | ||||
|       { | ||||
|          color2 = ParseColor(tokenList, 6, p->colorMode_); | ||||
|       } | ||||
| 
 | ||||
|       p->colorMap_[key] = std::make_pair(color1, color2); | ||||
|    } | ||||
|    else if (tokenList[0] == "SolidColor:") | ||||
|    { | ||||
|       // SolidColor: value R G B
 | ||||
|       float key = std::stof(tokenList[1]); | ||||
| 
 | ||||
|       boost::gil::rgba8_pixel_t color1 = | ||||
|          ParseColor(tokenList, 2, p->colorMode_, false); | ||||
| 
 | ||||
|       p->colorMap_[key] = std::make_pair(color1, color1); | ||||
|    } | ||||
|    else if (tokenList[0] == "SolidColor4:") | ||||
|    { | ||||
|       // SolidColor4: value R G B A
 | ||||
|       float key = std::stof(tokenList[1]); | ||||
| 
 | ||||
|       boost::gil::rgba8_pixel_t color1 = | ||||
|          ParseColor(tokenList, 2, p->colorMode_); | ||||
| 
 | ||||
|       p->colorMap_[key] = std::make_pair(color1, color1); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| static boost::gil::rgba8_pixel_t | ||||
| ParseColor(const std::vector<std::string>& tokenList, | ||||
|            size_t                          startIndex, | ||||
|            ColorMode                       colorMode, | ||||
|            bool                            hasAlpha) | ||||
| { | ||||
| 
 | ||||
|    uint8_t r; | ||||
|    uint8_t g; | ||||
|    uint8_t b; | ||||
|    uint8_t a = 255; | ||||
| 
 | ||||
|    if (colorMode == ColorMode::RGBA) | ||||
|    { | ||||
|       r = StringToDecimal<uint8_t>(tokenList[startIndex + 0]); | ||||
|       g = StringToDecimal<uint8_t>(tokenList[startIndex + 1]); | ||||
|       b = StringToDecimal<uint8_t>(tokenList[startIndex + 2]); | ||||
| 
 | ||||
|       if (hasAlpha && tokenList.size() >= startIndex + 4) | ||||
|       { | ||||
|          a = StringToDecimal<uint8_t>(tokenList[startIndex + 3]); | ||||
|       } | ||||
|    } | ||||
|    else // if (colorMode == ColorMode::HSLuv)
 | ||||
|    { | ||||
|       double h = std::stod(tokenList[startIndex + 0]); | ||||
|       double s = std::stod(tokenList[startIndex + 1]); | ||||
|       double l = std::stod(tokenList[startIndex + 2]); | ||||
|       double dr; | ||||
|       double dg; | ||||
|       double db; | ||||
| 
 | ||||
|       hsluv2rgb(h, s, l, &dr, &dg, &db); | ||||
| 
 | ||||
|       r = RoundChannel<uint8_t>(dr * 255.0); | ||||
|       g = RoundChannel<uint8_t>(dg * 255.0); | ||||
|       b = RoundChannel<uint8_t>(db * 255.0); | ||||
|    } | ||||
| 
 | ||||
|    return boost::gil::rgba8_pixel_t {r, g, b, a}; | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| T RoundChannel(double value) | ||||
| { | ||||
|    return std::clamp<int>(std::lround(value), | ||||
|                           std::numeric_limits<T>::min(), | ||||
|                           std::numeric_limits<T>::max()); | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| T StringToDecimal(const std::string& str) | ||||
| { | ||||
|    return std::clamp<int>(std::stoi(str), | ||||
|                           std::numeric_limits<T>::min(), | ||||
|                           std::numeric_limits<T>::max()); | ||||
| } | ||||
| 
 | ||||
| } // namespace common
 | ||||
| } // namespace scwx
 | ||||
|  | @ -38,7 +38,7 @@ public: | |||
|    void HandleMessage(std::shared_ptr<rda::Message>& message); | ||||
|    void LoadLDMRecords(std::ifstream& f); | ||||
|    void ParseLDMRecords(); | ||||
|    void ProcessRadarData(std::shared_ptr<rda::DigitalRadarData>& message); | ||||
|    void ProcessRadarData(std::shared_ptr<rda::DigitalRadarData> message); | ||||
|    void ProcessVcpData(); | ||||
| 
 | ||||
|    std::string tapeFilename_; | ||||
|  | @ -245,7 +245,7 @@ void Ar2vFileImpl::HandleMessage(std::shared_ptr<rda::Message>& message) | |||
| } | ||||
| 
 | ||||
| void Ar2vFileImpl::ProcessRadarData( | ||||
|    std::shared_ptr<rda::DigitalRadarData>& message) | ||||
|    std::shared_ptr<rda::DigitalRadarData> message) | ||||
| { | ||||
|    uint16_t azimuthIndex   = message->azimuth_number() - 1; | ||||
|    uint16_t elevationIndex = message->elevation_number() - 1; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat