Mobile Tutorial: Using Location Sensors (iOS and Android)

From RAD Studio
Jump to: navigation, search

Go Up to Mobile Tutorials: Mobile Application Development (iOS and Android)


Before starting this tutorial, you should read and perform the following tutorial sessions:

Note: On Android devices, TLocationSensor requires specific Uses Permissions to be set, specifically Access coarse location and Access fine location.

This tutorial describes the basic steps to locate your mobile device (using latitude and longitude), and to use Reverse Geocoding to convert to a readable address, such as in the following picture:


IOSLocationDemo.PNG

Design the User Interface

This demo application is designed with two major components: a TListBox (on the left-hand side) and a TWebBrowser.

Note: Before proceeding with this scenario, in the Projects Window, set the active target platform to iOS Device or Android. Otherwise, you cannot add the TWebBrowser component.

DesignLocationDemo.png

Drop a TListBox and a TWebBrowser component on the form.

  • In the TListBox, set the Align property to Left to reserve the left side of the UI. Then create the following subcomponents under the ListBox:
    • A TListBoxHeader component with the following sub-components:
      • A TLabel component with the text "Location Demo"
      • A TSwitch (Switch1) component to select on/off of TLocationSensor
    • A TListBoxGroupHeader with the text "Your Location"
    • A TListBoxItem with the name "ListBoxItemLatitude" and "Latitude" as text
    • A TListBoxItem with the name "ListBoxItemLongitude" and "Longitude" as text
    • A TListBoxGroupHeader with the text "Current Address"
    • A TListBoxItem with the name "ListBoxItemAdminArea" and "AdminArea" as text
    • A TListBoxItem with the name "ListBoxItemCountryCode" and "CountryCode" as text
    • A TListBoxItem with the name "ListBoxItemCountryName" and "CountryName" as text
    • A TListBoxItem with the name "ListBoxItemFeatureName" and "FeatureName" as text
    • A TListBoxItem with the name "ListBoxItemLocality" and "Locality" as text
    • A TListBoxItem with the name "ListBoxItemPostalCode" and "PostalCode" as text
    • A TListBoxItem with the name "ListBoxItemSubAdminArea" and "SubAdminArea" as text
    • A TListBoxItem with the name "ListBoxItemSubLocality" and "SubLocality" as text
    • A TListBoxItem with the name "ListBoxItemSubThoroughfare" and "SubThoroughfare" as text
    • A TListBoxItem with the name "ListBoxItemThoroughfare" and "Thoroughfare" as text
  • A TWebBrowser component (WebBrowser1) to show the Web Page (Google Maps). Set the Align property to Client.

After you create these components, select all TListBoxItem items and select listboxitemleftdetail in the StyleLookup property. This allows TListBoxItem to show both a label and detailed text.

The Location Sensor

The location sensor is wrapped by the TLocationSensor component.

TLocationSensor fires an OnLocationChanged event when the device detects movement. You can adjust the sensitivity of TLocationSensor using the Distance and Accuracy properties.

  • The Distance property specifies the minimum distance (in meters) by which the device must move in order to make the location sensor relocate the device and return new location information. For example, if you set Distance to "10", TLocationSensor fires an OnLocationChanged event when you move "10 meters".
  • The Accuracy property represents the level of precision (in meters) by which the sensor locates the device geographically, relative to the geographical point at which the device is actually located.
Tip: You should specify the lowest accuracy that works for your application; the higher the accuracy, the more time and power that the sensor requires to determine the location. The recommended values: Distance=0; Accuracy=0.

Read Location Information (Latitude, Longitude) from the LocationSensor Component

The TLocationSensor component needs to be activated for use. You can turn on/off TLocationSensor based on your input, such as a TSwitch component, or other Application events.

  1. Place a TLocationSensor component from the Tool Palette.
  2. On the Form Designer, select the TSwitch component.
  3. In the Object Inspector, in the Events tab double-click OnSwitch event.
  4. Add the following code to the OnSwitch event handler:

Delphi:

procedure TForm1.Switch1Switch(Sender: TObject);
begin
  LocationSensor1.Active := Switch1.IsChecked;
end;

C++:

void __fastcall TForm1::Switch1Switch(TObject *Sender)
{
        LocationSensor1->Active = Switch1->IsChecked;
}

As discussed earlier, TLocationSensor fires an OnLocationChanged event when you move the mobile device. You can show the current location (Latitude and Longitude) using parameters with an event handler.

  1. On the Form Designer, select the TLocationSensor.
  2. In the Object Inspector, in the Events tab double-click OnLocationChange event.
  3. Add the following code to the OnLocationChange event handler:

Delphi:

procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
  const OldLocation, NewLocation: TLocationCoord2D);
var
  LDecSeparator: String;
begin
  LDecSeparator := FormatSettings.DecimalSeparator;
  FormatSettings.DecimalSeparator := '.';
  // Show current location
  ListBoxItemLatitude.ItemData.Detail  := Format('%2.6f', [NewLocation.Latitude]);
  ListBoxItemLongitude.ItemData.Detail := Format('%2.6f', [NewLocation.Longitude]);
end;

C++:

void __fastcall TForm1::LocationSensor1LocationChanged(TObject *Sender, const TLocationCoord2D &OldLocation,
                  const TLocationCoord2D &NewLocation)
{
        char LDecSeparator = FormatSettings.DecimalSeparator;
        FormatSettings.DecimalSeparator = '.';
        // Show current location
        ListBoxItemLatitude->ItemData->Detail = ListBoxItemLatitude->ItemData->Detail.sprintf(L"%2.6f", NewLocation.Latitude);
        ListBoxItemLongitude->ItemData->Detail = ListBoxItemLongitude->ItemData->Detail.sprintf(L"%2.6f", NewLocation.Longitude);
}

Show the Current Location Using Google Maps via a TWebBrowser Component

As discussed in the Mobile Tutorial: Using the Web Browser Component (iOS and Android), the TWebBrowser component wraps a Web browser for mobile platforms.

You can call Google Maps from the TWebBrowser component with the following URL parameters:

 https://maps.google.com/maps?q=(Latitude-value),(Longitude-value)

So you can add this URL to your previously created event handler OnLocationChanged as follows:

Delphi:

procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
  const OldLocation, NewLocation: TLocationCoord2D);
var
  URLString: String;
begin
  // code for previous step goes here

  // Show Map using Google Maps
  URLString := Format(
    'https://maps.google.com/maps?q=%s,%s',
      [Format('%2.6f', [NewLocation.Latitude]), Format('%2.6f', [NewLocation.Longitude])]);
  WebBrowser1.Navigate(URLString);
end;

C++:

void __fastcall TForm1::LocationSensor1LocationChanged(TObject *Sender, const TLocationCoord2D &OldLocation,
                  const TLocationCoord2D &NewLocation)
{
        // code for previous step goes here

        // Show Map using Google Maps
        String LLongitude = FloatToStr(NewLocation.Longitude, FormatSettings);
        String URLString = "";
        URLString = URLString.sprintf(L"https://maps.google.com/maps?q=%2.6f,%2.6f",
                NewLocation.Latitude, NewLocation.Longitude);

        FormatSettings.DecimalSeparator = LDecSeparator;
        WebBrowser1->Navigate(URLString);
}

Use Reverse Geocoding

TGeocoder is an object which wraps the Geocoding (or Reverse Geocoding) service.

Geocoding is the process of transforming geographic data, such as the address and zip code, into geographic coordinates. Reverse geocoding is the process of transforming geographical coordinates into other geographical data, such as the address.

In this case, we use TGeocoder to "Reverse Geocode" our location (in Latitude and Longitude) to readable address information.

Here is the basic sequence of actions with TGeocoder:

  1. Create an instance of TGeocoder.
  2. Define an event OnGeocodeReverse so that you can receive the event later.
  3. Set data to execute "Reverse Geocoding".
  4. TGeocoder accesses the service on the network to resolve the address information.
  5. TGeocoder fires an OnGeocodeReverse event.
  6. Your iOS App receives the address information through the parameter on the OnGeocodeReverse event and updates the user interface.
Note: As TGeocoder is not a component (this is just a class), you need to define these steps through your code (you cannot drop a component, nor assign an event handler through the Object Inspector).

First, define a new field "FGeocoder" in the private section of the form. You can also define an "OnGeocodeReverseEvent procedure" as in the following code snippets.

Delphi:

type
  TForm1 = class(TForm)
    // IDE defines visible (or non-visual) components here automatically
  private
    { Private declarations }
    FGeocoder: TGeocoder;
    procedure OnGeocodeReverseEvent(const Address: TCivicAddress);
  public
    { Public declarations }
  end;

C++:

Note: Place this code snippet in the header file (.h)
class TForm1 : public TForm
{
        // IDE defines visible (or non-visual) components here automatically
private:        // User declarations
        TGeocoder *FGeocoder;
        void __fastcall OnGeocodeReverseEvent(TCivicAddress* const Address);
public:         // User declarations
        __fastcall TForm1(TComponent* Owner);
};


Now you can create an instance of TGeocoder and set it up with data with the following Delphi or C++ code.

TGeocoder.Current gives the type of class that actually implements the Geocoding Service. The code in "TGeocoder.Current.Create" calls the constructor (Create) for the specified type, and saves it to the FGeocoder field. You also need to specify an event handler, which is fired when TGeocoder completes Reverse Geocoding. Assign OnGeocodeReverseEvent (which you just defined in the previous step) to FGeocoder.OnGeocodeReverse.

Finally, if you successfully created an instance of TGeocoder, and TGeocoder is not running, call TGeocoder.GeocodeReverse with location information. After TGeocoder receives data, the OnGeocodeReverseEvent event is fired.

Delphi:

procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
  const OldLocation, NewLocation: TLocationCoord2D);
begin
  // code for previous steps goes here
try
    // Setup an instance of TGeocoder
    if not Assigned(FGeocoder) then
    begin
      if Assigned(TGeocoder.Current) then
        FGeocoder := TGeocoder.Current.Create;
      if Assigned(FGeocoder) then
        FGeocoder.OnGeocodeReverse := OnGeocodeReverseEvent;
    end;

    // Translate location to address
    if Assigned(FGeocoder) and not FGeocoder.Geocoding then
      FGeocoder.GeocodeReverse(NewLocation);
  except
    ListBoxGroupHeader1.Text := 'Geocoder service error';
  end;
end;

C++:

void __fastcall TForm1::LocationSensor1LocationChanged(TObject *Sender, const TLocationCoord2D &OldLocation,
                  const TLocationCoord2D &NewLocation)
{
        // code for previous steps goes here

                // Setup an instance of TGeocoder
        try {
        if (FGeocoder == NULL) {
            if (TGeocoder::Current != NULL) {
                FGeocoder = (TGeocoder*)new TGeocoderClass(TGeocoder::Current);
            }
            if (FGeocoder != NULL) {
                FGeocoder->OnGeocodeReverse = OnGeocodeReverseEvent;
            }
        }
        // Translate location to address

        if ((FGeocoder != NULL) && (!(FGeocoder->Geocoding()))) {
            FGeocoder->GeocodeReverse(NewLocation);
        }
    }
    catch (...) {
        ListBoxGroupHeader1->Text = "Geocoder service error";
    }

}

Show a Readable Address in the ListBox Component

As described earlier, after Reverse Geocoding is completed, an OnGeocodeReverseEvent is fired.

Next, assign properties in the TCivicAddress address parameter to show readable address information in the list box fields:

Delphi:

procedure TForm1.OnGeocodeReverseEvent(const Address: TCivicAddress);
begin
  ListBoxItemAdminArea.ItemData.Detail       := Address.AdminArea;
  ListBoxItemCountryCode.ItemData.Detail     := Address.CountryCode;
  ListBoxItemCountryName.ItemData.Detail     := Address.CountryName;
  ListBoxItemFeatureName.ItemData.Detail     := Address.FeatureName;
  ListBoxItemLocality.ItemData.Detail        := Address.Locality;
  ListBoxItemPostalCode.ItemData.Detail      := Address.PostalCode;
  ListBoxItemSubAdminArea.ItemData.Detail    := Address.SubAdminArea;
  ListBoxItemSubLocality.ItemData.Detail     := Address.SubLocality;
  ListBoxItemSubThoroughfare.ItemData.Detail := Address.SubThoroughfare;
  ListBoxItemThoroughfare.ItemData.Detail    := Address.Thoroughfare;
end;

C++:

void __fastcall TForm1::OnGeocodeReverseEvent(TCivicAddress* const Address)
{
          if (Address != NULL){
                ListBoxItemAdminArea->ItemData->Detail       = Address->AdminArea;
                ListBoxItemCountryCode->ItemData->Detail     = Address->CountryCode;
                ListBoxItemCountryName->ItemData->Detail     = Address->CountryName;
                ListBoxItemFeatureName->ItemData->Detail     = Address->FeatureName;
                ListBoxItemLocality->ItemData->Detail        = Address->Locality;
                ListBoxItemPostalCode->ItemData->Detail      = Address->PostalCode;
                ListBoxItemSubAdminArea->ItemData->Detail    = Address->SubAdminArea;
                ListBoxItemSubLocality->ItemData->Detail     = Address->SubLocality;
                ListBoxItemSubThoroughfare->ItemData->Detail = Address->SubThoroughfare;
                ListBoxItemThoroughfare->ItemData->Detail    = Address->Thoroughfare;
        }
}

Describing Why Your Application Needs the User Location

Before you deploy your final application you should select Project > Options > Version Info, with iOS Device as target, and update the values of NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription with a message that explains why your application is asking for the user location. Your users see this message when your application asks them to authorize iOS to provide the location of the iOS device to your application.

See Also

Samples