Postal Code Validation

Address validation is probably the most commonly used case next to searching for an address; in most cases it can even be the same thing. This is because address validation is usually accomplished by search operators. For example to validate a non-normalised text address such as ‘L. Kifisias 97 Athens’ a search operation using this text as an input can be used. The output of such an operation would map the textual address to a a normalised address with address components containing information such as the street name, street number and any appropriate administrative levels.

Address Validation vs Address searching

Nevertheless, in the general case address validation and address searching is not the exact same thing, especially when it comes to certain address components or if the input that does not match to a point address.

Note

Point addresses are addresses with a unique geographical location, usually represented by a certain street number. For example ‘L. Kifisias 97 Athens’ matches to a point address, while ‘L. Kifisias Athens’ does not. In the later case the result is a set of street centroids. These centroids are produced by dissolving the results based on street information.

When searching for an address the client is expected to get one or more results that meet the input requirements. Using this set of results the client can suggest a number of options to the user and validate user’s input. However this set of results may not be complete if the input does not match to a point address, because in such cases the results are street centroids produced by dissolving the street based on street information. These centroids do not contain all the available street information, leaving out important details that could help clients suggest accurate information to the user.

Let’s present an example to explain the case.

Postal Code

One special kind of address validation is Postal Code validation. Postal code is very important because it can greatly assist in converting an address to an official one.

Postal code validation is usually trivial when entering a point address. For example geocoding will return 3 point addresses when searching for ‘Κηφισίας 97’:

  • ‘Κηφισίας 97 Δ. ΑΜΑΡΟΥΣΙΟΥ 15122’
  • ‘Λεωφόρος Κηφισίας 97 Δ. ΑΜΑΡΟΥΣΙΟΥ 15124’
  • ‘Λεωφόρος Κηφισίας 97 Δ. ΑΘΗΝΑΙΩΝ 11523’

In this case the result set is complete, none of the results is a street centroid and no important details have been excluded by some dissolving process.

On the other hand let’s examine what happens when the input query is ‘3ης Σεπτεμβρίου Δ. ΑΘΗΝΑΙΩΝ’. Geocoding this address returns the following road centroid results:

  • ‘3ης Σεπτεμβρίου Δ. ΑΘΗΝΑΙΩΝ 10432’
  • ‘3ης Σεπτεμβρίου Δ. ΑΘΗΝΑΙΩΝ 10434’

Map Server has found the street on the given municipality and has split the road in two parts based on certain street attributes (e.g. the type of the road and its geometry). Afterwards the server returns the information of the centroids of the two parts.

It would be wrong to assume that only 10432 and 10434 are valid postal codes for the given address. In fact there are 3 more postal codes that are equally valid. The only way to get any of those 3 postal codes is to let the user type the correct one and do a new geocode (and actually validate the input) or get all of them by using the special roadDissolve option of Geocode Address. We will focus on the later as this provides the best user experience.

Implementing Postal Code Validation

This example builds on top of previous ones by letting the user type an address using the typeahead control. Once searching or selecting a suggested address the client will list all valid postal codes for the first result.

Note

To understand typeahead and how to set it up see previous section typeahead.

Step 1: Including required scripts

This example uses both the mapperjs typeahead library and the mapperjs core library. Mapperjs typeahead usage is explained in previous sections. Mapperjs core is a utility library that contains the basic models to build a geocoding request.

Note

Both of the libraries are available at https://maps.geointelligence.gr/lib/mapperjs/mapperjs.typeahead.bundle.min.js and https://maps.geointelligence.gr/lib/mapperjs/mapperjs.core.min.js . If you own a GI Mapper Server instance then you can also find it on <path to your server>/lib/mapperjs/. That being said, we strongly recommend developers bundling and deploying the libraries in their web application.

    <script src="lib/jquery/dist/jquery.min.js"></script>
<script src="lib/mapperjs/mapperjs.typeahead.bundle.min.js"></script>
<script src="lib/mapperjs/mapperjs.core.js"></script>

Step 2: Setting up typeahead for address validation

Setting up the typeahead control is almost identical as in previous examples. The only difference is that in this case the validation process requires the address components to be available once the user selects an address. To make the typeahead control return the address components the developer has to use the fetchAddressComponents attribute during setup as shown below:

var typeaheadNoMap = new gimapperjs.typeahead.MapperJSTypeahead({
        hostElementSelector: '#gitypeaheadNoMap',
        apiKey: apiKey,
        host: baseHost,
        fetchAddressComponents: true
});

Step 3: Detecting selection

Mapperjs typeahead control exposes a couple of events to assist clients capture user interaction. One of them is gimapperjs.typeahead.mapperjs_selected triggered when a user selects an address result either after selecting a suggestion or after searching for an address.

The provided callback function will be called when the event is triggered taking as an argument the selected address result.

typeaheadNoMap.on(gimapperjs.typeahead.mapperjs_selected, function (res) { });

Step 4: Building the geocode request

As the typeahead control returns the address components (due to fetchAddressComponents attribute) the client can use these to build a geocode address request.

typeaheadNoMap.on(gimapperjs.typeahead.mapperjs_selected, function (res) {
        if (res && res.addressComponents) {
                var geocodeRequest = new gimapperjscore.geocode.GeocodeAddressRequest();
                let address = new gimapperjscore.geocode.GeocodeAddress();
                address.id = 1;
                geocodeRequest.Addresses.push(address);
                address.roadDissolve = gimapperjscore.geocode.DissolveMode.Zip;
                var i = 0;
                for (i = 0; i < res.addressComponents.length; i++) {
                        if (res.addressComponents[i].type == gimapperjs.maps.AddressComponentType.Street) {
                                address.addressName = res.addressComponents[i].name;
                                address.minScore = 1;
                                address.addressNameScoreWeight = 1;
                        }
                        else if (res.addressComponents[i].type == gimapperjs.maps.AddressComponentType.Region) {
                                address.addressRegion = res.addressComponents[i].name;
                                address.addressRegionMinScore = 1;
                                address.addressRegionScoreWeight = 1;
                        }
                        else if (res.addressComponents[i].type == gimapperjs.maps.AddressComponentType.Settlement) {
                                address.addressLocation = res.addressComponents[i].name;
                                address.addressLocationMinScore = 1;
                                address.addressLocationScoreWeight = 1;
                                address.addressLocationRestrictions = gimapperjscore.common.LocationType.Settlement;
                        }
                        else if (res.addressComponents[i].type == gimapperjs.maps.AddressComponentType.StreetNumber) {
                                address.addressNumber = res.addressComponents[i].name;
                        }
                        else if (res.addressComponents[i].type == gimapperjs.maps.AddressComponentType.PostalCode && res.formatted.includes(res.addressComponents[i].name)) {
                                $('#zips').html(res.addressComponents[i].name);
                                return;
                        }
                }
        }
});

In the previous script we iterate over the address components and build the appropriate Geocode address request. We circumvent the operation only if one of the address components is a postal code and this postal code is contained in the formatted address. In this case we have already validated the postal code.

Note

A postal code is returned as part of the formatted address only if the code was part of the input in the first place. In that case validation has already been completed by the search operation.

Step 5: Making the geocode request to the Core API

The last thing to do is making the call and getting all the valid postal codes. We use jquery to make the ajax call.

let settings = {
        url: baseHost + '/api/k/core/v2/geocodeAddress',
        headers: {
                'Content-Type': 'application/json',
                'Authorization-Token': apiKey,
                'Cache-Control': 'no-cache',
                'Pragma': 'no-cache'
        },
        type: 'POST',
        data: JSON.stringify(geocodeRequest)
};
$.ajax(settings).done(function (data) {
        let response = data;
        let zips = [];
        let zipsString = '';
        $('#zips').html(zipsString);
        if (response && response.geocodeAddressResults) {
                for (let res of response.geocodeAddressResults) {
                        for (let gres of res.results) {
                                if (gres.zip && !zips.includes(gres.zip) && gres.geocodingScore == 1) {
                                        zips.push(gres.zip);
                                        zipsString = zipsString + ' ' + gres.zip;
                                }
                        }
                }
        }
        $('#zips').html(zipsString);
}).fail(function () {
        $('#zips').html('error');
});

As the call uses address components returned by the typeahead control we know for sure that these components are accurate and accept only results with geocoding score of 1.

The view requires only a few tags:

<div class="form-group">
        <label class="control-label" for="gitypeaheadNoMap">Address:</label>
        <input id="gitypeaheadNoMap" type="text" placeholder="e.g. Λ Κηφισίας 97, Αθήνα"
                   class="form-control typeahead"
                   autocomplete="off" />
</div>
<div class="form-group">
        <label class="control-label" for="zips">Zip code validation for '<span id="selectedAddress">No address selected</span>'</label>
        <div>
                <p class="form-control-static" id="zips"></p>
        </div>

</div>