[units] subtract_typeof_helper<> help!
I'm trying to use boost::geometry and boost::units together. For some reason boost::geometry doesn't represent the difference of two points as vectors. So, I'm making my own Vector class and defining Vector = Point - Point. The sample program below doesn't compile because I need to tell the units library about the special properties of point subtraction (I think). terry #include <boost/geometry/geometry.hpp> #include <boost/units/systems/si.hpp> #include <boost/units/quantity.hpp> #include <boost/units/io.hpp> #include <iostream> #include <iomanip> using namespace std; using namespace boost; using namespace boost::geometry; using namespace boost::units; typedef point<double, 3, cs::cartesian> Point; class Vector { Point _impl; friend Vector operator-(const Point& lhs, const Point& rhs); explicit Vector(Point p) : _impl(p) { } public: Vector() : _impl(0) { } Vector(double dx, double dy, double dz=0) : _impl(dx, dy, dz) { } friend ostream& operator<<(ostream& os, const Vector& v) { return os << dsv(v._impl); } }; // Vector inline Vector operator-(const Point& lhs, const Point& rhs) { Point tmp(lhs); subtract_point(tmp, rhs); return Vector(tmp); } inline ostream& operator<<(ostream& os, const Point& pt) { return os << dsv(pt); } int main() { Point p1(1, 2, 3); Point p2(4, 5, 6); typedef quantity<si::length, Point> Position; typedef quantity<si::length, Vector> Displacement; Position start( p1 * si::meters); Position finish(p2 * si::meters); Displacement disp(finish - start); cout << "disp=" << disp << endl; return EXIT_SUCCESS; } // main
AMDG Terry Golubiewski wrote:
I'm trying to use boost::geometry and boost::units together. For some reason boost::geometry doesn't represent the difference of two points as vectors. So, I'm making my own Vector class and defining Vector = Point - Point. The sample program below doesn't compile because I need to tell the units library about the special properties of point subtraction (I think).
The problem is that your operator- can't be found by ADL.
using namespace boost::geometry;
typedef point<double, 3, cs::cartesian> Point; <snip>
inline Vector operator-(const Point& lhs, const Point& rhs);
In Christ, Steven Watanabe
On May 17, 9:45 pm, "Terry Golubiewski" <tjgol...@netins.net> wrote:
I'm trying to use boost::geometry and boost::units together. For some reason boost::geometry doesn't represent the difference of two points as vectors. So, I'm making my own Vector class and defining Vector = Point - Point. The sample program below doesn't compile because I need to tell the units library about the special properties of point subtraction (I think).
please post your solution if you find one. I have a similar problem. Alfredo
Here's my latest stab at a Vector implementation using boost geometry and units libraries. This seems like way too much work for way too little benefit. Perhaps, boost::units should be used only as an interface tool. Once you try to use aggregates like matrices and points, its use becomes cumbersome. terry ----- Original Message ----- From: "alfC" <alfredo.correa@gmail.com> Newsgroups: gmane.comp.lib.boost.user To: <boost-users@lists.boost.org> Sent: Wednesday, May 19, 2010 4:28 AM Subject: Re: [units] subtract_typeof_helper<> help! On May 17, 9:45 pm, "Terry Golubiewski" <tjgol...@netins.net> wrote:
I'm trying to use boost::geometry and boost::units together. For some reason boost::geometry doesn't represent the difference of two points as vectors. So, I'm making my own Vector class and defining Vector = Point - Point. The sample program below doesn't compile because I need to tell the units library about the special properties of point subtraction (I think).
please post your solution if you find one. I have a similar problem. Alfredo
This version serves about as well, doesn't use [units], and uses very little of [geometry]. terry ----- Original Message ----- From: "alfC" <alfredo.correa@gmail.com> Newsgroups: gmane.comp.lib.boost.user To: <boost-users@lists.boost.org> Sent: Wednesday, May 19, 2010 4:28 AM Subject: Re: [units] subtract_typeof_helper<> help! On May 17, 9:45 pm, "Terry Golubiewski" <tjgol...@netins.net> wrote:
I'm trying to use boost::geometry and boost::units together. For some reason boost::geometry doesn't represent the difference of two points as vectors. So, I'm making my own Vector class and defining Vector = Point - Point. The sample program below doesn't compile because I need to tell the units library about the special properties of point subtraction (I think).
please post your solution if you find one. I have a similar problem. Alfredo
This version serves about as well, doesn't use [units], and uses very little of [geometry]. terry
I don't know what the main purpose of Boost.Geometry is... Boost.Units is intended to ensure compile-time dimensional correctness. It is probably only useful if your code is likely to have mixed use of vectors/points having different length metrics. The code posted here has no idea if the distances are in microns or furlongs and will happily mix the two without any warning. Matthias
----- Original Message ----- From: "Matthias Schabel" <boost@schabel-family.org> Newsgroups: gmane.comp.lib.boost.user To: <boost-users@lists.boost.org> Sent: Wednesday, May 19, 2010 2:19 PM Subject: Re: [units] subtract_typeof_helper<> help!
This version serves about as well, doesn't use [units], and uses very little of [geometry]. terry
I don't know what the main purpose of Boost.Geometry is... Boost.Units is intended to ensure compile-time dimensional correctness. It is probably only useful if your code is likely to have mixed use of vectors/points having different length metrics. The code posted here has no idea if the distances are in microns or furlongs and will happily mix the two without any warning.
Matthias
What I'm struggling with is how to provide a units-aware Vector/Point system for physical computation. Currently, I'm favoring using units::quantity<U, Vector<double>> rather than Vector<units::quantity<U>>. I expected to use boost::geometry, but that didn't seem to provide much benefit. Now that I'm not using boost::geometry to implement Vector/Point, it might be much easier to support Vector<units::quantity<U>> as well. My example was an intermediate stage. The example file that I failed to post looked like this, along with the sample output. It is interesting that (somehow) the Point/Vector quantities figured themselves out. Its interesting because: Vector = Point - Point Point = Point + Vector Vector = Vector + Vector but _not_ Point = Point + Point and the dot product Scalar = Vector * Vector #include "BasicVector.h" #include <boost/units/systems/si.hpp> #include <boost/units/systems/si/prefixes.hpp> #include <boost/units/base_units/us/foot.hpp> #include <boost/units/quantity.hpp> #include <boost/units/io.hpp> #include <iostream> #include <iomanip> #include <cstddef> using namespace std; using namespace boost; using namespace boost::units; using namespace tjg_basic; typedef quantity<si::length, Point<double> > Position; typedef quantity<si::length, Vector<double> > Displacement; typedef quantity<si::velocity, Vector<double> > Velocity; typedef quantity<si::plane_angle> Angle; typedef quantity<si::time> Time; int main() { static const si::length m = si::meter; static const si::time s = si::second; Position p1 = Point<double>(1.0, 2.0, 3.0) * m; Position p2 = Point<double>(4.0, 5.0, 6.0) * m; Position p3 = Point<double>(3.0, 9.0, 4.0) * m; Displacement disp1 = p2 - p1; cout << disp1 << endl; cout << abs(disp1) << endl; cout << abs_squared(disp1) << endl; p1 += disp1; cout << p1 << endl; Time dt = Time(0.1 * s); Velocity v = (p3 - p2) / dt; cout << v << endl; return EXIT_SUCCESS; } // main Output: <3 3 3> m 5.19615 m 27 m^2 (4 5 6) m <-10 40 -20> m s^-1
Sorry to revive this old thread, First I would like to know if there had been any progress in this front. Second, I would like to pick the attention of Boost.Geometry again in order to suggest them use boost.units as a way to make the Boost.Geometry library very general. Third, after some thinking I have some thoughts about the original question in this post What I'm struggling with is how to provide a units-aware Vector/Point system
for physical computation. Currently, I'm favoring using units::quantity<U, Vector<double>> rather than Vector<units::quantity<U>>.
I think none of the two options makes general sense. The only point at which units make sense is at the "metric" of the space. Since in Boost.geometry library coordinate system an metric are tied together, we need a special type of coordinates system. For example one suggestion could be to extend the coordinate systems of Boost.Geometry to include the units and dimension of space. For example one way to specify points in space could be by means of a type like this model::point<double, 3, cs::unit::cartesian<si::length> > and then specialize all the template functions that use cartesian as coordinate system, for example distance. This makes sense because the only point at which units are useful is when we take distances, modulus or projections between vectors in that "space" with unit/dimension.
I expected to use boost::geometry, but that didn't seem to provide much benefit.
Regardless of whether we see benefit or not at this point I think this is a real problem that we should face. This is a conceptual problem that must be solved sooner or later.
Now that I'm not using boost::geometry to implement Vector/Point, it might be much easier to support Vector<units::quantity<U>> as well.
My example was an intermediate stage. The example file that I failed to post looked like this, along with the sample output. It is interesting that (somehow) the Point/Vector quantities figured themselves out. Its interesting because: Vector = Point - Point Point = Point + Vector Vector = Vector + Vector but _not_ Point = Point + Point and the dot product Scalar = Vector * Vector
#include "BasicVector.h"
can you please post what is your current implementation of BasicVector.h? cout << disp1 << endl;
cout << abs(disp1) << endl; cout << abs_squared(disp1) << endl; p1 += disp1; cout << p1 << endl; Time dt = Time(0.1 * s); Velocity v = (p3 - p2) / dt; cout << v << endl; return EXIT_SUCCESS; } // main
Output: <3 3 3> m (4 5 6) m <-10 40 -20> m s^-1
this is the sort of things that from a dimensional point of view don't make sense. In the previous "m" or "m s^-1" is just a tag for the vector but it is not a real unit of the vector. This is more ovbius when vectors are represented in spherical coordinates. Any thoughts? Thank you, Alfredo
Hi,
Second, I would like to pick the attention of Boost.Geometry again in order to suggest them use boost.units as a way to make the Boost.Geometry library very general. (...) I think none of the two options makes general sense. The only point at which units make sense is at the "metric" of the space. Since in Boost.geometry library coordinate system an metric are tied together, we need a special type of coordinates system. For example one suggestion could be to extend the coordinate systems of Boost.Geometry to include the units and dimension of space. For example one way to specify points in space could be by means of a type like this
model::point<double, 3, cs::unit::cartesian<si::length> >
and then specialize all the template functions that use cartesian as coordinate system, for example distance. This makes sense because the only point at which units are useful is when we take distances, modulus or projections between vectors in that "space" with unit/dimension.
I will look at this usage soon. Thanks, Barend
Hi Barend, I will look at this usage soon.
Great, please keep me (us) posted. I have several half implemented routines to handle geometric objects (e.g point) with "dimension" and also a set of adapted_boost_array_units.hpp. I am stuck now because I reached the point at which I can't understand the internals of the library. What I have so far is for example using namespace boost::geometry; using namespace boost::units; model::point<double, 3, cs::units_cartesian<si::length> > p1(1.*si::meter,2.*si::meter,3.*si::meter); std::clog << distance(p1,p1) << std::endl; // outputs 0. * meter and #include<boost/geometry/geometries/adapted/boost_array_units_cartesian.hpp> ... using namespace boost::units; boost::array<quantity<si::length>, 3> p5={{10.*si::meter,11.*si::meter,12.*si::meter}}; std::clog << distance(p5,p5) << std::endl; //outputs 0. * meter the implementation I have is very primitive and it is practically a mockup because it bypasses most of the features of the Boost.Geometry library; mainly because I didn't understand them internally. Thank you, Alfredo
Hi Alfredo,
Great, please keep me (us) posted
Actually the timing is convenient because this indeed was still an action to be taken, mentioned in the review report.
What I have so far is for example using namespace boost::geometry; using namespace boost::units; model::point<double, 3, cs::units_cartesian<si::length> > p1(1.*si::meter,2.*si::meter,3.*si::meter); std::clog << distance(p1,p1) << std::endl; // outputs 0. * meter
This does not work for me. The type declaration works, but coordinates are still doubles here. The construction effectively translates to "double d = 1.*si::meter". This gives me the message "cannot convert from 'boost::units::quantity<Unit>' to 'double'". So how does this work for you?
#include<boost/geometry/geometries/adapted/boost_array_units_cartesian.hpp> ... using namespace boost::units; boost::array<quantity<si::length>, 3> p5={{10.*si::meter,11.*si::meter,12.*si::meter}}; std::clog << distance(p5,p5) << std::endl; //outputs 0. * meter
Construction works here for me, but calculating distance does not. Because distance behind the screens takes the square root, after squaring. This requires thorough integration with Boost.Units at those places, which is not planned.
the implementation I have is very primitive and it is practically a mockup because it bypasses most of the features of the Boost.Geometry library; mainly because I didn't understand them internally.
So Boost.Geometry is not dependant on Boost.Units. I think the way to go is define a point type using a coordinate system using Boost.Units, like you did, and also add quantities there. I just tried that. However, the output is then still in values, so distance will result a value (e.g. double). It runs like this: // Note that the last 3 template parameters have defaults typedef model::quantity_point<si::length, 2, double, cs::units_cartesian<si::length> > point; point p1(1 * meter, 2 * meter); // only quantities expected point p2(3 * meter, 4 * meter); quantity<si::length> d = distance(p1, p2) * si::meter; Then it IS possible to create a set of overloaded functions, e.g. boost::geometry::units::set(point, quantity) to set it from a quantity boost::geometry::units::get(point) to return a quantity boost::geometry::units::distance(a,b) -> returns a quantity in the coordinate dimensions from the measured distance boost::geometry::units::area(a) -> returns a quantity e.g. in square_meters from a polygon So I added two of these functions as well, giving: std::cout << units::get<0>(p2) << std::endl; quantity<si::length> d = units::distance(p1, p2); std::cout << d << std::endl; reporting all quantities including units. It required some small additional metafunctions as well. I just committed it, for the moment as a new example (including definitions), here: http://bit.ly/dEip8s Regards, Barend
On Jan 2, 6:18 am, Barend Gehrels <bar...@geodan.nl> wrote:
Hi Alfredo,
Great, please keep me (us) posted
Actually the timing is convenient because this indeed was still an action to be taken, mentioned in the review report.
What I have so far is for example using namespace boost::geometry; using namespace boost::units; model::point<double, 3, cs::units_cartesian<si::length> > p1(1.*si::meter,2.*si::meter,3.*si::meter); std::clog << distance(p1,p1) << std::endl; // outputs 0. * meter
This does not work for me. The type declaration works, but coordinates are still doubles here. The construction effectively translates to "double d = 1.*si::meter". This gives me the message "cannot convert from 'boost::units::quantity<Unit>' to 'double'". So how does this work for you?
as I told you, my implementation is a total mock up and I am not sure I am doing it with the right philosophy. I added the code of boost_geometry_units.hpp file at the end of this post. I guess it works for me because I specialised the class model::point<double, 3, cs::units_cartesian<...> > and its constructor. The logic is that once you introduce units, the types of the coordinates are not uniform types, for example, a real space vector can have its cartesian coordinates in meters, but in spherical coordinates, only the radial part is in meters, (the others are angles). But maybe you are right the coordinates should be double upon constructions and the metric and units is only contained in the type.
#include<boost/geometry/geometries/adapted/boost_array_units_cartesian.hpp> ... using namespace boost::units; boost::array<quantity<si::length>, 3> p5={{10.*si::meter,11.*si::meter,12.*si::meter}}; std::clog << distance(p5,p5) << std::endl; //outputs 0. * meter
(yes, I adapted boost::array< ... > in this way because it can be a handy and the only way the boost::array can carry units is in its values.)
Construction works here for me, but calculating distance does not. Because distance behind the screens takes the square root, after squaring. This requires thorough integration with Boost.Units at those places, which is not planned.
I understand that integrations with Boost.Units is not planned and even not desirable. But making the implicit assumption that typeof(sqrt(s)) == typeof(s) or that typeof(s*s)==type(s) makes it incompatible with Boost.units. Those assumptions are good as defaults but there should be a way to tell Boost.Geometry that sometimes it is not the case.
the implementation I have is very primitive and it is practically a mockup because it bypasses most of the features of the Boost.Geometry library; mainly because I didn't understand them internally.
So Boost.Geometry is not dependant on Boost.Units. I think the way to go is define a point type using a coordinate system using Boost.Units, like you did, and also add quantities there.
yes. Even at the interface level I am not sure for example if constructors should carry units or not. I added the units to my constructors to make it more dimensionally type safe. Maybe the boost.units people will have clearer idea.
I just tried that. However, the output is then still in values, so distance will result a value (e.g. double). It runs like this:
// Note that the last 3 template parameters have defaults typedef model::quantity_point<si::length, 2, double, cs::units_cartesian<si::length> > point; point p1(1 * meter, 2 * meter); // only quantities expected point p2(3 * meter, 4 * meter); quantity<si::length> d = distance(p1, p2) * si::meter;
distance should return the quantity directly in my opinion.
Then it IS possible to create a set of overloaded functions, e.g. boost::geometry::units::set(point, quantity) to set it from a quantity boost::geometry::units::get(point) to return a quantity boost::geometry::units::distance(a,b) -> returns a quantity in the coordinate dimensions from the measured distance
yes, like this.
boost::geometry::units::area(a) -> returns a quantity e.g. in square_meters from a polygon
So I added two of these functions as well, giving:
std::cout << units::get<0>(p2) << std::endl; quantity<si::length> d = units::distance(p1, p2); std::cout << d << std::endl;
reporting all quantities including units. It required some small additional metafunctions as well.
yes, I the same thing in my first implementation, with a namespace boost::geometry::units that have all the overloaded functions but then I though it would have a lot a repeated code.
I just committed it, for the moment as a new example (including definitions), here:http://bit.ly/dEip8s
regarding the second part of the example, I think that the library in principle can be generalized to put all the unit aware stuff in the coordinate system. i.e. using the plain model::point< ???, 3, cs::unit_cartesian<si::length> >. The first step would be to allow for the library to accept that typeof(t*t) != typeof(t). (by the way, I think part of the confusion comes from the fact that if ??? are *coordinates* types, then it should potentially be a list of types, for example imagine that I want to implement a spherical coordinates with the radial coordinate being of type positive<double>, and the radial coordinates being double, all this with out even involving units. for example, fusion<double, double, double> can be adapted as cartesian vector and fusion<positive<double>, double, double> can be adapted as spherical coordinates but not as cartesian). Thank you for the good discussion, Alfredo below the "mock up" code for boost_geometry_units.hpp. It is not good code, it is just barely enough to make it work with model::point<double, 3, cs::units_cartesian<Unit> > #ifndef BOOST_GEOMETRY_UNITS_HPP #define BOOST_GEOMETRY_UNITS_HPP // units/tags.hpp, similar to core/tags.hpp namespace boost{namespace geometry{ template<class Unit> struct units_cartesian_tag {}; }} // units/cs.hpp, similar to core/cs.hpp #include<boost/geometry/core/cs.hpp> namespace boost{namespace geometry{ namespace cs{ template<class Unit> struct units_cartesian{}; } namespace traits{ template<class Unit> struct cs_tag<cs::units_cartesian<Unit> >{ typedef units_cartesian_tag<Unit> type; }; } }} // similar to #include<boost/geometry/strategies/distance.hpp> #include<boost/geometry/geometries/point.hpp> #include<boost/units/systems/si.hpp> #include<boost/geometry/geometry.hpp> namespace boost { namespace geometry{ namespace model{ template<class T, std::size_t Dimension, class Unit> struct point<T, Dimension, cs::units_cartesian<Unit> > : protected model::point<T, Dimension, cs::cartesian >{ }; template<class T, class Unit> struct point<T, 2, cs::units_cartesian<Unit> > : protected model::point<T, 2, cs::cartesian >{ point( boost::units::quantity<Unit, T> const& x, boost::units::quantity<Unit, T> const& y ) : model::point<T, 2, cs::cartesian >(x.value(), y.value()){} }; template<class T, class Unit> struct point<T, 3, cs::units_cartesian<Unit> > : protected model::point<T, 3, cs::cartesian >{ point( boost::units::quantity<Unit, T> const& x, boost::units::quantity<Unit, T> const& y, boost::units::quantity<Unit, T> const& z ) : model::point<T, 3, cs::cartesian >(x.value(), y.value(), z.value() ){} }; } template<class T, std::size_t Dimension, class Unit> boost::units::quantity<Unit, T> distance( model::point<T, Dimension, cs::units_cartesian<Unit> > const& p1, model::point<T, Dimension, cs::units_cartesian<Unit> > const& p2 ){ return boost::units::quantity<Unit, T>::from_value( boost::geometry::distance( (model::point<T, Dimension, cs::cartesian > const&)p1, (model::point<T, Dimension, cs::cartesian > const&)p2 ) ); } namespace strategy{namespace distance{namespace services{ template<class Unit> struct return_type<boost::geometry::units_cartesian_tag<Unit> >{ typedef boost::units::quantity<Unit> type; }; template<std::size_t Dimension, class Unit> struct default_strategy< boost::geometry::point_tag, boost::geometry::model::point< double, Dimension, boost::geometry::cs::units_cartesian<Unit> >, boost::geometry::model::point< double, Dimension, boost::geometry::cs::units_cartesian<Unit> >, boost::geometry::units_cartesian_tag<Unit>, boost::geometry::units_cartesian_tag<Unit>, void
{ typedef boost::geometry::units_cartesian_tag<Unit> type; }; }}}
}}
Construction works here for me, but calculating distance does not. Because distance behind the screens takes the square root, after squaring. This requires thorough integration with Boost.Units at those places, which is not planned.
I understand that integrations with Boost.Units is not planned and even not desirable. But making the implicit assumption that typeof(sqrt(s)) == typeof(s) or that typeof(s*s)==type(s) makes it incompatible with Boost.units. Those assumptions are good as defaults but there should be a way to tell Boost.Geometry that sometimes it is not the case.
I took a stab at a generic solution to this problem of units and data types in a geometry library with Polygon. My coordinate_traits defines a distance time, area type etc. I then look up what data type to use for the return value of a distance or area calculation. Because it is a point of customization the user can specify data types with assoicated Boost.Units. My main concern was overflow at the time, but I think having a point of customization (meta function that can be specialized) and using it consistently would allow a generic geometry library to interoperate with Boost.Units without needing to be integrated with Boost.Units. Generic programing should allow exactly these types of compatibility between libraries without directly coupling them. Is there any reason why this approach wouldn't work? Thanks, Luke
Construction works here for me, but calculating distance does not. Because distance behind the screens takes the square root, after squaring. This requires thorough integration with Boost.Units at those places, which is not planned.
I understand that integrations with Boost.Units is not planned and even not desirable. But making the implicit assumption that typeof(sqrt(s)) == typeof(s) or that typeof(s*s)==type(s) makes it incompatible with Boost.units. Those assumptions are good as defaults but there should be a way to tell Boost.Geometry that sometimes it is not the case.
I took a stab at a generic solution to this problem of units and data types in a geometry library with Polygon. My coordinate_traits defines a distance time, area type etc. I then look up what data type to use for the return value of a distance or area calculation. Because it is a point of customization the user can specify data types with assoicated Boost.Units. My main concern was overflow at the time, but I think having a point of customization (meta function that can be specialized) and using it consistently would allow a generic geometry library to interoperate with Boost.Units without needing to be integrated with Boost.Units. Generic programing should allow exactly these types of compatibility between libraries without directly coupling them. Is there any reason why this approach wouldn't work?
I agree - the general sloppiness in treating mathematical operators in libraries that are intended to generically support them for UDTs seems to be a widespread problem, even within Boost. For example, std::complex (which "circumvents" the problem by disallowing UDT value types) defines template<class T> complex<T> operator/(const complex<T>& lhs, const complex<T>& rhs); However, even for a simple value type (e.g. integers - N.B. not int) this is not correct since integer/integer -> rational so this should really be template<class T> complex<rational> operator*(const complex<integer>& lhs, const complex<integer>& rhs); In general, anywhere that template<class T> something<T> operator blah(const something<T>& lhs, const something<T>& rhs); appears should look like (pseudocode) template<class T> something<typeof(T blah T)> operator*(const something<T>& lhs, const something<T>& rhs); instead. This requires a bit more care, but I think it is demonstrably the right thing to do and should probably be an expectation in Boost libraries. For example, I know that Boost.Quaternions, Boost.Octonions, and Boost.uBlas do not do the right thing in this context. It would also probably make sense to provide a Boost.Complex that allows UDTs and handles them correctly. It sounds like Boost.Geometry also does not correctly handle basic mathematical operators. Matthias
We had the same problem in nt2 and ended up turning everything in polymorphic function object allowing external specialisation, thus making each operators or function to be overloaded with a sensible return type. IIRC we speak of this at the end of our boost'con 2010 talk
Hi,
Construction works here for me, but calculating distance does not. Because distance behind the screens takes the square root, after squaring. This requires thorough integration with Boost.Units at those places, which is not planned.
I understand that integrations with Boost.Units is not planned and even not desirable. But making the implicit assumption that typeof(sqrt(s)) == typeof(s) or that typeof(s*s)==type(s) makes it incompatible with Boost.units. Those assumptions are good as defaults but there should be a way to tell Boost.Geometry that sometimes it is not the case.
I took a stab at a generic solution to this problem of units and data types in a geometry library with Polygon. My coordinate_traits defines a distance time, area type etc.
Luke, do you mean this (boost/polygon/polygon_traits.hpp): typedef typename coordinate_traits<T>::coordinate_distance area_type; where compilation fails? Or this (same file): area_type x1 = (area_type)x(previous); area_type x2 = (area_type)x(*begin_range); I didn't reach this point but I'm quite sure that Boost.Units will not accept this C cast, if area_type is square_meters and coordinates are in meters. Maybe you can try it with Boost.Polygon, and deliver a working example for it, because I got immediately stuck.
I agree - the general sloppiness in treating mathematical operators in libraries that are intended to generically support them for UDTs seems to be a widespread problem, even within Boost.
I doubt if Boost.Units quantity is really an UDT in the sense UDT is normally used. Boost.Geometry fully supports ttmath (great lib!), and two other high precision arithmetic numbers (GMP/CLN). It is highly probable that other UDT's defining all mathematic operations (operators, functions) are supported as well. Boost.Units adds something more: physics. Therefore, it forbids mathematical operations as a*b because the result are not meters, but square meters. To support that, more is necessary indeed. Let's call this type an UDT+. It is (probably) therefore that the review report stated: "The library does not need to be completetly integrated with Boost.Units, although it appears to be reasonable to deal with types as exposed by Boost.Units especially for distance and area calculations.". I think that is handled.
For example, std::complex (which "circumvents" the problem by disallowing UDT value types) defines
template<class T> complex<T> operator/(const complex<T>& lhs, const complex<T>& rhs); However, even for a simple value type (e.g. integers - N.B. not int) this is not correct since integer/integer -> rational so this should really be
template<class T> complex<rational> operator*(const complex<integer>& lhs, const complex<integer>& rhs);
In general, anywhere that
template<class T> something<T> operator blah(const something<T>& lhs, const something<T>& rhs);
appears should look like (pseudocode)
template<class T> something<typeof(T blah T)> operator*(const something<T>& lhs, const something<T>& rhs);
instead. This requires a bit more care, but I think it is demonstrably the right thing to do and should probably be an expectation in Boost libraries. For example, I know that Boost.Quaternions, Boost.Octonions, and Boost.uBlas do not do the right thing in this context. It would also probably make sense to provide a Boost.Complex that allows UDTs and handles them correctly. It sounds like Boost.Geometry also does not correctly handle basic mathematical operators.
Maybe not in the physical meaning that Boost.Units is using; this is not required and not planned. I cannot speak of the other libraries, but I think the proposal I wrote yesterday is sufficient: the input is a Boost.Units quantity, the output is a Boost.Units quantity, all in their correct physical meanings. What is happening internally, and we do many many calculations there, all that is detail, black box for this UDT+. Regards, Barend
Hi Alfredo,
as I told you, my implementation is a total mock up and I am not sure I am doing it with the right philosophy. I added the code of boost_geometry_units.hpp file at the end of this post. I guess it works for me because I specialised the class model::point<double, 3, cs::units_cartesian<...> > and its constructor.
Thanks for this code.
The logic is that once you introduce units, the types of the coordinates are not uniform types, for example, a real space vector can have its cartesian coordinates in meters, but in spherical coordinates, only the radial part is in meters, (the others are angles).
I see, good point.
But maybe you are right the coordinates should be double upon constructions and the metric and units is only contained in the type.
I understand that integrations with Boost.Units is not planned and even not desirable. But making the implicit assumption that typeof(sqrt(s)) == typeof(s) or that typeof(s*s)==type(s) makes it incompatible with Boost.units. Those assumptions are good as defaults but there should be a way to tell Boost.Geometry that sometimes it is not the case.
I see what you mean. Yesterday in other answer I already referred to this. This would mean a total rework of all calculations, which is impossible, at least with respect to the time available for it. Look e.g. here: http://bit.ly/gOT5Yk and try to give them a physical meaning with quantities etc... Besides the amount of work, with many calculations such as the dot product and the determinant, the physical meaning is there but normally not used in this context. Dot product and determinant just returns a value, AFAIK normally not expressed in square meters. Determinant (and "side" which is similar) are e.g. used in segment intersection; those calculations are just steps in between; the end product are coordinates again. Having units applied there would be a burden nobody expects there (I think)...
So Boost.Geometry is not dependant on Boost.Units. I think the way to go is define a point type using a coordinate system using Boost.Units, like you did, and also add quantities there. yes. Even at the interface level I am not sure for example if constructors should carry units or not. I added the units to my constructors to make it more dimensionally type safe. Maybe the boost.units people will have clearer idea.
OK, I'm curious.
boost::geometry::units::area(a) -> returns a quantity e.g. in square_meters from a polygon
So I added two of these functions as well, giving:
std::cout<< units::get<0>(p2)<< std::endl; quantity<si::length> d = units::distance(p1, p2); std::cout<< d<< std::endl;
reporting all quantities including units. It required some small additional metafunctions as well.
yes, I the same thing in my first implementation, with a namespace boost::geometry::units that have all the overloaded functions but then I though it would have a lot a repeated code.
Yes, it would, but on the other hand, it makes a lot of things much and much simpler. Besides that, there are not so many functions like this: area, length, perimeter, distance are quantified values, but many others as intersection, union, simplify; and boolean predicates as within, intersects, all can do without any overload.
I just committed it, for the moment as a new example (including definitions), here:http://bit.ly/dEip8s (...) below the "mock up" code for boost_geometry_units.hpp. It is not good code, it is just barely enough to make it work with (...)
template<class T, std::size_t Dimension, class Unit> boost::units::quantity<Unit, T> distance( model::point<T, Dimension, cs::units_cartesian<Unit> > const& p1, model::point<T, Dimension, cs::units_cartesian<Unit> > const& p2 ){ return boost::units::quantity<Unit, T>::from_value( boost::geometry::distance( (model::point<T, Dimension, cs::cartesian> const&)p1, (model::point<T, Dimension, cs::cartesian> const&)p2 ) ); }
I understand how you got this working now. It is similar to my solution (forwarding to distance) but here you have the need to many casts, and many additional metafunctions and specializations implemented...
namespace strategy{namespace distance{namespace services{
template<class Unit> struct return_type<boost::geometry::units_cartesian_tag<Unit> >{ typedef boost::units::quantity<Unit> type; }; (...)
This, and the other code, is necessary here, but not in the proposal I sent... So there are more than one approaches, you can write an own distance strategy, correctly handling x*x and sqrt(x) w.r.t. Boost.Units. This might be doable for distance, but for huge algorithms as intersections, where you actually do not have any profit from units (input = coordinates, output = coordinates), I personally think it is way too much. I'm actually curious to know, is does the solution (that example) work for you? Regards, Barend
participants (7)
-
alfC
-
Barend Gehrels
-
Joel Falcou
-
Matthias Schabel
-
Simonson, Lucanus J
-
Steven Watanabe
-
Terry Golubiewski