使用boost :: karma格式化纬度/经度字符串 [英] using boost::karma to format latitude/longitude strings
问题描述
我需要将double
值格式化为具有非常特定格式的坐标字符串,"DDMMSS.SSX"
其中:
I need to format double
values into coordinate strings that have a very specific format, "DDMMSS.SSX"
where:
- "DD"是全学位
- "MM"是完整的分钟数
- "SS.SS"是带分数的秒数
- "X"取决于半球是"N"还是"S"
字段必须用零填充.不能接受空格.格式示例如下:
The fields need to be padded with zeroes. Spaces cannot be accepted. Examples for the formatting is as follows:
47.2535 ==> "471512.45N"
-0.123345 ==> "000724.04S"
我设法创建了完成该任务的以下程序.但是我有一些疑问:
I have managed to create the following program that does the job. However I have some questions:
-
locls
规则是否有更优雅的方法?目的是将绝对值存储到局部变量value
中.是否有(希望更优雅)访问fabs()
函数的方法? - 在我看来,
_1
(_1 = _val
等)的分配是不必要的,因为我在局部变量value
中具有值.但是,如果我删除这些分配,则只能得到"000000.00N"
. - 这种格式的主力军"是int_生成器,我在计算并转换原始的
value
之后使用它.有更好的方法吗? - 对于这种问题,通常有更好的解决方案吗?
- is there a more elegant way for the
locls
rule? It's purpose is to store the absolute value into the local variablevalue
. Is there a (hopefully more elegant) way to access thefabs()
function? - In my opinion the assignments to
_1
(_1 = _val
etc.) are unnecessary since I have the value in the local variablevalue
. However if I remove these assignments, all I get is"000000.00N"
. - the "workhorse" of this formatting is the int_ generator, which I use after calculating and casting the original
value
. Is there a better approach? - is there generally a better solution for this kind of problem?
我很高兴获得一些反馈
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/bind.hpp>
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> iterator_type;
struct genLongitude : karma::grammar<iterator_type, double()>
{
genLongitude()
: genLongitude::base_type(start)
{
using karma::eps;
using karma::int_;
using karma::char_;
using karma::_1;
using karma::_val;
using karma::right_align;
using boost::phoenix::static_cast_;
using boost::phoenix::ref;
using boost::phoenix::if_;
start = locls
<< degrees << minutes << seconds
<< ( eps(_val < 0.0) << char_('E') | char_('W') );
locls = eps[_1 = _val, if_(_val < 0.0) [ref(value) = - _val] .else_ [ref(value) = _val]];
degrees = right_align(3,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 60 ];
minutes = right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 60 ];
seconds = right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< char_(".")
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 100 ]
<< right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]];
}
private:
double value;
karma::rule<iterator_type, double()> start, locls, degrees, minutes, seconds;
};
int main()
{
for(auto & value : std::vector<double>{ 47.25346, 13.984364, -0.1233453, -44.3 })
{
std::string generated;
iterator_type outiter(generated);
auto rv = karma::generate(outiter, genLatitude(), value);
std::cout << "(" << rv << ") " << value << " ==> " << generated << std::endl;
}
}
更新:
仅出于完整性考虑,在任何示例(和答案)中都很难做到这一点
纬度的格式为"DDMMSS.SSX"
,经度为"DDDMMSS.SSX"
.这是因为纬度的范围是-90至+90,而经度的范围是-180至+180.
Update:
Just for completeness, this is actually trivial to fix in any of the examples (and answers)
The format of the Latitude is "DDMMSS.SSX"
, the Longitude is "DDDMMSS.SSX"
. This is because range of the latitude is -90 to +90 while the longitude is -180 to +180.
推荐答案
多加思考,让我回答
问. 通常对于这种问题有更好的解决方案吗?
在这种情况下,使用Boost Format可能会更好.重复使用LatLongRep
-来自其他答案的计算工具,您可以非常轻松地创建IO机械手:
In this you may be better off with Boost Format. Reusing LatLongRep
- the calculation work-horse from my other answer, you can create IO manipulators really easily:
namespace manip {
struct LatLongRepIO : LatLongRep {
LatLongRepIO(double val, char const* choices) : LatLongRep(val), _display(choices) { }
private:
char const* _display;
friend std::ostream& operator<<(std::ostream& os, LatLongRepIO const& llr) {
return os << boost::format("%03d%02d%05.2f%c")
% llr._deg % llr._min % llr._sec
% (llr._display[llr._hemi]);
}
};
LatLongRepIO as_latitude (double val) { return { val, "WE" }; }
LatLongRepIO as_longitude(double val) { return { val, "NS" }; }
}
这完全放弃了Boost Spirit,Phoenix和Fusion的使用,并且使使用变得轻而易举:
This forgoes the use of Boost Spirit, Phoenix and Fusion alltogether, and makes usage a breeze:
int main() {
using namespace helpers::manip;
for(double value : { 47.25346, 13.984364, -0.1233453, -44.3 })
std::cout << as_latitude(value) << "\t" << as_longitude(value) << "\n";
}
演示
#include <boost/format.hpp>
#include <cmath>
namespace helpers {
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
namespace manip {
struct LatLongRepIO : LatLongRep {
LatLongRepIO(double val, char const* choices) : LatLongRep(val), _display(choices) { }
private:
char const* _display;
friend std::ostream& operator<<(std::ostream& os, LatLongRepIO const& llr) {
return os << boost::format("%03d%02d%05.2f%c")
% llr._deg % llr._min % llr._sec
% (llr._display[llr._hemi]);
}
};
LatLongRepIO as_latitude (double val) { return { val, "WE" }; }
LatLongRepIO as_longitude(double val) { return { val, "NS" }; }
}
}
#include <iostream>
int main() {
using namespace helpers::manip;
for(double value : { 47.25346, 13.984364, -0.1233453, -44.3 })
std::cout << as_latitude(value) << "\t" << as_longitude(value) << "\n";
}
打印
0471512.46E 0471512.46S
0135903.71E 0135903.71S
0000724.04W 0000724.04N
0441760.00W 0441760.00N
这篇关于使用boost :: karma格式化纬度/经度字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!