User:Andrew Nikitin/Angular

From J Wiki
Jump to navigation Jump to search

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