User:Andrew Nikitin/Angular
Angular units
Angular values come in different shapes and under different names. Angles can be measured in radians, degrees, hours. Military have their own unit, called mil (which comes in at least 3 different varieties, depending on a country) and French surveyors at some point used (or, maybe still use) grad. Even when expressed in degrees, the fractional part can be expressed in familiar decimals, minutes or minutes and seconds.
This script attempts to deal with some of this complexity. [{{#file: "angular.ijs"}} Download script: angular.ijs ]
coinsert 'geo' cocurrent 'geo' NB. Angular units formatting and conversion. require 'regex' «convert» «format» «parse» «misc»
Conversion
Units conversion
[{{#file: "convert"}} Download script: convert ]
NB.*θfζ v convert angular units: obtain θ units from ζ units NB. Units: NB. d - degrees NB. m - minutes(angular) =nm NB. r - radians NB. g - grads NB. h - hours dfr=:180p_1&* mfr=:(180 * 60 * 1p_1)&* gfr=:200p_1&* hfr=:12p_1&* rfd=:dfr^:_1 rfm=:mfr^:_1 rfg=:gfr^:_1 rfh=:hfr^:_1
f in the name stands for from. This way dfr reads degrees from radians. Since data is visualized as "flowing" from right to left, this seems to be better mnemonic than r2d.
h here is angular hour, such as one used to describe right ascension. There are 24 hours in 360°.
m means minute of arc =1/60°, not 1/60 of an hour. It is also equal to 1 nautical mile on Earth surface.
RFD=:%DFR=:180p_1 RFM=:%MFR=:(180 * 60 * 1p_1) RFG=:%GFR=:200p_1 RFH=:%HFR=:12p_1
Originally the script included these conversion constants, but there seems to be little use for them.
Base 60 Conversion
Sometimes fraction of a degree (or an hour) is expressed as minutes and fraction of a minute is expressed as seconds. [{{#file: "convert"}} Download script: convert ]
tomin=:f60=:((_: , #&60)@[ #: 60&^@[ * ]) frommin=:e60=:(60&#. % 60&^@<:@#)
f60 (format-60) converts decimal fraction representation into given number of base 60 parts. x = number of such parts (0 = none, 1 = minutes and decimal fractions of minutes, 2 = minutes, seconds and decimal fraction of seconds, etc.) This works so far only for positive. Negatives need to be investigated
e60 (eval-60) performs backward conversion -- from list of base 60 units into decimal fraction. [{{#file: "convert"}} Download script: convert ]
NB.*cms v cover verb "convert minutes and seconds" NB. optional left argument specifies how many base-60 parts should NB. be on output cms=:e60 : (f60 e60)
For example:
cms 1 20 NB. 1 hour 20 minutes 1.33333 2 cms 1 20.11 NB. 1 hour 20.11 minutes in hours, minutes, seconds 1 20 6.6
[{{#file: "convert"}} Download script: convert ]
NB.*toneg v force y into (-x/2..x/2] range toneg=:(| - [ * | > -:@[) NB.*round v round y to nearest multiple of x round=:([ (] - |) -:@[ + ]) NB.*roundup v round y up to a multiple of x roundup=:] + (| -) NB.*rounddown v round y down to a multiple of x rounddown=:] - |
Formatting
All formatting verbs have names starting with f.
Format individual values
[{{#file: "format"}} Download script: format ]
DMS=:'-',u: 16bb0 16bb4 16b2dd NB. unicode positions, >j601
DMS is graphic symbols for sign (-), degree (°), minute (´) and second (˝).
Alternatives for DMS:
DMS=:'- ''"' NB. ascii DMS=:'-',16bb0 16b92 16b94{a. NB. pre j601, single byte encoding
[{{#file: "format"}} Download script: format ]
HMS=:'?hms'
Labels for hours, minutes, seconds.
All the following formatting verbs take angular value y given in radians and format it as a string. The verbs perform conversion, round and add appropriate marking symbols depending on the assumed meaning of their argument. [{{#file: "format"}} Download script: format ]
f2=:(({.@[ #~ 0 > ]) , ;@(}.@[ (,~ ":)&.> 2&tomin@|@]))
This is a generic verb that formats possibly fractional y into 2 level base-60 fraction using x for sign symbol and names for whole, first and second base-60 components [{{#file: "format"}} Download script: format ]
fh2=: HMS&f2@hfr fd2=: DMS&f2@dfr
Format radians as degrees-minutes-seconds or hours-minutes-seconds with proper marking.
fpl=:('fd2';'fd2';']')"_ apply&> ]
Some remnant. Looks like a formatting for a triplet representing a point in spherical coordinates. [{{#file: "format"}} Download script: format ]
NB.*faz v format azimuth faz=:(1{DMS) ,~ _5 {. '00' , 0j1 ": 360 | dfr
Format as azimuth. Show in range 0..360° rounded to 0.1° [{{#file: "format"}} Download script: format ]
NB.*fdist v format distance fdist_nm=:'nm' ,~ '0.1' >@(8!:0) mfr fdist_km=:'km' ,~ '0.1' >@(8!:0) 6372.797&* fdist=:fdist_nm
Angular distances on the globe can be displayed in linear units. fdist name is default distance formatter and it selects one of the formatters. Format is rounded to 0.1 of a unit. Appends unit name (nm, km or mi). [{{#file: "format"}} Download script: format ]
NB.*fdm v format to decimal minutes fdm=:3 : 0 'd m'=.1 tomin | dfr y ('-'#~0>y),(":d),(1{DMS),(_4{.'0',0j1":m),(2{DMS) : 'd m'=.1 tomin | (0.1 % 60) round dfr y (x{~0>y),~(":d),(1{DMS),(_4{.'0',0j1":m),(2{DMS) )
Another generic verb that, probably would not be useful on itself. It outputs the number rounded to 0.1´. Optional x is a 2-character list of names for positive and negative values. [{{#file: "format"}} Download script: format ]
NB.*falt v format altitude falt=:fdm
Format observed/computed altitude of a body. [{{#file: "format"}} Download script: format ]
NB.*fmin v format angle as angular minutes fmin=: '´' ,~ '0.1' >@(8!:0) mfr
Same as fdist_nm, but different graphic sign. Useful to format corrections/offsets. [{{#file: "format"}} Download script: format ]
NB.*flat v format latitude (positive=N, negative=S) flat=:'NS'&fdm
Positive is North, negative is South. The latitude "name" (sign) is located after the numeric values. In some texts it is located in front of the numbers, but placing name in the back seems to be more common. [{{#file: "format"}} Download script: format ]
NB.*flon v format longitude (positive=E, negative=W) flon=:'EW'&fdm
Same, but East and West. This verb may benefit from applying 2p1&toneg before it, but so far it is only applied to a longitude which is already in canonical form. [{{#file: "format"}} Download script: format ]
NB.*fdec v format declination (positive=N, negative=S) fdec=:_1 |. flat
It seems that, unlike latitude, declination has its name displayed before the numeric values more often than after.
Format compound values
[{{#file: "format"}} Download script: format ]
NB.*fmt v generic format applies formatting verbs from x to elements of y NB. and joins result together using ' ' as a delimiter fmt=:(<' ') ;@}.@,@,. apply&.>
[{{#file: "format"}} Download script: format ]
NB.*fpos v format 2-element position array (lat,lon) fpos=:('flat';'flon')&fmt
[{{#file: "format"}} Download script: format ]
NB.*fvec v format 2-element vector array (azimuth, distance) fvec=:('faz';'fdist')&fmt
[{{#file: "format"}} Download script: format ]
NB.*fobs v format 2-element array of sextant observation (altitude, azimuth) fobs=:('falt';'faz')&fmt
This is almost same as fvec, except that Hs/Hc are usually recorded with altitude first. [{{#file: "format"}} Download script: format ]
NB.*fcat v format catalog entry (Dec,RA) fcat=:('fdec';'fh2')&fmt
Format non-angular values
[{{#file: "format"}} Download script: format ]
NB.*fdur v format time duration in hours; hours above 24 are displayed NB. as days, fractions of an hour are displayed as minutes and seconds. fdur=:3 : 0 s=.'-' {.~y<0 d=.(|y) <.@% 24 y=.1r3600 round 24|y NB. round to nearest second t=.(-@(0={:) }. ]) 2 tomin y s,((":d),'d ')&,^:(d>0) (":@{.t) , ,(':' , _2{.'00',":)"0 <.}.t )
This verb is for formatting time intervals expressed in hours. Stretches longer than 24 hours are converted to days. Example:
fdur 700%6 NB. 700 miles at 6 knots 4d 20:40 fdur 70%6 NB. 70 miles at 6 knots 11:40
[{{#file: "format"}} Download script: format ]
NB.*mmin v move decimal point and everything after it after last symbol mmin=:(i:~ ({. , _1 |. }.) ])
Parsing
[{{#file: "parse"}} Download script: parse ]
NB.*angfstr v parse string containing 1 or more angles in degrees, minutes, etc. NB. Returns numeric vector angfstr=:3 : 0 p=.'([-+NnSsWwEe]?)([0-9.]+)(\s*[°d]\s*([0-9.]+)(\s*[′´''m]\s*(([0-9.]+)\s*[″˝"s]?)?)?)?([NnSsWwEe]?)' rxmatches y r=.i.0 for_m. p do. d=.frommin {.@".&> (2 4 7{m) rxfrom y h=.{.toupper >y rxfrom~ 1{m s=.1 _1 1 _1 1 _1 1{~ 'NSEW+- ' i. h h=.{.toupper >y rxfrom~ 8{m r=.r,d*s*1 _1 1 _1 1 _1 1{~ 'NSEW+- ' i. h end. {.^:(1=#) rfd r )
hoursfstr=:([: frommin [: ".;._1 ':'&,)
Stub for time/duration parsing. Not included in final script.
According to http://en.wikipedia.org/wiki/Geographic_coordinate_conversion all of the following is valid and acceptable way to write coordinates
GP_TEST=:<;._2 ] 0 : 0 40:26:46N,79:56:55W 40:26:46.302N 79:56:55.903W 40°26'47"N 79°58'36"W 40d 26' 47" N 79d 58' 36" W 40.446195N 79.948862W 40.446195, -79.948862 40° 26.7717, -79° 56.93172 N4°35.0´ W12°30.6´ 4°35.0´N 12°30.6´W )
[{{#file: "parse"}} Download script: parse ]
NB.*posfstr v parse string into (lat, lon) position NB. Understands variety of input forms posfstr=: 3 : 0 atstart=.(] rxmatch~ '^\s*(' , ')\s*' ,~ [) SIGN=.'[-+NnSsWwEe]' TAILSIGN=.'[NnSsWwEe]' NUMBER=.'[0-9.]+' DEG=.'[do°º\*:]' MIN=.'[m′''´’‘:]' SEC=.'[s″"˝”“]|''''' SEP=.'[;,/]|$' found=._1 0 -.@-: {. piece=. >@rxfrom~ 1&{ r=.i.0 NB. numberic results o=.i.0 NB. inferred order while. '' -.@-: y do. NB. components sgn=.'' d=.0 m=.0 s=.0 p=.SIGN atstart y sgn=.y piece p y=.({:{.p)}.y p=.NUMBER atstart y assert. found p d=._ ". y piece p y=.y}.~{:{.p if. -.found p=.DEG atstart y do. goto_trail. end. y=.y}.~{:{.p if. -.found p=.NUMBER atstart y do. goto_trail. end. m=._ ". y piece p y1=.y NB. save in case need to rollback y=.y}.~{:{.p if. found p=.MIN atstart y do. y=.y}.~{:{.p else. if. found p=.DEG atstart y do. NB. rollback m=.0 y=.y1 end. goto_trail. end. if. -.found p=.NUMBER atstart y do. goto_trail. end. s=._ ". y piece p y1=.y NB. save in case need to rollback y=.y}.~{:{.p if. found p=.SEC atstart y do. y=.y}.~{:{.p else. if. found p=.DEG atstart y do. NB. rollback m=.0 y=.y1 end. goto_trail. end. label_trail. NB. check for sign if sign is currently empty if. ''-:sgn do. p=.TAILSIGN atstart y if. found p do. sgn=.y piece p y=.y}.~{:{.p end. end. NB. skip separators/trainiling spaces if any if. found p=.SEP atstart y do. y=.y}.~{:{.p end. sgn=.toupper {.sgn r=.r,(frommin d,m,s)*1 _1 1 _1 1 _1 1{~ 'NSEW+- ' i. sgn o=.o,_1 _1 1 1 0 {~ 'NSEW' i. sgn end. rfd r /: o )
This sequential parsing routine replaces older version which was simpler and faster, but did not handle some of the example cases.
The intention of this verb is to be able to process copy/paste geo coordinates taken from a random source into canonical form. At least, in some of the cases.
Other
[{{#file: "misc"}} Download script: misc ]
makepos=:rfd
converts numeric latitude and longitude (in degrees) to radians. [{{#file: "misc"}} Download script: misc ]
NB.*makevec v convert numeric directional vector into radians NB. y=(direction in degrees, speed/distance in knots/nm) makevec=:('rfd';'rfm')&(apply&>)
This verb better belongs to drnav script, but all necessary routines are here.
Contributed by Andrew Nikitin