mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 08:00:06 +00:00
Coded time motion location
This commit is contained in:
parent
b96455190f
commit
db4f37a37d
5 changed files with 322 additions and 0 deletions
90
test/source/scwx/awips/coded_time_motion_location.test.cpp
Normal file
90
test/source/scwx/awips/coded_time_motion_location.test.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include <scwx/awips/coded_time_motion_location.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ =
|
||||||
|
"[scwx::awips::coded_time_motion_location.test] ";
|
||||||
|
|
||||||
|
TEST(CodedTimeMotionLocation, LeadingZeroes)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
std::vector<std::string> data = {
|
||||||
|
"TIME...MOT...LOC 0128Z 004DEG 9KT 3480 10318"};
|
||||||
|
|
||||||
|
CodedTimeMotionLocation tml;
|
||||||
|
bool dataValid = tml.Parse(data);
|
||||||
|
|
||||||
|
ASSERT_EQ(dataValid, true);
|
||||||
|
|
||||||
|
EXPECT_EQ(tml.time().to_duration(), 1h + 28min);
|
||||||
|
EXPECT_EQ(tml.direction(), 4);
|
||||||
|
EXPECT_EQ(tml.speed(), 9);
|
||||||
|
|
||||||
|
auto coordinates = tml.coordinates();
|
||||||
|
|
||||||
|
ASSERT_EQ(coordinates.size(), 1);
|
||||||
|
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].latitude_, 34.80);
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].longitude_, -103.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CodedTimeMotionLocation, Stationary)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
std::vector<std::string> data = {
|
||||||
|
"TIME...MOT...LOC 1959Z 254DEG 0KT 3253 11464"};
|
||||||
|
|
||||||
|
CodedTimeMotionLocation tml;
|
||||||
|
bool dataValid = tml.Parse(data);
|
||||||
|
|
||||||
|
ASSERT_EQ(dataValid, true);
|
||||||
|
|
||||||
|
EXPECT_EQ(tml.time().to_duration(), 19h + 59min);
|
||||||
|
EXPECT_EQ(tml.direction(), 254);
|
||||||
|
EXPECT_EQ(tml.speed(), 0);
|
||||||
|
|
||||||
|
auto coordinates = tml.coordinates();
|
||||||
|
|
||||||
|
ASSERT_EQ(coordinates.size(), 1);
|
||||||
|
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].latitude_, 32.53);
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].longitude_, -114.64);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CodedTimeMotionLocation, TwoCoordinates)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
std::vector<std::string> data = {
|
||||||
|
"TIME...MOT...LOC 2113Z 345DEG 42KT 2760 8211 2724 8198"};
|
||||||
|
|
||||||
|
CodedTimeMotionLocation tml;
|
||||||
|
bool dataValid = tml.Parse(data);
|
||||||
|
|
||||||
|
ASSERT_EQ(dataValid, true);
|
||||||
|
|
||||||
|
EXPECT_EQ(tml.time().to_duration(), 21h + 13min);
|
||||||
|
EXPECT_EQ(tml.direction(), 345);
|
||||||
|
EXPECT_EQ(tml.speed(), 42);
|
||||||
|
|
||||||
|
auto coordinates = tml.coordinates();
|
||||||
|
|
||||||
|
ASSERT_EQ(coordinates.size(), 2);
|
||||||
|
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].latitude_, 27.6);
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[0].longitude_, -82.11);
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[1].latitude_, 27.24);
|
||||||
|
EXPECT_DOUBLE_EQ(coordinates[1].longitude_, -81.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
|
|
@ -9,6 +9,7 @@ find_package(GTest)
|
||||||
|
|
||||||
set(SRC_MAIN source/scwx/wxtest.cpp)
|
set(SRC_MAIN source/scwx/wxtest.cpp)
|
||||||
set(SRC_AWIPS_TESTS source/scwx/awips/coded_location.test.cpp
|
set(SRC_AWIPS_TESTS source/scwx/awips/coded_location.test.cpp
|
||||||
|
source/scwx/awips/coded_time_motion_location.test.cpp
|
||||||
source/scwx/awips/pvtec.test.cpp
|
source/scwx/awips/pvtec.test.cpp
|
||||||
source/scwx/awips/text_product_file.test.cpp)
|
source/scwx/awips/text_product_file.test.cpp)
|
||||||
set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp)
|
set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp)
|
||||||
|
|
|
||||||
46
wxdata/include/scwx/awips/coded_time_motion_location.hpp
Normal file
46
wxdata/include/scwx/awips/coded_time_motion_location.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <scwx/common/geographic.hpp>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/range/any_range.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
class CodedTimeMotionLocationImpl;
|
||||||
|
|
||||||
|
class CodedTimeMotionLocation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::any_range<std::string, boost::forward_traversal_tag>
|
||||||
|
StringRange;
|
||||||
|
|
||||||
|
explicit CodedTimeMotionLocation();
|
||||||
|
~CodedTimeMotionLocation();
|
||||||
|
|
||||||
|
CodedTimeMotionLocation(const CodedTimeMotionLocation&) = delete;
|
||||||
|
CodedTimeMotionLocation& operator=(const CodedTimeMotionLocation&) = delete;
|
||||||
|
|
||||||
|
CodedTimeMotionLocation(CodedTimeMotionLocation&&) noexcept;
|
||||||
|
CodedTimeMotionLocation& operator=(CodedTimeMotionLocation&&) noexcept;
|
||||||
|
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::minutes> time() const;
|
||||||
|
uint16_t direction() const;
|
||||||
|
uint8_t speed() const;
|
||||||
|
std::vector<common::Coordinate> coordinates() const;
|
||||||
|
|
||||||
|
bool Parse(const StringRange& lines, const std::string& wfo = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<CodedTimeMotionLocationImpl> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
183
wxdata/source/scwx/awips/coded_time_motion_location.cpp
Normal file
183
wxdata/source/scwx/awips/coded_time_motion_location.cpp
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Enable chrono formatters
|
||||||
|
#ifndef __cpp_lib_format
|
||||||
|
# define __cpp_lib_format 202110L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <scwx/awips/coded_time_motion_location.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
namespace scwx
|
||||||
|
{
|
||||||
|
namespace awips
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string logPrefix_ =
|
||||||
|
"[scwx::awips::coded_time_motion_location] ";
|
||||||
|
|
||||||
|
class CodedTimeMotionLocationImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CodedTimeMotionLocationImpl() :
|
||||||
|
time_ {}, direction_ {0}, speed_ {0}, coordinates_ {}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CodedTimeMotionLocationImpl() {}
|
||||||
|
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::minutes> time_;
|
||||||
|
uint16_t direction_;
|
||||||
|
uint8_t speed_;
|
||||||
|
std::vector<common::Coordinate> coordinates_;
|
||||||
|
};
|
||||||
|
|
||||||
|
CodedTimeMotionLocation::CodedTimeMotionLocation() :
|
||||||
|
p(std::make_unique<CodedTimeMotionLocationImpl>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
CodedTimeMotionLocation::~CodedTimeMotionLocation() = default;
|
||||||
|
|
||||||
|
CodedTimeMotionLocation::CodedTimeMotionLocation(
|
||||||
|
CodedTimeMotionLocation&&) noexcept = default;
|
||||||
|
CodedTimeMotionLocation& CodedTimeMotionLocation::operator=(
|
||||||
|
CodedTimeMotionLocation&&) noexcept = default;
|
||||||
|
|
||||||
|
std::chrono::hh_mm_ss<std::chrono::minutes>
|
||||||
|
CodedTimeMotionLocation::time() const
|
||||||
|
{
|
||||||
|
return p->time_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CodedTimeMotionLocation::direction() const
|
||||||
|
{
|
||||||
|
return p->direction_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CodedTimeMotionLocation::speed() const
|
||||||
|
{
|
||||||
|
return p->speed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<common::Coordinate> CodedTimeMotionLocation::coordinates() const
|
||||||
|
{
|
||||||
|
return p->coordinates_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CodedTimeMotionLocation::Parse(const StringRange& lines,
|
||||||
|
const std::string& wfo)
|
||||||
|
{
|
||||||
|
bool dataValid = true;
|
||||||
|
|
||||||
|
std::vector<std::string> tokenList;
|
||||||
|
|
||||||
|
for (std::string line : lines)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
std::istringstream tokenStream {line};
|
||||||
|
|
||||||
|
while (tokenStream >> token)
|
||||||
|
{
|
||||||
|
tokenList.push_back(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First token is "TIME...MOT...LOC"
|
||||||
|
// At a minimum, one point (latitude/longitude pair) will be included
|
||||||
|
dataValid = (tokenList.size() >= 6 && tokenList.size() % 2 == 0 &&
|
||||||
|
tokenList.at(0) == "TIME...MOT...LOC");
|
||||||
|
|
||||||
|
if (dataValid)
|
||||||
|
{
|
||||||
|
const bool wfoIsWest = (wfo != "PGUM");
|
||||||
|
double westLongitude = (wfoIsWest) ? -1.0 : 1.0;
|
||||||
|
bool straddlesDateLine = false;
|
||||||
|
|
||||||
|
// Time: hhmmZ
|
||||||
|
std::string time = tokenList.at(1);
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
static const std::string timeFormat {"%H%MZ"};
|
||||||
|
|
||||||
|
std::istringstream in {time};
|
||||||
|
minutes tp;
|
||||||
|
in >> parse(timeFormat, tp);
|
||||||
|
|
||||||
|
if (time.size() == 5 && !in.fail())
|
||||||
|
{
|
||||||
|
p->time_ = hh_mm_ss {tp};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
|
<< logPrefix_ << "Invalid time: \"" << time << "\"";
|
||||||
|
p->time_ = hh_mm_ss<minutes> {};
|
||||||
|
dataValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction: dirDEG
|
||||||
|
std::string direction = tokenList.at(2);
|
||||||
|
if (direction.size() == 6 && direction.ends_with("DEG"))
|
||||||
|
{
|
||||||
|
p->direction_ =
|
||||||
|
static_cast<uint16_t>(std::stoul(direction.substr(0, 3)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
|
<< logPrefix_ << "Invalid direction: \"" << direction << "\"";
|
||||||
|
dataValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed: <sp>KT
|
||||||
|
std::string speed = tokenList.at(3);
|
||||||
|
if (speed.size() >= 3 && speed.size() <= 4 && speed.ends_with("KT"))
|
||||||
|
{
|
||||||
|
p->speed_ =
|
||||||
|
static_cast<uint8_t>(std::stoul(speed.substr(0, speed.size() - 2)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
|
<< logPrefix_ << "Invalid speed: \"" << speed << "\"";
|
||||||
|
dataValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Location
|
||||||
|
for (auto token = tokenList.cbegin() + 4; token != tokenList.cend();
|
||||||
|
++token)
|
||||||
|
{
|
||||||
|
double latitude = std::stod(*token) * 0.01;
|
||||||
|
++token;
|
||||||
|
double longitude = std::stod(*token) * 0.01;
|
||||||
|
|
||||||
|
// If a given product straddles 180 degrees longitude, those points
|
||||||
|
// west of 180 degrees will be given as if they were west longitude
|
||||||
|
if (longitude > 180.0)
|
||||||
|
{
|
||||||
|
longitude -= 360.0;
|
||||||
|
straddlesDateLine = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
longitude *= westLongitude;
|
||||||
|
|
||||||
|
p->coordinates_.push_back({latitude, longitude});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wfoIsWest && straddlesDateLine)
|
||||||
|
{
|
||||||
|
for (auto& coordinate : p->coordinates_)
|
||||||
|
{
|
||||||
|
coordinate.longitude_ *= -1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace awips
|
||||||
|
} // namespace scwx
|
||||||
|
|
@ -3,6 +3,7 @@ project(scwx-data)
|
||||||
find_package(Boost)
|
find_package(Boost)
|
||||||
|
|
||||||
set(HDR_AWIPS include/scwx/awips/coded_location.hpp
|
set(HDR_AWIPS include/scwx/awips/coded_location.hpp
|
||||||
|
include/scwx/awips/coded_time_motion_location.hpp
|
||||||
include/scwx/awips/message.hpp
|
include/scwx/awips/message.hpp
|
||||||
include/scwx/awips/phenomenon.hpp
|
include/scwx/awips/phenomenon.hpp
|
||||||
include/scwx/awips/pvtec.hpp
|
include/scwx/awips/pvtec.hpp
|
||||||
|
|
@ -11,6 +12,7 @@ set(HDR_AWIPS include/scwx/awips/coded_location.hpp
|
||||||
include/scwx/awips/text_product_message.hpp
|
include/scwx/awips/text_product_message.hpp
|
||||||
include/scwx/awips/wmo_header.hpp)
|
include/scwx/awips/wmo_header.hpp)
|
||||||
set(SRC_AWIPS source/scwx/awips/coded_location.cpp
|
set(SRC_AWIPS source/scwx/awips/coded_location.cpp
|
||||||
|
source/scwx/awips/coded_time_motion_location.cpp
|
||||||
source/scwx/awips/message.cpp
|
source/scwx/awips/message.cpp
|
||||||
source/scwx/awips/phenomenon.cpp
|
source/scwx/awips/phenomenon.cpp
|
||||||
source/scwx/awips/pvtec.cpp
|
source/scwx/awips/pvtec.cpp
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue