I use the following script to generate this page
function initialize() {
var mapCanvas = document.getElementById('map');
var mapOptions = {center:new google.maps.LatLng(latitudeMid,longitudeMid),zoom:15,mapTypeId:google.maps.MapTypeId.ROADMAP,streetViewControl:false,mapTypeControl:true,scaleControl:true,scaleControlOptions:{position:google.maps.ControlPosition.TOP_RIGHT}};
var map = new google.maps.Map(mapCanvas, mapOptions);
var i;
var insertion;
var previousMarker;
// -------------------------------
//show locations on the map
// -------------------------------
for (i = 0; i < fotoCount; i++) {
var myLatLng=new google.maps.LatLng(Latituden[i], Longituden[i]);
var marker = new StyledMarker({styleIcon:new StyledIcon(StyledIconTypes.MARKER,{color:'00ff00',text:Letters[i]}),position:myLatLng,map:map});
marker.set('zIndex', -i);
insertion='<img src=\"http://www.pdavis.nl/Ams/'.concat(Bestanden[i],'.jpg\"></img>');
insertion=insertion.concat('<table class=width100><tr><td>Bestand: ',Bestanden[i],'</td><td class=pright>Lokatie: ',Latituden[i],' °N., ',Longituden[i],' °E. (',Letters[i],')</td>');
insertion=insertion.concat('<td class=pright>Genomen: ',Datums[i],'</td></tr><td colspan=3>Object: ',Objecten[i],'</td></table>');
google.maps.event.addListener(marker, 'click', function() {
$('#photo').html(insertion);
this.styleIcon.set('color', 'ff0000');
if(previousMarker!=null){previousMarker.styleIcon.set('color', '00ff00')};
previousMarker=this;
});
}
Clicking a marker should do two things: (i) turn the marker red (and any existing red marker green) and (ii) show the appropriate photo with information in the right-hand panel. The first does work, but the second always shows the photo corresponding to the last marker. Using
"alert(insertion);" shows that this is correct for each marker.
You can't do it this way because at the end of the loop, "i" will always be the last index. And of course when you click on a marker, "i" value inside the callback is the last index, so you should always have the last picture displayed.
Just put insertion code inside your click callback isn't enough because of the i value. You didn't bind anything to fix value in your callback so you will have the same problem.
The following solution use the marker object to bind the "i" value, like this you can use it in your callback.
Script tested on your page :).
Adapt it as you want !
function initialize() {
var mapCanvas = document.getElementById('map');
var mapOptions = {center:new google.maps.LatLng(latitudeMid,longitudeMid),zoom:15,mapTypeId:google.maps.MapTypeId.ROADMAP,streetViewControl:false,mapTypeControl:true,scaleControl:true,scaleControlOptions:{position:google.maps.ControlPosition.TOP_RIGHT}};
var map = new google.maps.Map(mapCanvas, mapOptions);
var i;
var previousMarker;
// -------------------------------
//show locations on the map
// -------------------------------
for (i = 0; i < fotoCount; i++) {
var myLatLng=new google.maps.LatLng(Latituden[i], Longituden[i]);
var marker = new StyledMarker({styleIcon:new StyledIcon(StyledIconTypes.MARKER,{color:'00ff00',text:Letters[i]}),position:myLatLng,map:map});
marker.set('zIndex', -i);
marker.myIndex = i;
google.maps.event.addListener(marker, 'click', function() {
var insertion = "";
insertion='<img src=\"http://www.pdavis.nl/Ams/'.concat(Bestanden[this.myIndex],'.jpg\"></img>');
insertion=insertion.concat('<table class=width100><tr><td>Bestand: ',Bestanden[this.myIndex],'</td><td class=pright>Lokatie: ',Latituden[this.myIndex],' °N., ',Longituden[this.myIndex],' °E. (',Letters[this.myIndex],')</td>');
insertion=insertion.concat('<td class=pright>Genomen: ',Datums[this.myIndex],'</td></tr><td colspan=3>Object: ',Objecten[this.myIndex],'</td></table>');
$('#photo').html(insertion);
this.styleIcon.set('color', 'ff0000');
if(previousMarker!=null){previousMarker.styleIcon.set('color', '00ff00')};
previousMarker=this;
});
}
}
insertion should be an array. This way when you iterate, in eacj iteration you are just overwriting the content of insertion. In the end you have last value from image array as insertion.
var insertionArr = [];
...
insertion=insertion.concat('<td class=pright>Genomen: ',Datums[i],'</td></tr><td colspan=3>Object: ',Objecten[i],'</td></table>');
insertionArr[marker] = insertion; // Add it to the array
google.maps.event.addListener(marker, 'click', function() {
$('#photo').html(insertionArr[this]);// get it from the array
...
});
This is not tested code.
Related
Guys need a bit of help with google maps, I'm creating a tracking web app using MySQL DB coordinates, the tracking works well so far except the google map markers keep repeating when refreshed, I have set the map marker refresh using a setInterval function for every 5 seconds(for testing). I've tried clearoverlays() methods and remove map markers method(from google sample) but doesn't work. Appreciate your help, thank you
<script defer
src="https://maps.googleapis.com/maps/api/js?key="KEY"8&callback=initMap">
</script>
<script type="text/javascript">
setInterval(function () {
BindMarker();
}, 5000);
var customIcons = {
blue: { icon: 'blue48.png'},
};
var marker;
var map = null;
var infoWindow = null;
function load() {
map = new google.maps.Map(document.getElementById("map"), {
center: new google.maps.LatLng(1.4370993, 110.3387572),
zoom:15,
});
infoWindow = new google.maps.InfoWindow;
}
function BindMarker() {
downloadUrl('maps1.php', function (data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var point = new google.maps.LatLng(
parseFloat(markers[i].getAttribute("lat")),
parseFloat(markers[i].getAttribute("lng")));
var icon = customIcons["blue"] || {};
marker = new google.maps.Marker({
map: map,
animation: google.maps.Animation.BOUNCE,
position: point,
icon: icon.icon,
shadow: icon.shadow
});
}
});
}
function bindInfoWindow(marker, map, infoWindow) {
google.maps.event.addListener(marker, 'click', function () {
map.setCenter(marker.getPosition());
});
}
function downloadUrl(url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function () {
if (request.readyState == 4) {
request.önreadystatechange = doNothing;
callback(request, request.status); }
};
request.open('GET', url, true);
request.send(null);
}
function doNothing() { }
</script>
The functions I tried are given below( I called the remove overlays function, remove markers and also the delete markers function before BindMarker() inside the setInterval function so that it would remove the markers before binding new markers
function setMapOnAll(map) {
for (let i = 0; i < markers.length; i++) { markers[i].setMap(map); }
}
function clearMarkers() {setMapOnAll(null); }
function deleteMarkers() {clearMarkers(); markers = [];}
function clearOverlays() {
while(markers.length) { markers.pop().setMap(null); }
markers.length = 0;
}
I believe you've confounded the examples, which your attempted code shows verbatim and what your code is actually doing.
It the sample code you're drawing from, the variable markers is an array of Google Map marker objects. So this code makes sense in that regard:
function deleteMarkers() {clearMarkers(); markers = [];}
But, your code is not creating an array of those objects. Your code does not have a variable named markers that is accessible outside of the downloadUrl() function. The variable markers does show up inside that function, but it contains an HTMLCollection element - not an array of markers.
To fix this you need to create an array named markers right after the variable name marker is created.
Then, within the for loop, just after you create the marker push that marker on to the markers array. You will also have to rename the existing markers variable to something more appropriate like markerElements.
Now, the deleteMarkers() function will work as expected.
I'm coding a demo of how I want a future search function to work. I want to query google on an address or area and return the area bounds as well as nearby places. To do this, I am using places autocomplete, geocode, and places search.
So far I am successfully getting predicted search queries resulting in the bounds being drawn as a rectangle on my map. However, when I try to implement markers for the place search result no markers are appearing on my map. I know the search is working because putting an alert in the createMarker function on each location returns several lat/lng's that coincide with my area.
I suspect that maybe the map object is not being passed to my createMarker function, but I am kind of a noob when it comes to Javascript. I tried passing an additional parameter but it didn't do much.
It should be noted that I am able to create a marker within the initialize function, when I attempt to just create one static marker.
EDIT: I have removed the type parameter for the place search request, but the code doesn't work even with the parameter ['store'].
var map;
var infowindow;
function initialize() {
var view_lat = document.getElementById('view_lat').value;
var view_lng = document.getElementById('view_lng').value;
var focus = new google.maps.LatLng(view_lat,view_lng);
var swlat = document.getElementById('swlat').value;
var swlng = document.getElementById('swlng').value;
var nelat = document.getElementById('nelat').value;
var nelng = document.getElementById('nelng').value;
var map_canvas = document.getElementById('map-canvas_embed');
var map_options = {
center: new google.maps.LatLng(parseFloat(view_lat), view_lng),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var mysw = new google.maps.LatLng(swlat,swlng)
var map = new google.maps.Map(map_canvas, map_options)
var rectangle = new google.maps.Rectangle({
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: map,
bounds: new google.maps.LatLngBounds(
new google.maps.LatLng(swlat, swlng),
new google.maps.LatLng(nelat, nelng))
});
var request = {
location: focus,
radius: 500
};
var place_search = new google.maps.places.PlacesService(map);
place_search.nearbySearch(request,callback)
}
Handles the result from google places search
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
}
}
function createMarker(place) {
var placeLoc = place.geometry.location;
var marker = new google.maps.Marker({
position: place.geometry.location
});
marker.setMap(map);
}
Autocomplete variables
var input = document.getElementById('location');
var options = {
componentRestrictions: {country: 'se'},
types: ['geocode']
}
var searchform = document.getElementById('searchform');
var place;
var autocomplete = new google.maps.places.Autocomplete(input, options);
Add listener to detect autocomplete selection
google.maps.event.addListener(autocomplete, 'place_changed', function () {
place = autocomplete.getPlace();
//console.log(place);
});
Add listener to search (This function isn't working, hence my work around in the initialize function)
searchform.addEventListener("submit", function() {
var newlatlong = new google.maps.LatLng(place.geometry.location.lat(),place.geometry.location.lng());
map.setCenter(newlatlong);
marker.setPosition(newlatlong);
map.setZoom(12);
google.maps.event.trigger(map, 'resize');
});
Reset the inpout box on click
input.addEventListener('click', function(){
input.value = "";
});
Call the initialize function
google.maps.event.addDomListener(window, 'load', initialize);
Indeed it looks like a problem of JavaScript's scope.
Consider these lines of your code:
function initialize() {
//...
var map = new google.maps.Map(map_canvas, map_options)
//...
}
Declaring map inside the initialize function makes your map variable unreachable out of your initialize function.
So when you try to reach it in your searchform, map.setCenter() will show in your console that map is undefined.
You could solve your issue in several ways.
declare map at the beginning of your script (before initialize is executed, anyways)
declare the searchform.addEventListener("submit", function(){}) inside your initialize() function and use it before initialize() ends its execution.
I have several markers (in an array) on my map, each with a custom ID tag i've given them.
What I want:
When I click on a marker, i wish to add it's ID to another array.
The problem:
The mouse event from Google does not have a target attribute, only the position, so I can't seem to access the ID directly.
I don't really want to have to resort to using the position to find the closest marker to it and returning it's ID this way, it's rather convoluted.
All help is appreciated
This is really easy, thanks to a feature in JavaScript and many other languages called a closure.
Simply put the code that creates the marker and sets up its event listener(s) insidea function, and call that function for each marker with the data needed for that specific marker. For example:
var places = [
{
id: 'one', lat: 1, lng: -1, name: 'First'
},
{
id: 'two', lat: 2, lng: -2, name: 'Second'
}
];
for( var i = 0; i < places.length; i++ ) {
addPlace( places[i] );
}
function addPlace( place ) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng( place.lat, place.lng ),
title: place.name
});
google.maps.event.addListener( 'click', function() {
alert( 'Clicked ' + place.id + ': ' + place.name );
});
}
I didn't test this Maps API code, but the specifics of the code are not important. What is important to understand is that place variable you see used in the code. This is the key part: that variable is accessible inside the event listener, simply because the event listener is nested inside the addPlace() function which has place as a parameter.
Note the difference between that code and code like this, which will not work:
for( var i = 0; i < places.length; i++ ) {
var place = places[i];
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng( place.lat, place.lng ),
title: place.name
});
google.maps.event.addListener( 'click', function() {
alert( 'Clicked ' + place.id + ': ' + place.name );
});
}
The only difference between the two is that the working version puts the loop body in a separate function which is called from the loop, instead of having all that code directly inside the loop. Having that code in a function that you call each time is what creates the closure, and that's what lets the inner event listener function "see" the variables in the outer function.
The great thing about closures is that you can use them in any similar situation. It isn't specific to the Maps API or the objects that the API uses. You may have even used them already and not realized it, for example in a setTimeout() call like this:
// Display an alert 'time' milliseconds after this function is called
function slowAlert( message, time ) {
setTimeout( function() {
alert( message );
}, time );
}
slowAlert( 'Howdy!', 1000 ); // Wait a second and then say Howdy!
Where the alert() call is made inside the setTimeout() callback function is made, it's using the closure on the slowAlert() function to pick up the value of the message variable that was passed into that function.
This should help. I added a customId property to the marker object and then in the marker click event I assign the id property to the new array.
function initialize() {
var map;
var centerPosition = new google.maps.LatLng(38.713107, -90.42984);
var options = {
zoom: 6,
center: centerPosition,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var bounds = new google.maps.LatLngBounds();
map = new google.maps.Map($('#map')[0], options);
var infoWindow = new google.maps.InfoWindow();
//marker array
var markers = [];
//sencondary array to store markers that were clicked on.
var markerIdArray = [];
for (i = 0; i < 6; i++) {
var lat = 38.713107 + Math.random();
var lng = -90.42984 + Math.random();
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(lat, lng),
customId: i //add a custom id to the marker
});
bounds.extend(marker.position);
google.maps.event.addListener(marker, 'click', function () {
//add the id to the other array.
markerIdArray.push(this.customId);
//log the content of the array to the console.
console.log(markerIdArray);
});
markers.push(marker);
}
map.fitBounds(bounds);
}
Here is an example of this in action.
I'm trying to iterate through a collection of Markers and using a counter variable to wire up an event. "When someone clicks a marker, show the infobox in position 'count'"
The problem is as the script executes, the count variable is shared among each and every click event subscriber.
Meaning, a click in Marker A or B is wired to the same position, meaning the same InfoBox.
var popups = [];
var count = 0;
#foreach (var marker in Model) {
<text>
/* Create Markers */
popups[count] = {};
popups[count]["marker"] = new google.maps.Marker({
map: themap
});
popups[count]["content"] = document.createElement("div");</div>';
popups[count]["infoboxoptions"] = {
boxClass: 'infobox-custom'
};
popups[count]["infobox"]= new InfoBox(popups[count]["infoboxoptions"]);
google.maps.event.addListener(popups[count]["marker"], "click", function (e) {
popups[count]["infobox"].open(themap, this);
/* PROBLEM IS HERE! ^ */
});
count++;
</text>
}
I understand the problem but don't know how to tackle it. Javascript is using the same variable for each click event and incrementing it after every iteration is also incrementing it for each click event.
Any suggestions?
Here's one solution. I used the Razor engine to ouput a hardcoded number value.
var popups = [];
var count = 0;
#{ var i = 0; }
#foreach (var marker in Model) {
<text>
/* Create Markers */
popups[count] = {};
popups[count]["marker"] = new google.maps.Marker({
map: themap
});
popups[count]["content"] = document.createElement("div");</div>';
popups[count]["infoboxoptions"] = {
boxClass: 'infobox-custom'
};
popups[count]["infobox"]= new InfoBox(popups[count]["infoboxoptions"]);
google.maps.event.addListener(popups[count]["marker"], "click", function (e) {
popups[#i]["infobox"].open(themap, this);
});
count++;
</text>
i++;
}
I have the following code, and having read this, i understand it wont work because the getJSON call is asynchronous. How do i need to change this so that the MarkerClusterer function gets triggered with a full set of markers? I've tried putting the MarkerClusterer function inside the getJSON call but with no luck...
var mcOptions = {gridSize: 50, maxZoom: 9};
var markers = [];
function parse_json(json) {
if (json.length > 0) {
for (i=0; i<json.length; i++) {
var report = json[i];
var latLng = new google.maps.LatLng(report.latitude, report.longitude);
markers[i] = new google.maps.Marker({
position: latLng,
title: report.name + ' ' + report.surf_size_ft_round,
url: "/place/"+report.slug
});
google.maps.event.addListener(markers[i], 'click', function() {
window.location.href = markers[i].url;
});
markers.push(markers[i]);
}
}
};
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
}
});
alert(markers[5].title); // sanity check - empty
var mc = new MarkerClusterer(map, markers, mcOptions);
Why not put this code snippet:
mc = new MarkerClusterer(map, markers, mcOptions);
inside the anonymous callback function in your $.getJSON? Just declare var mc; somewhere outside the $.getJSON scope to be able to have access to it elsewhere.
Alternatively, you can fire an event at the end of your parse_json function, listen to that event and then fire up another function that creates your MarkerClusterer object when the event has fired. Check this out: How to trigger event in JavaScript?
EDIT:
Upon inspecting your code a bit more, I can see that you set markers[i] to a new Marker instance and then push onto the markers array that same instance. You probably want to either set markers[i] to a new Marker instance or you want to create a var marker, setting it to a new Marker instance and then pushing on the markers array.
Maybe you need to put it inside the success function you give as an input to $.getJSON?
$.getJSON('<%= request.fullpath + ".json" %>', function(stream) {
if (stream.length > 0) {
parse_json(stream);
alert(markers[1].title); //sanity check - gives result
mc = new MarkerClusterer(map, markers, mcOptions);
}
});
alert(markers[5].title); // sanity check - empty