ubhub_maps 0.2.1 copy "ubhub_maps: ^0.2.1" to clipboard
ubhub_maps: ^0.2.1 copied to clipboard

Official Dart SDK for the UBHUB Geospatial API, with typed geocoding, reverse geocoding, routing, and secure API-key authentication.

UBHUB Dart SDK #

ubhub_maps is the official Dart client for the UBHUB public Geospatial API. Phase 1 provides geocoding, reverse geocoding, and routing. Despite the package name, it does not render maps or provide navigation UI or offline maps.

Documentation: https://developers.ubhub.mn Publisher: developers.ubhub.mn API version: v1 · Package version: 0.2.1 · License: MIT

The default API origin is https://developers.ubhub.mn. Its live OpenAPI contract was verified against the SDK on 2026-10-08. Authenticated live service verification is pending; unit tests use mocked responses. TLS verification is always enabled.

Installation #

dart pub add ubhub_maps

For Flutter:

flutter pub add ubhub_maps

Import the single supported entry point:

import 'package:ubhub_maps/ubhub_maps.dart';

Authentication #

final ubhub = UbHubGeo(apiKey: 'YOUR_API_KEY');

The client adds Authorization: Bearer <API_KEY> automatically. Empty keys and keys containing whitespace are rejected before a request is made. Create keys through the UBHUB developer console. Never commit keys to source control.

Keys embedded in Flutter, mobile, desktop, or browser applications are not secrets: applications can be inspected or reverse engineered. Use restricted keys, endpoint restrictions, quotas, and rate limits. Add application/package restrictions where the backend supports them; this SDK does not implement such restrictions itself. Use your own trusted backend for operations requiring a server-side secret. Environment variables help keep CLI/backend credentials out of source files; they do not make client-distributed credentials secret.

Geocoding #

import 'package:ubhub_maps/ubhub_maps.dart';

Future<void> main() async {
  final ubhub = UbHubGeo(apiKey: 'YOUR_API_KEY');
  try {
    final result = await ubhub.geocoding.search('Sukhbaatar Square', limit: 5);
    for (final place in result.features) {
      print('${place.name}: ${place.address}');
      print(place.location);
    }
  } on UbHubException catch (e) {
    print('${e.message} Request: ${e.requestId}');
  } finally {
    ubhub.close();
  }
}

search accepts a 2–200 character query and a limit from 1–20 (default 5). The server performs normalization. Only q and limit are transmitted. results and features refer to the same immutable list. An empty list is a successful no-match response. This is not a GeoJSON FeatureCollection.

Reverse geocoding #

import 'package:ubhub_maps/ubhub_maps.dart';

Future<void> main() async {
  final ubhub = UbHubGeo(apiKey: 'YOUR_API_KEY');
  try {
    final result = await ubhub.geocoding.reverse(
      location: const GeoPoint(latitude: 47.9184, longitude: 106.9177),
    );
    for (final place in result.results) {
      print('${place.address ?? place.name}: ${place.distanceMeters} metres');
    }
  } on UbHubException catch (e) {
    print('${e.message} Request: ${e.requestId}');
  } finally {
    ubhub.close();
  }
}

Reverse lookups use GET /v1/geocode with lat and lon. The current API has no /reverse-geocode endpoint. Nullable address/distance fields remain nullable; the SDK does not fabricate a match.

Routing #

import 'package:ubhub_maps/ubhub_maps.dart';

Future<void> main() async {
  final ubhub = UbHubGeo(apiKey: 'YOUR_API_KEY');
  try {
    final result = await ubhub.routing.route(
      origin: const GeoPoint(latitude: 47.9184, longitude: 106.9177),
      destination: const GeoPoint(latitude: 47.9220, longitude: 106.9057),
      mode: TravelMode.driving,
    );
    print('${result.route.distanceMeters} metres');
    print('${result.route.durationSeconds} seconds');
    print(result.route.geometry.toJson());
    print(result.waypoints.first.location);
  } on UbHubException catch (e) {
    print('${e.message} Request: ${e.requestId}');
  } finally {
    ubhub.close();
  }
}

Modes are TravelMode.driving (default) and TravelMode.walking. Geometry is a GeoJSON LineString; waypoints preserve submitted and snapped coordinates. There are no invented alternatives, instructions, profile, avoid, or language options. Flutter consumers can hide this package's Route type if they also import Flutter's navigation Route: import 'package:ubhub_maps/ubhub_maps.dart' hide Route;.

Coordinate order and models #

GeoPoint uses explicit latitude and longitude names. GeoJSON arrays and route wire parameters use longitude, latitude. Latitude must be finite and within ±90; longitude within ±180. Constant points are checked at service boundaries, or explicitly with point.validate(). Invalid values are never clamped or silently swapped.

The route backend accepts at most 15 fractional decimal places and no exponent notation. The SDK expands representable small values and rejects values that would require rounding. Model fromJson methods throw a safe FormatException for malformed data; requests convert these into ResponseException with HTTP context. toJson retains the public wire field names and omits HTTP metadata. New unknown response fields are ignored. No raw infrastructure data is exposed.

Error handling #

import 'package:ubhub_maps/ubhub_maps.dart';

Future<void> lookup(UbHubGeo ubhub) async {
  try {
    await ubhub.geocoding.search('Ulaanbaatar');
  } on RateLimitException catch (e) {
    print('Retry after ${e.retryAfter}; remaining ${e.remaining}');
    print(e.requestId);
  } on UbHubException catch (e) {
    print('${e.message} HTTP ${e.statusCode}, code ${e.code}');
    print(e.requestId);
  }
}
Failure Exception
400, 422 ValidationException
401 AuthenticationException
402 PaymentRequiredException
403 AuthorizationException
404 NotFoundException
408 or client deadline TimeoutException
429 RateLimitException
500–599, including 504 ServerException
Other non-200 status, including redirects HttpException
Transport failure NetworkException
Malformed/oversized success response ResponseException

Invalid local configuration/arguments throw ArgumentError; using a closed client throws StateError. SDK messages are safe local descriptions; raw server messages, details, bodies, headers, and transport exceptions are deliberately not included. Known public API error codes are preserved. Request IDs come from X-Request-ID, falling back to the error envelope. Invalid or credential- containing IDs are discarded. Nothing retries automatically, including 429.

Successful results expose .context.requestId, .context.rateLimit, .context.rateLimitRemaining, .context.rateLimitReset (Unix seconds), and .context.retryAfter. Rate-limit exceptions expose .retryAfter, .limit, .remaining, and .reset. Retry-After accepts integer seconds and IMF-fixdate HTTP dates. Missing or malformed headers remain null. No pagination or attribution schema exists in the current geocoding/routing contract.

Configuration #

final ubhub = UbHubGeo(
  apiKey: 'YOUR_API_KEY',
  baseUrl: 'https://developers.ubhub.mn',
  timeout: const Duration(seconds: 10),
  coalesceRequests: true, // Share identical requests while they are in flight.
  debug: true,
  logger: (message) { /* Forward safe diagnostics to your application logger. */ },
);
  • The default endpoint is centralized in ApiClientConfig.defaultBaseUrl.
  • Base URL overrides are advanced configuration. Only trusted HTTPS origins are accepted, optionally with a port and trailing slashes. Paths, credentials, queries, and fragments are rejected. Your API key will be sent to that origin; an override is not a proxy target or a way to bypass backend access policies.
  • Version v1 is centralized and appended automatically. Do not add /v1 to baseUrl.
  • The positive timeout (default 10 seconds) covers sending and response body reading. Timeouts abort supported transports without closing other requests.
  • Responses are buffered up to 8 MiB, then rejected and aborted.
  • Debug logging requires both debug: true and a callback. It includes only method, fixed endpoint path, status, and elapsed time—no queries, keys, response bodies, or authorization headers. Nothing prints by default.
  • Redirects are rejected, never followed. TLS validation cannot be disabled.
  • Reuse the client and call synchronous, idempotent close() when finished.
  • You may inject an http.Client using httpClient: for tests/custom transport. It remains caller-owned. A custom transport must honor redirect/TLS/abort semantics; keep it trusted. Closing the SDK cancels its own pending requests and prevents new requests, but does not close an injected client.

Keep one UbHubGeo client for a feature or application lifetime to reuse connections. Identical concurrent operations on that client automatically share one HTTP request and one immutable result. They share the original deadline, request ID, and rate-limit snapshot. Debug logging runs once per HTTP request. Different queries, limits, modes, or client instances stay independent. Set coalesceRequests: false when each caller needs its own HTTP request.

There is no completed-response cache: once a request finishes or fails, a later call goes back to the API. There is no cross-client credential or result sharing. Closing a client aborts requests shared by its callers.

For a search-as-you-type UI, debounce input in the app and ignore responses for outdated text. Different queries are not cancelled or debounced by the SDK. Run independent operations concurrently with Future.wait when appropriate, but keep concurrency within your plan's rate limits. Client optimizations cannot remove server processing time or network latency.

Response buffering uses typed bytes and fused UTF-8/JSON decoding. JSON/model parsing is still synchronous in the calling isolate; very large responses can still affect a UI frame. See PERFORMANCE.md in the repository for the local benchmark, measurement limits, and reproduction commands.

Supported platforms #

Dart 3.6 or newer with package:http, without Flutter dependencies or global state. Supports Dart CLI/backend programs and Flutter on mobile, desktop, and web. Create clients per isolate; do not share live transports across isolates. Browser requests remain subject to the API's CORS policy. Mobile/desktop apps must enable normal platform networking permissions. The example uses dart:io for a CLI; the library itself is browser-compatible.

Example and tests #

Run dart run example/main.dart with UBHUB_API_KEY in the process environment. Optionally set UBHUB_BASE_URL to a trusted staging origin. The example demonstrates all three services, error handling, and cleanup.

dart pub get
dart format .
dart analyze
dart test
dart pub publish --dry-run

Unit tests use mocks and never require production access. Opt-in staging tests live in integration_test/; see RELEASING.md in the repository. The package follows semantic versioning; 0.x allows iteration before a stable 1.0 contract.

License #

MIT, copyright 2026 UBHUB. See LICENSE.

0
likes
0
points
581
downloads

Documentation

Documentation

Publisher

verified publisherdevelopers.ubhub.mn

Weekly Downloads

Official Dart SDK for the UBHUB Geospatial API, with typed geocoding, reverse geocoding, routing, and secure API-key authentication.

Homepage
Repository (GitHub)
View/report issues

Topics

#geocoding #routing #geospatial

License

unknown (license)

Dependencies

http

More

Packages that depend on ubhub_maps