BETA Unlocked: Address Input!

By all means please let us know how you have managed with connecting the Address Input to the Google Maps API.

One example for what you can do with this input element is to sort a list of places by their distance from the address a visitor has entered.

We hope to have an example that demonstrates a similar use case soon, but in a nutshell you can achieve this by:

  1. Creating a Data Collection that holds the information about the places. In your case you’ll have a row for each student apartment, and columns for the name of the place, its address etc.

  2. Connecting a Repeater to this collection using a Dataset. Each item in the repeater will display information about a single student apartment.

  3. Adding an event handler for the onChange event of AddressInput and use the value property to obtain the location of the entered address in lat/lon GEO coordinates. Then you can calculate its distance from that of each place’s location (use their value property the same way) and sort the repeater items accordingly.

Please let me know if this is detailed enough or not. As said, I hope we have an example soon.

HTH

@eyalc an example showing that would be absolutely perfect! I am building a site right now that needs to show restaurants under events, but i want to take the approach ‘Restaurants near this event’…

Hi @eyalc ,

I’m working with the address input but I realise that the valid method on the component only check if there is some text in the input field, not if an address has been selected.

It would be very useful to have an option that says: require valid address so the input is only valid if the user selected an address and not only if he typed something invalid

Thanks @quentin !
Yes, we plan to add an option to the Settings panel to give you control over whether visitors must select one of the suggestions (otherwise the Address Input switches into Error state), or are allowed to enter “free text”.
Will update when we start working on it.

I’ve received an update that it could take us a bit to publish an example for the Address Input (team in charge of the examples has higher priorities), so in the meantime I want to share with you a demo I created.

Frankly, the code (and explanations) is not on par with the standard of our examples, but I’ll do my best to help you with it.

First things first; head over to the demo , enter an address (in the UK) and find Cat Cafes nearby.

Next, let’s go over how I set up the Editor Elements and Data before I present the Corvid code.

Editor Elements used:

  1. Address Input

  2. Google Map

  3. Repeater with information on the Cat Cafes. Each Repeater Item holds 3 Text Elements:

  • Name of the Cat Cafe

  • Its address

  • Its distance from the entered address

Data Collection and Dataset setup:

  • The Collection has two fields: ‘Name’ (Text) and ‘Address’ (Address)

  • I manually entered the details of 10 Cat Cafes in the UK

  • Corresponding Dataset is Read-only and displays 20 items at a go
    Note that I made it easy on myself: because all items fit in a single Dataset Page I didn’t have to write code to handle multiple pages.

Address Input setup:

Repeater setup:

  • Connected to Cat Cafes Dataset

  • Connected Name & Address Text Elements to the corresponding fields

Google Map setup (AKA Welcome to the Realm of Workarounds):

  • Configured to Multiple Locations.

  • I would have liked to connect the element to the addresses in the Cat Cafes collection, but unfortunately this is not supported yet (hint hint).

  • As a fallback, I would have liked to retrieve these addresses in code and set them to the location property, but alas, setting multiple locations through code is not supported yet either (yes, hint hint, I get it).

  • So instead I manually entered 11 addresses: the first one is a placeholder that will be used to set the address the visitor will enter (I set its default location to Buckingham Palace), the other addresses are duplicates of those in the data collection.

Finally, the code:

HTH
I’m here for questions, which I bet you have :slight_smile:

  1. //===== Init =====

  2. var catCafesData = null ;

  3. export async function catCafes_ready() {

  4. // Fetch Cat Cafes data once (it is static) and keep it in a global variable. 
    
  5. catCafesData =  **await**  getCatCafesData($w('#catCafes')); 
    
  6. // Set the default address to be the first Google Maps location, which is used as the placeholder for the visitor entered address. 
    
  7. let DEFAULT_VISITOR_ADDRESS = $w(‘#googleMaps1’).location;

  8. // Display distances of all Cat Cafes from this address and sort them by it. 
    
  9. displayDistances(DEFAULT_VISITOR_ADDRESS); 
    
  10. sortByDistance(); 
    
  11. }

  12. function getCatCafesData(catCafesDataset) {

  13. return catCafesDataset.getItems(0, catCafesDataset.getPageSize())

  14. .then( (result) => { 
    
  15. return result.items;

  16. } ) 
    
  17. }

  18. //===== Event Handlers =====

  19. export async function addressInput1_change(event) {

  20. let address = $w(‘#addressInput1’).value;

  21. if (!address || !address.location) return ;

  22. viewAddressOnMap(address); 
    
  23. displayDistances(address.location); 
    
  24. sortByDistance(); 
    
  25. }

  26. //===== View address on Google Maps =====

  27. function viewAddressOnMap(address) {

  28. if  (!address || !address.location)  **return** ; 
    
  29. $w("#googleMaps1").location = { 
    
  30.     "latitude": address.location.latitude, 
    
  31.     "longitude": address.location.longitude, 
    
  32.     "description": address.formatted 
    
  33. }; 
    
  34. }

  35. //===== Display and sort Cat Cafes (Repeater items) by distance from an address =====

  36. function displayDistances(addressLocation) {

  37. let catCafeNameToDistance = catCafesData.reduce((map, item) => (map[item.title] = getDistanceFromLatLonInKm(addressLocation.latitude, addressLocation.longitude, item.address.location.lat, item.address.location.lng), map), {});

  38. // Add the distance of the address from each Cat Cafe on the Repeater data. We will need if for sorting. 
    
  39. $w("#catCafesInfo").data = $w("#catCafesInfo").data.map((item) => { item['distance'] = catCafeNameToDistance[item.title];  **return**  item}); 
    
  40. // Update the distance text indication for each Repeater item. 
    
  41. $w("#catCafesInfo").forEachItem( ($item, itemData, index) => { 
    
  42.     $item("#catCafeDistance").text = itemData.distance.toFixed(2) + ' km'; 
    
  43. } ); 
    
  44. }

  45. function sortByDistance() {

  46. $w("#catCafesInfo").data = $w("#catCafesInfo").data.sort( 
    
  47.     function  (item1, item2) { 
    
  48.         let  d1 = item1.distance; 
    
  49.         let  d2 = item2.distance; 
    
  50.         if  (d1 < d2) { 
    
  51.             **return**  -1; 
    
  52.         } 
    
  53.         if  (d1 > d2) { 
    
  54.             **return**  1; 
    
  55.         } 
    
  56.         **return**  0; 
    
  57.     } 
    
  58. ); 
    
  59. }

  60. //===== Lat/lon distance calculations =====

  61. //

  62. // I simply copied it from:

  63. // algorithm - Calculate distance between two latitude-longitude points? (Haversine formula) - Stack Overflow

  64. function deg2rad(deg) {

  65. return deg * (Math.PI/180)

  66. }

  67. function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {

  68. var  R = 6371; // Radius of the earth in km 
    
  69. var  dLat = deg2rad(lat2-lat1); 
    
  70. var  dLon = deg2rad(lon2-lon1); 
    
  71. var  a = 
    
  72. Math.sin(dLat/2) * Math.sin(dLat/2) +

  73. Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *

  74. Math.sin(dLon/2) * Math.sin(dLon/2);

  75. var  c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    
  76. var  d = R * c; // Distance in km 
    
  77. **return**  d; 
    
  78. }

@Eyal Michael Cohen could you format your code with the code formatter? It will be much more easy to read.

I worked myself on a small prototype for a dynamic map with multi-location :
https://quentin4686.wixsite.com/leg-me-up/listing-on-map

I’m using the new address element combine with a map embedded in an iframe

Regarding the function getDistanceFromLatLonInKm . Would it not be easier/clean to use some npm package surch as https://github.com/Turfjs/turf-distance and https://www.npmjs.com/package/geolib ?

if so, do you think you can get http://turfjs.org/ added to the npm package?

//===== Init =====

var catCafesData = null;

export async function catCafes_ready() {

// Fetch Cat Cafes data once (it is static) and keep it in a global variable.

catCafesData = await getCatCafesData($w('#catCafes'));

// Set the default address to be the first Google Maps location, which is used as the placeholder for the visitor entered address.

let DEFAULT_VISITOR_ADDRESS = $w('#googleMaps1').location;

// Display distances of all Cat Cafes from this address and sort them by it.

displayDistances(DEFAULT_VISITOR_ADDRESS);
sortByDistance();
}

function getCatCafesData(catCafesDataset) {
return catCafesDataset.getItems(0, catCafesDataset.getPageSize())
.then((result) => {
return result.items;
})
}


//===== Event Handlers =====

export async function addressInput1_change(event) {
let address = $w('#addressInput1').value;
if (!address || !address.location) return;
viewAddressOnMap(address);
displayDistances(address.location);
sortByDistance();
}

//===== View address on Google Maps =====

function viewAddressOnMap(address) {
if (!address || !address.location) return;
$w("#googleMaps1").location = {
"latitude": address.location.latitude,
"longitude": address.location.longitude,
"description": address.formatted
};
}

//===== Display and sort Cat Cafes (Repeater items) by distance from an address =====

function displayDistances(addressLocation) {
let catCafeNameToDistance = catCafesData.reduce((map, item) => (map[item.title] = getDistanceFromLatLonInKm(addressLocation.latitude, addressLocation.longitude, item.address.location.lat, item.address.location.lng), map), {});

// Add the distance of the address from each Cat Cafe on the Repeater data. We will need if for sorting.

$w("#catCafesInfo").data = $w("#catCafesInfo").data.map((item) => { item['distance'] = catCafeNameToDistance[item.title]; return item });

// Update the distance text indication for each Repeater item.

$w("#catCafesInfo").forEachItem(($item, itemData, index) => {
$item("#catCafeDistance").text = itemData.distance.toFixed(2) + ' km';
});
}

function sortByDistance() {
$w("#catCafesInfo").data = $w("#catCafesInfo").data.sort(
function (item1, item2) {
let d1 = item1.distance;
let d2 = item2.distance;
if (d1 < d2) {
return -1;
}
if (d1 > d2) {
return 1;
}
return 0;
}
);
}

//===== Lat/lon distance calculations =====

// I simply copied it from: https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula
 
function deg2rad(deg) {
return deg * (Math.PI / 180)
}

function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1);
var dLon = deg2rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}

@quentin I see Robert has beaten me to it.
About turfjs.org - sounds like a great idea. I’ll pass the request to the Corvid team.

@quentin I submitted a request add the turfjs package. Do mind though that only backend packages are supported, so the distance calculations would have to take place there.

@eyalc Yes I’m aware of that and that’s enough. Thank you :slight_smile:

@quentin Do you have a favourite between turfjs.org , geolib or any other library? If so, you can submit additional requests through the same link.

BTW, looks like turfjs has not been maintained much in the past two years.

@eyalc I’ll submit a request for geolib. Even Though it has less feature it has no dependency and is maintained.

I think both have their utility :slight_smile:

Pessoal, eu criei e inseri a API do Google Maps, mas não está funcionando no site ao vivo, alguém pode me ajudar?