PDL-Transform-Proj4

 view release on metacpan or  search on metacpan

lib/PDL/Transform/Proj4.pd  view on Meta::CPAN


pp_addhdr(<<'EOHDR');
#include "proj.h"
#include <string.h>
/* from proj_api.h */
#define RAD_TO_DEG      57.295779513082321
#define DEG_TO_RAD      .017453292519943296
EOHDR

sub wrap_code {
  my ($name, $in, $out, $is_fwd) = @_;
  pp_def($name,
    Code => <<EOF,
PJ_CONTEXT *pj_context = proj_context_create();
if (!pj_context)
  \$CROAK("Projection context creation failed: %s\\n", proj_errno_string(proj_errno(NULL)));
PJ *proj = proj_create( pj_context, \$COMP(params) );
if (!proj)
  \$CROAK("Projection initialization failed: %s\\n", proj_errno_string(proj_errno(NULL)));
char rad_in = @{[$is_fwd ? 'proj_angular_input(proj, PJ_FWD)' : '0']},
  deg_out = @{[$is_fwd ? '0' : 'proj_angular_output(proj, PJ_INV)']};
loop (n) %{
  PJ_COORD in, out;
  PDL_IF_BAD(
  if ( @{[ join '||', map "\$ISBAD($in(ncoord=>$_))", 0,1 ]} ) {
    loop (ncoord) %{ \$SETBAD($out()); %}
    continue;
  },)
  loop (ncoord) %{ in.v[ncoord] = rad_in ? DEG_TO_RAD*\$$in() : \$$in(); %}
  out = proj_trans(proj, PJ_@{[$is_fwd ? 'FWD' : 'INV']}, in);
  if (out.v[0] == HUGE_VAL) {
    PDL_IF_BAD(
    loop (ncoord) %{ \$SETBAD($out()); %}
    continue;
    ,
    \$CROAK("Projection conversion failed at (%f, %f): %s\\n",
      \$$in(ncoord=>0), \$$in(ncoord=>1), proj_errno_string(proj_errno(proj)));
    )
  }
  loop (ncoord) %{ \$$out() = deg_out ? RAD_TO_DEG*out.v[ncoord] : out.v[ncoord]; %}
%}
proj_destroy(proj);
proj_context_destroy(pj_context);
EOF
    Pars => "$in(ncoord=2,n); [o] $out(ncoord,n);",
    GenericTypes => ['D'],
    OtherPars => 'char* params;',
    HandleBad => 1,
    Inplace => 1,
    Doc => undef,
  );
}

wrap_code('fwd_transform', 'lonlat', 'xy', 1);
wrap_code('inv_transform', 'xy', 'lonlat', 0);

# Utility functions for getting projection description information (in a general case).
pp_addxs('', <<'ENDXS' );
void
proj_version(...)
  PPCODE:
    EXTEND(sp, 3);
    mPUSHu(PROJ_VERSION_MAJOR);
    mPUSHu(PROJ_VERSION_MINOR);
    mPUSHu(PROJ_VERSION_PATCH);

# returns input_units, output_units
void
units(proj_str)
  char *proj_str
PPCODE:
  PJ *proj = proj_create( NULL, proj_str ); /* Init the projection */
  if (!proj)
    croak("Failed to create PJ from '%s': %s", proj_str, proj_errno_string(proj_errno(proj)));
  EXTEND(sp, 2);
  char *input_u = proj_angular_input(proj, PJ_FWD) ||
    proj_degree_input(proj, PJ_FWD) ? "degrees" : "metres";
  char *output_u = proj_angular_output(proj, PJ_FWD) ||
    proj_degree_output(proj, PJ_FWD) ? "degrees" : "metres";
  mPUSHs(newSVpv(input_u, 0));
  mPUSHs(newSVpv(output_u, 0));
  proj_destroy(proj);
ENDXS

my %SKIP = map +($_=>1), qw(
  and or Special for Madagascar
  fixed Earth For CH1903
);
sub load_projection_information {
    my ($text, $stderr, $exit_code) = Alien::proj->run_utility ("proj", "-lP");
    warn $stderr if $stderr;
    die "proj -lP error $exit_code. See above for error text." if $exit_code;
    my @chunks = $text =~ /(.+?)(?=(?:^\S|\z))/gms;
    chomp for @chunks;
    my %descriptions = map {
      my ($id, $rest) = split /\s*:\s*/, $_, 2;
    } @chunks;
    my %info;
    foreach my $projection ( sort keys %descriptions ) {
        my $description = $descriptions{$projection};
        my %hash = (CODE => $projection);
        my @lines = split( /\n/, $description );
        chomp @lines;
        # Can this projection do inverse?
        $hash{INVERSE} = 1;
        # Full name of this projection:
        ($hash{NAME}, my $temp) = splice @lines, 0, 2;
        if ($temp) {
          # The second line is usually a list of projection types this one is:
          $hash{INVERSE} = 0 if $temp =~ s/no inv\.*,*//i;
          $temp =~ s/or//;
          my @temp_types = split(/[,&\s]/, $temp );
          my @types = grep( /.+/, @temp_types );
          $hash{CATEGORIES} = \@types;
        }
        # If there's more than 2 lines, then it usually is a listing of parameters:
        # Projection Specific Parameters:
        $hash{PARAMS}{PROJ} = [
          grep !$SKIP{$_}, map {s/=//; s/[,\[\]]//sg; $_}
            grep length, map split(/\s+/), @lines
        ];
        $info{$projection} = \%hash;
    }
    # A couple of overrides:
    #
    $info{ob_tran}{PARAMS}{PROJ} =
        [ 'o_proj', 'o_lat_p', 'o_lon_p', 'o_alpha', 'o_lon_c',
          'o_lat_c', 'o_lon_1', 'o_lat_1', 'o_lon_2', 'o_lat_2' ];
    $info{nzmg}{CATEGORIES} = [ 'fixed Earth' ];
    return \%info;
}



( run in 0.520 second using v1.01-cache-2.11-cpan-5511b514fd6 )