New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for horizontal grid shift (nadgrids) in transformation. #469

Merged
merged 3 commits into from May 10, 2018

Conversation

Projects
None yet
2 participants
@awulkiew
Member

awulkiew commented Apr 7, 2018

This PR adds support for nadgrids. Relevant proj4 code was converted to C++ and Boostified. So all grids formats are supported (ctable, ctable2, ntv1, ntv2), also vertical geoid grid GTX format but it is not used right now. There are some differences though:

  • proj4 stores loaded grids in global storage and pointers to a relevant subset of grids in projection parameters structure. In Boost.Geometry grids are moved outside transformation to allow users to place grids storage(s) wherever they like.
  • in proj4 the grids are loaded implicitly when there is +nadgrids parameter passed. In Boost.Geometry an object representing a subset of grids explicitly has to be initialized and then passed into transforming function.
  • in proj4 grids has to be "installed" into certain directories. In Boost.Geometry the user can implement StreamPolicy opening any input stream having unformatted input interface. The default one uses std::ifstream opening files having the same names as the ones in +nadgrids parameter in working directory.

I'm open to suggestion, in particular regarding the interface, names, etc.

This is how it looks like right now by default:

std::string from = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
std::string to = "+proj=aea +lat_1=34 +lat_2=40.5 +lat_0=0 +lon_0=-120 +x_0=0 +y_0=-4000000 +ellps=clrk66 +datum=NAD27 +units=m +no_defs";
// +datum=NAD27 uses +nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat

bg::srs::transformation<> tr = {bg::srs::proj4(from), bg::srs::proj4(to)};

bg::srs::grids_storage<> gs;

// reads headers of grids, adds them to grids_storage
// and creates representation of grids that are used by this transformation
bg::srs::transformation_grids<> tg = tr.initialize_grids(gs);

point_geo san_fran(-122.419167, 37.779167);
point_xy xy;

tr.forward(san_fran, xy);
std::cout << "without grids: " << bg::wkt(xy) << std::endl;

// loads content of relevant grid if needed and stores it in grids_storage
tr.forward(san_fran, xy, tg);
std::cout << "with grids: " << bg::wkt(xy) << std::endl;

Now let's say a user wants to pass his own StreamPolicy opening files from directory "/abc" and share single grid storage between multiple threads:

struct my_stream_policy
{
    typedef std::ifstream stream_type;

    static inline void open(stream_type & is, std::string const& gridname)
    {
        is.open(std::string("/abc/") + gridname, std::ios::binary);
    }
};

typedef bg::srs::grids_storage
    <
        my_stream_policy,
        bg::srs::shared_grids
    > my_grids_storage;

typedef bg::srs::transformation_grids
    <
        my_grids_storage
    > my_transformation_grids;

// the rest is similar
my_grids_storage gs;

boost::thread t1{[&](){
    bg::srs::transformation<> tr = {bg::srs::proj4(from1), bg::srs::proj4(to1)};
    my_transformation_grids tg = tr.initialize_grids(gs);    
    for (int i = 0 ; i < 1000 ; ++i)
        tr.forward(san_fran, xy, tg);
}};
boost::thread t2{[&](){
    bg::srs::transformation<> tr = {bg::srs::proj4(from2), bg::srs::proj4(to2)};
    my_transformation_grids tg = tr.initialize_grids(gs);
    for (int i = 0 ; i < 1000 ; ++i)
        tr.forward(san_fran, xy, tg);
}};
t1.join();
t2.join();

Note that sharing the same transformation object between threads should work too because neither transformation nor transformation_grids do not change while the transformation is performed:

my_grids_storage gs;
bg::srs::transformation<> tr = {bg::srs::proj4(from), bg::srs::proj4(to)};
my_transformation_grids tg = tr.initialize_grids(gs);
boost::thread t1{[&](){
    for (int i = 0 ; i < 1000 ; ++i)
        tr.forward(san_fran, xy, tg);
}};
boost::thread t2{[&](){
    for (int i = 0 ; i < 1000 ; ++i)
        tr.forward(san_fran, xy, tg);
}};
t1.join();
t2.join();

This PR is implemented on top of #468 and while the other PR is not merged this one will contain some unrelated changes.

awulkiew added some commits Mar 20, 2018

[srs] Improve projections' parameters handling.
Replace pj_param() taking type of parameter in a string with a set of
functions:
- pj_param_exist() - check if a parameter exists,
- pj_param_X() - check if a parameter exists and assign it to variable,
- pj_get_param_X() - return parameter if it exists or default value,
may be:
- s (returning std::string),
- i (int),
- f (floating point or user-defined numeric type),
- r (angle defined in DMS format as radians in FP or UD type),
- b (bool)

Use the above e.g. to avoid traversing parameters list twice per
parameter in some cases.

Add pj_mkparam() overload taking name and value as separate arguments to
avoid parsing of string in 'param=value' format.

@awulkiew awulkiew added this to the 1.68 milestone Apr 7, 2018

@awulkiew awulkiew added the enhancement label Apr 7, 2018

@awulkiew awulkiew force-pushed the awulkiew:feature/projections_grids branch 2 times, most recently from 56f7580 to ae8ea27 Apr 7, 2018

[srs] Support horizontal shift grids (nadgrids) in transformation.
Relevant proj4 code was converted to C++ and Boostified. So all grids formats
are supported (ctable, ctable2, ntv1, ntv2), also vertical geoid grid GTX
format but it is not used right now. There are some differences though:
- proj4 stores loaded grids in global storage and pointers to a relevant subset
  of grids in projection parameters structure. In Boost.Geometry this is moved
  outside transformation structure to allow users to place global storage(s)
  wherever they like.
- in proj4 the grids are loaded implicitly when there is +nadgrids parameter
  passed. In Boost.Geometry an object representing a subset of grids explicitly
  has to be initialized and then passed into transforming function.
- in proj4 grids has to be "installed" into certain directories. In
  Boost.Geometry user can implement StreamPolicy opening any input stream
  having unformatted input interface. The default one uses std::ifstream
  opening files having the same names as the ones in +nadgrids parameter in
  working directory.

Added classes:
  srs::grids, srs::ifstream_policy, srs::grids_storage,
  srs::projection_grids, srs::transformation_grids
and for multithreading:
  srs::shared_grids

Added functions (also overloads)
  srs::transformation::initialize_grids(grids_storage)
  srs::transformation::forward(in, out, transformation_grids)
  srs::transformation::inverse(in, out, transformation_grids)

@awulkiew awulkiew force-pushed the awulkiew:feature/projections_grids branch from ae8ea27 to 1aeef5d Apr 8, 2018

@awulkiew awulkiew requested review from barendgehrels and vissarion Apr 18, 2018

@vissarion

This comment has been minimized.

Contributor

vissarion commented Apr 20, 2018

It seems ok to me. Thanks!
One general comment, how Boost.Geometry is going to keep track of updates on proj4 (or proj5 or any later version)?

@awulkiew

This comment has been minimized.

Member

awulkiew commented Apr 20, 2018

Currently all updates have to be made manually.

When projections were first converted to Boost.Geometry (AFAIR it was proj4 4.9.1) at least the code containing projections was converted automatically. However the code in proj4 was changed since then so I'd expect the old converter does not work anymore. Besides, I don't know where it is and how easy would it be to update it. Plus the rest of the code has to be updated manually anyway as it was in the past.

@awulkiew awulkiew merged commit ca62c0e into boostorg:develop May 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment