Coded location

This commit is contained in:
Dan Paulat 2022-01-25 21:39:28 -06:00
parent f25bb63ea0
commit ecf5b1f5f0
6 changed files with 428 additions and 3 deletions

View file

@ -0,0 +1,42 @@
#pragma once
#include <scwx/common/geographic.hpp>
#include <memory>
#include <string>
#include <vector>
#include <boost/range/any_range.hpp>
namespace scwx
{
namespace awips
{
class CodedLocationImpl;
class CodedLocation
{
public:
typedef boost::any_range<std::string, boost::forward_traversal_tag>
StringRange;
explicit CodedLocation();
~CodedLocation();
CodedLocation(const CodedLocation&) = delete;
CodedLocation& operator=(const CodedLocation&) = delete;
CodedLocation(CodedLocation&&) noexcept;
CodedLocation& operator=(CodedLocation&&) noexcept;
std::vector<common::Coordinate> coordinates() const;
bool Parse(const StringRange& lines, const std::string& wfo = "");
private:
std::unique_ptr<CodedLocationImpl> p;
};
} // namespace awips
} // namespace scwx

View file

@ -0,0 +1,25 @@
#pragma once
namespace scwx
{
namespace common
{
struct Coordinate
{
double latitude_;
double longitude_;
Coordinate(double latitude, double longitude) :
latitude_ {latitude}, longitude_ {longitude}
{
}
bool operator==(const Coordinate& o) const
{
return latitude_ == o.latitude_ && longitude_ == o.longitude_;
}
};
} // namespace common
} // namespace scwx

View file

@ -0,0 +1,155 @@
#include <scwx/awips/coded_location.hpp>
#include <sstream>
#include <boost/log/trivial.hpp>
namespace scwx
{
namespace awips
{
static const std::string logPrefix_ = "[scwx::awips::coded_location] ";
class CodedLocationImpl
{
public:
explicit CodedLocationImpl() : coordinates_ {} {}
~CodedLocationImpl() {}
std::vector<common::Coordinate> coordinates_;
};
CodedLocation::CodedLocation() : p(std::make_unique<CodedLocationImpl>()) {}
CodedLocation::~CodedLocation() = default;
CodedLocation::CodedLocation(CodedLocation&&) noexcept = default;
CodedLocation& CodedLocation::operator=(CodedLocation&&) noexcept = default;
std::vector<common::Coordinate> CodedLocation::coordinates() const
{
return p->coordinates_;
}
bool CodedLocation::Parse(const StringRange& lines, const std::string& wfo)
{
enum class LocationFormat
{
WFO,
NationalCenter
};
bool dataValid = true;
LocationFormat format;
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 "LAT...LON"
// At a minimum, three points (latitude/longitude pairs) will be included
dataValid = (tokenList.size() >= 4 && tokenList.at(0) == "LAT...LON");
if (dataValid)
{
format = (tokenList.at(1).size() == 8) ? LocationFormat::NationalCenter :
LocationFormat::WFO;
if (format == LocationFormat::WFO)
{
dataValid = (tokenList.size() >= 7 && tokenList.size() % 2 == 1);
}
}
if (dataValid)
{
if (format == LocationFormat::WFO)
{
const bool wfoIsWest = (wfo != "PGUM");
double westLongitude = (wfoIsWest) ? -1.0 : 1.0;
bool straddlesDateLine = false;
for (auto token = tokenList.cbegin() + 1; 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;
}
}
}
else
{
for (auto token = tokenList.cbegin() + 1; token != tokenList.cend();
++token)
{
if (token->size() != 8)
{
dataValid = false;
break;
}
double latitude = std::stod(token->substr(0, 4)) * 0.01;
double longitude = std::stod(token->substr(4, 4)) * -0.01;
// Longitudes of greater than 100 degrees will drop the leading 1;
// i.e., 105.22 W would be coded as 0522. This is ambiguous
// with 5.22 W, so we assume everything east of 65 W (east of Maine,
// easternmost point of CONUS) should have 100 degrees added to it.
// Points in the Atlantic or western Alaska will not be correct, but
// it is assumed that products will not contain those points coded
// using this methodology.
if (longitude > -65.0)
{
longitude -= 100.0;
}
p->coordinates_.push_back({latitude, longitude});
}
}
}
if (dataValid)
{
// If the last point is a repeat of the first point, remove it as
// redundant
if (p->coordinates_.front() == p->coordinates_.back())
{
p->coordinates_.pop_back();
}
}
return dataValid;
}
} // namespace awips
} // namespace scwx

View file

@ -2,14 +2,16 @@ project(scwx-data)
find_package(Boost)
set(HDR_AWIPS include/scwx/awips/message.hpp
set(HDR_AWIPS include/scwx/awips/coded_location.hpp
include/scwx/awips/message.hpp
include/scwx/awips/phenomenon.hpp
include/scwx/awips/pvtec.hpp
include/scwx/awips/significance.hpp
include/scwx/awips/text_product_file.hpp
include/scwx/awips/text_product_message.hpp
include/scwx/awips/wmo_header.hpp)
set(SRC_AWIPS source/scwx/awips/message.cpp
set(SRC_AWIPS source/scwx/awips/coded_location.cpp
source/scwx/awips/message.cpp
source/scwx/awips/phenomenon.cpp
source/scwx/awips/pvtec.cpp
source/scwx/awips/significance.cpp
@ -19,6 +21,7 @@ set(SRC_AWIPS source/scwx/awips/message.cpp
set(HDR_COMMON include/scwx/common/characters.hpp
include/scwx/common/color_table.hpp
include/scwx/common/constants.hpp
include/scwx/common/geographic.hpp
include/scwx/common/products.hpp
include/scwx/common/types.hpp
include/scwx/common/vcp.hpp)