aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/Geo/Coordinates/CH1903Plus.pm
blob: 8487dacbe98fefc2c71bd472c1d9c1b2548bec9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Geo::Coordinates::CH1903Plus
# Conversion between WGS84 and Swiss CH1903+ (aka LV95).
#
# Copyright (c) 2012 UK Citizens Online Democracy. This module is free
# software; you can redistribute it and/or modify it under the same terms as
# Perl itself.
#
# WWW: http://www.mysociety.org/

package Geo::Coordinates::CH1903Plus;

$Geo::Coordinates::CH1903Plus::VERSION = '1.00';

use strict;

# Use the same calcs as CH1903 but with offset.
# Maximum distortion is 3M which should be sufficient for our purposes.
use constant LV95_X_OFFSET => 2000000;
use constant LV95_Y_OFFSET => 1000000;

=head1 NAME

Geo::Coordinates::CH1903Plus

=head1 VERSION

1.00

=head1 SYNOPSIS

    use Geo::Coordinates::CH1903Plus;

    my ($lat, $lon) = ...;
    my ($e, $n) = Geo::Coordinates::CH1903Plus::from_latlon($lat, $lon);
    my ($lat, $lon) = Geo::Coordinates::CH1903Plus::to_latlon($e, $n);

=head1 FUNCTIONS

=over 4

=cut

sub from_latlon($$) {
    my ($lat, $lon) = @_;

    $lat *= 3600;
    $lon *= 3600;

    my $lat_aux = ($lat - 169028.66) / 10000;
    my $lon_aux = ($lon - 26782.5) / 10000;

    my $x = 600072.37 + LV95_X_OFFSET
        + (211455.93 * $lon_aux)
        - (10938.51 * $lon_aux * $lat_aux)
        - (0.36 * $lon_aux * $lat_aux**2)
        - (44.54 * $lon_aux**3);

    my $y = 200147.07 + LV95_Y_OFFSET
        + (308807.95 * $lat_aux)
        + (3745.25 * $lon_aux**2)
        + (76.63 * $lat_aux**2)
        - (194.56 * $lon_aux**2 * $lat_aux)
        + (119.79 * $lat_aux**3);

    return ($x, $y);
}

sub to_latlon($$) {
    my ($x, $y) = @_;

    my $x_aux = ($x - 600000 - LV95_X_OFFSET) / 1000000;
    my $y_aux = ($y - 200000 - LV95_Y_OFFSET) / 1000000;

    my $lat = 16.9023892
        + (3.238272 * $y_aux)
        - (0.270978 * $x_aux**2)
        - (0.002528 * $y_aux**2)
        - (0.0447 * $x_aux**2 * $y_aux)
        - (0.0140 * $y_aux**3);

    my $lon = 2.6779094
        + (4.728982 * $x_aux)
        + (0.791484 * $x_aux * $y_aux)
        + (0.1306 * $x_aux * $y_aux**2)
        - (0.0436 * $x_aux**3);

    $lat = $lat * 100 / 36;
    $lon = $lon * 100 / 36;

    return ($lat, $lon);
}

=head1 AUTHOR AND COPYRIGHT

Maths courtesy of the Swiss Federal Office of Topography:
http://www.swisstopo.admin.ch/internet/swisstopo/en/home/products/software/products/skripts.html

Written by Matthew Somerville

Copyright (c) UK Citizens Online Democracy.

This module is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

1;