@zukall/zap 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/PROTOCOL-V1.md +117 -0
  3. package/PROTOCOL.md +176 -0
  4. package/README.md +554 -2
  5. package/THIRD-PARTY-NOTICES.md +19 -0
  6. package/asset/zukall-diagnol-example.webp +0 -0
  7. package/asset/zukall-north-south-example.webp +0 -0
  8. package/asset/zukall-west-east.webp +0 -0
  9. package/dist/cjs/address.d.ts +3 -0
  10. package/dist/cjs/address.js +38 -0
  11. package/dist/cjs/address_bounty.d.ts +26 -0
  12. package/dist/cjs/address_bounty.js +2334 -0
  13. package/dist/cjs/address_province_bounty.d.ts +56 -0
  14. package/dist/cjs/address_province_bounty.js +5563 -0
  15. package/dist/cjs/country.d.ts +6 -0
  16. package/dist/cjs/country.js +27 -0
  17. package/dist/cjs/data.d.ts +3 -0
  18. package/dist/cjs/data.js +33 -0
  19. package/dist/cjs/directions.d.ts +5 -0
  20. package/dist/cjs/directions.js +13 -0
  21. package/dist/cjs/encoding.d.ts +3 -0
  22. package/dist/cjs/encoding.js +20 -0
  23. package/dist/cjs/grid.d.ts +23 -0
  24. package/dist/cjs/grid.js +152 -0
  25. package/dist/cjs/index.d.ts +27 -0
  26. package/dist/cjs/index.js +26 -0
  27. package/dist/cjs/internal.d.ts +3 -0
  28. package/dist/cjs/internal.js +19 -0
  29. package/dist/cjs/legacy-address.d.ts +3 -0
  30. package/dist/cjs/legacy-address.js +44 -0
  31. package/dist/cjs/package.json +1 -0
  32. package/dist/cjs/protocol.d.ts +22 -0
  33. package/dist/cjs/protocol.js +25 -0
  34. package/dist/cjs/types.d.ts +114 -0
  35. package/dist/cjs/types.js +2 -0
  36. package/dist/esm/address.d.ts +3 -0
  37. package/dist/esm/address.js +35 -0
  38. package/dist/esm/address_bounty.d.ts +26 -0
  39. package/dist/esm/address_bounty.js +2331 -0
  40. package/dist/esm/address_province_bounty.d.ts +56 -0
  41. package/dist/esm/address_province_bounty.js +5560 -0
  42. package/dist/esm/country.d.ts +6 -0
  43. package/dist/esm/country.js +22 -0
  44. package/dist/esm/data.d.ts +3 -0
  45. package/dist/esm/data.js +2 -0
  46. package/dist/esm/directions.d.ts +5 -0
  47. package/dist/esm/directions.js +10 -0
  48. package/dist/esm/encoding.d.ts +3 -0
  49. package/dist/esm/encoding.js +17 -0
  50. package/dist/esm/grid.d.ts +23 -0
  51. package/dist/esm/grid.js +148 -0
  52. package/dist/esm/index.d.ts +27 -0
  53. package/dist/esm/index.js +16 -0
  54. package/dist/esm/internal.d.ts +3 -0
  55. package/dist/esm/internal.js +15 -0
  56. package/dist/esm/legacy-address.d.ts +3 -0
  57. package/dist/esm/legacy-address.js +41 -0
  58. package/dist/esm/package.json +1 -0
  59. package/dist/esm/protocol.d.ts +22 -0
  60. package/dist/esm/protocol.js +22 -0
  61. package/dist/esm/types.d.ts +114 -0
  62. package/dist/esm/types.js +1 -0
  63. package/package.json +68 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zukall Addressing Protocol contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PROTOCOL-V1.md ADDED
@@ -0,0 +1,117 @@
1
+ # ZAP protocol version 1
2
+
3
+ ZAP stands for **Zukall Address Protocol**. The npm package version and protocol
4
+ version are separate. This specification describes the bundled version-1 grid
5
+ and projection rules.
6
+
7
+ ## Address representation
8
+
9
+ Address text ends with `ZAP`. Result metadata identifies the protocol as `ZAP`;
10
+ `ZAP_PROTOCOL.addressSuffix` exposes the suffix.
11
+
12
+ Country-wide grid:
13
+
14
+ ```text
15
+ <northSouthMeters> <N|S> <eastWestMeters> <E|W>, <countryCode>, ZAP
16
+ ```
17
+
18
+ Province grid:
19
+
20
+ ```text
21
+ <northSouthMeters> <N|S> <eastWestMeters> <E|W>, <provinceCode>, <countryCode>, ZAP
22
+ ```
23
+
24
+ Fields are separated by a comma and one ASCII space. Offset magnitudes and their
25
+ directions are separated by one ASCII space within the first field. Country and
26
+ province codes are normalized
27
+ to uppercase and must exist in the bundled snapshot. Source-specific province
28
+ codes, including `NE_` codes and leading-zero strings, are retained.
29
+
30
+ Offsets are nonnegative integer magnitudes in whole meters. `S` and `W` denote
31
+ negative signed offsets. Zero uses `N` and `E`; the numerical result normalizes
32
+ negative zero. The string identifies a cell under the associated grid rules.
33
+
34
+ ## Inputs and grid selection
35
+
36
+ - Coordinates are WGS84 decimal degrees, with finite numeric latitude in
37
+ `[-90, 90]` and longitude in `[-180, 180]`.
38
+ - The interval is an integer from 1 through 1000 meters, defaulting to 5.
39
+ - Selection is explicit: province-grid countries require a configured province;
40
+ country-wide grids reject a province selection.
41
+ - A point must be inside the selected rectangle, including its edges. This is a
42
+ rectangle check, not an administrative-boundary or land-membership test.
43
+
44
+ Corners are ordered top-left, top-right, bottom-right, bottom-left. They describe
45
+ latitude/longitude-aligned rectangles. A western longitude greater than the
46
+ eastern longitude denotes a date-line crossing. The explicit `-180` to `180`
47
+ rectangle denotes a full 360-degree span. Other supported spans are greater than
48
+ zero and below 180 degrees.
49
+
50
+ The grid center is the midpoint of the north/south bounds and the midpoint of the
51
+ wrapped longitude span, with center longitude normalized to `[-180, 180)`.
52
+
53
+ ## Projection and snapping
54
+
55
+ The projection is spherical azimuthal equidistant, identified as
56
+ `AEQD-SPHERE-v1`, with mean Earth radius **6,371,008.8 meters**. It is centered on
57
+ the selected rectangle's center.
58
+
59
+ For location latitude `φ`, origin latitude `φ₀`, and longitude difference `Δλ`
60
+ (all in radians), define:
61
+
62
+ ```text
63
+ eastComponent = cos(φ) × sin(Δλ)
64
+ northComponent = cos(φ₀) × sin(φ) − sin(φ₀) × cos(φ) × cos(Δλ)
65
+ cosAngle = sin(φ₀) × sin(φ) + cos(φ₀) × cos(φ) × cos(Δλ)
66
+ sinAngle = hypot(eastComponent, northComponent)
67
+ angle = atan2(sinAngle, cosAngle)
68
+ scale = angle / sinAngle
69
+ ```
70
+
71
+ For `sinAngle < 1e-15`, use `scale = 1`. Points within `1e-10` radians of the
72
+ antipode are rejected because the projection is singular there.
73
+
74
+ ```text
75
+ northing = radius × scale × northComponent
76
+ easting = radius × scale × eastComponent
77
+ snapped = Math.round(offset / interval) × interval
78
+ ```
79
+
80
+ Halfway ties round toward the positive axis, following JavaScript `Math.round`.
81
+ Snapping is in the projected plane; the interval is not a promise of uniform
82
+ ground dimensions everywhere in a large grid. Original coordinates remain in
83
+ the result.
84
+
85
+ Approximate rectangle width follows a parallel at the center latitude; height
86
+ follows a meridian. Both are rounded to whole meters in the result.
87
+
88
+ ## Identity and persistence
89
+
90
+ Every result includes:
91
+
92
+ ```text
93
+ protocol = ZAP
94
+ protocolVersion = 1
95
+ gridId = ZAP:1:<country/province prefix>:<country snapshot version>:<grid snapshot version>:AEQD-SPHERE-v1:6371008.8:<center latitude>:<center longitude>:<interval>:nearest-positive-tie
96
+ ```
97
+
98
+ Numeric identity components use JavaScript's canonical number-to-string
99
+ representation. Treat `gridId` as opaque and persist it alongside the address,
100
+ original coordinates, and grid interval. The plain address does not encode its
101
+ snapshot, origin, or interval; cell identity includes both `gridId` and address.
102
+
103
+ The bundled bounds and versions are immutable at runtime. Never change bounds
104
+ under an existing snapshot version. A change to projection, radius, rounding,
105
+ or canonical representation requires a protocol version review. Preserve old
106
+ configuration snapshots when interpreting previously issued addresses.
107
+
108
+ ## Snapshot coverage
109
+
110
+ The package contains 239 country/territory entries: 217 country-wide grids and
111
+ 22 countries with 530 province grids. Thirteen additional Natural Earth source
112
+ areas are exported separately and are outside address generation.
113
+
114
+ Coverage reflects the supplied Natural Earth snapshot, including historical
115
+ divisions and map conventions. See the exported `provinceDataNotes` and
116
+ [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md). The package does not infer or
117
+ certify sovereignty, current administrative membership, or postal deliverability.
package/PROTOCOL.md ADDED
@@ -0,0 +1,176 @@
1
+ # ZAP — Zukall Addressing Protocol, version 2
2
+
3
+ Protocol 2 uses **100-meter grid blocks** and **1-meter detail**, computed from
4
+ unrounded projected GPS coordinates. The npm package version (`0.2.1`) and
5
+ protocol version (`2`) are separate. Previously issued protocol-1 addresses retain
6
+ their original text and identity; see [PROTOCOL-V1.md](PROTOCOL-V1.md).
7
+
8
+ ## Display representation
9
+
10
+ ```text
11
+ <northSouthBlock> <N|S> <eastWestBlock> <E|W> #<northSouthRemainder>-<eastWestRemainder>, <city>, <provinceCode> <countryCode> ZAP
12
+ ```
13
+
14
+ City and province are optional display components. A province code is required
15
+ when the selected country has province grids. Omit unavailable components,
16
+ including their punctuation. The location tail uses single spaces, not commas:
17
+
18
+ ```text
19
+ 1194 S 382 E #16-20, Kampala, UG ZAP
20
+ 2106 S 3614 E #22-64, Miami, FL US ZAP
21
+ 580 S 770 E #43-46, HT ZAP
22
+ 0 N 0 E #03-06, UG ZAP
23
+ 0 S 0 W #00-00, UG ZAP
24
+ ```
25
+
26
+ These illustrate the encoding; city labels do not certify a point's location.
27
+ Blocks have an unrestricted number of decimal digits. Both remainders have
28
+ exactly two digits, including leading zeros. The first remainder is always
29
+ north/south; the second is east/west. Apartment, floor, unit and street labels
30
+ remain separate metadata and never replace or extend the coordinate suffix.
31
+
32
+ Country/province codes are trimmed, uppercased and checked against the bundled
33
+ snapshot. Source-specific province codes, including `NE_` keys and leading-zero
34
+ strings, retain their identities. City labels are trimmed and runs of whitespace
35
+ become one space; empty labels are omitted. Labels never change the grid origin.
36
+ Optional street labels receive the same normalization and are returned only as
37
+ separate display metadata, without entering the coordinate address text.
38
+
39
+ ## Select the grid
40
+
41
+ Inputs are finite WGS84 decimal degrees: latitude in `[-90, 90]`, longitude in
42
+ `[-180, 180]`, and a configured country code. Select that country's existing
43
+ configuration. If it has `provinces`, require an explicit configured province
44
+ code and use that province's corners. Otherwise, use the country corners and
45
+ reject a province code. Do not select a province by searching rectangles.
46
+
47
+ Corners remain ordered top-left, top-right, bottom-right, bottom-left and are
48
+ latitude/longitude-aligned. West greater than east denotes a date-line crossing.
49
+ The explicit west `-180`, east `180` rectangle covers all 360 degrees; other
50
+ supported spans are greater than zero and below 180 degrees. Edges are inclusive.
51
+ Rectangles may overlap. Containment does not establish administrative membership.
52
+
53
+ The fixed center is the midpoint of the north/south bounds and the midpoint of
54
+ the wrapped longitude span, with longitude normalized to `[-180, 180)`. All
55
+ configured corners, origins and country/province snapshot versions remain
56
+ unchanged from protocol 1.
57
+
58
+ ## Original projection
59
+
60
+ Use spherical azimuthal equidistant (`AEQD-SPHERE-v1`) with Earth radius
61
+ **6,371,008.8 meters**, centered on the selected grid. Preserve the original GPS
62
+ coordinates and full numerical precision throughout the calculation.
63
+
64
+ For point latitude `φ`, center latitude `φ₀`, and longitude difference `Δλ`, in
65
+ radians:
66
+
67
+ ```text
68
+ eastComponent = cos(φ) × sin(Δλ)
69
+ northComponent = cos(φ₀) × sin(φ) − sin(φ₀) × cos(φ) × cos(Δλ)
70
+ cosAngle = sin(φ₀) × sin(φ) + cos(φ₀) × cos(φ) × cos(Δλ)
71
+ sinAngle = hypot(eastComponent, northComponent)
72
+ angle = atan2(sinAngle, cosAngle)
73
+ scale = angle / sinAngle
74
+ northingMeters = radius × scale × northComponent
75
+ eastingMeters = radius × scale × eastComponent
76
+ ```
77
+
78
+ For `sinAngle < 1e-15`, use `scale = 1`. Reject points within `1e-10` radians of
79
+ the antipode because the projection is singular there. Normalize exact numerical
80
+ zero, including negative zero, to positive zero. Do not round these offsets to
81
+ the old 5-meter interval or adjust values near integer boundaries with epsilon.
82
+ The implementation retains JavaScript number precision.
83
+
84
+ Approximate rectangle width follows the parallel at the center latitude; height
85
+ follows a meridian. These dimensions alone are rounded to whole meters in the
86
+ result. The returned projected offsets retain their fractional values.
87
+
88
+ ## Encode each signed axis
89
+
90
+ For each unrounded signed offset `offset`:
91
+
92
+ ```text
93
+ direction = offset < 0 ? negativeDirection : positiveDirection
94
+ whole = floor(abs(offset))
95
+ block = floor(whole / 100)
96
+ remainder = whole % 100
97
+ ```
98
+
99
+ Northing uses negative `S`, positive `N`. Easting uses negative `W`, positive `E`.
100
+ Exact zero and negative zero use `N/E`. Direction comes from the original signed
101
+ offset, not from its floored magnitude: `-0.3` meters remains `S/W` with block
102
+ `0` and remainder `00`.
103
+
104
+ Pad each remainder to two decimal digits. Form the suffix as
105
+ `#<northSouthRemainder>-<eastWestRemainder>`. For northing `-119416.594` and easting
106
+ `38220.747`, the position text is `1194 S 382 E #16-20`.
107
+
108
+ An offset of magnitude `99.999` has block `0`, remainder `99`; magnitude `100`
109
+ has block `1`, remainder `00`. No nearest-meter or nearest-5-meter rounding occurs.
110
+
111
+ ## Roads and sequences
112
+
113
+ Axes remain fixed north/south and east/west in the selected projected grid. A
114
+ north/south sequence may hold easting constant; an east/west sequence may hold
115
+ northing constant. Diagonal and curved sequences may change both. Generate every
116
+ address independently from its GPS point; do not rotate grids or assign
117
+ sequential house numbers.
118
+
119
+ Moving exactly 100 projected meters away from the origin on one axis increments
120
+ only that axis's block and preserves both remainders and the other axis. Moving
121
+ toward the origin decrements its magnitude while on the same side; crossing the
122
+ origin can change direction. These are projected-plane distances, not a guarantee
123
+ of uniform ground scale throughout a large spherical projection.
124
+
125
+ ## Structured result and identity
126
+
127
+ Results include formatted address text, numeric blocks, directions, numeric
128
+ remainders (`0`–`99`), the padded `coordinateSuffix`, original coordinates,
129
+ country/province codes, optional city, grid name, center, country/grid snapshot
130
+ versions, raw signed offsets, approximate dimensions and encoding metadata:
131
+
132
+ ```text
133
+ protocol = ZAP
134
+ protocolVersion = 2
135
+ blockSizeMeters = 100
136
+ detailSizeMeters = 1
137
+ encoding = blocks-100m-detail-1m-floor-absolute-dashed-suffix-2x2-v1
138
+ gridId = ZAP:2:<country/province prefix>:<country version>:<grid version>:AEQD-SPHERE-v1:6371008.8:<center latitude>:<center longitude>:blocks-100m-detail-1m-floor-absolute-dashed-suffix-2x2-v1
139
+ ```
140
+
141
+ The encoding identifies 100-meter blocks, 1-meter detail, absolute-value flooring,
142
+ and two pairs of two digits separated by a dash. Numeric identity components use
143
+ JavaScript's canonical number-to-string representation. Treat `gridId` as opaque;
144
+ persist the complete result with the address and original GPS coordinates.
145
+
146
+ Display labels do not change `gridId`. When identifying a coordinate cell, use
147
+ `gridId` plus the structured blocks, directions and remainders, without city or
148
+ street labels.
149
+
150
+ ## Preserve previously issued addresses
151
+
152
+ Protocol-1 addresses used nearest-interval snapping, default 5 meters. Their text,
153
+ `protocolVersion`, original coordinates, interval and `gridId` stay attached to
154
+ the original record. Do not replace them merely because the package was upgraded.
155
+
156
+ `generateAddress` creates protocol-2 results and rejects the old `gridSizeMeters`
157
+ option at runtime and in TypeScript. `generateLegacyAddress` explicitly
158
+ reproduces protocol 1 using the frozen configurations and original interval.
159
+ Its result schema, address text and grid ID match the original generator.
160
+
161
+ If a user chooses to issue a protocol-2 address for the same GPS point, save a
162
+ new result and retain the prior record as historical data. No automatic
163
+ conversion, storage mutation or reverse decoding is performed by this package.
164
+ Future configuration changes must retain old snapshots under their old versions.
165
+
166
+ Directions always use saved GPS coordinates, never the formatted text, city,
167
+ unit, blocks, or a reprojected cell corner. `getDirectionsUrl` accepts either
168
+ version's saved record and builds a link from its original latitude/longitude.
169
+
170
+ ## Snapshot coverage
171
+
172
+ The immutable snapshot contains 239 country/territory entries: 217 country-wide
173
+ grids and 22 countries with 530 province grids, for 747 selectable grids.
174
+ Thirteen additional Natural Earth source areas are exposed separately and remain
175
+ outside address generation. Snapshot divisions can be historical; see
176
+ `provinceDataNotes` and [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).