#pike __REAL_VERSION__ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float lat; |
|
|
|
float long; |
|
|
|
|
|
|
|
|
|
void create(int|float|string _lat,void|int|float|string _long) |
{ |
if (stringp(_lat)) |
{ |
if (zero_type(_long)) |
{ |
string tmp; |
if (sscanf(_lat,"%sN %s",tmp,_long)==2) _lat=tmp+"N"; |
else if (sscanf(_lat,"%sS %s",tmp,_long)==2) _lat=tmp+"S"; |
else if (sscanf(_lat,"%sW %s",tmp,_lat)==2) _long=tmp+"W"; |
else if (sscanf(_lat,"%sE %s",tmp,_lat)==2) _long=tmp+"N"; |
else if (sscanf(_lat,"%s %s",tmp,_long)==2) _lat=tmp; |
} |
_lat=dwim(_lat,"NS"); |
if (stringp(_lat)) |
error("Failed to understand latitude %O\n",lat); |
} |
if (stringp(_long)) |
{ |
_long=dwim(_long,"EW"); |
if (stringp(_long)) |
error("Failed to understand longitude %O\n",long); |
} |
lat=(float)_lat; |
long=(float)_long; |
} |
|
float|string dwim(string what,string direction) |
{ |
float d,m,s; |
string dir=0; |
int neg=0; |
#define DIV "%*[ \t\r\n'`°\"]" |
|
if (sscanf(what,"-%s",what)) neg=1; |
|
what=upper_case(what); |
|
sscanf(what,"%f"DIV"%f"DIV"%f"DIV"%["+direction+"]",d,m,s,dir)==7 || |
sscanf(what,"%f"DIV"%f"DIV "%["+direction+"]",d,m, dir)==5 || |
sscanf(what,"%f"DIV "%["+direction+"]",d, dir); |
|
if (dir==direction[1..1]) neg=!neg; |
|
d+=m/60+s/3600; |
return neg?-d:d; |
} |
|
string prettyprint(float what,int n,string directions) |
{ |
if (what<0) what=-what,directions=directions[1..]; |
else directions=directions[..0]; |
|
switch (n) |
{ |
case -1: return sprintf("%.5g",what); |
case 1: |
return sprintf("%.5g°%s",what,directions); |
case 3: |
return sprintf("%d°%d'%.3g\"%s", |
(int)floor(what),(int)floor(60*(what-floor(what))), |
3600*(what-floor(60*what)/60), |
directions); |
default: |
return sprintf("%d°%.5g'%s", |
(int)floor(what),60*(what-floor(what)), |
directions); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string latitude(void|int n) |
{ |
return prettyprint(lat,n,"NS"); |
} |
|
string longitude(void|int n) |
{ |
return prettyprint(long,n,"EW"); |
} |
|
|
|
|
string standard_grid() { |
if(lat>84.0 || lat<-80.0) return "UPS"; |
return "UTM"; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constant ellipsoids = |
([ "Airy" : ({ 6377563, 0.00667054 }), |
"Australian National" : ({ 6378160, 0.006694542 }), |
"Bessel 1841" : ({ 6377397, 0.006674372 }), |
"Bessel 1841 (Nambia)" : ({ 6377484, 0.006674372 }), |
"Clarke 1866" : ({ 6378206, 0.006768658 }), |
"Clarke 1880" : ({ 6378249, 0.006803511 }), |
"Everest" : ({ 6377276, 0.006637847 }), |
"Fischer 1960 (Mercury)" : ({ 6378166, 0.006693422 }), |
"Fischer 1968" : ({ 6378150, 0.006693422 }), |
"GRS 1967" : ({ 6378160, 0.006694605 }), |
"GRS 1980" : ({ 6378137, 0.00669438 }), |
"Helmert 1906" : ({ 6378200, 0.006693422 }), |
"Hough" : ({ 6378270, 0.00672267 }), |
"International" : ({ 6378388, 0.00672267 }), |
"Krassovsky" : ({ 6378245, 0.006693422 }), |
"Modified Airy" : ({ 6377340, 0.00667054 }), |
"Modified Everest" : ({ 6377304, 0.006637847 }), |
"Modified Fischer 1960" : ({ 6378155, 0.006693422 }), |
"South American 1969" : ({ 6378160, 0.006694542 }), |
"WGS 60" : ({ 6378165, 0.006693422 }), |
"WGS 66" : ({ 6378145, 0.006694542 }), |
"WGS-72" : ({ 6378135, 0.006694318 }), |
"WGS-84" : ({ 6378137, 0.00669438 }), |
]); |
|
|
|
|
|
|
|
int UTM_zone_number() { |
int zone = (int)((long + 180)/6) + 1; |
|
if( lat >= 56.0 && lat < 64.0 && long >= 3.0 && long < 12.0 ) |
zone = 32; |
|
|
if( lat >= 72.0 && lat < 84.0 ) |
{ |
if( long >= 0.0 && long < 9.0 ) zone = 31; |
else if( long >= 9.0 && long < 21.0 ) zone = 33; |
else if( long >= 21.0 && long < 33.0 ) zone = 35; |
else if( long >= 33.0 && long < 42.0 ) zone = 37; |
} |
|
return zone; |
} |
|
|
|
string UTM_zone_letter() |
{ |
if(lat > 84) return "Z"; |
int min = 72; |
foreach( "XWVUTSRQPNMLKJHGFEDC"/1, string code ) { |
if(lat >= min) return code; |
min -= 8; |
} |
return "Z"; |
} |
|
|
|
|
|
|
array(float) UTM_offset(float radius, float ecc) { |
|
float k0 = 0.9996; |
float LatRad = lat * Math.pi/180; |
float LongRad = long * Math.pi/180; |
|
float LongOriginRad = ((UTM_zone_number() - 1)*6 - 180 + 3) * Math.pi/180; |
|
float eccPrime = ecc/(1-ecc); |
|
float N = radius/sqrt(1-ecc*sin(LatRad)*sin(LatRad)); |
float T = tan(LatRad)*tan(LatRad); |
float C = eccPrime*cos(LatRad)*cos(LatRad); |
float A = cos(LatRad)*(LongRad-LongOriginRad); |
|
float M = radius* |
((1 - ecc/4 - 3*ecc*ecc/64 - 5*ecc*ecc*ecc/256)*LatRad |
- (3*ecc/8 + 3*ecc*ecc/32 + 45*ecc*ecc*ecc/1024)*sin(2*LatRad) |
+ (15*ecc*ecc/256 + 45*ecc*ecc*ecc/1024)*sin(4*LatRad) |
- (35*ecc*ecc*ecc/3072)*sin(6*LatRad)); |
|
float UTME = (k0*N*(A+(1-T+C)*A*A*A/6 |
+ (5-18*T+T*T+72*C-58*eccPrime)*A*A*A*A*A/120) |
+ 500000.0); |
|
float UTMN = (k0*(M+N*tan(LatRad)* |
(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24 |
+ (61-58*T+T*T+600*C-330*eccPrime)*A*A*A*A*A*A/720))); |
if(lat < 0) |
UTMN += 10000000.0; |
|
return ({ UTME, UTMN }); |
} |
|
|
|
|
|
|
string UTM(float radius, float ecc) { |
return sprintf("%d%s %f %f", UTM_zone_number(), UTM_zone_letter(), |
@UTM_offset(radius, ecc)); |
} |
|
void set_from_UTM(float radius, float ecc, float UTME, float UTMN, int zone_num, string zone_char) { |
|
float k0 = 0.9996; |
float e1 = (1-sqrt(1-ecc))/(1+sqrt(1-ecc)); |
|
UTME -= 500000.0; |
if(zone_char[0]-'N' < 0) |
UTMN -= 10000000.0; |
|
float LongOrigin = (zone_num - 1)*6 - 180 + 3.0; |
|
float eccPrime = (ecc)/(1-ecc); |
|
float M = UTMN / k0; |
float mu = M/(radius*(1-ecc/4-3*ecc*ecc/64-5*ecc*ecc*ecc/256)); |
|
float phi1Rad = mu + (3*e1/2-27*e1*e1*e1/32)*sin(2*mu) |
+ (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu) |
+(151*e1*e1*e1/96)*sin(6*mu); |
float phi1 = phi1Rad * 180/Math.pi; |
|
float N1 = radius/sqrt(1-ecc*sin(phi1Rad)*sin(phi1Rad)); |
float T1 = tan(phi1Rad)*tan(phi1Rad); |
float C1 = eccPrime*cos(phi1Rad)*cos(phi1Rad); |
float R1 = radius*(1-ecc)/pow(1-ecc*sin(phi1Rad)*sin(phi1Rad), 1.5); |
float D = UTME/(N1*k0); |
|
lat = phi1Rad - (N1*tan(phi1Rad)/R1)*(D*D/2-(5+3*T1+10*C1-4*C1*C1-9*eccPrime)*D*D*D*D/24 |
+(61+90*T1+298*C1+45*T1*T1-252*eccPrime-3*C1*C1)*D*D*D*D*D*D/720); |
lat = lat * 180/Math.pi; |
|
long = (D-(1+2*T1+C1)*D*D*D/6+(5-2*C1+28*T1-3*C1*C1+8*eccPrime+24*T1*T1) |
*D*D*D*D*D/120)/cos(phi1Rad); |
long = LongOrigin + long * 180/Math.pi; |
} |
|
|
|
|
|
string|array cast(string to) |
{ |
if (to[..4]=="array") |
return ({lat,long}); |
|
if (to[..5]=="string") |
return latitude()+" "+longitude(); |
|
error("can't cast to %O\n",to); |
} |
|
int __hash() |
{ |
return (int)(lat*3600000+long*3600000); |
} |
|
int `==(object pos) |
{ |
return (pos->lat==lat && pos->long==long); |
} |
|
int `<(object pos) |
{ |
if (pos->lat>lat) return 1; |
else if (pos->lat==lat && pos->long>long) return 1; |
return 0; |
} |
|
int `>(object pos) |
{ |
if (pos->lat<lat) return 1; |
else if (pos->lat==lat && pos->long<long) return 1; |
return 0; |
} |
|
string _sprintf(int t) |
{ |
if (t=='O') |
return "Position("+latitude()+", "+longitude()+")"; |
return 0; |
} |
|
|