source: trunk/anuga_core/source/anuga/coordinate_transforms/redfearn.py @ 8065

Last change on this file since 8065 was 7317, checked in by rwilson, 15 years ago

Replaced 'print' statements with log.critical() calls.

File size: 6.3 KB
Line 
1"""Implementation of Redfearn's formula to compute UTM projections from latitude and longitude
2
3Based in part on spreadsheet
4www.icsm.gov.au/gda/gdatm/redfearn.xls
5downloaded from INTERGOVERNMENTAL COMMITTEE ON SURVEYING & MAPPING (ICSM)
6http://www.icsm.gov.au/icsm/
7
8"""
9from anuga.coordinate_transforms.geo_reference import Geo_reference, DEFAULT_ZONE
10
11
12def degminsec2decimal_degrees(dd,mm,ss):
13    assert abs(mm) == mm
14    assert abs(ss) == ss   
15
16    if dd < 0:
17        sign = -1
18    else:
19        sign = 1
20   
21    return sign * (abs(dd) + mm/60. + ss/3600.)     
22   
23def decimal_degrees2degminsec(dec):
24
25    if dec < 0:
26        sign = -1
27    else:
28        sign = 1
29
30    dec = abs(dec)   
31    dd = int(dec)
32    f = dec-dd
33
34    mm = int(f*60)
35    ss = (f*60-mm)*60
36   
37    return sign*dd, mm, ss
38
39def redfearn(lat, lon, false_easting=None, false_northing=None,
40             zone=None, central_meridian=None, scale_factor=None):
41    """Compute UTM projection using Redfearn's formula
42
43    lat, lon is latitude and longitude in decimal degrees
44
45    If false easting and northing are specified they will override
46    the standard
47
48    If zone is specified reproject lat and long to specified zone instead of
49    standard zone
50
51    If meridian is specified, reproject lat and lon to that instead of zone. In this case
52    zone will be set to -1 to indicate non-UTM projection
53
54    Note that zone and meridian cannot both be specifed
55    """
56
57
58    from math import pi, sqrt, sin, cos, tan
59   
60
61
62    # GDA Specifications
63    a = 6378137.0                       #Semi major axis
64    inverse_flattening = 298.257222101  #1/f
65    if scale_factor is None:
66        K0 = 0.9996                         #Central scale factor   
67    else:
68        K0 = scale_factor
69    zone_width = 6                      #Degrees
70
71    longitude_of_central_meridian_zone0 = -183   
72    longitude_of_western_edge_zone0 = -186
73
74    if false_easting is None:
75        false_easting = 500000
76
77    if false_northing is None:
78        if lat < 0:
79            false_northing = 10000000  #Southern hemisphere
80        else:
81            false_northing = 0         #Northern hemisphere)
82       
83   
84    # Derived constants
85    f = 1.0/inverse_flattening
86    b = a*(1-f)       #Semi minor axis
87
88    e2 = 2*f - f*f#    = f*(2-f) = (a^2-b^2/a^2   #Eccentricity
89    e = sqrt(e2)
90    e2_ = e2/(1-e2)   # = (a^2-b^2)/b^2 #Second eccentricity
91    e_ = sqrt(e2_)
92    e4 = e2*e2
93    e6 = e2*e4
94
95    # Foot point latitude
96    n = (a-b)/(a+b) #Same as e2 - why ?
97    n2 = n*n
98    n3 = n*n2
99    n4 = n2*n2
100
101    G = a*(1-n)*(1-n2)*(1+9*n2/4+225*n4/64)*pi/180
102
103
104    phi = lat*pi/180     #Convert latitude to radians
105
106    sinphi = sin(phi)   
107    sin2phi = sin(2*phi)
108    sin4phi = sin(4*phi)
109    sin6phi = sin(6*phi)
110
111    cosphi = cos(phi)
112    cosphi2 = cosphi*cosphi
113    cosphi3 = cosphi*cosphi2
114    cosphi4 = cosphi2*cosphi2
115    cosphi5 = cosphi*cosphi4   
116    cosphi6 = cosphi2*cosphi4
117    cosphi7 = cosphi*cosphi6
118    cosphi8 = cosphi4*cosphi4       
119
120    t = tan(phi)
121    t2 = t*t
122    t4 = t2*t2
123    t6 = t2*t4
124   
125    # Radius of Curvature
126    rho = a*(1-e2)/(1-e2*sinphi*sinphi)**1.5
127    nu = a/(1-e2*sinphi*sinphi)**0.5
128    psi = nu/rho
129    psi2 = psi*psi
130    psi3 = psi*psi2
131    psi4 = psi2*psi2
132
133    # Meridian distance
134    A0 = 1 - e2/4 - 3*e4/64 - 5*e6/256
135    A2 = 3.0/8*(e2+e4/4+15*e6/128)
136    A4 = 15.0/256*(e4+3*e6/4)
137    A6 = 35*e6/3072
138   
139    term1 = a*A0*phi
140    term2 = -a*A2*sin2phi
141    term3 = a*A4*sin4phi
142    term4 = -a*A6*sin6phi
143
144    m = term1 + term2 + term3 + term4 #OK
145
146    if zone is not None and central_meridian is not None:
147        msg = 'You specified both zone and central_meridian. Provide only one of them'
148        raise Exception, msg
149   
150    # Zone
151    if zone is None:
152        zone = int((lon - longitude_of_western_edge_zone0)/zone_width)
153
154    # Central meridian
155    if central_meridian is None:
156        central_meridian = zone*zone_width+longitude_of_central_meridian_zone0
157    else:
158        zone = -1
159
160    omega = (lon-central_meridian)*pi/180 #Relative longitude (radians)
161    omega2 = omega*omega
162    omega3 = omega*omega2
163    omega4 = omega2*omega2
164    omega5 = omega*omega4
165    omega6 = omega3*omega3
166    omega7 = omega*omega6
167    omega8 = omega4*omega4
168     
169    # Northing
170    term1 = nu*sinphi*cosphi*omega2/2 
171    term2 = nu*sinphi*cosphi3*(4*psi2+psi-t2)*omega4/24
172    term3 = nu*sinphi*cosphi5*\
173            (8*psi4*(11-24*t2)-28*psi3*(1-6*t2)+\
174             psi2*(1-32*t2)-psi*2*t2+t4-t2)*omega6/720
175    term4 = nu*sinphi*cosphi7*(1385-3111*t2+543*t4-t6)*omega8/40320
176    northing = false_northing + K0*(m + term1 + term2 + term3 + term4)
177
178    # Easting
179    term1 = nu*omega*cosphi
180    term2 = nu*cosphi3*(psi-t2)*omega3/6
181    term3 = nu*cosphi5*(4*psi3*(1-6*t2)+psi2*(1+8*t2)-2*psi*t2+t4)*omega5/120
182    term4 = nu*cosphi7*(61-479*t2+179*t4-t6)*omega7/5040
183    easting = false_easting + K0*(term1 + term2 + term3 + term4)
184   
185    return zone, easting, northing
186
187
188
189def convert_from_latlon_to_utm(points=None,
190                               latitudes=None,
191                               longitudes=None,
192                               false_easting=None,
193                               false_northing=None):
194    """Convert latitude and longitude data to UTM as a list of coordinates.
195
196
197    Input
198
199    points: list of points given in decimal degrees (latitude, longitude) or
200    latitudes: list of latitudes   and
201    longitudes: list of longitudes
202    false_easting (optional)
203    false_northing (optional)
204
205    Output
206
207    points: List of converted points
208    zone:   Common UTM zone for converted points
209
210
211    Notes
212
213    Assume the false_easting and false_northing are the same for each list.
214    If points end up in different UTM zones, an ANUGAerror is thrown.   
215    """
216
217    old_geo = Geo_reference()   
218    utm_points = []
219    if points == None:
220        assert len(latitudes) == len(longitudes)
221        points =  map(None, latitudes, longitudes)
222       
223    for point in points:
224       
225        zone, easting, northing = redfearn(float(point[0]),
226                                           float(point[1]),
227                                           false_easting=false_easting,
228                                           false_northing=false_northing)
229        new_geo = Geo_reference(zone)
230        old_geo.reconcile_zones(new_geo)       
231        utm_points.append([easting, northing])
232
233    return utm_points, old_geo.get_zone()
Note: See TracBrowser for help on using the repository browser.