I'm trying to use a combination of firebase and google maps API to create interactive markers on a map.
I can generate markers, but I want to find a way to remove them.
I generate a list of addresses that include a button you can pus. If pushed, the button removes the marker by setting setMap to null.
However, when referencing my markers in the clearItem function it give ems the error "cannot reference setMap of undefined"
Any help?
I use an array such as
var markerArray = [];
firebase.database().ref().on('value', function(snapshot)
{
var pointerLocations = document.getElementById("locations");
var databaseKeys = snapshot.val(); //Returns one object of many object attributes
var list="";
if(databaseKeys == null){
console.log("Error handled");
}
else{
for(i = 0 ; i < Object.keys(databaseKeys).length; i ++){
list += databaseKeys[Object.keys(databaseKeys)[i]].address + "<br>";
list += databaseKeys[Object.keys(databaseKeys)[i]].name + "<br>";
var keyString = Object.keys(databaseKeys)[i].toString();
list += "<button class = \"clrbtn\" id = \"clrbtn_"+i+"\" onclick = \"clearItem('"+keyString+"',"+i+")\">Delete Item</button>"
//Calls google map
codeAddress(databaseKeys[Object.keys(databaseKeys)[i]].address, map,i);
}
}
pointerLocations.innerHTML = list;
}
);
function codeAddress(address,mapGlobl,i) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == 'OK') {
// map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
markers.push(marker);
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
function clearItem(databaseID,position){
firebase.database().ref(databaseID).remove();
markers[i].setMap(null);
}
EDIT: Responding to comment
Using the i variable to use an object instead of array
firebase.database().ref().on('value', function(snapshot)
{
var pointerLocations = document.getElementById("locations");
var databaseKeys = snapshot.val(); //Returns one object of many object attributes
var list="";
if(databaseKeys == null){
console.log("Error handled");
}
else{
for(i = 0 ; i < Object.keys(databaseKeys).length; i ++){
list += databaseKeys[Object.keys(databaseKeys)[i]].address + "<br>";
list += databaseKeys[Object.keys(databaseKeys)[i]].name + "<br>";
var keyString = Object.keys(databaseKeys)[i].toString();
list += "<button class = \"clrbtn\" id = \"clrbtn_"+i+"\" onclick = \"clearItem('"+keyString+"',"+i+")\">Delete Item</button>"
//Calls google map
codeAddress(databaseKeys[Object.keys(databaseKeys)[i]].address, databaseKeys[Object.keys(databaseKeys)[i]].name, map,i);
}
}
pointerLocations.innerHTML = list;
}
);
function codeAddress(address, name, mapGlobl,i) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == 'OK') {
// map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
latLongObj[i] = {lat : results[0].geometry.location.lat(), lng : results[0].geometry.location.lng(), add: address, name : name};
console.log("Placing Market at " + i);
markers['marker'+i] = marker;
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
function clearItem(databaseID,position){
console.log(markers);
console.log("Removing at position: "+ position);
firebase.database().ref(databaseID).remove();
markers['marker'+position].setMap(null);
delete markers['marker'+position];
console.log(markers);
}
The issue is that the geocoder.geocode() function is asynchronous. From the Google Maps JS documentation:
Accessing the Geocoding service is asynchronous, since the Google Maps API needs to make a call to an external server. For that reason, you need to pass a callback method to execute upon completion of the request.
Your markers.push(marker); is happening inside that callback function. Since it's asynchronous, there's no guarantee when/if that will ever get called. The push() function will naively use the next available array index to add the marker to the array, but you're doing everything based on the i value you're passing around between function calls. If anything goes wrong with the Google Maps API and the callback function never gets called, or gets called after a delay (e.g. due to network latency), things may get added to the markers array out of order or with gaps in the array indices.
The simplest solution is to change markers.push(marker); to markers[i] = marker; This guarantees that it gets added to the markers array with the index you're expecting (and it matches up with the i value of that <button> element).
Related
im using the Google API, i want to make it that there can be multiple addresses marked on the map so im using the Geocoder. However the rest of my code is running before this Geocoder returns the result it seems!
// Handle addresses
var addressesHandled = [];
function handleAddresses(addressObj) {
for (i = 0; i < addressObj.length; i++) {
addressesHandled[i] = new Address(addressObj[i]['title'], addressObj[i]['address'], addressObj[i]['latlng'], addressObj[i]['defaultOpen']);
}
}
// Address object
function Address(title, address, latlng, defaultOpen) {
this.title = title;
this.address = address;
this.latlng = latlng;
if (latlng == undefined) {
this.latlng = codeAddress(address);
}
this.defaultOpen = defaultOpen;
}
As you can see im going through each object and getting the address if the lat and lng values are undefined. If these are undefined I then execute the codeAddress function which will get the lat and lng values from the current address, however I think that the rest of the script is still running whilst this happens!
Below is the codeAddress function, I thought I was unable to the return the result however now I believe that it simply isn't being returns quick enough.
How can I fix this issue so the rest of my script waits until each address has had it's lat and lng calculated!?
function codeAddress(address, callback) {
var geocoder;
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var loc = [];
// loc = { lat: results[0].geometry.location.lat(), lng: results[0].geometry.location.lng() };
loc[0] = results[0].geometry.location.lat(); loc[1] = results[0].geometry.location.lng();
console.log(loc);
callback(loc);
} else {
console.log("Geocode was not successful for the following reason: " + status);
}
});
}
Edit:
Hmm, I just thought would it be easier just to process the marker and add it to the map as each geocode function has it's result returned ?
I am trying to visualize some fusion tables data on google maps.
I have a number of records with addresses grouped by area number.
Basically what I would want to happen is the following:
I pull data from ft
For each record, I geocode the address
and assign a custom marker according to the area number
I visualize all the different records grouped by different markers
Here is what I've done so far:
This is the query to ft:
var query = "SELECT 'Full Address' , Territory FROM " + tableid;
query = encodeURIComponent(query);
var gvizQuery = new google.visualization.Query(
'http://www.google.com/fusiontables/gvizdata?tq=' + query);
Now I want to elaborate the query data
gvizQuery.send(function(response) {
var numRows = response.getDataTable().getNumberOfRows();
// For each row in the table, create a marker
for (var i = 0; i < numRows; i++) {
var stringaddress = response.getDataTable().getValue(i, 0);
var territory = response.getDataTable().getValue(i,1);
**latlng**(stringaddress,
function(data){
console.log(data);
**createMarker**(data,territory,stringaddress);//callback
});
}
});
Here is the latlng function: that returns a google maps point from the string address
function latlng(address,callback){
var latlng;
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
"address": address
}, function(results,status) {
if ( status == google.maps.GeocoderStatus.OK ) {
latlng= new google.maps.LatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
callback(latlng);
}
});
}
And finally here is the create marker function
var createMarker = function(coordinate, territory,address) {
console.log("now drawing marker for " + coordinate + "found in territory number " + territory);
var markerpath="images/icon"+territory+".png";
var marker = new google.maps.Marker({
map: map,
position: coordinate,
icon: new google.maps.MarkerImage(markerpath)
});
google.maps.event.addListener(marker, 'click', function(event) {
infoWindow.setPosition(coordinate);
infoWindow.setContent('Address: ' + address + '<br>Territory = ' + territory);
infoWindow.open(map);
});
};
The issue I am facing is that ,although I should be calling the createmarker function for each record of my ft, it is actually only being called a couple of times (out of 250) and only one territory is being represented (number 7).
What am I mising?
Thank you for your help!
The geocoder is subject to a rate limit and a quota, after about 10 geocode operations you will see the status returned of OVER_QUERY_LIMIT (which your code silently ignores). To see the issue, log the status returned:
function latlng(address,callback){
var latlng;
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
"address": address
}, function(results,status) {
if ( status == google.maps.GeocoderStatus.OK ) {
latlng= new google.maps.LatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
callback(latlng);
} else {
console.log("geocode failed: "+status);
}
});
}
(or you could add an alert, which would be really annoying for 200 markers)
You need to handle the OVERY_QUERY_LIMIT appropriately (throttle your requests), but that will probably make your map load too slow.
Best option: geocode the addresses offline and store the coordinates in the FusionTable, return them in your query and use those to display the markers.
I have a google map which has a marker on it so people can move it around. When they do I'm attempting to reverse geocode the location into a proper address but I only really want the town/city and the country, I don't want the postcode returned
Is it possible to just get the locality without having to use a regex to remove the postcode - which would prob be difficult!
Thanks in advance
The response didn't only return the address, it also contains the address_components, an array with the specific details of the location, e.g. country, city, street etc. (see https://developers.google.com/maps/documentation/geocoding/#JSON)
fetch the desired components out of this array.
Reverse GeoCoding returns an address_components array containing few objects.
(You can print this object in console to get the feel.)
Extracting required information from this array is very easy.
Now have a look at the code -
function getLatLong(position) {
geocoder = new google.maps.Geocoder();
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
// Reverse Geocoding, Location name from co-ordinates.
var latlng = new google.maps.LatLng(latitude, longitude);
geocoder.geocode({'latLng': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
var components=results[0].address_components;
for (var component=0;component<(components.length);component++){
if(components[component].types[0]=="administrative_area_level_1"){
var admin_area=components[component].long_name;
}
if(components[component].types[0]=="country"){
var country=components[component].long_name;
}
if(components[component].types[0]=="postal_code"){
var postal_code=components[component].long_name;
}
}
}
}
}
}
I think you can!
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
for (var i = 0; i < results.length; i++) {
if (results[i].types[0] === "locality") {
var city = results[i].address_components[0].short_name;
var state = results[i].address_components[2].short_name;
alert('Serial=' + i+ ' city=' + city+ ' state=' + state)
};
};
};
};
Please help me resolve this problem.
I want to get address from latitude, longitude in Google Maps.
Here is my functions:
function codeLatLng() {
var geocoder = new google.maps.Geocoder();
var lati = document.getElementById("latitude_value").value;
var lngi = document.getElementById("longitude_value").value;
var latlng = new google.maps.LatLng(lati, lngi);
var infowindow = new google.maps.InfoWindow();
var ngo;
geocoder.geocode({'latLng': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
map.setZoom(11);
marker = new google.maps.Marker({
position: latlng,
map: map
});
ngo = results[1].formatted_address;
infowindow.setContent(results[1].formatted_address);
infowindow.open(map, marker);
}
} else {
alert("Geocoder failed due to: " + status);
}
});
return ngo;
}
When this function is executed. The address is displayed in Maps.
However, this is not what I need. I just want to assign this address to variable 'ngo' as a string.
This function return 'ngo' which is displayed in the text field as 'undefinded'.
I need some help to solved this problem.
Thanks.
I just want to assign this address to variable 'ngo' as a string.
That's the problem right there. You can't do that. JavaScript just doesn't work that way. The geocoder call is asynchronous. It returns before the data is received from the server. The data isn't ready until the geocoder callback function is called.
What you need to do instead is to use that ngo data in the callback function itself, or call another function and pass it the data, and use the data there.
For example, where you have this line:
ngo = results[1].formatted_address;
you can replace it with:
useNGO( results[1].formatted_address );
where useNGO is a function you've defined (anywhere) like this:
function useNGO( ngo ) {
// Do stuff with ngo here
}
I believe your problem is that using the var keyword when declaring var ngo makes ngo a local variable, so it doesn't exist outside of codeLatLng(). Try deleting var ngo, placing ngo = ""; somewhere outside of any function declarations (like right before function codeLatLng() {), and let me know if that works :)
in my Asp.net Web Application where i am using the setTimeout to Get rid of
geocoder OVER_QUERY_LIMIT, the shorter time out is 10ms which is too longer for me, I have 800 above addresses coming from SQL SERVER which would be increased because of this setTimeout will take about 5 to 7 mints to take places of all the markers on map and that`s frustrating. I researched and saw this link setTimeout: how to get the shortest delay
but cant figure out what he want to do actually. please someone guide me....
function InitializeMap() {
// Here am calling the webService by PageMethods in which CityNames, Countries Name will take their places
PageMethods.GetCitiesbyUser_Extender(onSucess);
var myOptions =
{
zoom: 0,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
var map = new google.maps.Map(document.getElementById("map"), myOptions);
// Creating latlngbound to bound the markers on map
var bounds = new google.maps.LatLngBounds();
//// Creating an array that will contain the addresses
var places = [];
// Creating a variable that will hold the InfoWindow object
var infowindow;
// create this to add the marker Cluster on map
mc = new MarkerClusterer(map);
var popup_content = [];
var geocoder = new google.maps.Geocoder();
// image for ballon i want to change default ballon to this
var iconimage = "http://chart.apis.google.com/chart?cht=mm&chs=24x32&chco=FFFFFF,008CFF,000000&ext=.png";
var markers = [];
// Create this function for passing the values which was taken by webservice cntName is the return in webservice
function onSucess(cntName){
// loop through the cntName to pass the individual City one by one from geocode
for (i = 0; i < cntName.length; ++i) {
//for fixing the issue use closure to localize the cntName[i] variable before passing into geocode and callback function within it.
(function CreateMarkAndInfo(address) {
geocoder.geocode({ 'address': address },
function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
places[i] = results[0].geometry.location;
var marker = new google.maps.Marker({
position: places[i],
title: results[0].formatted_address,
map: map,
icon: iconimage
});
markers.push(marker);
mc.addMarker(marker);
google.maps.event.addListener(marker, 'click', function () {
if (!infowindow) {
infowindow = new google.maps.InfoWindow();
}
// Setting the content of the InfoWindow afterward
infowindow.setContent(popup_content[i]);
// Tying the InfoWindow to the marker afterward
infowindow.open(map, marker);
});
// Extending the bounds object with each LatLng
bounds.extend(places[i]);
// Adjusting the map to new bounding box
map.fitBounds(bounds);
// Zoom out after fitBound
var listener = google.maps.event.addListenerOnce(map, "idle", function () {
if (map.getZoom() < 10) map.setZoom(2);
});
}
else {
// if geocode will end the limit then make delay by timer in order to avoid the OVER_QUERY_LIMIT
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
setTimeout(function () { CreateMarkAndInfo(address); }, (15)); // here i think i should use better approch but for now it`s ok.
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
}
});
})(cntName[i]);// End closure trick
}
}
}
google.maps.event.addDomListener(window, 'load', InitializeMap);
Edit:
#just.another.programmer i cant because there is no latitute and longitude in DB, client will add cities and countries by him self thats why i had to convet city and country names by geocode and geocode doing it`s job accuretly here
How i am calling the City and country Names by web service
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod()]
public static string[] GetCitiesbyUser_Extender()
{
System.Data.DataSet dtst = new System.Data.DataSet();
string ses = HttpContext.Current.Session["UserName"].ToString();
USTER.Dal.clsSearch clssearch = new USTER.Dal.clsSearch();
// Assinging the Stroed Procedure Method to DataSet
dtst = clssearch.GetAllCitiesByUser(ses);
string[] cntName = new string[dtst.Tables[0].Rows.Count];
int i = 0;
try
{
foreach (System.Data.DataRow rdr in dtst.Tables[0].Rows)
{
// Columns Name in SQL Server Table "CityName" and "CountryName"
cntName.SetValue(rdr["CityName"].ToString() +","+ rdr["CountryName"].ToString() , i);
i++;
}
}
catch { }
finally
{
}
return cntName;
}
Geocode your addresses one time when you first get them, then store the lat/long in your db so you don't have to geocode again. This will dramatically reduce your geocode requests and remove the need for setTimeout.