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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions