code wiki / _hdl_build / nx_geo_geocode.nx
nx_geo_geocode.nx source
↩ module page · 85 lines · 3105 B
1// nx_geo_geocode.nx -- LIB: GEO-006 geocoding (address/place <-> coordinate). Sovereign: the place
2// gazetteer is DATA in the store (open data / OSM as last-mile DATA, never substrate); FORWARD =
3// name -> coords (case-insensitive match), REVERSE = coords -> nearest place (REUSES GEO-009
4// geo_nearest -- no new geometry). Pure-integer microdegree coords -> deterministic. The seeded
5// gazetteer is small + extensible; swap in a larger open-data set without code change. ORIGINAL
6import "nx_syscalls.nx"
7
8func gz_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
9
10// parse a signed integer from buf[s..e) (handles a leading '-').
11func gz_signed(buf: *u8, s: i64, e: i64) -> i64 {
12 var neg: i64 = 0
13 var p: i64 = s
14 if p < e { if buf[p] == (45 as u8) { neg = 1; p = p + 1 } }
15 var v: i64 = 0
16 while p < e {
17 let c: i64 = buf[p] as i64
18 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
19 p = p + 1
20 }
21 if neg == 1 { return 0 - v }
22 return v
23}
24
25// lowercase a byte (ASCII A-Z -> a-z).
26func gz_lc(c: i64) -> i64 {
27 if c >= 65 { if c <= 90 { return c + 32 } }
28 return c
29}
30
31// case-insensitive compare: a[0..n) vs b[0..n).
32func gz_ci_eq(a: *u8, b: *u8, n: i64) -> i64 {
33 var i: i64 = 0
34 while i < n {
35 if gz_lc(a[i] as i64) != gz_lc(b[i] as i64) { return 0 }
36 i = i + 1
37 }
38 return 1
39}
40
41// parse the gazetteer (lines: name<tab>lat<tab>lon) into a flat points array [lat,lon,...] plus
42// per-place name offset/length (into gaz). Returns the place count (capped at maxp).
43func geo_gaz_parse(gaz: *u8, gn: i64, pts: *i64, noff: *i64, nlen: *i64, maxp: i64) -> i64 {
44 var cnt: i64 = 0
45 var i: i64 = 0
46 while i < gn {
47 var le: i64 = i
48 var go: i64 = 1
49 while go == 1 { if le >= gn { go = 0 } else { if gaz[le] == (10 as u8) { go = 0 } else { le = le + 1 } } }
50 if le > i {
51 // first tab
52 var t1: i64 = i
53 var f1: i64 = 0
54 var p: i64 = i
55 while p < le { if gaz[p] == (9 as u8) { t1 = p; f1 = 1; p = le } else { p = p + 1 } }
56 // second tab (after t1)
57 var t2: i64 = t1
58 var f2: i64 = 0
59 p = t1 + 1
60 while p < le { if gaz[p] == (9 as u8) { t2 = p; f2 = 1; p = le } else { p = p + 1 } }
61 if f1 == 1 { if f2 == 1 { if cnt < maxp {
62 noff[cnt] = i
63 nlen[cnt] = t1 - i
64 pts[cnt * 2] = gz_signed(gaz, t1 + 1, t2)
65 pts[cnt * 2 + 1] = gz_signed(gaz, t2 + 1, le)
66 cnt = cnt + 1
67 } } }
68 }
69 i = le + 1
70 }
71 return cnt
72}
73
74// FORWARD geocode: index of the place whose name == query (case-insensitive, full match), else -1.
75func geocode_forward(gaz: *u8, noff: *i64, nlen: *i64, count: i64, query: *u8) -> i64 {
76 let qn: i64 = gz_len(query)
77 var c: i64 = 0
78 while c < count {
79 if nlen[c] == qn {
80 if gz_ci_eq((gaz as i64 + noff[c]) as *u8, query, qn) == 1 { return c }
81 }
82 c = c + 1
83 }
84 return 0 - 1
85}