geohash_bounds 0.1.0 copy "geohash_bounds: ^0.1.0" to clipboard
geohash_bounds: ^0.1.0 copied to clipboard

Pure-Dart geohash query bounds for Firestore spatial queries, with viewport-to-cell clustering and antimeridian handling.

geohash_bounds #

pub package CI

Hand-drawn globe pointing at a geohash

Pure-Dart geohash query bounds for spatial range queries — a port of the geofire-common npm package, plus viewport helpers for map-based apps.

Works with Firestore or any datastore that supports string range comparisons. Zero dependencies, no Flutter required.

Why this package? #

Most geo-query packages bundle a full Firestore streaming layer. This package only does the math: it hands you geohash range bounds and lets you build the queries yourself — with your own converters, caching, and pagination.

It also solves two problems the original geofire-common doesn't:

  • Viewport clustering: cellsForViewport enumerates a bounded number of coarse geohash cells covering the visible map, so at low zoom you can run one cheap aggregation count() per cell instead of downloading thousands of documents.
  • Antimeridian handling: viewportToCircle and wrapLongitude deal with date-line-crossing viewports and world-copy longitudes (e.g. lng: 200) that map SDKs like Mapbox and Google Maps produce.

Usage #

Store a geohash on each document alongside its coordinates:

import 'package:geohash_bounds/geohash_bounds.dart';

final geohash = GeohashUtil.encode(48.1351, 11.5820); // 'u281zd9z2h'

Radius query (Firestore example) #

final bounds = GeohashUtil.queryBounds(
  centerLat: 48.1351,
  centerLng: 11.5820,
  radiusInMeters: 5000,
);

final snapshots = await Future.wait(bounds.map((b) {
  return FirebaseFirestore.instance
      .collection('places')
      .orderBy('geohash')
      .startAt([b[0]])
      .endAt([b[1]])
      .get();
}));

// The bounds cover a bounding box, not an exact circle — filter false
// positives by distance:
final results = snapshots
    .expand((s) => s.docs)
    .where((doc) {
      final d = doc.data();
      return GeohashUtil.distanceBetween(
            48.1351, 11.5820, d['lat'], d['lng'],
          ) <=
          5000;
    })
    .toList();

Query the visible map area #

final circle = GeohashUtil.viewportToCircle(
  neLat: bounds.northeast.latitude,
  neLng: bounds.northeast.longitude,
  swLat: bounds.southwest.latitude,
  swLng: bounds.southwest.longitude,
);

final queryBounds = GeohashUtil.queryBounds(
  centerLat: circle.centerLat,
  centerLng: circle.centerLng,
  radiusInMeters: circle.radiusMeters,
);

Cheap clusters at low zoom #

When the user zooms out, fetching every document gets expensive. Instead, split the viewport into at most maxCells geohash cells and run one aggregation count per cell:

final cells = GeohashUtil.cellsForViewport(
  neLat: 55, neLng: 15,
  swLat: 45, swLng: 5,
  maxCells: 30,
);

for (final cell in cells) {
  final count = await FirebaseFirestore.instance
      .collection('places')
      .where('geohash', isGreaterThanOrEqualTo: cell.prefix)
      .where('geohash', isLessThanOrEqualTo: '${cell.prefix}~')
      .count()
      .get();
  // Place a cluster marker at (cell.centerLat, cell.centerLng)
  // showing count.count.
}

cellsForViewport picks the finest geohash precision whose grid still fits within maxCells, so the number of queries stays bounded at every zoom level.

API #

Method Description
encode(lat, lng, {precision}) Geohash string for a coordinate
queryBounds({centerLat, centerLng, radiusInMeters}) [start, end] hash pairs covering a circle
cellsForViewport({neLat, neLng, swLat, swLng, maxCells, maxPrefixLength}) Coarse cells covering a viewport, with cell centers
viewportToCircle({neLat, neLng, swLat, swLng}) Viewport corners → center + radius, antimeridian-safe
distanceBetween(lat1, lng1, lat2, lng2) Haversine distance in meters
wrapLongitude(lng) Normalize longitude into [-180, 180]

Additional information #

Battle-tested in Pin Your Plate, a food-mapping app querying pins worldwide. Issues and contributions welcome on GitHub.

1
likes
160
points
137
downloads

Documentation

API reference

Publisher

verified publisherpinyourplate.app

Weekly Downloads

Pure-Dart geohash query bounds for Firestore spatial queries, with viewport-to-cell clustering and antimeridian handling.

Repository (GitHub)
View/report issues

License

MIT (license)

More

Packages that depend on geohash_bounds