Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

Welcome to the new platform of Programmer's Heaven! We apologize for the inconvenience caused, if you visited us from a broken link of the previous version. The main reason to move to a new platform is to provide more effective and collaborative experience to you all. Please feel free to experience the new platform and use its exciting features. Contact us for any issue that you need to get clarified. We are more than happy to help you.

Perl to C# ?

vshankar_1982vshankar_1982 Posts: 2Member
Hi Wisers,

I have a perl script. Can any one do a converiosn of the below code to either C# or VB.NET?

package Geo::Coordinates::RDNAP;

use strict;
use warnings;

use vars qw($VERSION);
$VERSION = '0.11';

use Carp;
use Params::Validate qw/validate BOOLEAN SCALAR/;

use Exporter;
use vars qw/@ISA @EXPORT_OK/;
@ISA = qw/Exporter/;
@EXPORT_OK = qw/from_rd to_rd deg dms/;

sub deg {
my (@in) = @_;
my @out;

while (my($d, $m, $s) = splice (@in, 0, 3)) {
push @out, $d + ($m||0)/60 + ($s||0)/3600;
}
return @out;
}

sub dms {
return map {int($_), int($_*60)%60, ($_-int($_*60)/60)*3600} @_;
}

my %a = (
'01' => 3236.0331637,
20 => -32.5915821,
'02' => -0.2472814,
21 => -0.8501341,
'03' => -0.0655238,
22 => -0.0171137,
40 => 0.0052771,
23 => -0.0003859,
41 => 0.0003314,
'04' => 0.0000371,
42 => 0.0000143,
24 => -0.0000090,
);

my %b = (
10 => 5261.3028966,
11 => 105.9780241,
12 => 2.4576469,
30 => -0.8192156,
31 => -0.0560092,
13 => 0.0560089,
32 => -0.0025614,
14 => 0.0012770,
50 => 0.0002574,
33 => -0.0000973,
51 => 0.0000293,
15 => 0.0000291,
);

my %c = (
'01' => 190066.98903,
11 => -11830.85831,
21 => -114.19754,
'03' => -32.38360,
31 => -2.34078,
13 => -0.60639,
23 => 0.15774,
41 => -0.04158,
'05' => -0.00661,
);

my %d = (
10 => 309020.31810,
'02' => 3638.36193,
12 => -157.95222,
20 => 72.97141,
30 => 59.79734,
22 => -6.43481,
'04' => 0.09351,
32 => -0.07379,
14 => -0.05419,
40 => -0.03444,
);

my %bessel = (
a => 6377397.155,
e2 => 6674372e-9,
f_i => 299.1528128,
);

my %etrs89 = (
a => 6378137,
e2 => 6694380e-9,
f_i => 298.257222101,
);

# Transformation parameters from Bessel to ETRS89 with respect to
# Amersfoort.

my %b2e = (
tx => 593.032,
ty => 26,
tz => 478.741,
a => 1.9848e-6,
b => -1.7439e-6,
c => 9.0587e-6,
d => 4.0772e-6,
);

my %e2b = map {$_ => -$b2e{$_}} keys %b2e;

my @amersfoort_b = ( 3903_453.148, 368_135.313, 5012_970.306 );
my @amersfoort_e = ( 3904_046.180, 368_161.313, 5013_449.047 );

sub from_rd {
croak 'Geo::Coordinates::RDNAP::from_rd needs two or three arguments'
if (@_ !=2 && @_ != 3);

my ($x, $y, $h) = (@_, 0);

croak "Geo::Coordinates::RDNAP::from_rd: X out of bounds: $x"
if ($x < -7_000 or $x > 300_000);
croak "Geo::Coordinates::RDNAP::from_rd: Y out of bounds: $y"
if ($y < 289_000 or $y > 629_000);

# Use the approximated transformation.
# Step 1: RD -> Bessel (spherical coords)

$x = ($x/100_000) - 1.55;
$y = ($y/100_000) - 4.63;

my $lat = (52*60*60) + (9*60) + 22.178;
my $lon = (5 *60*60) + (23*60) + 15.5;

foreach my $i (keys %a) {
my ($m, $n) = split //, $i;
$lat += $a{$i} * ($x**$m) * ($y**$n);
}

foreach my $i (keys %b) {
my ($m, $n) = split //, $i;
$lon += $b{$i} * ($x**$m) * ($y**$n);
}

# Step 2: spherical coords -> X, Y, Z
my @coords = _ellipsoid_to_cartesian($lat/3600, $lon/3600, $h, %bessel);

# Step 3: Bessel -> ETRS89
@coords = _transform_datum( @coords, %b2e, @amersfoort_b );

# Step 4: X, Y, Z -> spherical coords
return _cartesian_to_ellipsoid(@coords, %etrs89);
}

sub to_rd {
croak 'Geo::Coordinates::RDNAP::to_rd needs two or three arguments'
if (@_ !=2 && @_ != 3);

my ($lat, $lon, $h) = (@_, 0);

# Use the approximated transformation.
# Step 1: spherical coords -> X, Y, Z
my @coords = _ellipsoid_to_cartesian($lat, $lon, $h, %etrs89);

# Step 2: ETRS89 -> Bessel
@coords = _transform_datum( @coords, %e2b, @amersfoort_e );

# Step 3: X, Y, Z -> spherical coords
($lat, $lon, $h) = _cartesian_to_ellipsoid(@coords, %bessel);

# Step 4: Bessel -> RD'

# Convert to units of 10_000 secs; as deltas from Amersfoort.
$lat = ($lat * 3600 - ((52*60*60) + (9*60) + 22.178))/10_000;
$lon = ($lon * 3600 - ((5 *60*60) + (23*60) + 15.5))/10_000;

my $x = 155e3;
my $y = 463e3;

foreach my $i (keys %c) {
my ($m, $n) = split //, $i;
$x += $c{$i} * ($lat**$m) * ($lon**$n);
}

foreach my $i (keys %d) {
my ($m, $n) = split //, $i;
$y += $d{$i} * ($lat**$m) * ($lon**$n);
}

croak "Geo::Coordinates::RDNAP::to_rd: X out of bounds: $x"
if ($x < -7_000 or $x > 300_000);
croak "Geo::Coordinates::RDNAP::to_rd: Y out of bounds: $y"
if ($y < 289_000 or $y > 629_000);

return ($x, $y, $h);
}

sub _to_rads {
return $_[0] * 2*3.14159_26535_89793 /360;
}

sub _from_rads {
return $_[0] / (2*3.14159_26535_89793) *360;
}

sub _ellipsoid_to_cartesian {
my ($lat, $lon, $h, $para) = @_;

my $sinphi = sin(_to_rads($lat));
my $cosphi = cos(_to_rads($lat));
my $n = $para->{a}/sqrt(1 - $para->{e2}*$sinphi*$sinphi);

return (($n+$h)*$cosphi*cos(_to_rads($lon)),
($n+$h)*$cosphi*sin(_to_rads($lon)),
($n*(1-$para->{e2})+$h)*$sinphi );
}

# Returns (lat, lon, h) in degrees.

sub _cartesian_to_ellipsoid {
my ($x, $y, $z, $para) = @_;

my $lon = atan2($y, $x);

my $r = sqrt($x*$x+$y*$y);
my $phi = 0;
my $n_sinphi = $z;
my $n;
my $oldphi;

do {
$oldphi = $phi;
$phi = atan2($z + $para->{e2}*$n_sinphi, $r);
my $sinphi = sin($phi);
$n = $para->{a}/sqrt(1-$para->{e2}*$sinphi*$sinphi);
$n_sinphi = $n*$sinphi;
} while (abs($oldphi-$phi) > 1e-8);

my $h = $r/cos($phi) - $n;

return (_from_rads($phi), _from_rads($lon), $h);
}

sub _transform_datum {
my ($x, $y, $z, $t, $centre) = @_;

return (
$x + $t->{d}*($x-$centre->[0]) + $t->{c}*($y-$centre->[1])
- $t->{b}*($z-$centre->[2]) + $t->{tx},
$y - $t->{c}*($x-$centre->[0]) + $t->{d}*($y-$centre->[1])
+ $t->{a}*($z-$centre->[2]) + $t->{ty},
$z + $t->{b}*($x-$centre->[0]) - $t->{a}*($y-$centre->[1])
+ $t->{d}*($z-$centre->[2]) + $t->{tz}
);
}

1;
__END__

V Shankar

Comments

  • PsightoplazmPsightoplazm Posts: 332Member ✭✭
    I reeeeeeeeeeally hate perl, but I will give you a really down and dirty blurb on converting between syntaxes:


    In perl you are working with Scalar ($), Array (@), and Hash (%) data types. - .NET has both arrays and hashes but you will have to replace scalar thinking with strongly typed variables.


    If you want a variable that deals with a decimal point number you will need to declare it as a 'double' or 'decimal'.
    [code]
    double VERSION = 0.11;
    [/code]


    That variable can only every be used as a double. If a subroutine - or another variables requires an 'integer' (meaning it can't hold decimal values) then you must do a type conversion. This is accomplished by specifying the type you want to convert to in parenthesis before the item you want to convert:
    [code]
    int MyInt = (int)VERSION;
    [/code]


    These conversions do not completely replace the functionality of scalar variables in that there are a very limited number of types that something can be converted to. For example, a type conversion can not be used to convert a 'double' into a 'string' that could then be printed or displayed on the screen. I will get into how to do this in .NET when I talk about classes.


    Arrays (@) in .NET may actually be rather frustrating to a perl user. When you define an array you must also define how many items it will hold and a single data type that the array will be dealing with. After defining the item count of an array it will not be able to deal with any more/less items. You could potentially just create a new array with the length of the original array +/- 1 to add or remove a slot, but .NET 'generics' offer much better methods for handling variable collection lengths.


    To define an array you must specify the array's type and dimension:
    [code]
    double[] MyNumbers = new double[10];
    double[] MyPredefinedNumbers = new double[] {1, 2, 3, 4, 5.5, 6};
    [/code]


    Read or write to the array like so:
    [code]
    double MyDouble = MyNumbers[0];
    MyNumbers[5] = 32;
    [/code]


    You can of course deal with an array index dynamically as well:
    [code]
    int ArrayCount = 10;
    double[] MyNumbers = new double[ArrayCount];
    int MyIndex = 5;
    MyNumbers[MyIndex] = 32;
    [/code]


    Before I get into converting Hash (%) types I should talk about .NET classes. In Perl the properties of a class/struct can be accessed using a little ascii arrow '->'.
    [code]
    $MyValue = $MyClass->property;
    [/code]


    In .NET the arrow is replaced with a dot '.'.
    [code]
    double MyDouble = MyClass.SomeDoubleValue;
    [/code]


    Everything - and I mean everything - in .NET is a class on some level. Even a simple double number is a class type - the type being double. On top of this - every single class in .NET inherits 'object' as its most root class. The reason I am explaining this is because earlier I promised to tell you how to convert a double into a string. Well - the 'object' class has a 'ToString' subroutine attached to it - which in turn means so does everything else. So if you wanted to work with a double, but as if it was a string you would do this like so:
    [code]
    string MyString = MyDouble.ToString();
    [/code]


    So when I refer to .NET generics I am actually refering to a collection of classes that have been made that utilize the .NET generic functionality. For our purposes I won't dive into what that really is and will instead just introduce you to a couple of the classes you will be interested in using to convert your code into .NET.


    One of these classes is the 'List' class. This works alot like an array, but has a whole slew of additional functionality to it and allows you to add and remove items with ease. So instead of creating an array of doubles - you might want to make a List of doubles. [code]
    List MyDoubles = new List();
    [/code]
    YOu can then add values to it after the fact:
    [code]
    MyDoubles.Add(10);
    [/code]
    And retreive values from it just like you would an array:
    [code]
    double MyValue = MyDoubles[0];
    [/code]
    Or remove values by their index or by their value:
    [code]
    MyDoubles.Remove(10); // By value
    MyDoubles.RemoveAt(0); // By index
    [/code]


    The Dictionary class is the one that will help you convert from Hash(%) values. This is very similar to the List class except that it operates using a key-value relationship for its data. In the list class you had to define what data type it would be working with. The dictionary will need you to define both the value's type and the key's type. So using your '%a' hash for example - this would be defined as so:
    [code]
    Dictionary MyHash = new Dictionary();
    MyHash.Add(1, 3236.0331637);
    MyHash.Add(20 , -32.5915821);
    MyHash.Add(2, -0.2472814);
    MyHash.Add(21, -0.8501341);
    MyHash.Add(3, -0.0655238);
    MyHash.Add(22, -0.0171137);
    MyHash.Add(40, 0.0052771);
    MyHash.Add(23, -0.0003859);
    MyHash.Add(41, 0.0003314);
    MyHash.Add(4, 0.0000371);
    MyHash.Add(42, 0.0000143);
    MyHash.Add(24, -0.0000090);
    [/code]
    And would then access the values like so:
    [code]
    double MyValue = MyHash[1];
    [/code]
    The code above would set 'MyValue' to '3236.0331637'.


    Now in your Perl code you are doing a bunch of foreach loops on your hash tables. In .NET these would look something like this:
    [code]
    foreach (int key in MyHash.Keys)
    {
    double value = MyHash[key];
    // Your code for dealing with each key and value goes here.
    }
    [/code]


    Now for Subroutines -

    In .NET subroutines are refered to as methods or functions. When defining a method in .NET you must give it a 'signature'. This means you must define what variables will be passed to it in the definition rather than retrieving them from the root array '@_' as you do in Perl. So let's say you need a method that will accept 2 doubles and a string, the definition would look something like this:
    [code]
    public void MyMethod(double x, double y, string text)
    {
    // Code goes here
    }
    [/code]


    The 'public' just means anything accessing the class this method belongs to has access to this method - but for your purposes don't worry about this - just make everything public.


    The 'void' is the return type. Each method can return one type of data. If you want to return more than one peice of information then you should create a class that can hold everything you want to return and then use that as the return type. To specify a return type - replace 'void' with whatever type of data you want to return. 'void' simply means there is no return data. Returning data is done the same way in C# as it is in Perl:
    [code]
    public double MyMethod(double x)
    {
    double myresult = x + 1;
    return myresult;
    }
    [/code]


    That should about cover what you need for converting that script. I know this seems like a huge text blob, but the syntax conversion is really not so difficult - the reason I hate perl so much is because actually trying to read it is just a stupid horrible experience - so that I will leave up to you.

    If I missed something let me know.

    ><//~Psightoplasm`~
Sign In or Register to comment.