source: project/release/5/geo-utils/trunk/geo-dms.scm @ 35995

Last change on this file since 35995 was 35995, checked in by Kon Lovett, 21 months ago

C5 initial

File size: 5.8 KB
Line 
1;;;; geo-dms.scm  -*- Scheme -*-
2;;;; Kon Lovett, Jul '18
3;;;; Kon Lovett, Sep '17
4;;;; Kon Lovett, May '17
5
6(module geo-dms
7
8(;export
9  degree-latitude? degree-longitude?
10  degree? minute? second?
11  w-dir? e-dir? n-dir? s-dir?
12  ;
13  dms-glyphs
14  dms->degree degree->dms
15  dms->string dms->string*
16  string-dms->degree
17  ;
18  degree->string string->degree)
19
20(import scheme
21  (chicken irregex)
22  (chicken fixnum)
23  (chicken flonum)
24  (chicken type)
25  ;mathh
26  ;fp-utils
27  ;moremacros
28  type-checks
29  type-errors
30  geopoint)
31
32;;;
33
34(define (degree-latitude? d)
35  (and (<= -90.0 d) (<= d 90.0)) )
36
37(define (degree-longitude? d)
38  (and (<= -180.0 d) (<= d 180.0)) )
39
40(define (degree? d #!optional lat?)
41  (if lat?
42    (degree-latitude? d)
43    (degree-longitude? d)) )
44
45(define (minute? m)
46  (and (<= 0 m) (<= m 59)) )
47
48(define (second? s)
49  (and (<= 0 s) (<= s 59)) )
50
51;;
52
53(define (w-dir? dir)
54  (case dir
55    ((#\W #\w)  #t )
56    (else       #f ) ) )
57
58(define (e-dir? dir)
59  (case dir
60    ((#\E #\e)  #t )
61    (else       #f ) ) )
62
63(define (n-dir? dir)
64  (case dir
65    ((#\N #\n)  #t )
66    (else       #f ) ) )
67
68(define (s-dir? dir)
69  (case dir
70    ((#\S #\s)  #t )
71    (else       #f ) ) )
72
73(define (ns-dir? dir)
74  (or (n-dir? dir) (s-dir? dir)) )
75
76(define (sw-dir? dir)
77  (or (s-dir? dir) (w-dir? dir)) )
78
79;DMS output tags
80;
81(define-constant DEGREE-UNIT-GLYPH "°")
82(define-constant MINUTE-UNIT-GLYPH "'")
83(define-constant SECOND-UNIT-GLYPH "\"")
84
85;degree-minute-second text form
86
87(define +dms-sre+
88  '(:
89    (* space)
90    (? ($ ("NSEWnsew")))            ;direction maybe here
91    (* space)
92    ($ (? #\-) (+ num)) (~ num)     ;shouldn't be neg, should be °
93    (* space)
94    ($ (+ num)) (~ num)             ;should be a '
95    (* space)
96    ($ (+ (or num #\.))) (~ num)    ;should be "
97    (* space)
98    (? ($ ("NSEWnsew")))            ;direction maybe here; 0,0 has no dir
99    (* space)))
100
101(define +dms-regex+ (sre->irregex +dms-sre+ 'utf8 'fast))
102
103;;
104
105(define-type dms-glyphs (list string string string))
106
107(define (dms-glyphs? x)
108  (and (list? x) (= 3 (length x))) )
109
110(define-check+error-type dms-glyphs)
111
112(: dms-glyphs (#!optional dms-glyphs -> dms-glyphs))
113;
114(define-warning-parameter dms-glyphs
115  `(,DEGREE-UNIT-GLYPH ,MINUTE-UNIT-GLYPH ,SECOND-UNIT-GLYPH)
116  dms-glyphs)
117
118;;
119
120; fixnum fixnum fixnum #!optional boolean boolean string -> string
121; the degree argument maybe negative
122(: dms->string (fixnum fixnum fixnum #!optional boolean boolean string --> string))
123;
124(define (dms->string d m s #!optional lat? leading-dir? (pad ""))
125  (if (and (fx= d 0) (fx= m 0) (fx= s 0))
126    ;so 0
127    (dms0 pad)
128    ;construct DMS N/S/E/W
129    (let* (
130      (neg? (fx< d 0))
131      (d (if neg? (fxneg d) d))
132      (str (dms->string* d m s pad))
133      (dir  (if lat? (if neg? "S" "N") (if neg? "W" "E")) ) )
134      ;
135      (if leading-dir?
136        (string-append dir pad str)
137        (string-append str pad dir) ) ) ) )
138
139; string #!optional boolean -> flonum
140; -122°45'10"E => -122.752777777778 even though E
141; -122°45'10"W => 122.752777777778 even though W
142;
143(: string-dms->degree (string #!optional boolean --> number))
144;
145(define (string-dms->degree str #!optional lat?)
146  (let ((match (irregex-match +dms-regex+ str)))
147    (unless match
148      (error 'string-dms->degree "improper DMS form" str) )
149    (let* (
150      (leading-dir (irregex-match-substring match 1))
151      (leading-dir (and leading-dir (string-ref leading-dir 0)))
152      (d (string->number (irregex-match-substring match 2)))
153      (m (string->number (irregex-match-substring match 3)))
154      (s (string->number (irregex-match-substring match 4)))
155      (trailing-dir (irregex-match-substring match 5))
156      (trailing-dir (and trailing-dir (string-ref trailing-dir 0)))
157      (dir (or leading-dir trailing-dir))
158      ;input string overrides parameters
159      (lat? (or (ns-dir? dir) lat?))
160      (neg? (sw-dir? dir)) )
161      (when (and dir (negative? d))
162        (error 'string-dms->degree "improper DMS sign with direction" str) )
163      (unless (and (degree? d lat?) (minute? m) (second? s))
164        (error 'string-dms->degree "improper DMS value" str) )
165      (dms->degree (if neg? (- d) d) m s) ) ) )
166
167;
168(define string->degree string-dms->degree)
169
170;
171(: degree->string (float #!optional boolean boolean string --> string))
172;
173(define (degree->string deg #!optional lat? leading-dir? (pad ""))
174  (receive (d m s) (degree->dms deg)
175    (dms->string d m s lat? leading-dir? pad) ) )
176
177;;
178
179; fixnum fixnum fixnum -> flonum
180; the degree argument maybe negative
181(: dms->degree (fixnum fixnum fixnum --> float))
182;
183(define (dms->degree d m s)
184  (let* (
185    (neg? (fx< d 0))
186    (d (if neg? (fxneg d) d))
187    (deg
188      (fp+
189        (exact->inexact d)
190        (fp+
191          (fp/ (exact->inexact m) 60.0)
192          (fp/ (exact->inexact s) 3600.0)))) )
193    (if neg? (fpneg deg) deg) ) )
194
195; flonum -> fixnum fixnum fixnum
196(: degree->dms ((or float fixnum) --> fixnum fixnum fixnum))
197;
198(define (degree->dms deg)
199  (let* (
200    (deg (exact->inexact deg))
201    (neg? (fp< deg 0.0)) )
202    ;
203    (let*-values (
204      ((sint sflt) (modf (fpabs deg)))
205      ((dint dflt) (modf (fp* sflt 60.0)))
206      ((mint mflt) (modf (fp* dflt 60.0))) )
207      ;
208      (let (
209        (ideg (inexact->exact dint))
210        (isec (inexact->exact (fpround (fp+ sint (fp* mflt 60.0)))))
211        (imin (inexact->exact mint)) )
212        ;
213        (values (if neg? (fxneg ideg) ideg) imin isec) ) ) ) )
214
215(: dms->string* (number number number #!optional string --> string))
216;
217(define (dms->string* d m s #!optional (pad ""))
218  (string-append
219    (number->string d) (degree-char)
220    pad
221    (number->string m) (minute-char)
222    pad
223    (number->string s) (second-char)) )
224
225;;
226
227(define (degree-char)
228  (car (dms-glyphs)) )
229
230(define (minute-char)
231  (cadr (dms-glyphs)) )
232
233(define (second-char)
234  (caddr (dms-glyphs)) )
235
236;;
237
238(define (dms0 #!optional (pad ""))
239  (dms->string* 0 0 0 pad) )
240
241) ;module geo-dms
Note: See TracBrowser for help on using the repository browser.