Description: Add script implementing the required tests.
 . 
 The RDNAP license requires the correct implementation of the grid
 corrections to be tested, as documented in the file:
 "Use of RDTRANS2008 and NAPTRANS2008.pdf"
 .
 There are 10 tests which need to be run in both directions,
 from ETRS89 to RD/NAP and vice versa.
 .
 The tests are defined in testrdtrans2008.json, and the test script
 testrdtrans2008.pl runs them for both directions.
 .
 Verbose output can be enabled by using the -v option, or setting the
 VERBOSE environment variable.
 .
 The points in tests 07 - 10 fall outside the grid, the output for
 these tests is not the expected floating point value, but a '*'.
 .
 These tests pass when the cs2cs output matches the overrides defined in
 testrdtrans2008.json.
 .
 Regarding tests 07 - 10, the "Use of RDTRANS2008 and NAPTRANS2008.pdf"
 file documents the following:
 .
   Points 07 - 10 are outside the region where interpolation between
   either the NLGEO2004 geoid or the RD correction grid points is
   possible. RD is defined only within the region enclosed by the
   following points (in RD), outside this region RD coordinates can
   be computed, but the output should be handled with care.
 .
   Corners of the validity region for RD:
   ┌────────┬────────┐
   │  x (m) │  y (m) │
   ├────────┼────────┤
   │ 140000 │ 630000 │
   │ 100000 │ 600000 │
   │  80000 │ 500000 │
   │  -8000 │ 390000 │
   │  -8000 │ 335000 │
   │ 100000 │ 335000 │
   │ 160000 │ 288000 │
   │ 220000 │ 288000 │
   │ 301000 │ 450000 │
   │ 301000 │ 615000 │
   │ 260000 │ 630000 │
   └────────┴────────┘
 .
Author: Bas Couwenberg <sebastic@debian.org>
Forwarded: not-needed

--- /dev/null
+++ b/epsg
@@ -0,0 +1,2 @@
+# ETRS89
+<4258> +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs  <>
--- /dev/null
+++ b/rdnap
@@ -0,0 +1,5 @@
+# RDNAP with NTv2 and VDatum
+<rdnap> +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +nadgrids=rdtrans2008.gsb +geoidgrids=naptrans2008.gtx +units=m +no_defs <>
+
+# RD with NTv2 only
+<rd> +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +nadgrids=rdtrans2008.gsb +units=m +no_defs <>
--- /dev/null
+++ b/testrdtrans2008.json
@@ -0,0 +1,182 @@
+[
+  {
+    "number"  : "01",
+    "name"    : "Texel",
+    "etrs89"  : {
+                  "latitude"  : "53.160753042",
+                  "longitude" : "4.824761912",
+                  "height"    : "42.8614"
+                },
+    "rdnap"   : {
+                  "x"   : "117380.1200",
+                  "y"   : "575040.3400",
+                  "nap" : "1.0000"
+                }
+  },
+  {
+    "number"  : "02",
+    "name"    : "Noord-Groningen",
+    "etrs89"  : {
+                  "latitude"  : "53.419482050",
+                  "longitude" : "6.776726674",
+                  "height"    : "42.3586"
+                },
+    "rdnap"   : {
+                  "x"   : "247380.5600",
+                  "y"   : "604580.7800",
+                  "nap" : "2.0000"
+                }
+  },
+  {
+    "number"  : "03",
+    "name"    : "Amersfoort",
+    "etrs89"  : {
+                  "latitude"  : "52.155172897",
+                  "longitude" : "5.387203657",
+                  "height"    : "43.2551"
+                },
+    "rdnap"   : {
+                  "x"   : "155000.0000",
+                  "y"   : "463000.0000",
+                  "nap" : "0.0000"
+                }
+  },
+  {
+    "number"  : "04",
+    "name"    : "Zeeuws-Vlaanderen",
+    "etrs89"  : {
+                  "latitude"  : "51.368607152",
+                  "longitude" : "3.397588595",
+                  "height"    : "47.4024"
+                },
+    "rdnap"   : {
+                  "x"   : "16460.9100",
+                  "y"   : "377380.2300",
+                  "nap" : "3.0000"
+                }
+  },
+  {
+    "number"  : "05",
+    "name"    : "Zuid-Limburg",
+    "etrs89"  : {
+                  "latitude"  : "50.792584908",
+                  "longitude" : "5.773795547",
+                  "height"    : "174.9478"
+                },
+    "rdnap"   : {
+                  "x"   : "182260.4500",
+                  "y"   : "311480.6700",
+                  "nap" : "129.000"
+                }
+  },
+  {
+    "number"  : "06",
+    "name"    : "Maasvlakte",
+    "etrs89"  : {
+                  "latitude"  : "51.947393898",
+                  "longitude" : "4.072887101",
+                  "height"    : "47.5968"
+                },
+    "rdnap"   : {
+                  "x"   : "64640.8900",
+                  "y"   : "440700.0101",
+                  "nap" : "4.0000"
+                }
+  },
+  {
+    "number"  : "07",
+    "name"    : "No_rd&geoid",
+    "etrs89"  : {
+                  "latitude"  : "50.687420405",
+                  "longitude" : "4.608971812",
+                  "height"    : ""
+                },
+    "rdnap"   : {
+                  "x"   : "100000.6700",
+                  "y"   : "300000.8900",
+                  "nap" : ""
+                },
+    "note"    : "point not within available datum shift grids",
+    "override": {
+                  "latitude"  : "*",
+                  "longitude" : "*",
+                  "height"    : "^-?(\\d+\\.\\d+|inf)$",
+                  "x"         : "*",
+                  "y"         : "*",
+                  "nap"       : "^-?(\\d+\\.\\d+|inf)$",
+                  "margin"    : "ignore"
+                }
+  },
+  {
+    "number"  : "08",
+    "name"    : "No_geoid",
+    "etrs89"  : {
+                  "latitude"  : "51.136825197",
+                  "longitude" : "4.601375361",
+                  "height"    : ""
+                },
+    "rdnap"   : {
+                  "x"   : "100000.6700",
+                  "y"   : "350000.8900",
+                  "nap" : ""
+                },
+    "note"    : "point not within available datum shift grids",
+    "override": {
+                  "latitude"  : "*",
+                  "longitude" : "*",
+                  "height"    : "^-?(\\d+\\.\\d+|inf)$",
+                  "x"         : "*",
+                  "y"         : "*",
+                  "nap"       : "^-?(\\d+\\.\\d+|inf)$",
+                  "margin"    : "ignore"
+                }
+  },
+  {
+    "number"  : "09",
+    "name"    : "No_rd",
+    "etrs89"  : {
+                  "latitude"  : "52.482440839",
+                  "longitude" : "4.268403889",
+                  "height"    : ""
+                },
+    "rdnap"   : {
+                  "x"   : "79000.0100",
+                  "y"   : "500000.2300",
+                  "nap" : ""
+                },
+    "note"    : "point not within available datum shift grids",
+    "override": {
+                  "latitude"  : "*",
+                  "longitude" : "*",
+                  "height"    : "^-?(\\d+\\.\\d+|inf)$",
+                  "x"         : "*",
+                  "y"         : "*",
+                  "nap"       : "^-?(\\d+\\.\\d+|inf)$",
+                  "margin"    : "ignore"
+                }
+  },
+  {
+    "number"  : "10",
+    "name"    : "edge_rd",
+    "etrs89"  : {
+                  "latitude"  : "51.003976532",
+                  "longitude" : "3.891247830",
+                  "height"    : ""
+                },
+    "rdnap"   : {
+                  "x"   : "50000.4500",
+                  "y"   : "335999.6700",
+                  "nap" : ""
+                },
+    "note"    : "point not within available datum shift grids",
+    "override": {
+                  "latitude"  : "*",
+                  "longitude" : "*",
+                  "height"    : "^-?(\\d+\\.\\d+|inf)$",
+                  "x"         : "*",
+                  "y"         : "*",
+                  "nap"       : "^-?(\\d+\\.\\d+|inf)$",
+                  "margin"    : "ignore"
+                }
+  }
+]
--- /dev/null
+++ b/testrdtrans2008.pl
@@ -0,0 +1,425 @@
+#!/usr/bin/perl -w
+#
+# Script to do some testing of various transformations
+# required by the RDNAPTRANS™2008 license.
+#
+# For the license and test sheet refer to the file:
+# "Use of RDTRANS2008 and NAPTRANS2008.pdf"
+
+use strict;
+use File::Basename;
+use File::Slurp;
+use Getopt::Long qw(:config bundling no_ignore_case);
+use IPC::Run qw(run);
+use JSON;
+
+$|=1;
+
+my %cfg = (
+            tests   => 'testrdtrans2008.json',
+            verbose => 0,
+            help    => 0,
+          );
+
+my $result = GetOptions(
+                         'v|verbose' => \$cfg{verbose},
+                         'h|help'    => \$cfg{help},
+                       );
+
+if(!$result || $cfg{help}) {
+    print STDERR "\n" if(!$result);
+
+    print "Usage: ". basename($0). " [OPTIONS]\n\n";
+    print "Options:\n";
+    print "-v, --verbose   Enable verbose output\n";
+    print "-h, --help      Display this usage information\n";
+
+    exit 1;
+}
+
+$cfg{verbose} = 1 if($ENV{VERBOSE});
+
+if(!-r $cfg{tests}) {
+    print "Error: Cannot read tests file: $cfg{tests}\n";
+    exit 1;
+}
+
+if($ENV{PROJ_LIB}) {
+    print "Using PROJ_LIB: $ENV{PROJ_LIB}\n" if($cfg{verbose});
+}
+
+my %proj_version = ();
+
+my ($in, $out, $err) = '';
+
+my @cmd = ('cs2cs');
+
+run \@cmd, \$in, \$out, \$err || die "Error: Command failed: @cmd ($?)";
+
+# Rel. 4.9.3, 15 August 2016
+# Rel. 5.2.0, September 15th, 2018
+# Rel. 6.0.0, March 1st, 2019
+if($err =~ /^Rel\. (\d+)\.(\d+)\.(\d+)(\S*?), /) {
+    %proj_version = (
+                      major => $1,
+                      minor => $2,
+                      patch => $3,
+                      extra => $4,
+                    );
+
+    print "PROJ version: $1.$2.$3$4\n" if($cfg{verbose});
+}
+else {
+    print "Error: Failed to extract version from cs2cs!\n";
+    exit 1;
+}
+
+my $json  = read_file($cfg{tests});
+my $tests = from_json($json);
+
+my %proj = (
+             etrs89 => {
+                         init   => '+init=epsg:4258',
+                         args   => '-r',
+                         format => '%.9f',
+                       },
+             rdnap  => {
+                         init   => '+init=rdnap:rdnap',
+                         args   => '-s',
+                         format => '%.4f',
+                       },
+           );
+
+if($proj_version{major} >= 6) {
+    $proj{etrs89}{init} = 'EPSG:4937';
+    $proj{etrs89}{args} = '';
+
+    $proj{rdnap}{init} = 'EPSG:7415';
+    $proj{rdnap}{args} = '';
+}
+
+my @directions = (
+                   {
+                     name => 'From ETRS89 to RD/NAP',
+                     src  => 'etrs89',
+                     dst  => 'rdnap',
+                   },
+                   {
+                     name => 'From RD/NAP to ETRS89',
+                     src  => 'rdnap',
+                     dst  => 'etrs89',
+                   },
+                 );
+
+my $errors = 0;
+
+foreach my $direction (@directions) {
+    print "Direction: $direction->{name}\n\n" if($cfg{verbose});
+
+    foreach my $test (@{$tests}) {
+        print "Test:   $test->{number} $test->{name}\n" if($cfg{verbose});
+
+        my @cmd = ('cs2cs');
+
+        if($proj{ $direction->{src} }->{args}) {
+            push @cmd, $proj{ $direction->{src} }->{args},
+        }
+
+        push @cmd, (
+                     $proj{ $direction->{src} }->{init},
+                     '+to',
+                     $proj{ $direction->{dst} }->{init},
+                     '-f', $proj{ $direction->{dst} }->{format},
+                   );
+
+        my ($in, $out, $err) = '';
+
+        if($direction->{src} eq 'etrs89') {
+            $in  = $test->{etrs89}->{latitude}." ".$test->{etrs89}->{longitude};
+            $in .= " ".$test->{etrs89}->{height} if($test->{etrs89}->{height});
+            $in .= "\n";
+        }
+        elsif($direction->{src} eq 'rdnap') {
+            $in  = $test->{rdnap}->{x}." ".$test->{rdnap}->{y};
+            $in .= " ".$test->{rdnap}->{nap} if($test->{rdnap}->{nap});
+            $in .= "\n";
+        }
+        else {
+            print "Warning: Unsupported source: $direction->{src}, skipping test\n";
+            next;
+        }
+
+        my $expect = '';
+
+        if($direction->{dst} eq 'etrs89') {
+            $expect  = $test->{etrs89}->{latitude}."\t".$test->{etrs89}->{longitude};
+            $expect .= " ".$test->{etrs89}->{height} if($test->{etrs89}->{height});
+            $expect .= "\n";
+        }
+        elsif($direction->{dst} eq 'rdnap') {
+            $expect  = $test->{rdnap}->{x}."\t".$test->{rdnap}->{y};
+            $expect .= " ".$test->{rdnap}->{nap} if($test->{rdnap}->{nap});
+            $expect .= "\n";
+        }
+        else {
+            print "Warning: Unsupported destination: $direction->{dst}, skipping test\n";
+            next;
+        }
+
+        if($cfg{verbose}) {
+            print "Exec:   @cmd\n";
+            print "Input:  $in";
+        }
+
+        run \@cmd, \$in, \$out, \$err || die "Error: Command failed: @cmd ($?)";
+
+        if($cfg{verbose}) {
+            print "STDERR: $err" if($err);
+            print "Output: $out";
+            print "Expect: $expect";
+        }
+
+        my $ok  = 1;
+        my $msg = '';
+
+        if($out ne $expect) {
+            chomp($out);
+            chomp($expect);
+
+            my @out_parts    = split /[\t ]/, $out;
+            my @expect_parts = split /[\t ]/, $expect;
+
+            my $output_override = 0;
+            my $margin_override = 0;
+
+            if($direction->{dst} eq 'etrs89') {
+                if($out_parts[0] =~ /^\d+\.\d+$/) {
+                    my $diff = 0;
+                    my $max  = 0.00000001;
+
+                    if($out_parts[0] > $expect_parts[0]) {
+                        $diff = $out_parts[0] - $expect_parts[0];
+                    }
+                    elsif($out_parts[0] < $expect_parts[0]) {
+                        $diff = $expect_parts[0] - $out_parts[0];
+                    }
+
+                    if($diff > $max) {
+                        print "Latitude exceeds $max degrees: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{latitude}) {
+                    if($out_parts[0] ne $test->{override}->{latitude}) {
+                        print "Latitude not expected: $out_parts[0], expected: $test->{override}->{latitude}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+
+                if($out_parts[1] =~ /^\d+\.\d+$/) {
+                    my $diff = 0;
+                    my $max  = 0.00000001;
+
+                    if($out_parts[1] > $expect_parts[1]) {
+                        $diff = $out_parts[1] - $expect_parts[1];
+                    }
+                    elsif($out_parts[1] < $expect_parts[1]) {
+                        $diff = $expect_parts[1] - $out_parts[1];
+                    }
+
+                    if($diff > $max) {
+                        print "Longitude exceeds $max degrees: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{longitude}) {
+                    if($out_parts[1] ne $test->{override}->{longitude}) {
+                        print "Longitude not expected: $out_parts[1], expected: $test->{override}->{longitude}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+
+                if($out_parts[2]    && $out_parts[2]    =~ /^\d+\.\d+$/ &&
+                   $expect_parts[2] && $expect_parts[2] =~ /^\d+\.\d+$/
+                ) {
+                    my $diff = 0;
+                    my $max  = 0.001;
+
+                    if($out_parts[2] > $expect_parts[2]) {
+                        $diff = $out_parts[2] - $expect_parts[2];
+                    }
+                    elsif($out_parts[2] < $expect_parts[2]) {
+                        $diff = $expect_parts[2] - $out_parts[2];
+                    }
+
+                    if($diff > $max) {
+                        print "Height exceeds $max meters: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{height}) {
+                    if($out_parts[2] !~ /$test->{override}->{height}/) {
+                        print "Height not expected: $out_parts[2], expected: $test->{override}->{height}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+            }
+            elsif($direction->{dst} eq 'rdnap') {
+                if($out_parts[0] =~ /^\d+\.\d+$/) {
+                    my $diff = 0;
+                    my $max  = 0.001;
+
+                    if($out_parts[0] > $expect_parts[0]) {
+                        $diff = $out_parts[0] - $expect_parts[0];
+                    }
+                    elsif($out_parts[0] < $expect_parts[0]) {
+                        $diff = $expect_parts[0] - $out_parts[0];
+                    }
+
+                    if($diff > $max) {
+                        print "x coordinate exceeds $max meters: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{x}) {
+                    if($out_parts[0] ne $test->{override}->{x}) {
+                        print "x coordinate not expected: $out_parts[0], expected: $test->{override}->{x}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+
+                if($out_parts[1] =~ /^\d+\.\d+$/) {
+                    my $diff = 0;
+                    my $max  = 0.001;
+
+                    if($out_parts[1] > $expect_parts[1]) {
+                        $diff = $out_parts[1] - $expect_parts[1];
+                    }
+                    elsif($out_parts[1] < $expect_parts[1]) {
+                        $diff = $expect_parts[1] - $out_parts[1];
+                    }
+
+                    if($diff > $max) {
+                        print "y coordinate exceeds $max meters: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{y}) {
+                    if($out_parts[1] ne $test->{override}->{y}) {
+                        print "y coordinate not expected: $out_parts[1], expected: $test->{override}->{y}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+
+                if($out_parts[2]    && $out_parts[2]    =~ /^\d+\.\d+$/ &&
+                   $expect_parts[2] && $expect_parts[2] =~ /^\d+\.\d+$/
+                ) {
+                    my $diff = 0;
+                    my $max  = 0.001;
+
+                    if($out_parts[2] > $expect_parts[2]) {
+                        $diff = $out_parts[2] - $expect_parts[2];
+                    }
+                    elsif($out_parts[2] < $expect_parts[2]) {
+                        $diff = $expect_parts[2] - $out_parts[2];
+                    }
+
+                    if($diff > $max) {
+                        print "NAP exceeds $max meters: $diff\n" if($cfg{verbose});
+                        if($test->{override} && $test->{override}->{margin} && $test->{override}->{margin} eq 'ignore') {
+                            $margin_override = 1;
+                        }
+                        else {
+                            $ok = 0;
+                        }
+                    }
+                }
+                elsif($test->{override} && $test->{override}->{nap}) {
+                    if($out_parts[2] !~ /$test->{override}->{nap}/) {
+                        print "NAP not expected: $out_parts[2], expected: $test->{override}->{nap}\n" if($cfg{verbose});
+                        $ok = 0;
+                    }
+
+                    $output_override = 1;
+                }
+            }
+
+            $msg = "Not identical, but within margin" if($ok);
+
+            if($margin_override) {
+                $msg = "Not identical, but margin ignored";
+            }
+            elsif($output_override) {
+                $msg = "Expected output overriden";
+
+                if($direction->{dst} eq 'etrs89'  &&
+                   $test->{override}->{latitude}  &&
+                   $test->{override}->{longitude} &&
+                   $test->{override}->{height}
+                ) {
+                    $msg .= ": $test->{override}->{latitude}\t$test->{override}->{longitude} $test->{override}->{height}";
+                }
+
+                if($direction->{dst} eq 'rdnap' &&
+                   $test->{override}->{x}       &&
+                   $test->{override}->{y}       &&
+                   $test->{override}->{nap}
+                ) {
+                    $msg .= ": $test->{override}->{x}\t$test->{override}->{y} $test->{override}->{nap}";
+                }
+            }
+        }
+
+        if($ok) {
+            print "Test OK: $direction->{name} - $test->{number} $test->{name}";
+            print " ($msg)" if($msg);
+            print "\n";
+        }
+        else {
+            print "Test FAILED: $direction->{name} - $test->{number} $test->{name}";
+            print " ($msg)" if($msg);
+            print "\n";
+
+            $errors++;
+        }
+
+        print "\n" if($cfg{verbose});
+    }
+}
+
+exit $errors;
