terriajs
Version:
Geospatial data visualization platform.
151 lines (140 loc) • 6.01 kB
JavaScript
;
/*global require*/
var VarType = require('../Map/VarType');
var AddressGeocoder = require('../Map/AddressGeocoder');
var BulkAddressGeocoderResult = require('../Map/BulkAddressGeocoderResult');
var DeveloperError = require('terriajs-cesium/Source/Core/DeveloperError');
var GnafApi = require('../Models/GnafApi');
var defined = require('terriajs-cesium/Source/Core/defined');
var JulianDate = require('terriajs-cesium/Source/Core/JulianDate');
/**
* Australia-only address converter, which uses the GNAF api to determine lat and long coordinates for a given address.
* If TableStructure has address column, adds two new columns: lat and long.
*
* @alias GnafAddressGeocoder
* @constructor
* @extends AddressGeocoder
*
*/
var GnafAddressGeocoder = function() {
AddressGeocoder.call(this);
};
/**
* Convert addresses in miniumum number of calls to the server. When the promise fulfills, the tableStructure has been
* updated with new lat and lon columns.
*
* @param {TableStructure} [tableStructure] A tableStructure that contains an address column.
* @param {CorsProxy} [corsProxy] Proxy for cross origin resource sharing
* @return {Promise} Promise that resolves to an BulkAddressGeocoderResult object.
*/
GnafAddressGeocoder.prototype.bulkConvertAddresses = function(tableStructure, corsProxy) {
this.startTime = JulianDate.now();
this.tableStructure = tableStructure;
if (!tableStructure.hasAddress) {
throw new DeveloperError('This tableStructure has no addresses!');
}
var addressesCol = tableStructure.columnsByType[VarType.ADDR][0];
if (addressesCol.length === 0) {
throw new DeveloperError('Even though the tableStructure reports it has an address column, '
+ 'it has no addresses!');
}
var suburbs = tableStructure.getColumnWithName("Suburb");
smooshColumn(addressesCol, suburbs);
var state = tableStructure.getColumnWithName("State");
smooshColumn(addressesCol, state);
var postCodeCol = tableStructure.getColumnWithName("Postcode");
smooshColumn(addressesCol, postCodeCol);
var gnafApi = new GnafApi(corsProxy);
var addressesPlusInd = prefilterAddresses(addressesCol.values);
this.skipIndices = addressesPlusInd.skipIndices;
this.addressesCol = addressesCol;
this.numberOfAddressesConverted = addressesPlusInd.addresses.length;
var that = this;
return gnafApi.bulkGeoCode(addressesPlusInd.addresses, undefined).then(function(info) {
var longValues = [];
var latValues = [];
var matchedAddresses = [];
var resultScores = [];
var missingAddresses = [];
var j = 0;
for (var i=0; i<that.addressesCol.values.length; i++) {
if (that.skipIndices.indexOf(i) !== -1
|| !defined(info[j])
|| !defined(info[j].location)
|| isNaN(info[j].location.longitude)
|| isNaN(info[j].location.latitude)) {
if (that.addressesCol.values[i] !== null) {
missingAddresses.push(that.addressesCol.values[i]);
}
longValues.push(null);
latValues.push(null);
resultScores.push(null);
matchedAddresses.push(null);
continue;
}
longValues.push(info[j].location.longitude);
latValues.push(info[j].location.latitude);
resultScores.push(info[j].score);
matchedAddresses.push(info[j].name);
j++;
}
that.tableStructure.addColumn("Matched Address", matchedAddresses);
that.tableStructure.addColumn("Lon", longValues);
that.tableStructure.addColumn("Lat", latValues);
that.tableStructure.addColumn("Score", resultScores);
var addressGeocoderData = new BulkAddressGeocoderResult(that.startTime,
that.numberOfAddressesConverted,
addressesPlusInd.nullAddresses,
missingAddresses);
return addressGeocoderData;
});
};
/**
* Add info to address, comma separated.
* @param {TableColumn} [addressesCol] Table column with addresses to be added to
* @param {TableColumn} [newCol] Table column with extra info to be added to addresses column
*
* @private
*/
function smooshColumn(addressesCol, newCol) {
if (!defined(newCol)) {
return;
}
if (newCol.values.length === addressesCol.values.length) {
for (var i=0; i<addressesCol.values.length; i++) {
if (addressesCol.values[i] !== null && newCol.values[i] !== null) {
addressesCol.values[i] = addressesCol.values[i] + " " + newCol.values[i];
}
}
}
}
/**
* Do not try to geocode addresses that don't look valid.
*
* @param {Array} addressList List of addresses that will be considered for geocoding
* @return {Object} Probably shorter list of addresses that should be geocoded, as well as indices of addresses that
* were removed.
* @private
*/
function prefilterAddresses(addressList) {
var addressesPlusInd = {skipIndices: [],
nullAddresses: 0,
addresses: []};
for (var i=0; i<addressList.length; i++)
{
var address = addressList[i];
if (address === null) {
addressesPlusInd.skipIndices.push(i);
addressesPlusInd.nullAddresses++;
continue;
}
if (address.toLowerCase().indexOf("po box") !== -1 ||
address.toLowerCase().indexOf("post office box") !== -1) {
addressesPlusInd.skipIndices.push(i);
continue;
}
addressesPlusInd.addresses.push(address);
}
return addressesPlusInd;
}
module.exports = GnafAddressGeocoder;