How to convert a what3words address to a street address
We will convert a users input (with Auto Suggest) into a street address and allow them to choose the correct one.
- Introduction
- Prerequisites
- Setting up the HTML page
- Auto Suggest
- Converting a what3words address into a street address
- Displaying it all on a Woosmap Map
- Conclusion
Introduction
This guide will explain to you how to build the following integration:
This example is made to showcase how-to:
- help a user input their what3words address using the /autosuggest endpoint.
- use the /convert-to-address endpoint to return a list of possible street addresses.
- allow the user to choose suitable address from the suggestions returned and get details on it thanks to a /details` on Localities (and therefore benefit from the richness of the Localities API)
Prerequisites
You will need a Woosmap Public Key with both:
- the
what3words
products enabled (Localities
is automatically enabled withwhat3words
) - the
codesandbox.io
domain whitelisted
You can follow this tutorial to learn how set up your account,
And this one to learn about Woosmap API Keys domain restrictions.
Setting up the HTML page
We will create our index.html
first, this will give us divs to display information to the user and later display
the map.
The following is a basic html starting block:
<!DOCTYPE html>
<html>
<head>
<title>Woosmap what3words address to street address Converter</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app">
<div id="map"></div>
<div class="addressDetails">
<div class="info">
<div class="words"></div>
<div class="nearest"></div>
</div>
<div class="options"></div>
</div>
<div class="w3w-input-container">
<div class="w3w-input">
<input
id="input"
type="search"
placeholder="Your what3words (e.g. filled.count.soap)"
autocomplete="off"
/>
</div>
<div id="all-results-container">
<div class="autosuggest-results-container">
<div class="autosuggest-results-title">what3words results</div>
<div class="autosuggest-results"></div>
<div class="w3w-results-container">
<div class="w3w-results"></div>
</div>
</div>
<div class="address-list-container">
<div class="autosuggest-results-title">
Woosmap addresses results
</div>
<div class="address-list"></div>
</div>
</div>
</div>
</div>
<script src="src/index.js"></script>
<script src="https://sdk.woosmap.com/map/map.js?key=<YOUR_PUBLIC_API_KEY>&language=en"></script>
</body>
</html>
You shall also need some styling for your integration. You can copy this CSS file to your local integration. We’ve also created a utils.js file which contains some shortcuts to display/hide some part of the UI.
Auto Suggest
Now that we have the visual part to display our results, we can create the JavaScript part. First lets create the function to Auto Suggest possible what3words addresses to the user. This feature works similarly to our Localities API Autocomplete feature.
import {
hideSection,
displaySection,
clearSection,
getAddressListContainer,
getAllResultsContainer,
convertToAddress,
getAddressDetailsContainer,
getAddressList,
getResultsContainer,
getInput,
} from "./utils.js";
function autosuggestW3W(input) {
return fetch(
`https://api.woosmap.com/what3words/autosuggest?key=${window.woosmap_key}&input=${input}&clip-to-country=GB,FR`
).then((response) => response.json());
}
async function displayW3wSuggestion(event) {
const { value } = getInput();
value.replace('"', '\\"').replace(/^\s+|\s+$/g, "");
if ((value.match(/[.]/g) || []).length !== 2) {
hideSection(getResultsContainer());
hideSection(getAddressListContainer());
return;
}
hideSection(getAddressListContainer());
hideSection(getAddressDetailsContainer());
displaySection(getAllResultsContainer());
const results = document.querySelector(".autosuggest-results");
window.marker.setMap(null);
const { suggestions } = await autosuggestW3W(value);
clearSection(results);
const list = document.createElement("ul");
if (suggestions) {
suggestions.forEach((suggestion) => {
const item = document.createElement("li");
item.className = "suggestion";
item.id = suggestion.rank;
item.innerHTML = `<div class="words">${suggestion.words}</div><div class="nearest">Nearest place : ${suggestion.nearestPlace}</div>`;
item.dataset.word = suggestion.words;
item.onclick = () => {
w3wClickCallback(suggestion);
};
list.appendChild(item);
});
results.appendChild(list);
} else {
const failed = document.createElement("div");
failed.innerHTML = "Failed to load suggestions";
results.appendChild(failed);
}
displaySection(getResultsContainer(), "flex");
}
function w3wClickCallback(suggestion) {
document.querySelector(".words").innerHTML = suggestion.words;
document.querySelector(
".nearest"
).innerHTML = `Nearest place : ${suggestion.nearestPlace}`;
getInput().value = `///${suggestion.words}`;
getPossibleAddress(suggestion.words);
}
The displayW3wSuggestion()
works by first returning the state of the HTML page to normal. Then requests the
/autosuggest endpoint.
We also check the users input contains at least two .
before sending the inputs. This is
because the /autosuggest
endpoint needs at least 2 words to work properly.
From the response of the request a list of the suggestions is created and displayed. Listeners for a click event are
added to each suggestion. These listeners set the input field value to the suggestion which was clicked and then call
the getPossibleAddress()
function which we will create in the next part.
Converting a what3words address into a street address
Now that we can retrieve a what3words address, we need to create the functions to transform it into possible street addresses.
function convertToAddress(words) {
return fetch(
`https://api.woosmap.com/what3words/convert-to-address?key=${window.woosmap_key}&words=${words}`
).then((response) => response.json());
}
async function getPossibleAddress(words) {
hideSection(getResultsContainer());
displaySection(getAddressListContainer());
hideSection(getAddressDetailsContainer());
clearSection(getAddressList());
const { results } = await convertToAddress(words);
if (results) {
displayAddressList(results);
}
}
function displayAddressList(addressDetails) {
const addressListContainer = getAddressList();
const addressList = document.createElement("ul");
if (!Array.isArray(addressDetails) || addressDetails.length === 0) {
const line = document.createElement("li");
line.className = "not-found";
line.innerHTML =
"what3words address invalid or no street address found within 200m of its location.";
addressList.appendChild(line);
} else {
addressDetails.forEach((address, index) => {
const line = document.createElement("li");
line.className = "address";
line.addEventListener("click", (event) =>
getAddressDetails(event.target, address.public_id)
);
line.innerHTML = `<span class='pin'></span>${address.formatted_address}`;
addressList.appendChild(line);
if (index === 0) {
getAddressDetails(line, address.public_id);
}
});
}
addressListContainer.appendChild(addressList);
}
There is a lot more happening in this file. The main entrypoint is of course the function we called earlier
getPossibleAddress()
. This works in a similar way as before, we clear the state of the integration and then we make a
request to the
/convert-to-address
endpoint. The response from this call is then displayed as a list of street address suggestions, each one of them containing a public_id
.
Use this public_id
to request Localities details endpoint and retrieve geometry (lat/lng) and address components. You are now ready to display results on a map with a marker and populate the information container in the bottom right of the map.
function getDetail(publicId) {
return fetch(
`https://api.woosmap.com/localities/details/?key=${window.woosmap_key}&public_id=${publicId}`
).then((response) => response.json());
}
async function getAddressDetails(target, publicId) {
if (window.selectedAddress) {
window.selectedAddress.classList.remove("selected");
}
window.selectedAddress = target;
window.selectedAddress.classList.add("selected");
const detailResponse = await getDetail(publicId);
const addressDetails = detailResponse.result;
if (addressDetails) {
createAddressMarker(addressDetails);
fillAddressDetails(addressDetails);
displaySection(getAddressDetailsContainer());
}
}
The code above fetches the detailed data for the first address of the list in order to display it automatically on the map. The other addresses are fetched on click. Then we can fill the detailed information container using the full address information.
function fillAddressDetails(addressDetails) {
const detailsHTML = document.querySelector(".addressDetails .options");
detailsHTML.innerHTML = "";
if (addressDetails.street_public_id) {
detailsHTML.innerHTML += `<p class='option-detail'><span class='option-detail-label'>Street public id :</span><span class='bold'>${addressDetails.street_public_id}</span></p>`;
}
if (addressDetails.formatted_address) {
detailsHTML.innerHTML += `<p class='option-detail'><span class='option-detail-label'>Formatted_address :</span><span class='bold'>${addressDetails.formatted_address}</span></p>`;
}
if (addressDetails.types && addressDetails.types[0]) {
detailsHTML.innerHTML += `<p class='option-detail'><span class='option-detail-label'>Type : </span><span class='bold'>${addressDetails.types[0].replace(
"_",
" "
)}</span></p>`;
}
if (addressDetails.geometry) {
const locationTypeString = addressDetails.geometry.location_type;
if (locationTypeString) {
detailsHTML.innerHTML += `<p class='option-detail'><span class='option-detail-label'>Location type :</span> <span class='bold'>${locationTypeString
.replace("_", " ")
.toLowerCase()}</span></p>`;
}
detailsHTML.innerHTML += `<div class='option-detail'><div><span class='option-detail-label'>Latitude :</span> <span class='bold'>${addressDetails.geometry.location.lat.toString()}</span></div><div><span class='option-detail-label'>Longitude : </span><span class='bold'>${addressDetails.geometry.location.lng.toString()}</span></div></div>`;
if (addressDetails.address_components) {
let compoHtml = "";
addressDetails.address_components.forEach((compo) => {
compoHtml += `<p class='option-detail'><span class='option-detail-label'>${compo.types[0]}:</span> <span class='bold'>${compo.long_name}</span></p>`;
});
detailsHTML.innerHTML += `<div class='address-components'><div class='title'>Address components</div><div>${compoHtml}</div>`;
}
}
}
Displaying it all on a Woosmap Map
Of course, it’s hard to put a marker on a Map without a Map, so the last Javascript file we need to create is
src/index.js
. In here we will initialise the input listener and create the Woosmap Map.
Creating a Woosmap Map is super easy…
function initMap() {
const mapElement = getMap();
window.myMap = new woosmap.map.Map(mapElement, {
center: {
lat: 48.8534,
lng: 2.3488
},
disableDefaultUI: true,
gestureHandling: "greedy",
zoom: 5,
styles: {
featureType: "poi",
stylers: [{ visibility: "off" }]
}
});
window.marker = new woosmap.map.Marker({
clickable: true,
icon: {
url: "https://www.woosmap.com/images/marker.png",
scaledSize: {
height: 59,
width: 37
}
}
});
window.selectedW3W = null;
}
function initUI() {
getInput().addEventListener("input", debounce(displayW3wSuggestion, 200));
}
initMap();
initUI();
The listener on the input uses a debounce function to stop making multiple requests from quick user input.
The panMap
function centers the map on the address taken as parameter.
The debounce
and panMap
functions are defined in the utils.js file.
Conclusion
Voilà, easily take your users what3words address and convert it into a street address.
We hope this how-to guide shows you the power of our partnership with what3words. This integration is a great example of how to use these APIs in a few different use cases. For example, taking users address quickly at checkout or to allow delivery drivers to take a what3words address but get an actual street address to route too.