Geocoding Multiple Addresses to Google Maps

Hi All –

I know that you can use Wix Fetch, combined with Google Maps API to geocode addresses. However, is it possible to geocode multiple addresses and map them all simultaneously? I don’t think I would have a problem writing up a loop that codes all of my addresses, but my current understanding of the Wix Map is that you can only show one address. Any thoughts? While we’re at it, is it possible to build a customer “marker” or “map pin”?

1 Like

Hi David,

Unfortunately, you can’t do this using Wix 's Google map. but you can do this using HTML component.
Here you can find a full guide for how to use Google maps in an HTML file.
You only need the explanation for how to set locations.

Then, use an HTML component (I know you know how :slight_smile: ), and send the data (locations) using its ‘postMessage’ function.

Listen to that data inside the component, and set it to the map.

Hope this helps,
Liran.

Ahhh, perfect! I’m thinking I can just geocode all of my addresses with a for loop/fetch API. Then I’ll postMessage with the newly geocoded coordinates to HTML file.

This is on my to-do list, but I’ll try to post back here once I’ve written the code.

Thanks Liran

1 Like

Liran,

I’ve just about got this, but am struggling writing a loop to geocode the addresses before I add their markers to the map.

Essentially, I’ve created an array of addresses from my Wix database, fed that into the Google API/Fetch and then return the geocoded coordinates. Unfortunately, it’s not working.

In the below code, if I remove the second loop it works perfectly. When adding the loop, it works, but one time. It’s not looping.

Thoughts?

import {fetch} from 'wix-fetch';
import wixData from 'wix-data';

let addresses = [];
let locations = [];

$w.onReady(function () {

	wixData.query("Project_Database")
			  .find()
			  .then( (results) => {
			    let projItems = results.items;
			    let itemsCount = results.items.length;
				
					for (var i = 0; i < itemsCount; i++) {
								
					addresses.push({
						"address": projItems[i]["streetAddress"] + ", " + projItems[i]["city"] + ", " + projItems[i]["state"]
					});
					}
					
for (var x = 0; x < addresses.length; x++) {	
	var url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + addresses[x].address + "&key=[API KEY]";

return fetch (url, {method: 'get'})
	.then( (httpResponse) => {
    if (httpResponse.ok) {
	   	console.log("HTTP OK");
      return httpResponse.json();
    }
  	} )
  	   	.then( (json) => {	
//  		console.log(JSON.stringify(json.results));
			locations.push({
			  	info: json.results[0].address_components[2].long_name,
				lat: json.results[0].geometry.location.lat,
				long: json.results[0].geometry.location.lng
			});
			console.log(locations);
		});
	
	}

});	

});

I think that removing the ‘return’ statement before your ‘fetch call’ will do.
You don’t want to return, you actually want to make fetch and then do something.

BUT,
This return is used for when putting the function in the backend code (which I suggest you do anyway, since you’re having you API key here).

See this example for calling an API from the backend.

So my suggestion is:
Create a backend function that look something like that:

function getLocationForAddress(address) {
  var url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + 
  "&key=[API KEY]"; 
  return fetch (url, {method: 'get'}).then( (httpResponse) => { 
    if (httpResponse.ok) { 	   	
      console.log("HTTP OK"); 
      return httpResponse.json(); 
    }
}

From your frontend code, use second loop as follows:

for (var x = 0; x < addresses.length; x++) {
    getLocationForAddress(addresses[i]).then(json => {
        //Do whatever you want with it: locations.push
    });
}

Hope this helps,

Liran.

So.Close…I can’t get the messaging between page code and HTML to work. The page code, which leverages the backend API call works perfectly. It creates a geocoded array of the addresses.

The HTML code also works perfectly, if I do not send any data into it and just hard code my geocoded addresses.

However, if I try to send the geocoded array of addresses to the HTML page, then use that to map it. It does not work. I’ve pasted below the page code and the HTML code, but bolded the messaging between the two, as I believe that is what is causing the problem.

I think this is the final step t o get this to work.

Thoughts?

Front End Code:

import {getLocationForAddress} from 'backend/googleMaps.jsw';
import {fetch} from 'wix-fetch';
import wixData from 'wix-data';

let addresses = [];
let locations = [];

$w.onReady(function () {
						
	wixData.query("Project_Database")
			  .find()
			  .then( (results) => {
			    let projItems = results.items;
			    let itemsCount = results.items.length;
				
					for (var i = 0; i < itemsCount; i++) {
					addresses.push({
						"address": projItems[i]["streetAddress"] + ", " + projItems[i]["city"] + ", " + projItems[i]["state"]
					});
					}
	
	let requests = 0;			
	for (var x = 0; x < addresses.length; x++) {	
		
		getLocationForAddress(addresses[x].address)
		.then( (geocode) => {
			requests++;
			
			locations.push({
			  	"info": JSON.stringify(geocode.results[0].formatted_address),
				"lat": JSON.stringify(geocode.results[0].geometry.location.lat),
				"long":JSON.stringify(geocode.results[0].geometry.location.lng),
				"count": requests-1
		}


			);
			   	console.log(requests);    
			   	if (requests === addresses.length) {
					console.log(locations);
					$w("#html1").postMessage(locations);
					
				}

		});		
	
	}
	
});	

});

<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google Maps APIs</title>

	<link href="style.css" rel="stylesheet">
</head>

<body>

	<div id="map"></div>

	<script src="script.js"></script>
	<script async defer 
					src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBFSeA4RlkyHhUudy7bQHwKEHrcc9WHDO8&callback=initMap"></script>
</body>

</html>

<style>

	html,
body {
	height: 100%;
	margin: 0;
	padding: 0;
}
#map {
	height: 100%;
}

	</style>

	<script>

window.onmessage = (event) => {
    if (event.data) {
       let receivedData = event.data;
    }
  };

function initMap() {

	// Wix Info
    
		var locations = receivedData;

	var map = new google.maps.Map(document.getElementById('map'), {
		zoom: 13,
		center: new google.maps.LatLng(35.223603, -80.836319),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	});

	var infowindow = new google.maps.InfoWindow({});

	var marker, i;

	for (i = 0; i < locations.length; i++) {
		marker = new google.maps.Marker({
			position: new google.maps.LatLng(locations[i].lat, locations[i].long),
			map: map
		});

		google.maps.event.addListener(marker, 'click', (function (marker, i) {
			return function () {
				infowindow.setContent(locations[i].info);
				infowindow.open(map, marker);
			}
		})(marker, i));
	}
}

	</script>

Seems like your ‘initMap()’ function runs upon script load:

<script async defer  					
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBFSeA4RlkyHhUudy7bQHwKEHrcc9WHDO8&callback=initMap"></script>

This means that when the google maps script is loaded in the HTML component, initMap() function runs.
What you really want is for it to run when receiving the data from the page:

window.onmessage = (event) => { 
    if (event.data) { 
        let receivedData = event.data; 
    } 
};

Your just setting a ‘let receivedData’ and then do nothing.

Try this:

window.onmessage = (event) => { 
    if (event.data) { 
        let receivedData = event.data; 
        initMap(receivedData);
    } 
};

Don’t forget to add the parameter to initMap() that way:

function initMap(receivedData) {

Hope this helps,
Liran.

So sick! Thanks Liran – Works great.

I also added some code to test out using custom image markers from Flaticon.com and then also added small code to pass data back to Wix Code on click. Pasting code here for reference:

<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google Maps APIs</title>
	<script src="/path/to/markerwithlabel.js" type="text/javascript"></script>
	<link href="style.css" rel="stylesheet">
</head>

<body>

	<div id="map"></div>

	<script src="script.js"></script>
	<script async defer 
					src="https://maps.googleapis.com/maps/api/js?key=[API KEY]&callback=initMap"></script>
</body>

</html>

<style>

	html,
body {
	height: 100%;
	margin: 0;
	padding: 0;
}
#map {
	height: 100%;
}

	</style>

	<script>
			
window.onmessage = (event) => { 
    if (event.data) { 
        let receivedData = event.data; 
        initMap(receivedData);
    } 
};

 function initMap(receivedData) {
    
		var locations = receivedData;

	var map = new google.maps.Map(document.getElementById('map'), {
		zoom: 13,
		center: new google.maps.LatLng(35.223603, -80.836319),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	});

	var infowindow = new google.maps.InfoWindow({});

	// var marker, i;
	
	// Upper Bound
	
	var markerIcon = {
	  url: 'http://image.flaticon.com/icons/svg/252/252025.svg',
	  scaledSize: new google.maps.Size(40, 40),
	  origin: new google.maps.Point(0, 0),
	  anchor: new google.maps.Point(32,65)
};

	var markerLabel = " "

	// Lower Bound

	for (i = 0; i < locations.length; i++) {
		marker = new google.maps.Marker({
			map: map,
			animation: google.maps.Animation.DROP,
			icon: markerIcon,
			position: new google.maps.LatLng(locations[i].lat, locations[i].long),			
			label: {
			    text: markerLabel,
			    fontSize: "16px",
    			fontWeight: "bold"
			  },
			labelAnchor: new google.maps.Point(18, 12),
  			labelClass: "my-custom-class-for-label"
		});

		google.maps.event.addListener(marker, 'click', (function (marker, i) {
			return function () {
				infowindow.setContent(locations[i].info);
				infowindow.open(map, marker);
				window.parent.postMessage(locations[i].count, "*");
			}
		})(marker, i));
	}
}

	</script>
2 Likes

Hello David,

I’m trying your code but I cannot make it happen… Can you please help with this?

Kevin – Can you paste your page code and HTML? I’ll see if anything sticks out.

Thanks David!

Page code is actually yours above… Do I need to change it too? Also, Have you created a .js or .jsw file?

Please find the html with Google API:

Google Maps APIs
<div id="map"></div> 

<script src="script.js"></script> 
<script async defer  
				src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCeFTy8Y77Lj62rE9uTbPL3E1HYMPdPQW8&callback=initMap"></script> 
html, body { height: 100%; margin: 0; padding: 0; } #map { height: 100%; }
<script> 

window.onmessage = (event) => {
if (event.data) {
let receivedData = event.data;
initMap(receivedData);
}
};

function initMap(receivedData) {

	var locations = receivedData; 

var map = new google.maps.Map(document.getElementById('map'), { 
	zoom: 13, 
	center: new google.maps.LatLng(50.8142175, 5.164454), 
	mapTypeId: google.maps.MapTypeId.ROADMAP 
}); 

var infowindow = new google.maps.InfoWindow({}); 

// var marker, i; 

// Upper Bound 

var markerIcon = { 
  url: 'http://image.flaticon.com/icons/svg/252/252025.svg', 
  scaledSize: new google.maps.Size(40, 40), 
  origin: new google.maps.Point(0, 0), 
  anchor: new google.maps.Point(32,65) 

};

var markerLabel = " " 

// Lower Bound 

for (i = 0; i < locations.length; i++) { 
	marker = new google.maps.Marker({ 
		map: map, 
		animation: google.maps.Animation.DROP, 
		icon: markerIcon, 
		position: new google.maps.LatLng(locations[i].lat, locations[i].long),			 
		label: { 
		    text: markerLabel, 
		    fontSize: "16px", 
			fontWeight: "bold" 
		  }, 
		labelAnchor: new google.maps.Point(18, 12), 
		labelClass: "my-custom-class-for-label" 
	}); 

	google.maps.event.addListener(marker, 'click', (function (marker, i) { 
		return function () { 
			infowindow.setContent(locations[i].info); 
			infowindow.open(map, marker); 
			window.parent.postMessage(locations[i].count, "*"); 
		} 
	})(marker, i)); 
} 

}

</script>

Hi David, Have you seen my code?

Thanks

Hmmm, unfortunately, I cannot spot anything Kevin. Perhaps someone else with more formal html experience can help out.

Kevin – I think I may be experiencing the same issue as you. For whatever, reason in Preview Mode my code works perfectly and maps the addresses. However, when I go live (Yes, I’ve copied sandbox to live data) it does not work on load.

I know for a fact that the code works, because when I am in live mode, while it doesn’t load onReady, if I create a button that “refreshes” the code, it pins all the locations to the map exactly as intended.

Liran – Do you have any idea what is causing this?

David, I had exactly the same problem: working in Preview, not in Live. Turned out that it was a timing problem. In Preview, the HTML-comp is already loaded (because difference between Design/Edit Mode and Preview is just a CSS-class taking away some controls).
Live, the comp has to be loaded, HTML parsed, DOM populated etc. If you do not wait for it to fully load, the sendmessage in your Wix code is executed before the comp is ready to receive.
Solution: start the conversation from the HTML comp when it is ready (in the body onLoad you call a function that sends a message, a heart beat. ANy message will do (“Elvis lives”).
On the Wix side you wait for this heart beat with onMessage and THEN send your data to the comp.
Hope this helps.

1 Like

Interesting – Ok I think this makes sense! This whole sync/async/timing of code execution is definitely throwing me off as I advance the skills. I was able to fix it for the time being. Much appreciated.

David, I need to do something similar with Maps. Would you be interested in sharing your latest code and in return I implement the roll-your-own onReady for the html-comp and share it back?

David, I need to do something similar with Maps. Would you be interested in sharing your latest code and in return I implement the roll-your-own onReady for the html-comp and share it back?

Here’s what I have at the moment. Let me know if this is what you need. Currently, the following code allows for the following:

  1. Ability to retrieve long/lat coordinates from an address
  2. Map line items with coordinates to a Google Map
  3. When a map icon is clicked, it sends back data to Wix Code, which in my case I have used to populate a separate side menu corresponding to hte clicked item.

Page Code:

function mapItems (projItems) {
			    let itemsCount = projItems.length;
				
					let locations = [];
					let requests = 0;
					for (var i = 0; i < itemsCount; i++) {
					requests++;			
						locations.push({
						  	"info": projItems[i]["oppTitle"],
							"lat": projItems[i]["lat"],
							"long":projItems[i]["long"],
							"count": projItems[i]["_id"],
						});
						
						if (requests === itemsCount) {
						$w("#html2").postMessage(locations);
						
						}

					}			
	
}

HTML Code:

<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google Maps APIs</title>
	<script src="/path/to/markerwithlabel.js" type="text/javascript"></script>
	<link href="style.css" rel="stylesheet">
</head>

<body>

	<div id="map"></div>

	<script src="script.js"></script>
	<script async defer 
					src="https://maps.googleapis.com/maps/api/js?key="APIKEY"&callback=initMap"></script>
</body>

</html>

<style>

	html,
body {
	height: 100%;
	margin: 0;
	padding: 0;
}
#map {
	height: 100%;
}

	</style>

	<script>
			
window.onmessage = (event) => { 
    if (event.data) { 
        let receivedData = event.data; 
        initMap(receivedData);
    } 
};

 function initMap(receivedData) {
    
		var locations = receivedData;

	var map = new google.maps.Map(document.getElementById('map'), {
		zoom: 13,
		center: new google.maps.LatLng(35.223603, -80.836319),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	});

	var infowindow = new google.maps.InfoWindow({});

	// var marker, i;
	
	// Upper Bound
	
	var markerIcon = {
	  url: 'http://image.flaticon.com/icons/svg/252/252025.svg',
	  scaledSize: new google.maps.Size(40, 40),
	  origin: new google.maps.Point(0, 0),
	  anchor: new google.maps.Point(32,65)
};

	var markerLabel = " "

	// Lower Bound

	for (i = 0; i < locations.length; i++) {
		marker = new google.maps.Marker({
			map: map,
			animation: google.maps.Animation.DROP,
			icon: markerIcon,
			position: new google.maps.LatLng(locations[i].lat, locations[i].long),			
			label: {
			    text: markerLabel,
			    fontSize: "16px",
    			fontWeight: "bold"
			  },
			labelAnchor: new google.maps.Point(18, 12),
  			labelClass: "my-custom-class-for-label"
		});

		google.maps.event.addListener(marker, 'click', (function (marker, i) {
			return function () {
				infowindow.setContent(locations[i].info);
				// infowindow.open(map, marker);
				window.parent.postMessage(locations[i].count, "*");
			}
		})(marker, i));
	}
}

	</script>

You’ll also need to make sure the addresses in the dataset have longitude and latitude, if not here is the back-end code the take an address and find the longitude and latitude.


export function getLocationForAddress(address) {
	var url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=AIzaSyBFSeA4RlkyHhUudy7bQHwKEHrcc9WHDO8";
//	console.log(url);
	return fetch (url, {method: 'get'})
		.then( (httpResponse) => {
	    if (httpResponse.ok) {
//		   	console.log("HTTP OK");
	      return httpResponse.json();
    	}
		});

}
1 Like

Thanks David, I am going to work on it next week and I will post back the result. Have a good weekend.