Skip to content

Replace TGeocoder with REST-based reverse geocoding for cross-platform support #10

@jimmckeeth

Description

@jimmckeeth

Overview

The training code uses Delphi's TGeocoder component for reverse geocoding (converting GPS coordinates to a human-readable address). TGeocoder is documented as not available on Android, making the feature iOS-only. A REST-based approach using the OpenStreetMap Nominatim API (free, no API key required) provides consistent behavior on both platforms and is a better teaching example for HTTP client usage.

Background

Current Approach

{$IFNDEF ANDROID}
  TGeocoder.Current.GeocodeReverse(Coord, OnGeocodeReverse);
{$ELSE}
  lblAddress.Text := 'Geocoding not available on Android';
{$ENDIF}

TGeocoder wraps the native platform geocoder:

  • iOS: CLGeocoder — works well, but is rate-limited and requires an active internet connection routed through Apple's servers.
  • Android: Not implemented in Delphi's FMX layer (TGeocoder.Current returns nil).

Proposed Replacement

Use the OpenStreetMap Nominatim reverse geocoding REST API:

GET https://nominatim.openstreetmap.org/reverse?format=json&lat={lat}&lon={lon}
  • Free, no API key
  • Returns JSON with full address breakdown
  • Works identically on iOS and Android
  • Uses TNetHTTPClient (already in the project)
  • Good training example for JSON parsing with System.JSON

The call should be made asynchronously via TTask.Run to avoid blocking the UI thread.

Files Affected

lab-src/Lab04.../frames/uEntryDetailsFrame.pas
lab-src/Lab05.../frames/uEntryDetailsFrame.pas
... (all labs with entry details, Lab04–Lab12)
lab-src/Lab*/forms/formMain.pas  (if geocoder is wired there)

Steps to Address

  1. Remove all {$IFNDEF ANDROID} / {$ENDIF} blocks around TGeocoder calls.
  2. Remove System.Sensors geocoder-related imports (TGeocoder, TCivicAddress).
  3. Implement an async reverse geocoding function:
    procedure ReverseGeocode(Lat, Lon: Double; Callback: TProc<string>);
    begin
      TTask.Run(procedure
      var
        HTTP: THTTPClient;
        URL, Address: string;
        JSON: TJSONObject;
      begin
        HTTP := THTTPClient.Create;
        try
          URL := Format('https://nominatim.openstreetmap.org/reverse?format=json&lat=%g&lon=%g', [Lat, Lon]);
          HTTP.CustomHeaders['User-Agent'] := 'FieldLogger-Training/1.0';
          var Resp := HTTP.Get(URL);
          JSON := TJSONObject.ParseJSONValue(Resp.ContentAsString) as TJSONObject;
          try
            Address := JSON.GetValue<string>('display_name', 'Address unavailable');
          finally
            JSON.Free;
          end;
        finally
          HTTP.Free;
        end;
        TThread.Synchronize(nil, procedure begin Callback(Address) end);
      end);
    end;
  4. Call this function from uEntryDetailsFrame.pas when coordinates are available.
  5. Display a "Looking up address…" placeholder while the async call is in progress.
  6. Handle the no-internet case gracefully (catch exception, show "Address unavailable").
  7. Note the Nominatim usage policy in lab instructions (max 1 req/sec, must include User-Agent).

Test Plan

  • On Android 13 emulator with location simulated: address label populates with a street address from Nominatim.
  • On iOS 17 simulator with simulated location: same behavior — no platform-conditional code required.
  • With airplane mode enabled: "Address unavailable" is shown; no crash or exception dialog.
  • The geocode request does not block the UI thread (spinner or placeholder shown while loading).
  • No {$IFDEF}/{$IFNDEF} blocks related to TGeocoder remain in the source.
  • The TGeocoder and TCivicAddress identifiers are no longer referenced anywhere in the codebase.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions