I am currently plotting multiple points on a map, using addresses from an object. The program loops over the object, geocodes the address, and plots a marker for each location.
The problem I am having is when a user clicks on a place in a list, the map is to pan to that location. The API has a panTo() function that accepts lat, lng values, but the results, i.e. results[0].geometry.location, from the geocode function are not available outside of it.
Question
How do I somehow retrieve the lat, lng from the results, maybe append them to the existing data object, and use them outside the function, so I am able to use them in the panTo() function?
[The lat/lng values are output in html data attributes]
Click handler
$('.place').on('click', function() {
$this = $(this);
lat = $this.data('lat');
lng = $this.data('lng');
map.panTo(new google.maps.LatLng(lat, lng));
});
Data
var locations = [
{
id: 'place1',
postcode: 'B1 1AA'
},
{
id: 'place2',
postcode: 'CB9 8PU'
}
];
Code
for (i = 0; i < locations.length; i++) {
geocoder.geocode({'address': locations[i].postcode}, function(results, status) {
if(status == google.maps.GeocoderStatus.OK) {
marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
} else {
console.log('Geocode was not successful ' + status);
}
}); // end geocode
$(places).append(
'<li class="place" data-id="'+locations[i].id+'" data-lat="<!--lat to go here-->" data-lng="<!--lng to go here-->">'+
'<div class="place-wrap">'+
'<h2>'+locations[i].name+'</h2>'+
'<p class="territory">'+locations[i].territory+'<p>'+
'</div>'+
'</li>'
);
} // end for
Give this a go. The IIFE is there to enclose the index so you don't keep hitting the last index of the locations array due to the asynchronous nature of geocode.
for (i = 0, l = locations.length; i < l; i++) {
(function(i) { // index passed in as parameter
geocoder.geocode({ address: locations[i].postcode }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
$('#places').append(
'<li class="place" data-id="' + locations[i].id + '" data-lat="' + results[0].geometry.location.lat() + '" data-lng="' + results[0].geometry.location.lng() + '">' +
'<div class="place-wrap">' +
'<h2>' + locations[i].name + '</h2>' +
'<p class="territory">' + locations[i].territory + '<p>'+
'</div>' +
'</li>'
);
} else {
console.log('Geocode was not successful ' + status);
}
});
}(i)); // index passed in to IIFE
}
Related
THe following code loops through a JSON array which contains 2 values - city_name and counter and places a pin on the map.
I am trying to display the city_name and counter on an info popup on the Google map, but the variables dont change after the first loop.
e.g. The last item in the JSON is Blackpool, but on all loops it remains Blackpool and will always be Blackpool.
function initMap() {
var resultsNonJSON = document.getElementsByName('tbResults')[0].value;
var jsonResults = JSON.parse(resultsNonJSON);
var geocoder = new google.maps.Geocoder();
var myLatLng = {lat: 53.810066, lng: -1.776427};
var votes;
var town;
var map = new google.maps.Map(document.getElementById('dvMap'), {
center: {lat: 54.636633, lng: -2.952166},
zoom: 6
});
for(var i = 0; i < jsonResults.length; i++) {
var obj = jsonResults[i];
town = obj.city_name;
geocoder.geocode({'address': obj.city_name.concat(", UK")}, function(results, status) {
if (status == 'OK')
{
var contentString = '<div id="content">'+
'<div id="siteNotice"></div>'+
'<h1 id="firstHeading" class="firstHeading">' + town + '</h1>'+
'<div id="bodyContent">'+
'</div>'+
'</div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map,
title: obj.city_name,
icon: {
url: "https://maps.google.com/mapfiles/ms/icons/green-dot.png"
}
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
}
else
{
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
}
Any ideas why this is happening? Also if I was to show the i value, it will always be 7. It seems to stop changing the variables after the geocode is called.
You are calling an Asynchronous code inside an array loop. In your case,
geocode is an Asynchronous function, and the result that you get in the callback function might be delayed for several reasons, and also this is not predictable.
There are various solutions to fix this.
One way to fix this is to use let scoped variable instead of var in your for loop if you are using ES6. See the example below.
let list = ['Canada', 'US', 'Japan', 'Mexico'];
for (let i = 0; i < list.length; i++) {
geoCode(list[i], function(index) {
createMarker(list[i]); // test the value (let vs var)
});
}
function geoCode(item, cb) {
setTimeout(function() {
cb(item);
}, 4000);
}
function createMarker(i) {
console.log(i);
}
In your case, just change
for(var i = 0; i < jsonResults.length; i++)
to
for(let i = 0; i < jsonResults.length; i++)
and move town variable inside the loop
for (let i = 0; i < jsonResults.length; i++) {
var obj = jsonResults[i];
geocoder.geocode({ 'address': obj.city_name.concat(", UK") }, function (results, status) {
if (status == 'OK') {
town = jsonResults[i].city_name;
Next solution is to use closure inside the loop. We need to wrap your async function with anonymous IIFE.
(function (town) {
geocoder.geocode({ 'address': obj.city_name.concat(", UK") }, function (results, status) {
if (status == 'OK') {
var contentString = '<div id="content">' +
'<div id="siteNotice"></div>' +
'<h1 id="firstHeading" class="firstHeading">' + town + '</h1>' +
'<div id="bodyContent">' +
'</div>' +
'</div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map,
title: obj.city_name,
icon: {
url: "https://maps.google.com/mapfiles/ms/icons/green-dot.png"
}
});
marker.addListener('click', function () {
infowindow.open(map, marker);
});
}
else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
})(town);
I have custom google map multiple markers.when i am using reverse geolocation for multiple marker i am receiving unknown adddress for most of the markers if i use alert btwn the call i get the result for most address
for(var z = 0 ; z < markersArray.length; z++){
alert(z);
geocoder.geocode({'location': markersArray[z].position}, function(results, status) {
if(status == 'OK'){
if(results != null){
address = address + z +" : "+ results[1].formatted_address + "<br>";
alert(z);
}
else
{
address = address + z +" : "+"Unknown Address" + "<br>";
}
}
else{
alert(z);
address = address + z +" : "+"Unknown Address"+ "<br>";
}
});
}
The geocoder is asynchronous. By the time the callback function runs the loop has completed and z is equal to markersArray.length (which isn't a valid entry of the array). This problem can be solved with function closure (example in [this question: Google Maps JS API v3 - Simple Multiple Marker Example for the marker click listener functions.
for (var z = 0; z < markersArray.length; z++) {
geocoder.geocode({
'location': markersArray[z].position
}, (function(z) {
// get function closure on z
return function(results, status) {
if (status == 'OK') {
if (results != null) {
var address = z + " : " + results[0].formatted_address + "<br>";
var marker = new google.maps.Marker({
position: markersArray[z].position,
title: address,
map: map
});
bounds.extend(marker.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(marker,'click', function(evt) {
infowindow.setContent(address);
infowindow.open(map, this);
})
} else {
var address = z + " : " + "Unknown Address" + "<br>";
}
} else {
var address = z + " : " + "Unknown Address" + "<br>";
}
}
})(z));
}
proof of concept fiddle
code snippet:
var geocoder;
var map;
var markersArray = [{position: {lat: 40.7127837,lng: -74.0059413}},
{position: {lat: 40.735657,lng: -74.1723667}},
{position: {lat:39.2903848,lng: -76.6121893}},
{position: {lat: 39.9525839,lng: -75.1652215}}];
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
geocoder = new google.maps.Geocoder();
var infowindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
for (var z = 0; z < markersArray.length; z++) {
geocoder.geocode({
'location': markersArray[z].position
}, (function(z) {
// get function closure on z
return function(results, status) {
if (status == 'OK') {
if (results != null) {
var address = z + " : " + results[0].formatted_address + "<br>";
var marker = new google.maps.Marker({
position: markersArray[z].position,
title: address,
map: map
});
bounds.extend(marker.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(marker, 'click', function(evt) {
infowindow.setContent(address);
infowindow.open(map, this);
})
} else {
var address = z + " : " + "Unknown Address" + "<br>";
}
} else {
var address = z + " : " + "Unknown Address" + "<br>";
}
}
})(z));
}
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas"></div>
Try to add a short delay between the calls. Had the same problem once, checking the result of each call and try resending them a couple times helped.
For each location I'm adding a png icon.
In my database I have many entries with the same lat and lng. Is there a way I can find the entries with the duplicate lat and lng and apply a different icon?
function displayStores(result){
if (result.length > 0){
for (i=0;i<result.length;i++){
//Append Store Address on Sidebar
var html = getEmbedHTML(i+1,result[i].name,result[i].address,result[i].distance);
$("#divStores").append(html);
//place a marker
var image = 'images/number_' + parseInt(i+1) + '.png';
var latlng = new google.maps.LatLng(result[i].lat,result[i].lng);
var marker = new google.maps.Marker({
position: latlng,
map: map,
icon: image
});
var msg = 'Location : ' + result[i].name + '<br/> ';
msg = msg + 'Address : ' + result[i].address + '<br/> ';
attachMessage(marker, msg);
}
} else {
$("#divStores").html('No Stores Found');
}
}
You can sort the points and compute the difference. When it's greater then 0 then you can change the icon.
I have an issue with getting longitude and latitude values geocoded and then display the relevant address in an info window. I have tried multiple ways without success, however I must admit that I am not so familiar with javascript. I am continuously getting back as value : "undefined".
Here is a snippet of my code showing the main components:
var position = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var geocoder = new google.maps.Geocoder();
var address;
if (geocoder) {
geocoder.geocode({ 'latLng': position }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
address = (results[0].formatted_address);
} else {
address = (position.coords.latitude + ', ' + position.coords.longitude);
}
});
}
var info =
('<span class="txt_bld">Location:</span> ' + address + '<br />' +
'<span class="txt_bld">Accuracy:</span> ' + position.coords.accuracy + '<br />' +
'<span class="txt_bld">Time:</span> ' + position.timestamp);
Can anyone tell me how I can translate the lat/lng in position to an address in order to show them in my infowindow?
EDIT
Updated Code:
var position = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
var geocoder = new google.maps.Geocoder();
var infowindow = new google.maps.InfoWindow();
var address;
if (geocoder) {
geocoder.geocode({ 'latLng': position }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
address == (results[0].formatted_address);
} else {
address == (position.coords.latitude + ', ' + position.coords.longitude);
}
var info =
('<span class="txt_bld">Location:</span> ' + address + '<br />' +
'<span class="txt_bld">Accuracy:</span> ' + position.coords.accuracy + '<br />' +
'<span class="txt_bld">Time:</span> ' + position.timestamp);
if(!infowindow){
infowindow = new google.maps.InfoWindow({
content: info
});
}else{
infowindow.setContent(info);
}
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map,marker);
setTimeout(function () {
infowindow.close();
}, 5000);
});
});
}
if(!marker){
marker = new google.maps.Marker({
position: position,
map: this.map,
icon: markericon,
draggable:false
});
}else{
marker.setPosition(point);
}
The geocoder is asynchronous. You need to use the data it returns in the callback function. Something like this (not tested):
var position = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
var geocoder = new google.maps.Geocoder();
var infowindow = new google.maps.InfoWindow();
var address;
if (geocoder) {
geocoder.geocode({ 'latLng': position }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
address = (results[0].formatted_address);
} else {
address = (position.coords.latitude + ', ' + position.coords.longitude);
}
var info =
('<span class="txt_bld">Location:</span> ' + address
+ '<br />' +
'<span class="txt_bld">Accuracy:</span> ' + position.coords.accuracy
+ '<br />' +
'<span class="txt_bld">Time:</span> ' + position.timestamp);
infowindow.setContent(info);
infowindow.setPosition(position);
infowindow.open(map);
});
}
Working example
We are using geocoder to get Lat long by address, as we don't have Lat long so we need to find Lat long via geocoder. In case if we have Lat long coordinates then we can use direct
var position = new google.maps.LatLng(markers[i][1], markers[i][2]);
bounds.extend(position);
marker = new google.maps.Marker({
position: position,
map: map,
title: markers[i][0]
});
I am wanting to search a KML file to see if a particular address falls within an overlay. Presently, I have the address converting to a geocode. However, I'm not sure what code is needed to add this functionality.
Here's the present code:
function initialize() {
var infowindow = new google.maps.InfoWindow({
content: '',
suppressMapPan:true
});
var myLatlng = new google.maps.LatLng(35.910200,-84.085100);
var myOptions = {
zoom: 12,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(
document.getElementById("map_canvas"), myOptions);
var d = new Date();
var n = d.getMilliseconds();
var nyLayer = new google.maps.KmlLayer(
'http://www.cspc.net/neighborhoods/groups.kml?rand=' + n,
{ suppressInfoWindows: true, map: map});
google.maps.event.addListener(nyLayer, 'click', function(kmlEvent) {
var url = kmlEvent.featureData.snippet;
var groupName = kmlEvent.featureData.name;
var insideContent = "<div style='width:250px;'><h2>" + groupName +
"</h1><p>We have a neighborhood contact in your area! </p>" +
"<p><a href='" + url + "' target='_blank'>Get connected!</a>" +
" They look forward to hearing from you.</p><p>If you have " +
"any additional questions, please contact our " +
"<a href='http://www.cspc.net/communitylife' target='_blank'>" +
"Community Life</a> staff for more information. Betsy Palk, " +
"the Administrative Assistant, may be reached at:<br/><br/>" +
"<b>Email:</b> <a href='mailto:betsypalk#cspc.net'>" +
"betsypalk#cspc.net</a><br/><b>Phone:</b> 865-291-5268<p></div>";
var clickPos = kmlEvent.latLng;
var posX = new google.maps.LatLng(clickPos.lat(), clickPos.lng());
infowindow.close();
infowindow.setPosition(posX);
infowindow.setContent(insideContent);
infowindow.open(map);
});
eventMapClick = google.maps.event.addListener(map, 'click',
function(event) {
var marker = new google.maps.Marker({ position: event.latLng });
var outsideContent = "<div style='width:250px;'><h2>Oops!</h1>" +
"<p> It seems we don't have a neighborhood contact in your " +
"area.</p><p>Please contact our <a " +
"href='http://www.cspc.net/communitylife' target= '_blank'>" +
"Community Life</a> staff for more information. " +
"Betsy Palk, the Administrative Assistant, may be reached at:" +
"<br/><br/><b>Email: </b> <a href='mailto:betsypalk#cspc.net'>" +
"betsypalk#cspc.net</a><br/><b>Phone:</b> 865-291-5268<p></div>";
infowindow.setContent(outsideContent);
infowindow.open(map, marker);
});
}
var geocoder = new google.maps.Geocoder();
function searchAddress(address) {
geocoder.geocode(
{'address': address},
function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var loc = results[0].geometry.location;
// use loc.lat(), loc.lng()
window.alert(loc);
}
else {
window.alert("Not found: " + status);
}
}
);
};
If I understand your question, I believe you want the formula to determine if a point (google.maps.LatLng) falls within one of your KML Placemark definitions (that you give names such as: Neighborhood Group 1). Within each Placemark, you define a Polygon and within each Polygon, you define a set of coordinates, which represent the vertices of the Polygon.
Using the coordinates within the Polygon and the LatLng you retrieve via geocoding, you could start with these formulas and select that one that is the best fit for you:
Fast Winding Number Inclusion of a Point in a Polygon
Point-In-Polygon Algorithm
A very similar question has also been asked here on SO: Determine the lat lngs of markers within a polygon.