feat(block2): geohash encoder in commons_core (for 'use my location')

Pure Dart lat/lon -> low-precision geohash (public-domain algorithm), the piece
the coarse-area capture needs so people never type a code. Precision-controlled
(5 chars ~ ±2.4 km). Tested against the canonical u4pruydqqvj vector + prefix/
proximity properties. 5 tests green.
This commit is contained in:
vjrj 2026-07-10 03:18:30 +02:00
parent 98110a6474
commit cb5f55e146
3 changed files with 92 additions and 0 deletions

View file

@ -0,0 +1,56 @@
/// Geohash encoding (public-domain algorithm), pure Dart. Turns a latitude /
/// longitude into a short base-32 string whose length sets its precision the
/// coarse "near you" area used by offers. Kept deliberately low-precision at the
/// call site so an exact position never leaves the device.
class Geohash {
const Geohash._();
static const _base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
/// Encodes [latitude]/[longitude] to a geohash of [precision] characters.
/// Precision cell size: 5 ±2.4 km, 4 ±20 km, 3 ±78 km.
static String encode(
double latitude,
double longitude, {
int precision = 5,
}) {
if (precision < 1) throw ArgumentError.value(precision, 'precision');
var latMin = -90.0, latMax = 90.0;
var lonMin = -180.0, lonMax = 180.0;
final out = StringBuffer();
var bit = 0;
var ch = 0;
var even = true; // start with longitude
while (out.length < precision) {
if (even) {
final mid = (lonMin + lonMax) / 2;
if (longitude >= mid) {
ch = (ch << 1) | 1;
lonMin = mid;
} else {
ch = ch << 1;
lonMax = mid;
}
} else {
final mid = (latMin + latMax) / 2;
if (latitude >= mid) {
ch = (ch << 1) | 1;
latMin = mid;
} else {
ch = ch << 1;
latMax = mid;
}
}
even = !even;
if (bit < 4) {
bit++;
} else {
out.write(_base32[ch]);
bit = 0;
ch = 0;
}
}
return out.toString();
}
}