Capturing Lat/Long Address Info Without a Map

The new APEX 21.2 Geocoded Address page item makes it easy to translate user-entered address information into a point you (or your user) can see on a map. After the user-entered address information has been geocoded and the user has confirmed which matching address is correct, the value of the Geocoded Address page item is the text of a GeoJSON document representing the point on the map for that address.

Your application can decide whether the end-user will provide their address input in one of two ways:

  • A single “unstructured” page item containing street, city, and postal code info, or
  • Separate “structured” page items for street, city, and postal code

In either case, the country can be fixed by your application, or can be a value from a Pn_COUNTRY page item into which your user enters (or chooses) a country.

For example, entering the address in a single field might look like the screenshot below this for your end-users, separating the street, postal code, and city by a comma as they enter the address information. Of course, in your application the latitude and longitude fields will likely be configured to be hidden and be related to corresponding columns in the underlying table. Here in the sample app I’ve built, they are just transient page items to show off the geocoding result.

By default, submitting the form automatically triggers the geocoding, but you can also configure the geocoded address page item to be triggered by a Dynamic Action. After configuring this page item property, to complete the functionality you then need to add a dynamic action event handler — for example, a Click event handler for a button — that includes an action step of type Trigger Geocoding.

The geocoded address page item includes a map display of the point that the user has confirmed. If your underlying table has a column of type SDO_GEOMETRY then the GeoJSON value of the geocoded address will effortlessly be saved by the default APEX DML process for the form region. So, these defaults provide a no-code experience that caters to the most common situation.

However, what happens if you are working with a data model that requires storing the longitude and latitude of the point in separate LONGITUDE and LATITUDE columns? And what if you don’t want to immediately display the point on the map, but only capture the numerical coordinates. Then read on to discover how to accomplish these two tweaks to the default behavior.

We can accomplish both tasks in a completely declarative way, but we’ll take the second part first since it’s the simpler of the two. To hide the map, there are two steps:

  1. Set the application-level component settings for the Geocoded Address page item to disable the Map Preview
  2. Set the AppearanceCSS Classes property on the item so that even the read-only display field is hidden.

The Geocoded Address component exposes some application-level configuration properties that you can set in the Shared ComponentsComponent SettingsGeocoded Address section of the APEX builder. In the screenshot below, you can see I’ve unchecked the Item checkbox in the Map Preview section. This ensures that not only will the map be visually hidden, but also that browser will avoid retrieving any map tile images. Also notice I’ve configured the Geocoder Match Mode to the value Relax All to give the most chance of matching the address on all pieces of information the user supplies. By clicking on the help icon next to this field, you can see the description of the different behavior the various values imply.

For the second step, just set the AppearanceCSS Classes property for the Geocoded Address page item to:

u-VisuallyHidden

See Universal Theme utility classes for more information on classes like this one and others.

Next let’s implement the assignment of the longitude and latitude values from the geocoded address. The value of the geocoded address page item will be the GeoJSON point information about the geocoded address. It will look something like this (with indenting added by me to improve readability):

{
  "type": "Point", 
  "coordinates": [
    -122.1173, 
      37.8923
  ]
}

This is the information that will be saved into the underlying SDO_GEOMETRY column if your table has that. However, here we’re considering the situation when the table has separate LONGITUDE and LATITUDE columns instead of an SDO_GEOMETRY column.

Luckily we won’t need to process this JSON document ourselves because APEX provides the dynamic action event Result Selection [Geocoded Address] that fires when the user confirms which of possibly-multiple matching addresses is the one they want to use. We can handle this event with a dynamic action event handler, whose action steps can assign the longitude and latitude without writing code.

When the event fires, its event object contains a data property that contains all of the information about the geocoded address. This information includes the latitude and longitude values that we need, along with a number of other interesting pieces of information like the following (discovered using the browser’s developer tools console):

data: {
  "country"         : "IT"
  "edgeId"          : 55331351
  "houseNumber"     : "123"
  "language"        : "ITA"
  "latitude"        : 45.40329
  "longitude"       : 11.87561
  "matchCode"       : 1
  "matchVector"     : "??010101010??000?"
  "matchVectorScore": 100
  "municipality"    : "Padova"
  "name"            : ""
  "percent"         : 0.17
  "postalCode"      : "35122"
  "region"          : "PADOVA"
  "sequence"        : 0
  "settlement"      : "Padova"
  "side"            : "R"
  "street"          : "Via Roma"
}

So, assigning the P1_LONGITUDE and P1_LATITUDE page items can be done by creating a dynamic action event handler for the Result Selection [Geocoded Address] event using two separate Set Value action steps configured as follows:

  • “True” Action Step 1
    • Action: Set Value
    • Settings > Set Type: JavaScript Expression
    • Settings > JavaScript Expression: this.data.longitude
    • Affected Elements > Selection Type: Items(s)
    • Affected Elements > Items(s): P1_LONGITUDE
    • Execution Options > Fire on Initialization: False
  • “True” Action Step 2
    • Action: Set Value
    • Settings > Set Type: JavaScript Expression
    • Settings > JavaScript Expression: this.data.latitude
    • Affected Elements > Selection Type: Items(s)
    • Affected Elements > Items(s): P1_LATITUDE
    • Execution Options > Fire on Initialization: False

The figure below shows the APEX Page Designer showing the properties of the second declarative dynamic action step for the structured Geocoded Address page item in the example app that is assigning the value of the latitude to the P1_LATITUDE page item.

Action step for structured Geocoded Address assigns latitude to P1_LATITUDE

Thanks to members of the APEX community on Twitter like @daust_de and @andremlde for suggesting several improvements that made this article better since my original published version.

You can download this sample application from here.