From 28a437f75f2b044740508a176076c707972f793e Mon Sep 17 00:00:00 2001 From: sufangl Date: Tue, 2 Jan 2024 08:16:03 +0800 Subject: [PATCH 01/35] 261043 to show enrichment elements for an address. --- index.html | 94 +++++++++++++++- src/ts/address-search.ts | 147 +++++++++++++++++++++--- src/ts/class-types.ts | 219 +++++++++++++++++++++++++++++++++++- src/ts/enrichment_output.ts | 212 ++++++++++++++++++++++++++++++++++ 4 files changed, 653 insertions(+), 19 deletions(-) create mode 100644 src/ts/enrichment_output.ts diff --git a/index.html b/index.html index f71d05d..c84f9fa 100644 --- a/index.html +++ b/index.html @@ -343,6 +343,9 @@

Validated address information

+ Download sample code @@ -468,11 +471,66 @@

Validated address information

// Display a map with the lat/long details after a data enrichment lookup address.events.on("post-enrichment", function(data) { if ((data.result.geocodes && data.result.geocodes.latitude) || - (data.result.what3words && data.result.what3words.latitude)) { + (data.result.nzl_regional_geocodes && data.result.nzl_regional_geocodes.latitude) || + (data.result.aus_regional_geocodes && data.result.aus_regional_geocodes.latitude) || + (data.result.usa_regional_geocodes && data.result.usa_regional_geocodes.latitude) || + (data.result.what3words && data.result.what3words.latitude)) { document.querySelector("#map").classList.remove("hidden"); + document.querySelector("#enrichment").classList.remove("hidden"); document.querySelector(".metadata #what3words-key").classList.add("hidden"); document.querySelector(".metadata #what3words-value").classList.add("hidden"); + // populate cv household attributes + if (address.cvHouseholdMap.size > 0) { + let enrichmentElement = document.querySelector("#enrichment"); + const cvHouseholdHeaderElement = document.createElement("h3"); + cvHouseholdHeaderElement.innerText = "Consumer View Household"; + enrichmentElement.append(cvHouseholdHeaderElement); + + let cvHouseholdMap = address.cvHouseholdMap; + cvHouseholdMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + htmlSpanElement.innerText = key + ": " + value; + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlSpanElement, htmlbrElement); + }); + + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlbrElement); + } + + // populate geocodes attributes + if (address.geocodesMap.size > 0) { + let enrichmentElement = document.querySelector("#enrichment"); + const geocodesHeaderElement = document.createElement("h3"); + geocodesHeaderElement.innerText = "Geocodes"; + enrichmentElement.append(geocodesHeaderElement); + + let geocodesMap = address.geocodesMap; + geocodesMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + htmlSpanElement.innerText = key + ": " + value; + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlSpanElement, htmlbrElement); + }); + } + + // populate premium location insight + if (address.premiumLocationInsightMap.size > 0) { + let enrichmentElement = document.querySelector("#enrichment"); + const premiumLocationInsightHeaderElement = document.createElement("h3"); + premiumLocationInsightHeaderElement.innerText = "Premium Location Insight"; + enrichmentElement.append(premiumLocationInsightHeaderElement); + + let premiumLocationInsightMap = address.premiumLocationInsightMap; + premiumLocationInsightMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + htmlSpanElement.innerText = key + ": " + value; + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlSpanElement, htmlbrElement); + }); + } + let w3wLat, w3wLong, w3wLatLong; if (data.result.what3words && data.result.what3words.latitude) { document.querySelector(".metadata #what3words-key").classList.remove("hidden"); @@ -491,6 +549,24 @@

Validated address information

geoLatLong = [geoLat, geoLong]; } + if (data.result.nzl_regional_geocodes && data.result.nzl_regional_geocodes.latitude) { + geoLat = data.result.nzl_regional_geocodes.latitude; + geoLong = data.result.nzl_regional_geocodes.longitude; + geoLatLong = [geoLat, geoLong]; + } + + if (data.result.aus_regional_geocodes && data.result.aus_regional_geocodes.latitude) { + geoLat = data.result.aus_regional_geocodes.latitude; + geoLong = data.result.aus_regional_geocodes.longitude; + geoLatLong = [geoLat, geoLong]; + } + + if (data.result.usa_regional_geocodes && data.result.usa_regional_geocodes.latitude) { + geoLat = data.result.usa_regional_geocodes.latitude; + geoLong = data.result.usa_regional_geocodes.longitude; + geoLatLong = [geoLat, geoLong]; + } + var zoom = 16; var attribution = '© OpenStreetMap contributors'; @@ -585,6 +661,22 @@

Validated address information

document.querySelector(".metadata #what3words-value").classList.add("hidden"); document.querySelector(".metadata").classList.add("invisible"); + // to remove all enrichment elements + const parent = document.getElementById("enrichment") + let spanElements = parent.getElementsByTagName("span"), index + let brElements = parent.getElementsByTagName("br"), brIndex + let subtitleElements = parent.getElementsByTagName("h3"), subTitleIndex + for (index = spanElements.length - 1; index >= 0; index--) { + spanElements[index].parentNode.removeChild(spanElements[index]); + } + for (brIndex = brElements.length - 1; brIndex >= 0; brIndex--) { + brElements[brIndex].parentNode.removeChild(brElements[brIndex]); + } + for (subTitleIndex = subtitleElements.length - 1; subTitleIndex >= 0; subTitleIndex--) { + subtitleElements[subTitleIndex].parentNode.removeChild(subtitleElements[subTitleIndex]); + } + + document.querySelector("#enrichment").classList.add("hidden"); document.querySelector("#map").classList.add("hidden"); } diff --git a/src/ts/address-search.ts b/src/ts/address-search.ts index 2f3cb28..a6282e5 100644 --- a/src/ts/address-search.ts +++ b/src/ts/address-search.ts @@ -1,9 +1,27 @@ import EventFactory from './event-factory'; import Request from './request'; -import { AddressSearchOptions, AddressValidationSearchType, AddressValidationMode, AddressValidationLookupKeywords, defaults } from './search-options'; -import { datasetCodes } from './datasets-codes'; -import { translations } from './translations'; -import { AddressValidationResult, LookupAddress, LookupV2Response, LookupW3WResponse, Picklist, PicklistItem, PoweredByLogo, SearchResponse, What3WordsPickList } from './class-types'; +import { + AddressSearchOptions, + AddressValidationLookupKeywords, + AddressValidationMode, + AddressValidationSearchType, + defaults +} from './search-options'; +import {datasetCodes} from './datasets-codes'; +import {translations} from './translations'; +import { + AddressValidationResult, + EnrichmentResponse, + LookupAddress, + LookupV2Response, + LookupW3WResponse, + Picklist, + PicklistItem, + PoweredByLogo, + SearchResponse, + What3WordsPickList +} from './class-types'; +import {enrichmentOutput, premiumLocationInsight} from "./enrichment_output"; export default class AddressValidation { public options: AddressSearchOptions; @@ -11,6 +29,9 @@ export default class AddressValidation { public avMode: AddressValidationMode; public events; public request: Request; + public geocodesMap = new Map(); + public cvHouseholdMap = new Map(); + public premiumLocationInsightMap = new Map(); private baseUrl = 'https://api.experianaperture.io/'; private searchEndpoint = 'address/search/v1'; @@ -55,19 +76,61 @@ export default class AddressValidation { public getEnrichmentData(globalAddressKey: string) { if (globalAddressKey) { - const data = { + let regionalAttributes = {}; + if (this.currentCountryCode == "NZL") { + regionalAttributes = { + nzl_regional_geocodes: Object.keys(enrichmentOutput.NZL.geocodes), + nzl_cv_household: Object.keys(enrichmentOutput.NZL.cv_household), + // todo: sufang to remove duplication + premium_location_insight: [ + "geocodes", + "geocodes_building_xy", + "geocodes_access", + "time" + ] + } + } else if (this.currentCountryCode == "AUS") { + regionalAttributes = { + aus_regional_geocodes: Object.keys(enrichmentOutput.AUS.geocodes), + aus_cv_household: Object.keys(enrichmentOutput.AUS.cv_household), + premium_location_insight: [ + "geocodes", + "geocodes_building_xy", + "geocodes_access", + "time" + ] + } + } else if (this.currentCountryCode == "USA") { + regionalAttributes = { + usa_regional_geocodes: Object.keys(enrichmentOutput.USA.geocodes), + premium_location_insight: [ + "geocodes", + "geocodes_building_xy", + "geocodes_access", + "time" + ] + } + } else { + regionalAttributes = { + geocodes: Object.keys(enrichmentOutput.GLOBAL.geocodes), + premium_location_insight: [ + "geocodes", + "geocodes_building_xy", + "geocodes_access", + "time" + ], + what3words: this.currentCountryCode == 'GBR' ? ['latitude', 'longitude', 'name', 'description'] : null + } + } + let data = { country_iso: this.currentCountryCode, keys: { global_address_key: globalAddressKey }, - attributes: { - geocodes: ['latitude', 'longitude', 'match_level'], - what3words: this.currentCountryCode == 'GBR' ? ['latitude', 'longitude', 'name', 'description'] : null - } - }; - + attributes: regionalAttributes + } this.events.trigger('pre-enrichment'); - this.request.send(this.baseUrl + this.enrichmentEndpoint, 'POST', this.handleEnrichmentResult.bind(this), JSON.stringify(data)); + this.request.send(this.baseUrl + this.enrichmentEndpoint, 'POST', this.result.handleEnrichmentResponse, JSON.stringify(data)); } } @@ -96,10 +159,6 @@ export default class AddressValidation { } } - private handleEnrichmentResult(response) { - this.events.trigger('post-enrichment', response); - } - private getParameter(name): string { name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp('[\\?&]' + name + '=([^&#]*)'), @@ -370,7 +429,7 @@ export default class AddressValidation { // (Re-)set the property stating whether the search input has been reset. // This is needed for instances when the search input is also an address - // output field. After an address has been returned, you don't want a new + // output field. After an address has been returned, you don't want a new // search being triggered until the field has been cleared. if (this.currentSearchTerm === '') { this.hasSearchInputBeenReset = true; @@ -1349,6 +1408,60 @@ export default class AddressValidation { // If there are no matches, then allow "use address entered" this.picklist.handleEmptyPicklist(response); } + }, + + handleEnrichmentResponse: (response: EnrichmentResponse) => { + this.geocodesMap.clear(); + this.cvHouseholdMap.clear(); + this.premiumLocationInsightMap.clear(); + + let geocodeResponse; + let geocodesExpectedAttributes; + let cvHouseholdResponse; + let cvHouseholdExpectedAttributes; + + if (response.result.aus_regional_geocodes) { + geocodeResponse = Object.entries(response.result.aus_regional_geocodes); + geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.AUS.geocodes)); + cvHouseholdResponse = Object.entries(response.result.aus_cv_household); + cvHouseholdExpectedAttributes = new Map(Object.entries(enrichmentOutput.AUS.cv_household)); + } else if (response.result.nzl_regional_geocodes) { + geocodeResponse = Object.entries(response.result.nzl_regional_geocodes); + geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.NZL.geocodes)); + cvHouseholdResponse = Object.entries(response.result.nzl_cv_household); + cvHouseholdExpectedAttributes = new Map(Object.entries(enrichmentOutput.NZL.cv_household)); + } else if (response.result.usa_regional_geocodes) { + geocodeResponse = Object.entries(response.result.usa_regional_geocodes); + geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.USA.geocodes)); + } else { + geocodeResponse = Object.entries(response.result.global_geocodes); + geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.GLOBAL.geocodes)); + } + + // todo: sufang to check response + if (response.result.premium_location_insight) { + let premiumLocationInsightResponse = Object.entries(response.result.premium_location_insight); + for (const [key, value] of premiumLocationInsightResponse) { + this.premiumLocationInsightMap.set(key, value); + } + } + + for (const [key, value] of geocodeResponse) { + if(!geocodesExpectedAttributes.has(key)){ + continue; + } + this.geocodesMap.set(geocodesExpectedAttributes.get(key), value); + } + + if (cvHouseholdResponse) { + for (const [key, value] of cvHouseholdResponse) { + if (!cvHouseholdExpectedAttributes.has(key)) { + continue; + } + this.cvHouseholdMap.set(cvHouseholdExpectedAttributes.get(key), value); + } + } + this.events.trigger('post-enrichment', response); } }; diff --git a/src/ts/class-types.ts b/src/ts/class-types.ts index 9799e04..62423b7 100644 --- a/src/ts/class-types.ts +++ b/src/ts/class-types.ts @@ -48,6 +48,7 @@ export class AddressValidationResult { createSearchAgainLink: () => void; renderInputList: (inputArray) => void; handleValidateResponse: (response: SearchResponse) => void; + handleEnrichmentResponse: (response: EnrichmentResponse) => void; } class CreateAddressLine { @@ -103,7 +104,223 @@ export interface PicklistItem { global_address_key?: string; additional_attributes?: { name: string, Value: string }[]; } - +export interface EnrichmentResponse { + result?: { + aus_regional_geocodes?: AUSRegionalGeocodes; + aus_cv_household?: AUSCVHousehold; + nzl_regional_geocodes?: NZLRegionalGeocodes; + nzl_cv_household?: NZLCVHousehold; + usa_regional_geocodes?: USARegionalGeocodes; + global_geocodes?: Geocodes; + premium_location_insight?: PremiumLocationInsight; + }; +} +export interface AUSRegionalGeocodes { + latitude?: number; + longitude?: number; + match_level?: string; + sa1?: string; + meshblock?: string; + lga_code?: string; + lga_name?: string; + street_pid?: string; + locality_pid?: string; + geocode_level_code?: string; + geocode_level_description?: string; + geocode_type_code?: string; + geocode_type_description?: string; + highest_level_longitude?: number; + highest_level_latitude?: number; + highest_level_elevation?: string; + highest_level_planimetric_accuracy?: string; + highest_level_boundary_extent?: string; + highest_level_geocode_reliability_code?: string; + highest_level_geocode_reliability_description?: string; + confidence_level_code?: string; + confidence_level_description?: string; + "2021_meshblock_id"?: string; + "2021_meshblock_code"?: string; + "2021_meshblock_match_code"?: string; + "2021_meshblock_match_description"?: string; + "2016_meshblock_id"?: string; + "2016_meshblock_code"?: string; + "2016_meshblock_match_code"?: string; + "2016_meshblock_match_description"?: string; + address_type_code?: string; + primary_address_pid?: string; + address_join_type?: string; + collector_district_id?: string; + collector_district_code?: string; + commonwealth_electoral_boundary_id?: string; + commonwealth_electoral_boundary_name?: string; + statistical_local_area_id?: string; + statistical_local_area_code?: string; + statistical_local_area_name?: string; + state_electoral_boundary_id?: string; + state_electoral_boundary_name?: string; + state_electoral_effective_start?: string; + state_electoral_effective_end?: string; + state_electoral_new_pid?: string; + state_electoral_new_name?: string; + state_electoral_new_effective_start?: string; + state_electoral_new_effective_end?: string; + address_level_longitude?: number; + address_level_latitude?: number; + address_level_elevation?: string; + address_level_planimetric_accuracy?: string; + address_level_boundary_extent?: string; + address_level_geocode_reliability_code?: string; + address_level_geocode_reliability_description?: string; + street_level_longitude?: number; + street_level_latitude?: number; + street_level_planimetric_accuracy?: string; + street_level_boundary_extent?: string; + street_level_geocode_reliability_code?: string; + street_level_geocode_reliability_description?: string; + locality_level_longitude?: number; + locality_level_latitude?: number; + locality_level_planimetric_accuracy?: string; + locality_level_geocode_reliability_code?: string; + locality_level_geocode_reliability_description?: string; + gnaf_legal_parcel_identifier?: string; + locality_class_code?: string; +} +export interface AUSCVHousehold { + address?: string; + adults_at_address_code?: string; + adults_at_address_description?: string; + affluence_code?: string; + affluence_description?: string; + channel_preference?: string; + channel_preference_description?: string; + children_at_address_code_0_10_years?: string; + children_at_address_code_11_18_years?: string; + children_at_address_description_0_10_years?: string; + children_at_address_description_11_18_years?: string; + credit_demand_code?: string; + credit_demand_description?: string; + gnaf_latitude?: number; + gnaf_longitude?: number; + gnaf_pid?: string; + head_of_household_age_code?: string; + head_of_household_age_description?: string; + hin?: string; + household_composition_code?: string; + household_composition_description?: string; + household_income_code?: string; + household_income_description?: string; + length_of_residence_code?: string; + length_of_residence_description?: string; + lifestage_code?: string; + lifestage_description?: string; + local_government_area_code?: string; + local_government_area_name?: string; + meshblock?: string; + mosaic_group?: string; + mosaic_segment?: string; + mosaic_type?: string; + postcode?: string; + residential_flag?: string; + risk_insight_code?: string; + risk_insight_description?: string; + sa1?: string; + state?: string; + suburb?: string; + mosaic_factor1_percentile?: string; + mosaic_factor1_score?: string; + mosaic_factor2_percentile?: string; + mosaic_factor2_score?: string; + mosaic_factor3_percentile?: string; + mosaic_factor3_score?: string; + mosaic_factor4_percentile?: string; + mosaic_factor4_score?: string; + mosaic_factor5_percentile?: string; + mosaic_factor5_score?: string; +} +export interface NZLRegionalGeocodes { + front_of_property_nztm_x_coordinate?: number; + front_of_property_nztm_y_coordinate?: number; + centroid_of_property_nztm_x_coordinate?: number; + centroid_of_property_nztm_y_coordinate?: number; + front_of_property_latitude?: number; + front_of_property_longitude?: number; + centroid_of_property_latitude?: number; + centroid_of_property_longitude?: number; + linz_parcel_id?: string; + property_purpose_type?: string; + addressable?: string; + mesh_block_code?: string; + territorial_authority_code?: string; + territorial_authority_name?: string; + regional_council_code?: string; + regional_council_name?: string; + general_electorate_code?: string; + general_electorate_name?: string; + maori_electorate_code?: string; + maori_electorate_name?: string; + match_level?: string; +} +export interface NZLCVHousehold { + adults_at_address?: string; + children_at_address?: string; + head_of_household_age?: string; + head_of_household_lifestage?: string; + household_composition?: string; + mosaic_group?: string; + mosaic_segment?: string; + mosaic_type_group?: string; +} +export interface USARegionalGeocodes { + latitude?: number; + longitude?: number; + match_level?: string; + census_tract?: string; + census_block?: string; + core_based_statistical_area?: string; + congressional_district_code?: string; + county_code?: string; +} +export interface Geocodes { + latitude?: number; + longitude?: number; + match_level?: string; +} +export interface PremiumLocationInsight { + geocodes?: Geocodes; + geocodes_building_xy?: GeocodesBuildingXY; + geocodes_access?: GeocodesAccess; + time?: Time; +} +export interface GeocodesBuildingXY { + x_coordinate?: number; + y_coordinate?: number; +} +export interface GeocodesAccess { + latitude?: number; + longitude?: number; +} +export interface Time { + time_zone_id?: string; + generic?: string; + standard?: string; + daylight?: string; + reference_time?: ReferenceTime; + time_transition?: TimeTransition; +} +export interface ReferenceTime { + tag?: string; + standard_offset?: string; + daylight_savings?: string; + sunrise?: string; + sunset?: string; +} +export interface TimeTransition { + tag?: string; + standard_offset?: string; + daylight_savings?: string; + utc_start?: string; + utc_end?: string; +} export class UseAddressEntered { element: HTMLElement; create: (confidence: string) => HTMLDivElement; diff --git a/src/ts/enrichment_output.ts b/src/ts/enrichment_output.ts new file mode 100644 index 0000000..25e6b36 --- /dev/null +++ b/src/ts/enrichment_output.ts @@ -0,0 +1,212 @@ +export const enrichmentOutput = { + AUS: { + geocodes: { + "latitude": "Latitude", + "longitude": "Longitude", + "match_level": "Match Level", + "sa1": "Sa1", + "meshblock": "Meshblock", + "lga_code": "Lga Code", + "lga_name": "Lga Name", + "street_pid": "Street Pid", + "locality_pid": "Locality Pid", + "geocode_level_code": "Geocode Level Code", + "geocode_level_description": "Geocode Level Description", + "geocode_type_code": "Geocode Type Code", + "geocode_type_description": "Geocode Type Description", + "highest_level_longitude": "Highest Level Longitude", + "highest_level_latitude": "Highest Level Latitude", + "highest_level_elevation": "Highest Level Elevation", + "highest_level_planimetric_accuracy": "Highest Level Plamimetric Accuracy", + "highest_level_boundary_extent": "Highest Level Boundary Extent", + "highest_level_geocode_reliability_code": "Highest Level Geocode Reliability Code", + "highest_level_geocode_reliability_description": "Highest Level Geocode Reliability Description", + "confidence_level_code": "Confidence Level Code", + "confidence_level_description": "Confidence Level Description", + "2021_meshblock_id": "2021 Meshblock Id", + "2021_meshblock_code": "2021 Meshblock Code", + "2021_meshblock_match_code": "2021 Meshblock Match Code", + "2021_meshblock_match_description": "2021 Meshblock Match Description", + "2016_meshblock_id": "2016 Meshblock Id", + "2016_meshblock_code": "2016 Meshblock Code", + "2016_meshblock_match_code": "2016 Meshblock Match Code", + "2016_meshblock_match_description": "2016 Meshblock Match Description", + "address_type_code": "Address Type Code", + "primary_address_pid": "Primary Address Pid", + "address_join_type": "Address Join Type", + "collector_district_id": "Collector District Id", + "collector_district_code": "Collector District Code", + "commonwealth_electoral_boundary_id": "Commonwealth Electoral Boundary Id", + "commonwealth_electoral_boundary_name": "Commonwealth Electoral Boundary Name", + "statistical_local_area_id": "Statistical Local Area Id", + "statistical_local_area_code": "Statistical Local Area Code", + "statistical_local_area_name": "Statistical Local Area Name", + "state_electoral_boundary_id": "State Electoral Boundary Id", + "state_electoral_boundary_name": "State Electoral Boundary Name", + "state_electoral_effective_start": "State Electoral Effective Start", + "state_electoral_effective_end": "State Electoral Effective End", + "state_electoral_new_pid": "State Electoral New Pid", + "state_electoral_new_name": "State Electoral New Name", + "state_electoral_new_effective_start": "State Electoral New Effective Start", + "state_electoral_new_effective_end": "State Electoral New Effective End", + "address_level_longitude": "Address Level Longitude", + "address_level_latitude": "Address Level Latitude", + "address_level_elevation": "Address Level Elevation", + "address_level_planimetric_accuracy": "Address Level Planimetric Accuracy", + "address_level_boundary_extent": "Address Level LatitudeBoundary Extent", + "address_level_geocode_reliability_code": "Address Level Geocode Reliability Code", + "address_level_geocode_reliability_description": "Address Level Geocode Reliability Description", + "street_level_longitude": "Street Level Longitude", + "street_level_latitude": "Street Level Latitude", + "street_level_planimetric_accuracy": "", + "street_level_boundary_extent": "Street Level Boundary Extent", + "street_level_geocode_reliability_code": "Street Level Geocode Reliability Code", + "street_level_geocode_reliability_description": "Street Level Geocode Reliability Description", + "locality_level_longitude": "Locality Level Longitude", + "locality_level_latitude": "Locality Level Latitude", + "locality_level_planimetric_accuracy": "Locality Level Latitude", + "locality_level_geocode_reliability_code": "Locality Level Geocode Reliability Code", + "locality_level_geocode_reliability_description": "Locality Level Geocode Reliability Description", + "gnaf_legal_parcel_identifier": "Gnaf Legal Parcel Identifier", + "locality_class_code": "Locality Class Code" + }, + cv_household: { + "address": "Address", + "adults_at_address_code": "Adults At Address Code", + "adults_at_address_description": "Adults At Address Description", + "affluence_code": "Affluence Code", + "affluence_description": "Affluence Description", + "channel_preference": "Channel Preference", + "channel_preference_description": "Channel Preference Description", + "children_at_address_code_0_10_years": "Children At Address Code 0 10 Years", + "children_at_address_code_11_18_years": "Children At Address Code 11 18 Years", + "children_at_address_description_0_10_years": "Children At Address Description 0 10 Years", + "children_at_address_description_11_18_years": "Children At Address Description 11 18 Years", + "credit_demand_code": "Credit Demand Code", + "credit_demand_description": "Credit Demand Description", + "gnaf_latitude": "Gnaf Latitude", + "gnaf_longitude": "Gnaf Longitude", + "gnaf_pid": "Gnaf Pid", + "head_of_household_age_code": "Head Of Household Age Code", + "head_of_household_age_description": "Head Of Household Age Description", + "hin": "Hin", + "household_composition_code": "Household Composition Code", + "household_composition_description": "Household Composition Description", + "household_income_code": "Household Income Code", + "household_income_description": "Household Income Description", + "length_of_residence_code": "Length Of Residence Code", + "length_of_residence_description": "Length Of Residence Description", + "lifestage_code": "Lifestage Code", + "lifestage_description": "Lifestage Description", + "local_government_area_code": "Local Government Area Code", + "local_government_area_name": "Local Government Area Name", + "meshblock": "Meshblock", + "mosaic_factor1_percentile": "Mosaic Factor1 Percentile", + "mosaic_factor1_score": "Mosaic Factor1 Score", + "mosaic_factor2_percentile": "Mosaic Factor2 Percentile", + "mosaic_factor2_score": "Mosaic Factor2 Score", + "mosaic_factor3_percentile": "Mosaic Factor3 Percentile", + "mosaic_factor3_score": "Mosaic Factor3 Score", + "mosaic_factor4_percentile": "Mosaic Factor4 Percentile", + "mosaic_factor4_score": "Mosaic Factor4 Score", + "mosaic_factor5_percentile": "Mosaic Factor5 Percentile", + "mosaic_factor5_score": "Mosaic Factor5 Score", + "mosaic_group": "Mosaic Group", + "mosaic_segment": "Mosaic Segment", + "mosaic_type": "Mosaic Type", + "postcode": "Postcode", + "residential_flag": "Residential Flag", + "risk_insight_code": "Risk Insight Code", + "risk_insight_description": "Risk Insight Description", + "sa1": "Sa1", + "state": "State", + "suburb": "Suburb" + } + }, + NZL: { + geocodes: { + "front_of_property_nztm_x_coordinate": "Front of Property NZTM X Coordinate", + "front_of_property_nztm_y_coordinate": "Front of Property NZTM Y Coordinate", + "centroid_of_property_nztm_x_coordinate": "Centroid of Property NZTM X Coordinate", + "centroid_of_property_nztm_y_coordinate": "Centroid of Property NZTM Y Coordinate", + "front_of_property_latitude": "Front of Property Latitude", + "front_of_property_longitude": "Front of Property Longitude", + "centroid_of_property_latitude": "Centroid of Property Latitude", + "centroid_of_property_longitude": "Centroid of Property Longitude", + "linz_parcel_id": "Linz Parcel Id", + "property_purpose_type": "Property Purpose Type", + "addressable": "Addressable", + "mesh_block_code": "Mesh Block Code", + "territorial_authority_code": "Territorial Authority Code", + "territorial_authority_name": "Territorial Authority Name", + "regional_council_code": "Regional Council Code", + "regional_council_name": "Regional Council Name", + "general_electorate_code": "General Electorate Code", + "general_electorate_name": "General Electorate Name", + "maori_electorate_code": "Maori Electorate Code", + "maori_electorate_name": "Maori Electorate Name", + "match_level": "Match Level" + }, + cv_household: { + "adults_at_address": "Adults At Address", + "children_at_address": "Children At Address", + "head_of_household_age": "Head Of Household Age", + "head_of_household_lifestage": "Head Of Household Lifestage", + "household_composition": "Household Composition", + "mosaic_group": "Mosaic Group", + "mosaic_segment": "Mosaic Segment", + "mosaic_type_group": "Mosaic Type Group" + } + }, + USA: { + geocodes: { + "latitude": "Latitude", + "longitude": "Longitude", + "match_level": "Match Level", + "census_tract": "Census Tract", + "census_block": "Census Block", + "core_based_statistical_area": "Core Based Statistical Area", + "congressional_district_code": "Congressional District Code", + "county_code": "Country Code" + } + }, + GLOBAL: { + geocodes: { + "latitude": "Latitude", + "longitude": "Longitude", + "match_level": "Match Level" + } + } +}; + +export const premiumLocationInsight = { + geocodes: { + "latitude": "Latitude", + "longitude": "Longitude", + "match_level": "Match Level" + }, + geocodes_access: { + "latitude": "Latitude", + "longitude": "Longitude", + }, + time: { + "time_zone_id": "LatitudeTime Zone Id", + "generic": "Generic", + "standard": "Standard", + "daylight": "Daylight", + reference_time: { + "tag": "Tag", + "standard_offset": "Standard Offset", + "daylight_savings": "Daylight Savings", + "sunrise": "Sunrise", + "sunset": "Sunset" + }, + time_transition: { + "tag": "Tag", + "standard_offset": "Standard Offset", + "daylight_savings": "Daylight Savings", + "utc_start": "UTC Start", + "utc_end": "UTC End" + }, + }, +}; \ No newline at end of file From eba061c6bbbcc88faeebc8dca4e0b5b4ee53966e Mon Sep 17 00:00:00 2001 From: sufangl Date: Tue, 2 Jan 2024 14:43:12 +0800 Subject: [PATCH 02/35] 268963 to show components and metadata collection for an address. --- index.html | 70 ++++++- src/css/experian-address-validation.css | 10 + src/ts/address-search.ts | 23 ++- src/ts/class-types.ts | 243 ++++++------------------ 4 files changed, 151 insertions(+), 195 deletions(-) diff --git a/index.html b/index.html index c84f9fa..f5ff78b 100644 --- a/index.html +++ b/index.html @@ -343,6 +343,12 @@

Validated address information

+ + @@ -647,8 +653,45 @@

Enrichment

document.querySelector(".metadata #delivery-address-key").innerHTML = data.result.address ? '' : ''; document.querySelector(".metadata #delivery-address-value").innerHTML = data.result.address ? Object.values(data.result.address).filter(line => line !== "").join("
") : ''; - document.querySelector(".metadata").classList.remove("invisible"); + + populateComponentMetadataCollection(address.componentsCollectionMap, document.querySelector("#components-collection")); + populateComponentMetadataCollection(address.metadataCollectionMap, document.querySelector("#metadata-collection")); + } + + function populateComponentMetadataCollection(collectionMap, collectionParentElement) { + if (collectionMap.size > 0) { + collectionParentElement.classList.remove("hidden"); + collectionMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + let htmlBrElement = document.createElement("br"); + if (typeof value == "object") { + htmlSpanElement.innerText = key + ": "; + collectionParentElement.append(htmlSpanElement, htmlBrElement); + addChildElement(Object.entries(value), collectionParentElement, 1) + } else { + htmlSpanElement.innerText = key + ": " + value; + collectionParentElement.append(htmlSpanElement, htmlBrElement); + } + }); + collectionParentElement.append(document.createElement("br")); + } + } + + function addChildElement(entries, componentCollectionsElement, tabspace){ + for (const [childKey, childValue] of entries) { + const htmlChildSpanElement = document.createElement("span"); + let htmlChildBrElement = document.createElement("br"); + htmlChildSpanElement.setAttribute("class", "tab".concat(tabspace)); + if (typeof childValue == "object") { + htmlChildSpanElement.innerText = childKey + ": "; + componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); + addChildElement(Object.entries(childValue), componentCollectionsElement, tabspace+1); + } else { + htmlChildSpanElement.innerText = childKey + ": " + childValue; + componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); + } + } } // Hide the "metadata" container @@ -661,8 +704,26 @@

Enrichment

document.querySelector(".metadata #what3words-value").classList.add("hidden"); document.querySelector(".metadata").classList.add("invisible"); - // to remove all enrichment elements - const parent = document.getElementById("enrichment") + + // to remove all components collection dynamic elements + const componentCollectionParent = document.getElementById("components-collection"); + removeDynamicMetadataElements(componentCollectionParent); + document.querySelector("#components-collection").classList.add("hidden"); + + // to remove all metadata collection dynamic elements + const metadataCollectionParent = document.getElementById("metadata-collection"); + removeDynamicMetadataElements(metadataCollectionParent); + document.querySelector("#metadata-collection").classList.add("hidden"); + + // to remove all enrichment dynamic elements + const enrichmentParent = document.getElementById("enrichment"); + removeDynamicMetadataElements(enrichmentParent); + document.querySelector("#enrichment").classList.add("hidden"); + + document.querySelector("#map").classList.add("hidden"); + } + + function removeDynamicMetadataElements(parent){ let spanElements = parent.getElementsByTagName("span"), index let brElements = parent.getElementsByTagName("br"), brIndex let subtitleElements = parent.getElementsByTagName("h3"), subTitleIndex @@ -675,9 +736,6 @@

Enrichment

for (subTitleIndex = subtitleElements.length - 1; subTitleIndex >= 0; subTitleIndex--) { subtitleElements[subTitleIndex].parentNode.removeChild(subtitleElements[subTitleIndex]); } - - document.querySelector("#enrichment").classList.add("hidden"); - document.querySelector("#map").classList.add("hidden"); } diff --git a/src/css/experian-address-validation.css b/src/css/experian-address-validation.css index f0e5f9c..613e89a 100644 --- a/src/css/experian-address-validation.css +++ b/src/css/experian-address-validation.css @@ -8,6 +8,16 @@ display: none; } +.tab1 { + display: inline-block; + margin-left: 2em; +} + +.tab2 { + display: inline-block; + margin-left: 4em; +} + /* Loading spinner styles */ @-webkit-keyframes spinner { 0% { diff --git a/src/ts/address-search.ts b/src/ts/address-search.ts index a6282e5..3628dcf 100644 --- a/src/ts/address-search.ts +++ b/src/ts/address-search.ts @@ -29,6 +29,9 @@ export default class AddressValidation { public avMode: AddressValidationMode; public events; public request: Request; + + public componentsCollectionMap = new Map(); + public metadataCollectionMap = new Map(); public geocodesMap = new Map(); public cvHouseholdMap = new Map(); public premiumLocationInsightMap = new Map(); @@ -491,7 +494,7 @@ export default class AddressValidation { default: { data = this.generateSearchDataForApiCall(); url = this.baseUrl + (this.searchType === AddressValidationSearchType.VALIDATE ? this.validateEndpoint : this.searchEndpoint); - headers = this.searchType === AddressValidationSearchType.VALIDATE ? [{ key: 'Add-Metadata', value: true }] : []; + headers = this.searchType === AddressValidationSearchType.VALIDATE ? [{ key: 'Add-Components', value: true }, { key: 'Add-Metadata', value: true }] : []; callback = this.searchType === AddressValidationSearchType.VALIDATE ? this.result.handleValidateResponse : this.picklist.show; break; } @@ -1179,6 +1182,24 @@ export default class AddressValidation { this.result.updateAddressLine(key, addressComponent, 'address-line-input'); } + this.componentsCollectionMap.clear(); + if (data.result.components) { + for (let i = 0; i < Object.keys(data.result.components).length; i++) { + const key = Object.keys(data.result.components)[i]; + const value = data.result.components[key]; + this.componentsCollectionMap.set(key, value); + } + } + + this.metadataCollectionMap.clear(); + if (data.metadata) { + for (let i = 0; i < Object.keys(data.metadata).length; i++) { + const key = Object.keys(data.metadata)[i]; + const value = data.metadata[key]; + this.metadataCollectionMap.set(key, value); + } + } + // Hide country and address search fields (if they have a 'toggle' class) this.toggleSearchInputs('hide'); diff --git a/src/ts/class-types.ts b/src/ts/class-types.ts index 62423b7..88ec966 100644 --- a/src/ts/class-types.ts +++ b/src/ts/class-types.ts @@ -63,9 +63,27 @@ export interface SearchResponse { suggestions_key?: string; confidence: string; address?: { [key: string]: string }; + components?: AddressComponent; + }, + metadata? :{ + address_info?: AddressInfo; + barcode?: { [key: string]: string }; + route_classification?: { [key: string]: string }; + address_classification?: AddressClassification; + dpv?: { [key: string]: string }; } } - +export interface AddressInfo { + sources: string[]; + number_of_households: string; + just_built_date: string; + identifier: { [key: string]: string }; +} +export interface AddressClassification { + address_type: { [key: string]: string }; + delivery_type: string; + is_deliverable: string; +} export interface LookupW3WResponse { result?: { more_results_available: boolean; @@ -104,201 +122,50 @@ export interface PicklistItem { global_address_key?: string; additional_attributes?: { name: string, Value: string }[]; } +export interface AddressComponent { + language?: string; + country_name?: string; + country_iso_3?: string; + country_iso_2?: string; + postal_code?: { [key: string]: string }; + delivery_service?: { [key: string]: string }; + secondary_delivery_service?: { [key: string]: string }; + sub_building?: AddressComponentSubBuilding; + building?: { [key: string]: string }; + organization?: { [key: string]: string }; + street?: { [key: string]: string }; + secondary_street?: { [key: string]: string }; + route_service?: { [key: string]: string }; + locality?: { [key: string]: { [key: string]: string }}; + physical_locality?: { [key: string]: { [key: string]: string }}; + additional_elements?: AddressComponentAdditionalElements; +} +export interface AddressComponentSubBuilding { + name?: string; + entrance?: { [key: string]: string }; + floor?: { [key: string]: string }; + door?: { [key: string]: string }; +} +export interface AddressComponentAdditionalElements { + locality?: { sub_region: { [key: string]: string }}; +} export interface EnrichmentResponse { result?: { - aus_regional_geocodes?: AUSRegionalGeocodes; - aus_cv_household?: AUSCVHousehold; - nzl_regional_geocodes?: NZLRegionalGeocodes; - nzl_cv_household?: NZLCVHousehold; - usa_regional_geocodes?: USARegionalGeocodes; - global_geocodes?: Geocodes; + aus_regional_geocodes?: { [key: string]: string }; + aus_cv_household?: { [key: string]: string }; + nzl_regional_geocodes?: { [key: string]: string }; + nzl_cv_household?: { [key: string]: string }; + usa_regional_geocodes?: { [key: string]: string }; + global_geocodes?: { [key: string]: string }; premium_location_insight?: PremiumLocationInsight; }; } -export interface AUSRegionalGeocodes { - latitude?: number; - longitude?: number; - match_level?: string; - sa1?: string; - meshblock?: string; - lga_code?: string; - lga_name?: string; - street_pid?: string; - locality_pid?: string; - geocode_level_code?: string; - geocode_level_description?: string; - geocode_type_code?: string; - geocode_type_description?: string; - highest_level_longitude?: number; - highest_level_latitude?: number; - highest_level_elevation?: string; - highest_level_planimetric_accuracy?: string; - highest_level_boundary_extent?: string; - highest_level_geocode_reliability_code?: string; - highest_level_geocode_reliability_description?: string; - confidence_level_code?: string; - confidence_level_description?: string; - "2021_meshblock_id"?: string; - "2021_meshblock_code"?: string; - "2021_meshblock_match_code"?: string; - "2021_meshblock_match_description"?: string; - "2016_meshblock_id"?: string; - "2016_meshblock_code"?: string; - "2016_meshblock_match_code"?: string; - "2016_meshblock_match_description"?: string; - address_type_code?: string; - primary_address_pid?: string; - address_join_type?: string; - collector_district_id?: string; - collector_district_code?: string; - commonwealth_electoral_boundary_id?: string; - commonwealth_electoral_boundary_name?: string; - statistical_local_area_id?: string; - statistical_local_area_code?: string; - statistical_local_area_name?: string; - state_electoral_boundary_id?: string; - state_electoral_boundary_name?: string; - state_electoral_effective_start?: string; - state_electoral_effective_end?: string; - state_electoral_new_pid?: string; - state_electoral_new_name?: string; - state_electoral_new_effective_start?: string; - state_electoral_new_effective_end?: string; - address_level_longitude?: number; - address_level_latitude?: number; - address_level_elevation?: string; - address_level_planimetric_accuracy?: string; - address_level_boundary_extent?: string; - address_level_geocode_reliability_code?: string; - address_level_geocode_reliability_description?: string; - street_level_longitude?: number; - street_level_latitude?: number; - street_level_planimetric_accuracy?: string; - street_level_boundary_extent?: string; - street_level_geocode_reliability_code?: string; - street_level_geocode_reliability_description?: string; - locality_level_longitude?: number; - locality_level_latitude?: number; - locality_level_planimetric_accuracy?: string; - locality_level_geocode_reliability_code?: string; - locality_level_geocode_reliability_description?: string; - gnaf_legal_parcel_identifier?: string; - locality_class_code?: string; -} -export interface AUSCVHousehold { - address?: string; - adults_at_address_code?: string; - adults_at_address_description?: string; - affluence_code?: string; - affluence_description?: string; - channel_preference?: string; - channel_preference_description?: string; - children_at_address_code_0_10_years?: string; - children_at_address_code_11_18_years?: string; - children_at_address_description_0_10_years?: string; - children_at_address_description_11_18_years?: string; - credit_demand_code?: string; - credit_demand_description?: string; - gnaf_latitude?: number; - gnaf_longitude?: number; - gnaf_pid?: string; - head_of_household_age_code?: string; - head_of_household_age_description?: string; - hin?: string; - household_composition_code?: string; - household_composition_description?: string; - household_income_code?: string; - household_income_description?: string; - length_of_residence_code?: string; - length_of_residence_description?: string; - lifestage_code?: string; - lifestage_description?: string; - local_government_area_code?: string; - local_government_area_name?: string; - meshblock?: string; - mosaic_group?: string; - mosaic_segment?: string; - mosaic_type?: string; - postcode?: string; - residential_flag?: string; - risk_insight_code?: string; - risk_insight_description?: string; - sa1?: string; - state?: string; - suburb?: string; - mosaic_factor1_percentile?: string; - mosaic_factor1_score?: string; - mosaic_factor2_percentile?: string; - mosaic_factor2_score?: string; - mosaic_factor3_percentile?: string; - mosaic_factor3_score?: string; - mosaic_factor4_percentile?: string; - mosaic_factor4_score?: string; - mosaic_factor5_percentile?: string; - mosaic_factor5_score?: string; -} -export interface NZLRegionalGeocodes { - front_of_property_nztm_x_coordinate?: number; - front_of_property_nztm_y_coordinate?: number; - centroid_of_property_nztm_x_coordinate?: number; - centroid_of_property_nztm_y_coordinate?: number; - front_of_property_latitude?: number; - front_of_property_longitude?: number; - centroid_of_property_latitude?: number; - centroid_of_property_longitude?: number; - linz_parcel_id?: string; - property_purpose_type?: string; - addressable?: string; - mesh_block_code?: string; - territorial_authority_code?: string; - territorial_authority_name?: string; - regional_council_code?: string; - regional_council_name?: string; - general_electorate_code?: string; - general_electorate_name?: string; - maori_electorate_code?: string; - maori_electorate_name?: string; - match_level?: string; -} -export interface NZLCVHousehold { - adults_at_address?: string; - children_at_address?: string; - head_of_household_age?: string; - head_of_household_lifestage?: string; - household_composition?: string; - mosaic_group?: string; - mosaic_segment?: string; - mosaic_type_group?: string; -} -export interface USARegionalGeocodes { - latitude?: number; - longitude?: number; - match_level?: string; - census_tract?: string; - census_block?: string; - core_based_statistical_area?: string; - congressional_district_code?: string; - county_code?: string; -} -export interface Geocodes { - latitude?: number; - longitude?: number; - match_level?: string; -} export interface PremiumLocationInsight { - geocodes?: Geocodes; - geocodes_building_xy?: GeocodesBuildingXY; - geocodes_access?: GeocodesAccess; + geocodes?: { [key: string]: string }; + geocodes_building_xy?: { [key: string]: string }; + geocodes_access?: { [key: string]: string }; time?: Time; } -export interface GeocodesBuildingXY { - x_coordinate?: number; - y_coordinate?: number; -} -export interface GeocodesAccess { - latitude?: number; - longitude?: number; -} export interface Time { time_zone_id?: string; generic?: string; From 9aa1a11605b372f7ce13d54ca63700c10c673ba0 Mon Sep 17 00:00:00 2001 From: sufangl Date: Wed, 3 Jan 2024 12:08:30 +0800 Subject: [PATCH 03/35] 268963 Refactoring. --- index.html | 113 ++++++++++++--------------------------- src/ts/address-search.ts | 46 ++++++---------- 2 files changed, 49 insertions(+), 110 deletions(-) diff --git a/index.html b/index.html index f5ff78b..983a1fd 100644 --- a/index.html +++ b/index.html @@ -476,66 +476,21 @@

Enrichment

// Display a map with the lat/long details after a data enrichment lookup address.events.on("post-enrichment", function(data) { - if ((data.result.geocodes && data.result.geocodes.latitude) || - (data.result.nzl_regional_geocodes && data.result.nzl_regional_geocodes.latitude) || - (data.result.aus_regional_geocodes && data.result.aus_regional_geocodes.latitude) || - (data.result.usa_regional_geocodes && data.result.usa_regional_geocodes.latitude) || - (data.result.what3words && data.result.what3words.latitude)) { + if (address.geocodesMap.size > 0 || (data.result.what3words && data.result.what3words.latitude)) { document.querySelector("#map").classList.remove("hidden"); - document.querySelector("#enrichment").classList.remove("hidden"); document.querySelector(".metadata #what3words-key").classList.add("hidden"); document.querySelector(".metadata #what3words-value").classList.add("hidden"); + let enrichmentElement = document.querySelector("#enrichment"); + enrichmentElement.classList.remove("hidden"); // populate cv household attributes - if (address.cvHouseholdMap.size > 0) { - let enrichmentElement = document.querySelector("#enrichment"); - const cvHouseholdHeaderElement = document.createElement("h3"); - cvHouseholdHeaderElement.innerText = "Consumer View Household"; - enrichmentElement.append(cvHouseholdHeaderElement); - - let cvHouseholdMap = address.cvHouseholdMap; - cvHouseholdMap.forEach((value, key) => { - const htmlSpanElement = document.createElement("span"); - htmlSpanElement.innerText = key + ": " + value; - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlSpanElement, htmlbrElement); - }); - - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlbrElement); - } + populateEnrichmentOutput(address.cvHouseholdMap, enrichmentElement, "Consumer View Household"); // populate geocodes attributes - if (address.geocodesMap.size > 0) { - let enrichmentElement = document.querySelector("#enrichment"); - const geocodesHeaderElement = document.createElement("h3"); - geocodesHeaderElement.innerText = "Geocodes"; - enrichmentElement.append(geocodesHeaderElement); - - let geocodesMap = address.geocodesMap; - geocodesMap.forEach((value, key) => { - const htmlSpanElement = document.createElement("span"); - htmlSpanElement.innerText = key + ": " + value; - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlSpanElement, htmlbrElement); - }); - } + populateEnrichmentOutput(address.geocodesMap, enrichmentElement, "Geocodes"); // populate premium location insight - if (address.premiumLocationInsightMap.size > 0) { - let enrichmentElement = document.querySelector("#enrichment"); - const premiumLocationInsightHeaderElement = document.createElement("h3"); - premiumLocationInsightHeaderElement.innerText = "Premium Location Insight"; - enrichmentElement.append(premiumLocationInsightHeaderElement); - - let premiumLocationInsightMap = address.premiumLocationInsightMap; - premiumLocationInsightMap.forEach((value, key) => { - const htmlSpanElement = document.createElement("span"); - htmlSpanElement.innerText = key + ": " + value; - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlSpanElement, htmlbrElement); - }); - } + populateEnrichmentOutput(address.premiumLocationInsightMap, enrichmentElement, "Premium Location Insight"); let w3wLat, w3wLong, w3wLatLong; if (data.result.what3words && data.result.what3words.latitude) { @@ -548,31 +503,13 @@

Enrichment

w3wLatLong = [w3wLat, w3wLong]; } - let geoLat, geoLong, geoLatLong; - if (data.result.geocodes && data.result.geocodes.latitude) { - geoLat = data.result.geocodes.latitude; - geoLong = data.result.geocodes.longitude; + let geoLat = address.geocodesMap.get("Latitude"); + let geoLong = address.geocodesMap.get("Longitude") + let geoLatLong; + if (geoLat && geoLong) { geoLatLong = [geoLat, geoLong]; } - if (data.result.nzl_regional_geocodes && data.result.nzl_regional_geocodes.latitude) { - geoLat = data.result.nzl_regional_geocodes.latitude; - geoLong = data.result.nzl_regional_geocodes.longitude; - geoLatLong = [geoLat, geoLong]; - } - - if (data.result.aus_regional_geocodes && data.result.aus_regional_geocodes.latitude) { - geoLat = data.result.aus_regional_geocodes.latitude; - geoLong = data.result.aus_regional_geocodes.longitude; - geoLatLong = [geoLat, geoLong]; - } - - if (data.result.usa_regional_geocodes && data.result.usa_regional_geocodes.latitude) { - geoLat = data.result.usa_regional_geocodes.latitude; - geoLong = data.result.usa_regional_geocodes.longitude; - geoLatLong = [geoLat, geoLong]; - } - var zoom = 16; var attribution = '© OpenStreetMap contributors'; @@ -618,6 +555,24 @@

Enrichment

var group = L.featureGroup(markerArray); addressValidationMap.fitBounds(group.getBounds().pad(0.25)); } + + function populateEnrichmentOutput(dataMap, enrichmentElement, elementTitle){ + if (dataMap.size > 0) { + const headerElement = document.createElement("h3"); + headerElement.innerText = elementTitle; + enrichmentElement.append(headerElement); + + dataMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + htmlSpanElement.innerText = key + ": " + value; + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlSpanElement, htmlbrElement); + }); + + let htmlbrElement = document.createElement("br"); + enrichmentElement.append(htmlbrElement); + } + } }); /* Demo specific code */ @@ -678,7 +633,7 @@

Enrichment

} } - function addChildElement(entries, componentCollectionsElement, tabspace){ + function addChildElement(entries, componentCollectionsElement, tabspace) { for (const [childKey, childValue] of entries) { const htmlChildSpanElement = document.createElement("span"); let htmlChildBrElement = document.createElement("br"); @@ -686,7 +641,7 @@

Enrichment

if (typeof childValue == "object") { htmlChildSpanElement.innerText = childKey + ": "; componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); - addChildElement(Object.entries(childValue), componentCollectionsElement, tabspace+1); + addChildElement(Object.entries(childValue), componentCollectionsElement, tabspace + 1); } else { htmlChildSpanElement.innerText = childKey + ": " + childValue; componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); @@ -707,23 +662,23 @@

Enrichment

// to remove all components collection dynamic elements const componentCollectionParent = document.getElementById("components-collection"); - removeDynamicMetadataElements(componentCollectionParent); + removeMetadataElements(componentCollectionParent); document.querySelector("#components-collection").classList.add("hidden"); // to remove all metadata collection dynamic elements const metadataCollectionParent = document.getElementById("metadata-collection"); - removeDynamicMetadataElements(metadataCollectionParent); + removeMetadataElements(metadataCollectionParent); document.querySelector("#metadata-collection").classList.add("hidden"); // to remove all enrichment dynamic elements const enrichmentParent = document.getElementById("enrichment"); - removeDynamicMetadataElements(enrichmentParent); + removeMetadataElements(enrichmentParent); document.querySelector("#enrichment").classList.add("hidden"); document.querySelector("#map").classList.add("hidden"); } - function removeDynamicMetadataElements(parent){ + function removeMetadataElements(parent) { let spanElements = parent.getElementsByTagName("span"), index let brElements = parent.getElementsByTagName("br"), brIndex let subtitleElements = parent.getElementsByTagName("h3"), subTitleIndex diff --git a/src/ts/address-search.ts b/src/ts/address-search.ts index 3628dcf..975de76 100644 --- a/src/ts/address-search.ts +++ b/src/ts/address-search.ts @@ -79,49 +79,35 @@ export default class AddressValidation { public getEnrichmentData(globalAddressKey: string) { if (globalAddressKey) { - let regionalAttributes = {}; + var regionalAttributes: {}; + var premium_location_insight: {} + premium_location_insight = [ + "geocodes", + "geocodes_building_xy", + "geocodes_access", + "time" + ] if (this.currentCountryCode == "NZL") { regionalAttributes = { nzl_regional_geocodes: Object.keys(enrichmentOutput.NZL.geocodes), nzl_cv_household: Object.keys(enrichmentOutput.NZL.cv_household), - // todo: sufang to remove duplication - premium_location_insight: [ - "geocodes", - "geocodes_building_xy", - "geocodes_access", - "time" - ] + premium_location_insight } } else if (this.currentCountryCode == "AUS") { regionalAttributes = { aus_regional_geocodes: Object.keys(enrichmentOutput.AUS.geocodes), aus_cv_household: Object.keys(enrichmentOutput.AUS.cv_household), - premium_location_insight: [ - "geocodes", - "geocodes_building_xy", - "geocodes_access", - "time" - ] + premium_location_insight } } else if (this.currentCountryCode == "USA") { regionalAttributes = { usa_regional_geocodes: Object.keys(enrichmentOutput.USA.geocodes), - premium_location_insight: [ - "geocodes", - "geocodes_building_xy", - "geocodes_access", - "time" - ] + premium_location_insight } } else { regionalAttributes = { geocodes: Object.keys(enrichmentOutput.GLOBAL.geocodes), - premium_location_insight: [ - "geocodes", - "geocodes_building_xy", - "geocodes_access", - "time" - ], + premium_location_insight, what3words: this.currentCountryCode == 'GBR' ? ['latitude', 'longitude', 'name', 'description'] : null } } @@ -1186,8 +1172,7 @@ export default class AddressValidation { if (data.result.components) { for (let i = 0; i < Object.keys(data.result.components).length; i++) { const key = Object.keys(data.result.components)[i]; - const value = data.result.components[key]; - this.componentsCollectionMap.set(key, value); + this.componentsCollectionMap.set(key, data.result.components[key]); } } @@ -1195,8 +1180,7 @@ export default class AddressValidation { if (data.metadata) { for (let i = 0; i < Object.keys(data.metadata).length; i++) { const key = Object.keys(data.metadata)[i]; - const value = data.metadata[key]; - this.metadataCollectionMap.set(key, value); + this.metadataCollectionMap.set(key, data.metadata[key]); } } @@ -1459,7 +1443,7 @@ export default class AddressValidation { geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.GLOBAL.geocodes)); } - // todo: sufang to check response + // todo: sufang to check response with valid token if (response.result.premium_location_insight) { let premiumLocationInsightResponse = Object.entries(response.result.premium_location_insight); for (const [key, value] of premiumLocationInsightResponse) { From 30c361a230483185ea0db44f54a4464ac24ba582 Mon Sep 17 00:00:00 2001 From: sufangl Date: Wed, 3 Jan 2024 12:39:11 +0800 Subject: [PATCH 04/35] 268963 Refactoring. --- index.html | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index 983a1fd..6a0fbc0 100644 --- a/index.html +++ b/index.html @@ -660,19 +660,16 @@

Enrichment

document.querySelector(".metadata").classList.add("invisible"); - // to remove all components collection dynamic elements - const componentCollectionParent = document.getElementById("components-collection"); - removeMetadataElements(componentCollectionParent); + // to remove all components collection elements + removeMetadataElements(document.getElementById("components-collection")); document.querySelector("#components-collection").classList.add("hidden"); - // to remove all metadata collection dynamic elements - const metadataCollectionParent = document.getElementById("metadata-collection"); - removeMetadataElements(metadataCollectionParent); + // to remove all metadata collection elements + removeMetadataElements(document.getElementById("metadata-collection")); document.querySelector("#metadata-collection").classList.add("hidden"); - // to remove all enrichment dynamic elements - const enrichmentParent = document.getElementById("enrichment"); - removeMetadataElements(enrichmentParent); + // to remove all enrichment elements + removeMetadataElements(document.getElementById("enrichment")); document.querySelector("#enrichment").classList.add("hidden"); document.querySelector("#map").classList.add("hidden"); From f9d12920494f89c06ceb6b12f29de1edf525518d Mon Sep 17 00:00:00 2001 From: sufangl Date: Fri, 5 Jan 2024 10:31:55 +0800 Subject: [PATCH 05/35] 261043 to update the alignment for premium location insight. --- index.html | 43 +++++--------- src/css/experian-address-validation.css | 9 +-- src/ts/address-search.ts | 30 +++++----- src/ts/class-types.ts | 78 +------------------------ 4 files changed, 37 insertions(+), 123 deletions(-) diff --git a/index.html b/index.html index 6a0fbc0..71190a6 100644 --- a/index.html +++ b/index.html @@ -484,13 +484,13 @@

Enrichment

let enrichmentElement = document.querySelector("#enrichment"); enrichmentElement.classList.remove("hidden"); // populate cv household attributes - populateEnrichmentOutput(address.cvHouseholdMap, enrichmentElement, "Consumer View Household"); + populateAddressAdditionalInfo(address.cvHouseholdMap, enrichmentElement, "Consumer View Household"); // populate geocodes attributes - populateEnrichmentOutput(address.geocodesMap, enrichmentElement, "Geocodes"); + populateAddressAdditionalInfo(address.geocodesMap, enrichmentElement, "Geocodes"); // populate premium location insight - populateEnrichmentOutput(address.premiumLocationInsightMap, enrichmentElement, "Premium Location Insight"); + populateAddressAdditionalInfo(address.premiumLocationInsightMap, enrichmentElement, "Premium Location Insight"); let w3wLat, w3wLong, w3wLatLong; if (data.result.what3words && data.result.what3words.latitude) { @@ -555,24 +555,6 @@

Enrichment

var group = L.featureGroup(markerArray); addressValidationMap.fitBounds(group.getBounds().pad(0.25)); } - - function populateEnrichmentOutput(dataMap, enrichmentElement, elementTitle){ - if (dataMap.size > 0) { - const headerElement = document.createElement("h3"); - headerElement.innerText = elementTitle; - enrichmentElement.append(headerElement); - - dataMap.forEach((value, key) => { - const htmlSpanElement = document.createElement("span"); - htmlSpanElement.innerText = key + ": " + value; - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlSpanElement, htmlbrElement); - }); - - let htmlbrElement = document.createElement("br"); - enrichmentElement.append(htmlbrElement); - } - } }); /* Demo specific code */ @@ -610,13 +592,19 @@

Enrichment

document.querySelector(".metadata #delivery-address-value").innerHTML = data.result.address ? Object.values(data.result.address).filter(line => line !== "").join("
") : ''; document.querySelector(".metadata").classList.remove("invisible"); - populateComponentMetadataCollection(address.componentsCollectionMap, document.querySelector("#components-collection")); - populateComponentMetadataCollection(address.metadataCollectionMap, document.querySelector("#metadata-collection")); + populateAddressAdditionalInfo(address.componentsCollectionMap, document.querySelector("#components-collection")); + populateAddressAdditionalInfo(address.metadataCollectionMap, document.querySelector("#metadata-collection")); } - function populateComponentMetadataCollection(collectionMap, collectionParentElement) { + function populateAddressAdditionalInfo(collectionMap, collectionParentElement, elementTitle) { if (collectionMap.size > 0) { collectionParentElement.classList.remove("hidden"); + if (elementTitle) { + const headerElement = document.createElement("h3"); + headerElement.innerText = elementTitle; + collectionParentElement.append(headerElement); + } + collectionMap.forEach((value, key) => { const htmlSpanElement = document.createElement("span"); let htmlBrElement = document.createElement("br"); @@ -633,15 +621,16 @@

Enrichment

} } - function addChildElement(entries, componentCollectionsElement, tabspace) { + function addChildElement(entries, componentCollectionsElement, level) { for (const [childKey, childValue] of entries) { const htmlChildSpanElement = document.createElement("span"); let htmlChildBrElement = document.createElement("br"); - htmlChildSpanElement.setAttribute("class", "tab".concat(tabspace)); + htmlChildSpanElement.setAttribute("class", "tab"); + htmlChildSpanElement.setAttribute("style", "--spaces: " + level * 2 + "em;"); if (typeof childValue == "object") { htmlChildSpanElement.innerText = childKey + ": "; componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); - addChildElement(Object.entries(childValue), componentCollectionsElement, tabspace + 1); + addChildElement(Object.entries(childValue), componentCollectionsElement, level + 1); } else { htmlChildSpanElement.innerText = childKey + ": " + childValue; componentCollectionsElement.append(htmlChildSpanElement, htmlChildBrElement); diff --git a/src/css/experian-address-validation.css b/src/css/experian-address-validation.css index 613e89a..fbae833 100644 --- a/src/css/experian-address-validation.css +++ b/src/css/experian-address-validation.css @@ -8,14 +8,9 @@ display: none; } -.tab1 { +.tab { display: inline-block; - margin-left: 2em; -} - -.tab2 { - display: inline-block; - margin-left: 4em; + margin-left: var(--spaces); } /* Loading spinner styles */ diff --git a/src/ts/address-search.ts b/src/ts/address-search.ts index 975de76..afd58e5 100644 --- a/src/ts/address-search.ts +++ b/src/ts/address-search.ts @@ -21,7 +21,7 @@ import { SearchResponse, What3WordsPickList } from './class-types'; -import {enrichmentOutput, premiumLocationInsight} from "./enrichment_output"; +import {enrichmentOutput} from "./enrichment_output"; export default class AddressValidation { public options: AddressSearchOptions; @@ -1169,18 +1169,20 @@ export default class AddressValidation { } this.componentsCollectionMap.clear(); - if (data.result.components) { - for (let i = 0; i < Object.keys(data.result.components).length; i++) { - const key = Object.keys(data.result.components)[i]; - this.componentsCollectionMap.set(key, data.result.components[key]); + let components = data.result.components; + if (components) { + for (let i = 0; i < Object.keys(components).length; i++) { + const key = Object.keys(components)[i]; + this.componentsCollectionMap.set(key, components[key]); } } this.metadataCollectionMap.clear(); - if (data.metadata) { - for (let i = 0; i < Object.keys(data.metadata).length; i++) { - const key = Object.keys(data.metadata)[i]; - this.metadataCollectionMap.set(key, data.metadata[key]); + let metadata = data.metadata; + if (metadata) { + for (let i = 0; i < Object.keys(metadata).length; i++) { + const key = Object.keys(metadata)[i]; + this.metadataCollectionMap.set(key, metadata[key]); } } @@ -1443,11 +1445,11 @@ export default class AddressValidation { geocodesExpectedAttributes = new Map(Object.entries(enrichmentOutput.GLOBAL.geocodes)); } - // todo: sufang to check response with valid token - if (response.result.premium_location_insight) { - let premiumLocationInsightResponse = Object.entries(response.result.premium_location_insight); - for (const [key, value] of premiumLocationInsightResponse) { - this.premiumLocationInsightMap.set(key, value); + let premiumLocationInsightResponse = response.result.premium_location_insight; + if (premiumLocationInsightResponse) { + for (let i = 0; i < Object.keys(premiumLocationInsightResponse).length; i++) { + let key = Object.keys(premiumLocationInsightResponse)[i]; + this.premiumLocationInsightMap.set(key, premiumLocationInsightResponse[key]); } } diff --git a/src/ts/class-types.ts b/src/ts/class-types.ts index 88ec966..774a501 100644 --- a/src/ts/class-types.ts +++ b/src/ts/class-types.ts @@ -63,26 +63,9 @@ export interface SearchResponse { suggestions_key?: string; confidence: string; address?: { [key: string]: string }; - components?: AddressComponent; + components?: { [key: string]: string }; }, - metadata? :{ - address_info?: AddressInfo; - barcode?: { [key: string]: string }; - route_classification?: { [key: string]: string }; - address_classification?: AddressClassification; - dpv?: { [key: string]: string }; - } -} -export interface AddressInfo { - sources: string[]; - number_of_households: string; - just_built_date: string; - identifier: { [key: string]: string }; -} -export interface AddressClassification { - address_type: { [key: string]: string }; - delivery_type: string; - is_deliverable: string; + metadata? : { [key: string]: string }; } export interface LookupW3WResponse { result?: { @@ -122,33 +105,6 @@ export interface PicklistItem { global_address_key?: string; additional_attributes?: { name: string, Value: string }[]; } -export interface AddressComponent { - language?: string; - country_name?: string; - country_iso_3?: string; - country_iso_2?: string; - postal_code?: { [key: string]: string }; - delivery_service?: { [key: string]: string }; - secondary_delivery_service?: { [key: string]: string }; - sub_building?: AddressComponentSubBuilding; - building?: { [key: string]: string }; - organization?: { [key: string]: string }; - street?: { [key: string]: string }; - secondary_street?: { [key: string]: string }; - route_service?: { [key: string]: string }; - locality?: { [key: string]: { [key: string]: string }}; - physical_locality?: { [key: string]: { [key: string]: string }}; - additional_elements?: AddressComponentAdditionalElements; -} -export interface AddressComponentSubBuilding { - name?: string; - entrance?: { [key: string]: string }; - floor?: { [key: string]: string }; - door?: { [key: string]: string }; -} -export interface AddressComponentAdditionalElements { - locality?: { sub_region: { [key: string]: string }}; -} export interface EnrichmentResponse { result?: { aus_regional_geocodes?: { [key: string]: string }; @@ -157,37 +113,9 @@ export interface EnrichmentResponse { nzl_cv_household?: { [key: string]: string }; usa_regional_geocodes?: { [key: string]: string }; global_geocodes?: { [key: string]: string }; - premium_location_insight?: PremiumLocationInsight; + premium_location_insight?: { [key: string]: string }; }; } -export interface PremiumLocationInsight { - geocodes?: { [key: string]: string }; - geocodes_building_xy?: { [key: string]: string }; - geocodes_access?: { [key: string]: string }; - time?: Time; -} -export interface Time { - time_zone_id?: string; - generic?: string; - standard?: string; - daylight?: string; - reference_time?: ReferenceTime; - time_transition?: TimeTransition; -} -export interface ReferenceTime { - tag?: string; - standard_offset?: string; - daylight_savings?: string; - sunrise?: string; - sunset?: string; -} -export interface TimeTransition { - tag?: string; - standard_offset?: string; - daylight_savings?: string; - utc_start?: string; - utc_end?: string; -} export class UseAddressEntered { element: HTMLElement; create: (confidence: string) => HTMLDivElement; From d10e221a7b5c7f16d89146bcefd6efe9427b3502 Mon Sep 17 00:00:00 2001 From: sufangl Date: Mon, 8 Jan 2024 13:07:03 +0800 Subject: [PATCH 06/35] 268964 to show authorized country datasets in the country dropdown and populated error if selected search type is not supported for the selected country dataset. --- index.html | 275 ++++++--------------------------------- src/ts/address-search.ts | 40 +++++- src/ts/class-types.ts | 14 +- 3 files changed, 86 insertions(+), 243 deletions(-) diff --git a/index.html b/index.html index 71190a6..59a10dc 100644 --- a/index.html +++ b/index.html @@ -35,247 +35,10 @@

Experian Address Validation

- + @@ -333,6 +96,10 @@
Enter partial address, then get results
+ +
- + + diff --git a/src/js/address-metadata-display.js b/src/js/address-metadata-display.js new file mode 100644 index 0000000..a925d5e --- /dev/null +++ b/src/js/address-metadata-display.js @@ -0,0 +1,300 @@ +// Display a map with the lat/long details after a data enrichment lookup +address.events.on("post-enrichment", function (data) { + let enrichmentElement = document.querySelector("#enrichment"); + if (address.geocodes.detailsMap.size > 0 || (data.result.what3words && data.result.what3words.latitude)) { + document.querySelector(".metadata #what3words-key").classList.add("hidden"); + document.querySelector(".metadata #what3words-value").classList.add("hidden"); + + enrichmentElement.classList.remove("hidden"); + // populate cv household attributes + populateAddressAdditionalInfo(address.cvHousehold.detailsMap, enrichmentElement, address.cvHousehold.title, 1); + + // populate geocodes attributes + populateAddressAdditionalInfo(address.geocodes.detailsMap, enrichmentElement, address.geocodes.title, 2); + + let w3wLat, w3wLong, w3wLatLong; + if (data.result.what3words && data.result.what3words.latitude) { + document.querySelector(".metadata #what3words-key").classList.remove("hidden"); + document.querySelector(".metadata #what3words-value").classList.remove("hidden"); + document.querySelector(".metadata #what3words-value").innerHTML = '///' + data.result.what3words.name; + + w3wLat = data.result.what3words.latitude; + w3wLong = data.result.what3words.longitude; + w3wLatLong = [w3wLat, w3wLong]; + } + + let geoLat, geoLong, geoLatLong; + if (data.result.nzl_regional_geocodes) { + geoLat = address.geocodes.detailsMap.get("Centroid of Property Latitude"); + geoLong = address.geocodes.detailsMap.get("Centroid of Property Longitude") + } else { + geoLat = address.geocodes.detailsMap.get("Latitude"); + geoLong = address.geocodes.detailsMap.get("Longitude") + } + if (geoLat && geoLong) { + geoLatLong = [geoLat, geoLong]; + } + + var zoom = 16; + var attribution = '© OpenStreetMap contributors'; + + // Define a custom icon for what3words + var w3wIcon = L.icon({ + iconUrl: './dist/images/w3w.loc.png', + iconSize: [30, 37], // size of the icon + iconAnchor: [15, 36], // point of the icon which will correspond to marker's location + popupAnchor: [0, -20] // point from which the popup should open relative to the iconAnchor + }); + + if (w3wLatLong || geoLatLong) { + document.querySelector("#map").classList.remove("hidden"); + // Instantiate a new map + if (!addressValidationMap) { + // The hardcoded coordinated are needed to initialize the map. Will be overwritten with the what3words and geocode markers. + addressValidationMap = L.map('map').setView([51.500264, 0.633506], zoom); + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution}).addTo(addressValidationMap); + } else { + // Update the previous map instance by removing any existing markers + if (addressValidationW3wMarker) { + addressValidationW3wMarker.remove(); + } + + if (addressValidationGeoMarker) { + addressValidationGeoMarker.remove(); + } + } + + // Add new markers for location insight datasets + var markerArray = []; + if (w3wLatLong) { + addressValidationMap.panTo(w3wLatLong, {duration: 1}); + addressValidationW3wMarker = L.marker(w3wLatLong, {icon: w3wIcon}).addTo(addressValidationMap); + markerArray.push(addressValidationW3wMarker); + } + + if (geoLatLong) { + addressValidationMap.panTo(geoLatLong, {duration: 1}); + addressValidationGeoMarker = L.marker(geoLatLong).addTo(addressValidationMap); + markerArray.push(addressValidationGeoMarker); + } + + // Ensure all markers fit onto the map + var group = L.featureGroup(markerArray); + addressValidationMap.fitBounds(group.getBounds().pad(0.25)); + } + } + // populate premium location insight + if (address.premiumLocationInsightMap.size > 0) { + enrichmentElement.classList.remove("hidden"); + populateAddressAdditionalInfo(address.premiumLocationInsightMap, enrichmentElement, "Premium Location Insight", 3); + } +}); + +// Display and populate the "metadata" container +function populateMetadata(data) { + // Try and get some geocoded enrichment data + address.getEnrichmentData(data.result.global_address_key); + + const confidence = data.result.confidence; + if (confidence) { + document.querySelector(".metadata #confidence-key").innerText = confidence === 'Verified match' ? '✔' : '❌'; + document.querySelector(".metadata #confidence-value").innerText = confidence; + } + + if (data.metadata && data.metadata.address_classification) { + const deliveryType = data.metadata.address_classification.delivery_type; + if (deliveryType) { + document.querySelector(".metadata #delivery-type-key").innerText = deliveryType === 'residential' ? '🏡' : '🏢'; + document.querySelector(".metadata #delivery-type-value").innerText = deliveryType.substring(0, 1).toUpperCase() + deliveryType.substring(1); + } + } + + document.querySelector(".metadata #delivery-address-key").innerHTML = data.result.address ? '' : ''; + document.querySelector(".metadata #delivery-address-value").innerHTML = data.result.address ? Object.values(data.result.address).filter(line => line !== "").join("
") : ''; + document.querySelector(".metadata").classList.remove("invisible"); + + populateAddressAdditionalInfo(address.componentsCollectionMap, document.querySelector("#components-collection")); + populateAddressAdditionalInfo(address.metadataCollectionMap, document.querySelector("#metadata-collection")); +} + +// Method to reuse to populate the address additional info. Eg: Address components, Address metadata, and enrichment data. +function populateAddressAdditionalInfo(collectionMap, parentElement, elementTitle, collapsibleIndex) { + if (collectionMap.size > 0) { + let enrichmentDivContentElement = parentElement.getElementsByClassName("content")[0]; + parentElement.classList.remove("hidden"); + let divContentElement = enrichmentDivContentElement; + + // create child element(eg: CV, Geocodes and Premium Location Insight) under Enrichment + if (elementTitle) { + let categoryDivElement = document.createElement("div"); + const headerElement = document.createElement("h3"); + headerElement.innerText = elementTitle; + categoryDivElement.append(headerElement); + if (collapsibleIndex) { + let spanElement = document.createElement("span"); + spanElement.classList.add("collapsible"); + createCollapsibleELement(spanElement, collapsibleIndex); + + let childDivElement = document.createElement("div"); + childDivElement.classList.add('content'); + childDivElement.setAttribute('style', "display: none;"); + divContentElement = childDivElement; + categoryDivElement.append(spanElement, childDivElement); + } + enrichmentDivContentElement.append(categoryDivElement); + } + populateContent(collectionMap, divContentElement) + } +} + +// to create collapsible child elements(CV, Geocodes and Premium Location Insight) under Enrichment +function createCollapsibleELement(parentSpanElement, collapsibleIndex) { + let hideSpanElement = document.createElement("span"); + hideSpanElement.innerText = "[Hide]"; + hideSpanElement.classList.add("hide" + `${collapsibleIndex}`); + hideSpanElement.classList.add("hidden"); + + let showSpanElement = document.createElement("span"); + showSpanElement.innerText = "[Show]"; + showSpanElement.classList.add("show" + `${collapsibleIndex}`); + + parentSpanElement.append(hideSpanElement, showSpanElement); + // to add event listener to the Hide & Show elements + addCollapsibleEventListener(parentSpanElement, ".hide" + `${collapsibleIndex}`, ".show" + `${collapsibleIndex}`) +} + +// to populate the address information from collectionMap into the divElement +function populateContent(collectionMap, divElement) { + collectionMap.forEach((value, key) => { + const htmlSpanElement = document.createElement("span"); + let htmlBrElement = document.createElement("br"); + if (typeof value == "object") { + htmlSpanElement.innerText = `${key}: `; + divElement.append(htmlSpanElement, htmlBrElement); + addChildElement(Object.entries(value), divElement, 1, true) + } else { + // to add tooltip description for each entry from tooltipDescriptionMap + if (address.tooltipDescriptionMap.has(key)) { + let tooltipDivElement = document.createElement("div"); + tooltipDivElement.classList.add("tooltip"); + tooltipDivElement.innerText = `${value}`; + + let tooltipSpanElement = document.createElement("span"); + tooltipSpanElement.classList.add("tooltiptext"); + tooltipSpanElement.innerText = `${address.tooltipDescriptionMap.get(key)}`; + tooltipDivElement.append(tooltipSpanElement); + + htmlSpanElement.innerText = `${key}: `; + htmlSpanElement.append(tooltipDivElement); + } else { + htmlSpanElement.innerText = `${key}: ${value}`; + } + divElement.append(htmlSpanElement, htmlBrElement); + } + }); + divElement.append(document.createElement("br")); +} + +// to iterate over the entries to populate the content into divElement +function addChildElement(entries, divElement, level, addSubtitle) { + for (const [childKey, childValue] of entries) { + const htmlChildSpanElement = document.createElement("span"); + let htmlChildBrElement = document.createElement("br"); + + // to handle the indention for an entry + htmlChildSpanElement.classList.add('tab'); + htmlChildSpanElement.setAttribute("style", "--spaces: " + level * 2 + "em;"); + if (typeof childValue == "object") { + let childLevel = level; + if (addSubtitle) { + htmlChildSpanElement.innerText = `${childKey}: `; + childLevel = level + 1; + divElement.append(htmlChildSpanElement, htmlChildBrElement); + } + addChildElement(Object.entries(childValue), divElement, childLevel, + !(Array.isArray(childValue) && childValue.length === 1)); + } else { + htmlChildSpanElement.innerText = `${childKey}: ${childValue}`; + divElement.append(htmlChildSpanElement, htmlChildBrElement); + } + } +} + +// Hide the "metadata" container +function resetMetadata() { + document.querySelector(".metadata #confidence-key").innerText = ''; + document.querySelector(".metadata #confidence-value").innerText = ''; + document.querySelector(".metadata #delivery-type-key").innerText = ''; + document.querySelector(".metadata #delivery-type-value").innerText = ''; + document.querySelector(".metadata #what3words-key").classList.add("hidden"); + document.querySelector(".metadata #what3words-value").classList.add("hidden"); + + document.querySelector(".metadata").classList.add("invisible"); + + resetMetadataElements(document.getElementById("validated-address-info")); + + // to remove all components collection elements + resetMetadataElements(document.getElementById("components-collection"), true); + document.querySelector("#components-collection").classList.add("hidden"); + + // to remove all metadata collection elements + resetMetadataElements(document.getElementById("metadata-collection"), true); + document.querySelector("#metadata-collection").classList.add("hidden"); + + // to remove all enrichment elements + resetMetadataElements(document.getElementById("enrichment"), true); + document.querySelector("#enrichment").classList.add("hidden"); + + document.querySelector("#map").classList.add("hidden"); +} + +// to remove child elements that were created +function resetMetadataElements(parent, containsChildElements) { + if (containsChildElements) { + parent.querySelector(".hide").classList.add("hidden"); + parent.querySelector(".show").classList.remove("hidden"); + } + let divElements = parent.querySelectorAll(".content"); + divElements.forEach(div => { + div.style.display = containsChildElements ? "none" : "block"; + + if (containsChildElements) { + removeElements(div.getElementsByTagName("div")); + removeElements(div.getElementsByTagName("span")); + removeElements(div.getElementsByTagName("br")); + removeElements(div.getElementsByTagName("h3")); + } + }); +} + +// to remove all the elements passed +function removeElements(elementsToRemove) { + Array.from(elementsToRemove).forEach(element => element.remove()) +} + +// to handle expand and collapse +function onContentLoaded() { + let collapsibleDivs = document.querySelectorAll(".collapsible"); + collapsibleDivs.forEach(div => addCollapsibleEventListener(div, ".hide", ".show")) +} + +// to add collapsible event listener to Hide & Show +function addCollapsibleEventListener(element, hideElementName, showElementName) { + element.addEventListener('click', function () { + let nextElementSibling = this.nextElementSibling; + let parentElement = nextElementSibling.parentElement; + let hideElement = parentElement.querySelector(hideElementName); + let showElement = parentElement.querySelector(showElementName); + if (nextElementSibling.style.display === "block") { + nextElementSibling.style.display = "none"; + hideElement.classList.add("hidden"); + showElement.classList.remove("hidden"); + } else { + nextElementSibling.style.display = "block"; + hideElement.classList.remove("hidden"); + showElement.classList.add("hidden"); + } + }) +} + +document.addEventListener("DOMContentLoaded", onContentLoaded); \ No newline at end of file diff --git a/src/js/search-address-handling.js b/src/js/search-address-handling.js new file mode 100644 index 0000000..6378a4a --- /dev/null +++ b/src/js/search-address-handling.js @@ -0,0 +1,192 @@ +// Set the custom options +var options = { + searchType: 'autocomplete', + maxSuggestions: 10, + useSpinner: false, + elements: { + countryList: document.querySelector("select"), + address_line_1: document.querySelector("input[name='address_line_1']"), + address_line_2: document.querySelector("input[name='address_line_2']"), + address_line_3: document.querySelector("input[name='address_line_2']"), + locality: document.querySelector("input[name='locality']"), + region: document.querySelector("input[name='region']"), + postal_code: document.querySelector("input[name='postal_code']"), + country: document.querySelector("input[name='country']"), + lookupButton: document.querySelector("button#find-address-button") + } +}; + +// Try and read a token from localStorage +if (localStorage && localStorage.getItem('address-validation-token')) { + options.token = localStorage.getItem('address-validation-token'); +} + +// Initialise address validation +var address = new AddressValidation(options); +var addressValidationMap, addressValidationW3wMarker, addressValidationGeoMarker; + +// Accept a new token from the token prompt and set this in the AddressValidation class +function addToken() { + address.setToken(document.querySelector('[name="token"]').value); + document.querySelector('main').classList.remove('inactive'); + document.querySelector('.token-prompt').classList.add('hidden'); + + // Save the token in localStorage for next time + if (localStorage) { + localStorage.setItem('address-validation-token', document.querySelector('[name="token"]').value); + } +} + +// populate the country dataset dropdown with the authorized country datasets +address.events.on("post-datasets-update", function() { + let countryListElement = options.elements.countryList; + let optionElements = countryListElement.getElementsByTagName("option") + Array.from(optionElements).filter(option => option.innerText !== 'Please select').forEach(option => option.remove()) + + let countries = address.countryDropdown; + for (const country of countries) { + const optionElement = document.createElement("option"); + optionElement.setAttribute("value", country.iso3Code); + optionElement.innerText = country.country; + countryListElement.append(optionElement); + } +}); + +// Show the large spinner while we're searching for the formatted address +address.events.on("pre-formatting-search", function() { + document.querySelector(".loader").classList.remove("hidden"); +}); + +// Hide the large spinner when a result is found +address.events.on("post-formatting-search", function(data) { + document.querySelector(".loader").classList.add("hidden"); + document.querySelector("#validated-address-info").classList.remove("hidden"); + + if (data.result.confidence !== "No matches" || address.searchType === 'autocomplete') { + // Show the formatted address fields + document.querySelector(".formatted-address").classList.remove("hidden"); + document.querySelectorAll(".formatted-address .hidden").forEach(element => element.classList.remove("hidden")); + // Hide the promptset as we have now captured the address + document.querySelector('.promptset').classList.add('hidden'); + } + + // Populate the metadata section with more details about this address + populateMetadata(data); +}); + +address.events.on("post-formatting-lookup", function(key, item) { + document.querySelector(".loader").classList.add("hidden"); + document.querySelector("#validated-address-info").classList.add("hidden"); + document.querySelectorAll(".formatted-address").forEach(element => element.classList.remove("hidden")); + document.querySelector('.promptset').classList.add('hidden'); + + // Populate the metadata section with more details about this address + address.getLookupEnrichmentData(key); + document.querySelector(".metadata").classList.remove("invisible"); +}); + +// Hide the formatted address container again upon reset +address.events.on("post-reset", function() { + document.querySelector(".formatted-address").classList.add("hidden"); + resetMetadata(); + document.querySelector('.promptset').classList.remove('hidden'); + // to reset the Lookup type dropdown selected value + if (address.searchType === "lookupv2") { + let lookupType = document.getElementById("address-input-0"); + lookupType.getElementsByTagName("option")[0].selected = "true"; + + let addAddresses = document.getElementById("address-input-1"); + addAddresses.getElementsByTagName("option")[0].selected = "true"; + } +}); + +// Hide the loader if the request results in a 400 Bad Request error +address.events.on("request-error-400", function() { + document.querySelector(".loader").classList.add("hidden"); +}); + +// Prompt for a token if the request is unauthorised (token is invalid or missing) +address.events.on("request-error-401", function() { + document.querySelector('main').classList.add('inactive'); + document.querySelector('.token-prompt').classList.remove('hidden'); +}); + +// When the promptset is changed, update the form fields accordingly +address.events.on("post-promptset-check", function(response) { + const inputs = []; + let errorElement = document.querySelector('.error-display'); + if (!errorElement.classList.contains("hidden")) { + errorElement.classList.add('hidden'); + document.querySelector('.promptset').classList.remove('hidden'); + } + // Clear any previous address input form fields + document.querySelector('.address-field-inputs').innerHTML = ""; + + // Iterate over each new line and create a new label and input + response.result.lines.forEach((line, idx) => { + const label = document.createElement("label"); + label.setAttribute("for", `address-input-${idx}`); + label.innerText = line.prompt; + + let input; + if (line.dropdown_options) { + input = document.createElement("select"); + input.classList.add("address-input"); + input.setAttribute("id", `address-input-${idx}`); + line.dropdown_options.forEach((dropdownOption) => { + const optionElement = document.createElement("option"); + optionElement.setAttribute("value", dropdownOption.key); + optionElement.innerText = dropdownOption.display; + input.append(optionElement); + }); + } else { + input = document.createElement("input"); + input.classList.add("address-input"); + input.setAttribute("type", "text"); + input.setAttribute("id", `address-input-${idx}`); + + if (line.suggested_input_length) { + input.setAttribute("size", line.suggested_input_length); + } + + if (line.example) { + input.setAttribute("placeholder", line.example); + } + } + inputs.push(input); + + document.querySelector('.address-field-inputs').append(label, input); + }); + + // Register the event listeners on the new inputs + address.setInputs(inputs); + + // Hide or show a "Find address" button depending on the search type + document.querySelector("button#find-address-button").classList[ + (address.searchType !== "autocomplete") ? 'remove' : 'add']("hidden"); +}); + +// To display error when unsupported search type is selected +address.events.on("error-display", function (error) { + document.querySelector('.promptset').classList.add('hidden'); + resetMetadata(); + document.querySelectorAll('.formatted-address').forEach(element => element.classList.add('hidden')); + + let errorElement = document.querySelector('.error-display'); + errorElement.classList.remove('hidden'); + + const labelElement = errorElement.getElementsByTagName('label')[0]; + labelElement.innerText = error; +}); + +/* Demo specific code */ +// Allow the user to change the search type +document.querySelectorAll('.search-type-selector').forEach(panel => panel.addEventListener('click', togglePanel)); + +function togglePanel(e) { + // Toggle which panel should be selected + document.querySelectorAll('.search-type-selector').forEach(panel => panel.classList.remove('search-type-selected')); + e.currentTarget.classList.add('search-type-selected'); + + address.setSearchType(e.currentTarget.dataset.panelType); +} \ No newline at end of file diff --git a/src/ts/address-search.ts b/src/ts/address-search.ts index 36b4905..bca63dc 100644 --- a/src/ts/address-search.ts +++ b/src/ts/address-search.ts @@ -545,6 +545,7 @@ export default class AddressValidation { break; } case AddressValidationMode.UDPRN: { + this.returnAddresses = true; data = this.generateLookupDataForApiCall(this.currentSearchTerm, AddressValidationLookupKeywords.UDPRN.key); url = this.baseUrl + this.lookupV2Endpoint; headers = [{ key: 'Add-Addresses', value: true }]; diff --git a/webpack.config.js b/webpack.config.js index 815fd2a..0f62c92 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,7 +21,8 @@ module.exports = { new CopyPlugin({ patterns: [ { from: './src/images', to: path.resolve(__dirname, 'dist/images') }, - { from: './src/css', to: path.resolve(__dirname, 'dist/css') } + { from: './src/css', to: path.resolve(__dirname, 'dist/css') }, + { from: './src/js', to: path.resolve(__dirname, 'dist/js') } ], }), ],