Google Maps APIv3 - adding markers via geocode - javascript

So I've got this script where I'm trying to add markers to a map I've created with Javascript. I think I'm pretty near to getting it right (yeah, right) but I'm not sure what I'm doing wrong and I can't seem to decipher the error I'm getting.
The first thing I do is add the google API key to my site.
Then, the first thing I do in my script is load the search and maps APIs:
(function ($) {
$("document").ready(function() {
google.load("maps", 2, {"callback": mapsLoaded});
});
})(jQuery);
After the 'maps' API has loaded, the mapsLoaded method gets called:
function mapsLoaded() {
//Create the options for the map and imediately after create the map.
var myOptions = {
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("result_map"), myOptions);
showUserLocation(map);
var image = '../misc/planbMarker.png';
var postcodes = document.getElementsByClassName('result-postcode');
var geocoder = new google.maps.Geocoder();
for(var i = 0; i < postcodes.length; i++) {
var address = postcodes[i].value;
geocoder.geocode({'address': address}, function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
map: map,
position: results[i].geometry.location
});
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
}
The function showUserLocation just gets the user's current location and puts it in the map:
function showUserLocation(map) {
//Define the user's initial location
var initialLocation;
//Define the variable to see if geolocation is supported.
var browserSupportFlag = new Boolean();
// Try W3C Geolocation (Preferred)
if(navigator.geolocation) {
browserSupportFlag = true;
navigator.geolocation.getCurrentPosition(function(position) {
initialLocation = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
map.setCenter(initialLocation);
}, function() {
handleNoGeolocation(browserSupportFlag);
});
// Browser doesn't support Geolocation
} else {
browserSupportFlag = false;
handleNoGeolocation(browserSupportFlag);
}
function handleNoGeolocation(errorFlag) {
if (errorFlag == true) {
alert("Geolocation service failed. Defaulting to Aberdeen.");
initialLocation = new google.maps.LatLng(57.149953,-2.104053);
} else {
alert("Your browser doesn't support geolocation. We've placed you in Aberdeen.");
initialLocation = new google.maps.LatLng(57.149953,-2.104053);
}
map.setCenter(initialLocation);
}
//Put a marker on the user's current position different than the markers for the venues.
navigator.geolocation.getCurrentPosition(function(position) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(position.coords.latitude,position.coords.longitude),
map: map,
title:"Hello World!"});
});
}
Now, the error I'm getting when I run this is: Uncaught TypeError: Cannot call method 'appendChild' of null at maps:1 followed by: Uncaught TypeError: Object #<Object> has no method '__gjsload__' and after about 5 seconds this also pops up in the javascript console:
Error in event handler for 'undefined': TypeError: Cannot call method 'postMessage' of null
at chrome/RendererExtensionBindings:100:18
at chrome-extension://bmagokdooijbeehmkpknfglimnifench/contentScript.js:128:13
at [object Object].dispatch (chrome/EventBindings:182:28)
at chrome/RendererExtensionBindings:99:24
at [object Object].dispatch (chrome/EventBindings:182:28)
at Object.<anonymous> (chrome/RendererExtensionBindings:149:22)
On a sidenote, and as another possibly symptom, when this error is happening Chrome is displaying a blank page (if you go to view source the markup is all there but the page is blank). I'm running Drupal 7.10.
Does anyone have any ideas on what I'm doing wrong?

Firstly, Google Maps API 3 doesn't require an API key (unless we're referring to the recently introduced key for sites with over 25,000 daily map views which might need to pay for usage). It sounds like you're mixing up the keys required for API 2 with code for API 3.
Secondly, this is all wrong. 'i' is a variable for the postcodes array, it has no relationship to the results array
for(var i = 0; i < postcodes.length; i++) {
var address = postcodes[i].value;
geocoder.geocode({'address': address}, function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
map: map,
position: results[i].geometry.location

Related

Google map clear specific marker help (JS)

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).

Google Maps Geocode - Results relevance

I am using the basic functionality of Google Maps Javascript API
Code sample from google :
https://developers.google.com/maps/documentation/javascript/examples/geocoding-simple
var geocoder;
var map;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
zoom: 8,
center: latlng
}
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
function codeAddress() {
var address = document.getElementById('address').value;
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
So as you can tell, when we type a street name, even if we have several results, the sample above is just showing one. I already changed that to loop throught all results and it works fine.
My questions are:
The geocoder.geocode results are sorted by any criteria?
If the answer is yes, is it possible to get the results sorted by most relevant / near place of the current user location ? (I will pass the current user location).
Thanks a lot
Closest thing that I know off is using region biasing: https://developers.google.com/maps/documentation/geocoding/intro#RegionCodes
You're using the wrong API for this, try using the Google Places API. This sorts by popularity (preferred).
Or you can use some code to loop through the results and find the distance from the original location. Then you can sort by distance (works, but I think popularity is what you want)

Google Maps API and JavaScript Issues :-(

Ive been working on a Google Maps application and have hit a hurdle due to a lack of knowledge in js me thinks?!
So... here's my JSON object
{"pickuppoint0":"LE9 8GB","pickuppoint1":"LE2 0QA","pickuppoint2":"LE3 6AF","pickuppoint3":"LE2 8GB","pickuppoint4":"LE8 8TE","pickuppoint5":"LE2 2GB","pickuppoint6":"LE1 6AF"}
And here's a loop through the JSON object...
$.each(alltravelgroups, function(k, v){
for(var i=0; i < boxes.length; i++){
var bounds = boxes[i];
if(bounds.contains(getLatLng(v))){
alert("im here");
}
}
});
And here's my getLatLng() method I've created...
function getLatLng(pickuppoint) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': pickuppoint}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
return results[0].geometry.location
} else {
alert('getLatLng Geocode was not successful for the following reason: ' + status);
}
});
}
Now... what I'm trying to do is simply take the value from each key/value pair and produce a LatLng object that can then be used within the "bounds.contains()" method for searching within the bounds box provided by the RouteBoxer class.
The problem I'm facing is the value returned by the getLatLng method is "undefined" when using alert(getLatLng(v)) and should just be a 'location' containing both latitude and longitde? Anyone able to point what I'm doing wrong?
Please refer the link below.
http://jsfiddle.net/y829C/13/
var mapOptions = {
center: new google.maps.LatLng(-33.8665433, 151.1956316),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
In the above code, semicolon is missing in return results[0].geometry.location.

better approach then setTimeout on Google Map V3

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.

Google Maps API - multiple info windows and automatic centering

I've been cobbling together various bits of code from around the internet (including StackOverflow) and I've got a working map (almost) which geocodes postcodes from an array and creates infowindows for each one.
Two problems:
1) my info windows, which should take their text from another array, always use the last array value
2) i can't get the map to center automatically. I'm using a bit of code which has worked in other circumstances, but it doesn't in my code.
The code is fairly self-explanatory:
var geocoder = new google.maps.Geocoder();
var map = new google.maps.Map(document.getElementById('map'), {
//zoom: 10,
//center: new google.maps.LatLng(-33.92, 151.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var postcodes = [
"EH14 1PR",
"KY7 4TP",
"IV6 7UP"
];
var descriptions = [
"Slateford",
"Cortachy",
"Marybank"
];
var markersArray = [];
var infowindow = new google.maps.InfoWindow();
var marker, i, address, description;
for(var i = 0; i < postcodes.length; i++) {
address = postcodes[i];
description = descriptions[i];
geocoder.geocode(
{
'address': address,
'region' : 'uk'
},
function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
markersArray[i] = marker;
console.log(markersArray[i]);
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent(description);
infowindow.open(map, marker);
}
})(marker, i));
} else {
alert("Geocode was not successful for the following reason: " + status);
return false;
}
}
);
var bounds = new google.maps.LatLngBounds();
$.each(markersArray, function (index, marker) {
bounds.extend(marker.position);
});
map.fitBounds(bounds);
console.log(bounds);
}
Any thoughts? The issue seems to be with the value of the counter i inside the geocode function.
Any help much appreciated!
1) my info windows, which should take their text from another array,
always use the last array value
2) i can't get the map to center automatically. I'm using a bit of
code which has worked in other circumstances, but it doesn't in my
code.
1) Yes this is a closure issue. This is how I get around it.
I create an object to store all of the properties I will be using. In your example I am going to use the postcode and the description.
function location(postalcode, desc){
this.PostalCode = postalcode;
this.Description = desc;
}
Now do a quick loop to add all the location objects to an array.
var locations = [];
for(var i = 0; i < postcodes.length; i++) {
locations.push(new location(postcodes[i], descriptions[i]));
}
Extract the geocode functionality into its own function with a parameter to take a location object. Then you can loop through the location object array and geocode each individually. So now both the post code and description are in scope when the request is built and sent.
function GeoCode(singleLocation){
geocoder.geocode(
{
'address': singleLocation.PostalCode,
'region' : 'uk'
},
function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
//quick and dirty way
bounds.extend(marker.position);
map.fitBounds(bounds);
markersArray[i] = marker;
console.log(markersArray[i]);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(singleLocation.Description);
infowindow.open(map, this); //this refers to the marker
});
} else {
alert("Geocode was not successful for the following reason: "
+ status);
return false;
}
}
);
}
2) As you can see above the quick an dirty way to do this is extend and fit the bounds inside of the callback function for the geocode. This causes the fitBounds function to be called multiple times and isnt really a big deal if you only have a few markers, but will cause problems if you have hundreds or thousands of markers. In that case the right-way to do this is to create an async loop function. You can see an example of it on one of my previous answers.
Here is a functioning example of the code based on your example.
Two problems:
1) my info windows, which should take their text from another array,
always use the last array value
2) i can't get the map to center automatically. I'm using a bit of
code which has worked in other circumstances, but it doesn't in my
code.
Answers:
1) This is because most likely you have a closure issue here.
2) center will define the center point of your map, but in your code you have commented this
//center: new google.maps.LatLng(-33.92, 151.25),
removing the comment for this line will center to map to latitude of -33.92 and longitude of 151.25.
Use below:
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: new google.maps.LatLng(-33.92, 151.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
});

Categories