Markercluster Doubling Markers - wordpress

For some reason, Markercluster is doubling the markers in a group. For example, when I click on a group I see four individual markers instead of two. The names are duplicated too. I looked through other posts but am still confused by this. Thanks!
<script>
var mapData = <?php echo wp_json_encode($mapData); ?>;
var map = L.map('map').setView([40, 0], 2);
var markers = new L.MarkerClusterGroup();
L.mapbox.accessToken = 'myToken';
var geocoder = L.mapbox.geocoder('mapbox.places');
for ( var i = 0; i < mapData.length; i++ )
{
geocoder.query(mapData[i].city, showLoc);
}
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=myToken', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(map);
function showLoc (err, data) {
var city = data.results.query.join(' ');
for ( var i = 0; i < mapData.length; i++ )
{
if ( mapData[i].city.toLowerCase() === city ) {
var marker = L.marker(new L.LatLng(data.latlng[0], data.latlng[1]), {
icon: L.mapbox.marker.icon({'marker-color': 'c70c2b'}),
title: mapData[i].name
});
//new L.Marker([data.latlng[0], data.latlng[1]], 2)
marker.bindPopup("<strong>" + mapData[i].name + "</strong>")
markers.addLayer(marker);
}
}
markers.addTo(map);
}

Think I figured it out. Very simple fix. Had to move var markers = new L.MarkerClusterGroup(); in my showLoc (err, data) function. I think it has to do with declaring it after the map tiles load. Not sure though.

Related

Google Map: Link to the map according to feature type

I have a google Map with markers on a webpage.
Each marker has a unique feature position and type
This is the scenario I would like to put in place:
On another webpage I have static links to different markers of the map.
If you click on one of those links, you are directed to the map in which, one of these markers is centered (and its info window open).
But the markers latitude and longitude might change while the links will never change.
This means, I need the links not to use latitude and longitude info but markers feature type instead (which are remain the same).
How can I do that?
Here is my sample google Map script so far:
<script>
function initMap() {
var mapDiv = document.getElementById('map');
var map = new google.maps.Map(mapDiv, {
center: {lat: 48.85639, lng: 2.33625}, // default centering
zoom: 18,
styles:
[
{featureType: 'poi',stylers: [{ visibility: 'off' }]},
{featureType: 'transit.station',stylers: [{ visibility: "off" }]}
]
});
var features = [
{position: new google.maps.LatLng(48.85659, 2.33555),type: 'markerone'},
{position: new google.maps.LatLng(48.85619, 2.33695),type: 'markertwo'}
];
var icons = {
'markerone': {icon: 'icon_one.png'},
'markertwo': {icon: 'icon_two.png'}
};
var contents= {
'markerone': {text: 'Content 1'},
'markertwo': {text: 'Content 2'}
};
for (var i = 0, feature; feature = features[i]; i++)
{
var marker = new google.maps.Marker({
position: feature.position,
icon: icons[feature.type].icon,
map: map
});
var content = contents[feature.type].text;
var infowindow = new google.maps.InfoWindow()
google.maps.event.addListener(marker,'mouseover', (function(marker,content,infowindow){
return function() {
infowindow.setContent(content);
infowindow.open(map,marker);
};
})(marker,content,infowindow));
google.maps.event.addListener(marker,'mouseout', (function(marker,content,infowindow){
return function() {
infowindow.close(map,marker);
};
})(marker,content,infowindow));
}
}
</script>
In this sample, I have to markers.
One has a feature type of "markerone" and the second is "markertwo".
How can I set my links to redirect and center the map around a specific marker in this kind of fashion:
http://www.mywebsite.com/mymap.php?myvariable=markertwo
Thank you.
First you would have to get the parameters. The example below gets all parameters and put them into an array. There you can search for your certain paramater like "markerType" and check if it's given or not. If not you have to perform a default action, otherwise you can handle the certain markerType like finding the correct marker, setting the map center to it and open the corrosponding infoWindow.
You just have to call the focusMarkerType-method onload of your page.
function getSearchParameters() {
var prmstr = window.location.search.substr(1);
return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}
function transformToAssocArray( prmstr ) {
var params = {};
var prmarr = prmstr.split("&");
for ( var i = 0; i < prmarr.length; i++) {
var tmparr = prmarr[i].split("=");
params[tmparr[0]] = tmparr[1];
}
return params;
}
function focusMarkerType(){
var params = getSearchParameters();
if(params.markerType!=null){
//Handling the certain marker type
var found = false;
for (var i = 0, feature; feature = features[i]; i++) {
if (feature.type == params.markerType) {
found = true;
map.setCenter(feature.position);
//more...
break;
}
}
if (!found) {
console.log("unknown type")
}
}else{
//Handling default behaviour if no marker type is given
}
}

Resposive Google Maps javascript api turns out grey (sometimes)

I have two websites which attempt to display a map of bushfires in an area, using the Google Maps javascript api and a GeoJson from the NSW RFS. Both are resposive sites.
The maps seem very twitchy about displaying the actual map background, and in one case I always get a grey background (though markers etc are properly displayed).
This seems to be related to the way the map canvas is sized; if a fixed width and height are set then all is well, but if % are used grey is displayed. In the one case setting width and height in vw (% of viewport width) works, but in the other it does not.
The site that works is http://lansdowne.rfsa.org.au/firemap.php
the one that does not is http://www.upperlansdownehall.org.au/firemap/ - if you zoon out to a larger area ytou will see that markers appear in both maps.
The major difference is that one is Wordpress, with the javascript enqueued with the footer, while the other is imbedded in the html.
I have tried setting the size (width and height) of the map-canvas in js (or that of an enclosing division) using the following code either in the initilize routine or just before the addDomListener(window, 'load', initialize);
var mw = document.getElementById('map-canvasout').offsetWidth;
var mw = document.documentElement.clientWidth * .5;
var mh = mw * .75;
var mhx = mh + "px";
var mapara = document.getElementById('map-canvasout');
mapara.style.height= mhx;
I would be very grateful for any ideas, as I am out of them. code for the wordpress scripts encode and js follows:-
add_action('wp_enqueue_scripts', 'ulmh_googlemaps', 105);
function ulmh_googlemaps(){
if (is_page('32') || is_page('233')) {
wp_register_script( 'gmap', 'https://maps.googleapis.com/maps/api/js?key=AIzaSyBAlriyR-tmJU4jrd0z9nmWEmw4XSS0nC0',true);
wp_register_script( 'ulmh_gmap', plugins_url( 'firemap.js', __FILE__ ),true);
wp_enqueue_script('gmap');
wp_enqueue_script('ulmh_gmap');
}
}
function initialize() {
var lat = -31.71 ;
var lng = 152.47;
var zom = 12;
var nam = "Upper Lansdowne";
var mapOptions = {
center: { lat: lat, lng: lng},
zoom: zom
};
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
map.data.loadGeoJson('http://www.upperlansdownehall.org.au/wp-content/uploads/majorIncidents.json');
map.data.setStyle(function(feature) {
var IconBase = 'http://www.rfs.nsw.gov.au/_designs/geojson/fires-near-me/images/';
var image = {
url: IconBase + 'watch-and-act.png',
size: new google.maps.Size(27, 27),
origin: new google.maps.Point(0,0),
anchor: new google.maps.Point(14, 13)
};
var ctg = feature.getProperty('category');
switch (ctg) {
case "Emergency Warning": image.url = IconBase + 'emergency-warning.png'; break;
case "Watch and Act": image.url = IconBase + 'watch-and-act.png'; break;
case "Advice": image.url = IconBase + 'advice.png'; break;
default: image.url = IconBase + 'not-applicable.png';
}
return {
icon: image
};
});
var infowindow = new google.maps.InfoWindow();
map.data.addListener('click', function(event) {
var ef = event.feature;
var myHTML = '<b>'+ef.getProperty("title") + '</b></br>' + ef.getProperty("description");
infowindow.setContent("<div style='width:250px;'>"+myHTML+"</div>");
infowindow.setPosition(event.latLng);
infowindow.setOptions({pixelOffset: new google.maps.Size(0,0)});
infowindow.open(map);
});
nam = 'Fire Map of the ' + nam + ' Area';
lat = Math.round(lat*10000)/10000;
lng = Math.round(lng*10000)/10000;
google.maps.event.addListener(map, 'idle', function() { // 'bounds_changed'
google.maps.event.trigger(map, 'resize');
var mapbnd = map.getBounds();
var nbrfre = 0;
map.data.forEach(function(feature) {
var geo = feature.getGeometry();
if (geo.getType() == 'Point') {
var LatLng = geo.get();
if (mapbnd.contains(LatLng)) {
nbrfre = nbrfre +1;
}
} else if (geo.getType() == 'GeometryCollection') {
var LatLng = geo.getAt(0).get();
console.log(LatLng);
if (mapbnd.contains(LatLng)) {
nbrfre = nbrfre +1;
}
}
});
var fretxt = ' (No Fires)';
if (nbrfre == 1) {
fretxt = ' (1 Fire)';
} else if (nbrfre > 1) {
fretxt = ' (' + nbrfre + ' Fires)';
}
var ctr = map.getCenter();
clt = Math.round(ctr.lat()*10000)/10000;
cln = Math.round(ctr.lng()*10000)/10000;
if ((clt != lat) || (cln != lng) ||(zom != map.getZoom())) {
nn = 'General Fire Map';
} else {
nn = nam;
}
var pstttl = document.getElementsByClassName("posttitle");
pstttl[0].innerHTML = nn + fretxt;
if (nbrfre != 0) {
pstttl[0].style.backgroundColor='red';
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Well it seems my unconscious is better at this than the rest of me, as I woke up this morning with a partial answer. One site was using the experimental version of Google Maps (by default) while the other specified v=3 and therefore got the release version.
Of course that raises the question of what is happening at Googles end, but I’ll chase that up with them.
Thanks to anyone who looked at the question.

Can't get Google Maps api v3 to display markers from xml file

I should have on my hands what is proper working JS and an xml file...I'm simply trying to take advantage of the huge database of locations that has been offered up as free for everyone to use - located # http://www.craftbeer.com/breweries/brewery-locator/find-a-us-brewery
I'm tried using their maps.js and xml file...but all I end up with is a blank maps screen...
Here is the Javascript...
/*********************************************
**Google Maps Custom API work**
This code can be reused on any site to display a local map of breweries from our local iMis dump, as well as pull in beermapping.
Hey! Don't forget to include API in functions.php! Function is conditionally initialized in footer.php
*/
//Set up variables to create marker
var gmarkers = [];
var mapMarkers = [];
var geocoder = new google.maps.Geocoder();
var icon = 'http://www.craftbeer.com/wp-content/uploads/marker.png';
//Initialize Map
function view_map(xml_file, latitude, longitude, varZoom) {
console.log('view map');
map = new google.maps.Map(document.getElementById("map_canvas"), { //Create the map, Set Default Zoom level and type
center: new google.maps.LatLng(latitude, longitude),
zoom: varZoom,
mapTypeId: 'roadmap',
panControl: true,
zoomControl: true,
mapTypeControl: false,
scaleControl: true,
streetViewControl: false,
overviewMapControl: false
});
//-- Listeners --//
google.maps.event.addListener(map, 'center_changed', function() {
console.log('center changed');
//clearMarkers();
});
google.maps.event.addListenerOnce(map, 'tilesloaded', function(){
//this part runs when the mapobject is created and rendered
console.log("loaded");
$("#loading-screen").fadeOut(250, function(){});
$("#loading-icon").fadeOut();
//google.maps.event.addListenerOnce(map, 'tilesloaded', function(){ });
});
//console.log(xml_file);
var download_file = xml_file; //this variable is passed in so we can re-use this script
//Pull in the XML feed
downloadUrl(download_file, function(data) {
console.log("downloading xml");
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
//Loop through all markers in the XML file
for (var i = 0; i < markers.length; i++) {
var state = markers[i].getAttribute("state");
var x = markers[i].getAttribute("lat");
var y = markers[i].getAttribute("lng");
if ( state != 'PR' && state != 'VI' && state != 'GU' && x != "" ){
//console.log(state);
var ids = parseFloat(markers[i].getAttribute("id"));
var z = new google.maps.LatLng(x,y,i);
//console.log(x);
var _id = markers[i].getAttribute("id");
var name = markers[i].getAttribute("company");
var address = markers[i].getAttribute("address");
var city = markers[i].getAttribute("city");
var zip = markers[i].getAttribute("zip");
var phone = markers[i].getAttribute("phone");
var url = markers[i].getAttribute("url");
var brewery_type = markers[i].getAttribute("type");
var member_type = '';
var member_type = markers[i].getAttribute("member_type");
var offer = markers[i].getAttribute("offer");
var html = "<div class='bubble_content'><strong>" + name + "</strong><br />";
html += address + "<br/>" + city + ", " + state + " " + zip;
if(url) html += "<br /><br /><a href='http://" + url + "' target='_blank'>Visit Web Site</a></div>";
//var marker = createMarker(ids, z, title, contentstring);
var marker = new google.maps.Marker({
position: z,
map: map,
title: name,
icon: icon,
html: html,
zindex: i
});
//Add a listener for every icon click
google.maps.event.addListener(marker,'click',function(){
infowindow.setContent(this.html);
infowindow.open(map,this);
//map.setZoom(10);
//map.setCenter(this.getPosition());
console.log(this);
});
gmarkers[ids] = marker;
mapMarkers.push(marker);
if( x && y ){
//console.log(ids);
var infowindow = new google.maps.InfoWindow({content: html});
}
/* google.maps.event.addListener(marker, 'click', function() {
console.log('marker clicked');
});
*/
//bounds.extend(z);
//map.fitBounds(bounds);
}//end check for US states only!
} //end for loop
var mcOptions = {gridSize: 60, maxZoom: 9};
var markerCluster = new MarkerClusterer(map, mapMarkers, mcOptions); //this is the function that groups the icons into markers
});
} //End full function to create map
/*
* FUNCTION
*
* click to bring up one icon when clicked from the list.
*/
function myclick(i){
console.log('clicked a title ' + i);
smoothScroll("#primary");
var lat = parseFloat(gmarkers[i].position.k);
var lng = parseFloat(gmarkers[i].position.B);
console.log(lat);
map.setCenter({lat: lat, lng: lng});
map.setZoom(13);
google.maps.event.trigger(gmarkers[i], 'click');
};
function createMarker(ids, z, title, contentstring){
console.log('createMarker');
var marker = new google.maps.Marker({
position: z,
map: map,
title: title,
html: contentstring,
icon: 'http://maps.google.com/mapfiles/ms/icons/orange-dot.png'
});
google.maps.event.addListener(marker,'click',function(){
infowindow.setContent(this.html);
infowindow.open(map,marker);
});
gmarkers[ids] = marker;
//console.log(gmarkers[ids]);
};
/*
* FUNCTION findAddress()
*
* given an adress string, zoom the map to the proper state
*/
function findAddress(position) {
//Let's determind if we have a state (address) or LatLng
var addressStr = $("#state_select li.active").data('state-id');
if (addressStr != 'Select a State') address = "US State of "+addressStr;
if(position) {
var lat = parseFloat(position['latitude']);
var lng = parseFloat(position['longitude']);
var latlng = new google.maps.LatLng(lat, lng);
//console.log(latlng);
}
//Now let's geocode - two different cases
if (geocoder && position) { //If LatLng
//console.log('near me');
geocoder.geocode( { 'location': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
if (results && results[0] && results[0].geometry && results[0].geometry.viewport && addressStr!="ON" && addressStr!="INT") {
map.fitBounds(results[0].geometry.viewport); //resize map to fit.
map.setZoom(13);
}
} else {
alert("No results found");
}
}
});
} else if (geocoder && address!="") { //If State
//console.log('by state');
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
if (results && results[0] && results[0].geometry && results[0].geometry.viewport && addressStr!="ON" && addressStr!="INT") {
map.fitBounds(results[0].geometry.viewport); //resize map to fit.
map.setZoom(6);
}
} else {
alert("No results found");
}
}
});
} else {
//alert("Geocode was not successful for the following reason: " + status);
}
}
function downloadUrl(url, callback) {
var request = window.ActiveXObject ?
new ActiveXObject('Microsoft.XMLHTTP') :
new XMLHttpRequest;
request.onreadystatechange = function() {
if (request.readyState == 4) {
request.onreadystatechange = doNothing;
callback(request, request.status);
}
};
request.open('GET', url, true);
request.send(null);
}
function doNothing() {}
And here is the link to the xml file....
http://www.straighttothepint.com/wp-content/uploads/wp-google-maps/Google_Map_APIs.xml
I've spent days doing google searches and testing different sections of code to at least get a basic google map to show up via the xml file markers...but to no avail - Any help would be tremendously appreciated!!
~Cheers
EDIT:
Everything worked as expected once I included the following script inline on the page prior to calling the maps.js file. Thanks everyone!
<script>
$(document).ready(function(){
var latitude = 39.300299;
var longitude = -97.382812;
view_map('./uploads/your_xml_file.xml', latitude,longitude,4);
//scripts to handle mapping are in js/mylibs/map.js.php
});
</script>
Looking at the html and the code you provided above I constructed a fiddle for you:
http://jsfiddle.net/loanburger/qafsex5x/
I added two markers in an xml string at the top in the fiddle.
I basically parse the xml doing this:
var markers = $(xml).find("marker");
Its then simply the same for loop you had:
for (var i = 0; i < markers.length; i++)
{
...
}
The fiddle will show you the markers.

Google Maps changing a marker back to its original image

A wee bit out of my depth here, I hope someone can help me resolve this...
I have a Google map which loads data from an array and also populates a sidebar list of the items.
A click on the sidebar item opens the corresponding infowindow as expected o.k.
When I mouseenter a sidebar item it pans to if necessary and highlights the corresponding marker (and closes any infowindow currently open ).
(.locs is the sidebar container, .loc is an item)
$(document).on("mouseenter",".loc",function() {
var thisloc = $(this).data("locid");
for(var i=0; i<markers.length; i++) {
if(markers[i].locid == thisloc) { //get the latlong
if(lastinfowindow instanceof google.maps.InfoWindow) lastinfowindow.close();
map.panTo(markers[i].getPosition());
markers[i].setIcon('../mapIcons/mini-white.png');
}
}
});
This bit works fine as I'm just setting a fixed image.
However, when I mouseleave the marker fails to revert back to its original state.
If I use the setIcon for a specic image it works fine, but I want to refer back to the original icon from the dataset item.
markers[i].setIcon("currmark");
currmark is the var I used to hold the original icon.
$(document).on("mouseleave",".loc",function() {
var thisloc = $(this).data("locid");
var currmark = getIcon("mapData.type");
for(var i=0; i<markers.length; i++) {
if(markers[i].locid == thisloc) { //get the latlong
map.panTo(markers[i].getPosition());
markers[i].setIcon("currmark");
}
}
});
An alert set on 'currmark', the var I used to hold the original icon, shows only the last icon set by the mouseenter setIcon.
This seems to idicate to me that the original icon from the dataset (mapdata.type) has been forgotten / replaced by the last setting applied to markers[i].
I think I need to reaccess the dataset to remind the var of the original state when I set
var currmark = getIcon("mapData.type");
Some added info....
I'm using a utility function that translates a given 'type' in the dataset to an icon, hence the getIcon(type) function.
function getIcon(type) {
switch(type) {
case "bar": return "../mapIcons/mini-blue.png";
case "restaurant": return "../mapIcons/mini-red.png";
case "cafe": return "../mapIcons/mini-yellow.png";
default: return "../mapIcons/mini-white.png";
}
}
An example of a data entry...
var data = [
{address:'Rua Calheta',detail:'Restaurant',title:'Dolphin',type:'restaurant',ico:'red',lat:"37.08570947136275",long:"-8.731633722782135"}
];
I hope I've explained this half sensibly. Can anyone assist me, as I was dead chuffed I'd got this far.
2nd Additional Info for Beetroot showing how my marker and sidebar are initially setup....
data.forEach(function(mapData,idx) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat,mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
var link=('<a href="#"' + mapData.title +'">');
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + mapData.title+"</a></h3>" + mapData.detail+"<br>"+mapData.address+"</div>";
var infowindow = new google.maps.InfoWindow({
content: contentHtml
});
google.maps.event.addListener(marker, 'click', function() {
if(lastinfowindow instanceof google.maps.InfoWindow) lastinfowindow.close();
marker.infowindow.open(map, marker);
lastinfowindow = marker.infowindow;
});
marker.locid = idx+1;
marker.infowindow = infowindow;
markers[markers.length] = marker;
var spot=('<img src="' + (getIcon(mapData.type)) + '" />');
var sideHtml = '<p class="loc" data-locid="'+marker.locid+'">'+spot+mapData.title+'</p>';
$("#locs").append(sideHtml);
if(markers.length == data.length) doFilter();
});
EDIT 02/07
The complete source after incorporating Beetroot-Beetroot's slick changes. Here in its entirety as I'm now vary aware that he can only help if he has the whole picture!
Now attempting to debug after learning I'm not such a smartass.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false& style=feature:all|element:labels|visibility:off">
</script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
var map;
var markers = [];
var lastinfowindow;
var locIndex;
//Credit: MDN
if ( !Array.prototype.forEach ) {
Array.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}
}
}
//my data ~ cutdown and local for testing
var data = [
{address:'Rua Calheta',detail:'Restaurant',title:'Dolphin',type:'restaurant',icotyp:'red',lat:"37.08570947136275",long:"-8.731633722782135"},
{address:'Rua Calheta',detail:'Cafe',title:'Cafe Calheta',type:'cafe',icotyp:'yellow',lat:"37.0858150589029",long:"-8.731550574302673"},
{address:'Rua Calheta',detail:'Bar',title:'Kellys',type:'bar',icotyp:'red',lat:"37.08583217639933",long:"-8.731239438056945"},
{address:'Rua 2nd Abril',detail:'Bar',title:'Godots',type:'bar',icotyp:'blue',lat:"37.08602046860496",long:"-8.731470108032226"}
];
// translate data type: to icon
function getIcon(type) {
switch(type) {
case "bar": return "../mapIcons/mini-blue.png";
case "restaurant": return "../mapIcons/mini-red.png";
case "cafe": return "../mapIcons/mini-yellow.png";
default: return "../mapIcons/mini-white.png";
}
}
function initialize() {
var latlng = new google.maps.LatLng(37.08597981464561, -8.730670809745788);
var myOptions = {
zoom: 18,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
// revised beetroot-beetroot 'loop' function to set markers and sidebar at the same time
var $locs = $("#locs");//Used repeatedly inside the loop.
$.each(data, function(idx, mapData) {
//Note: critical assignment of the new marker as a property of `mapData`.
mapData.marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat,mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
var link = '' + mapData.title + '';
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";
//What was `marker` must now be referred to as `mapData.marker`
mapData.marker.infowindow = new google.maps.InfoWindow({
content: contentHtml
});
google.maps.event.addListener(mapData.marker, 'click', function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
//`this` refers back to the clicked element; the appropriate marker
this.infowindow.open(map, this);
lastinfowindow = this.infowindow;
});
var spot = '<img src="' + (getIcon(mapData.type)) + '" />';
//Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
$('<p class="loc" />')
.html(spot + mapData.title)
.appendTo($locs)
.data('mapData', mapData);//<<<<<<<< critical
});
doFilter(); //It should be possible to execute this statement when the loop is finished.
$(document).on("mouseenter", ".loc", function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
var mapData = $(this).data("mapData");
map.panTo(mapData.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
mapData.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
var mapData = $(this).data("mapData");
mapData.marker.setIcon(getIcon(mapData.type));
}).on("click",".loc",function() {
var mapData = $(this).data("mapData");
mapData.marker.infowindow.open(map, mapData.marker);
lastinfowindow = mapData.marker.infowindow;
});
/*
Run on every change to any of the checkboxes
*/
function doFilter() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close(); //Close last info windows if still open and new filter applied
if(!locIndex) {
locIndex = {};
//I reverse index markers to figure out the position of loc to marker index
for(var x=0, len=markers.length; x<len; x++) {
locIndex[markers[x].locid] = x;
}
}
//what's checked?
var checked = $("input[type=checkbox]:checked");
var selTypes = [];
for(var i=0, len=checked.length; i<len; i++) {
selTypes.push($(checked[i]).val());
}
for(var i=0, len=data.length; i<len; i++) {
var sideDom = "p.loc[data-locid="+(i+1)+"]";
//Only hide if length != 0
if(checked.length !=0 && selTypes.indexOf(data[i].type) < 0) {
$(sideDom).hide();
markers[locIndex[i+1]].setVisible(false);
} else {
$(sideDom).show();
markers[locIndex[i+1]].setVisible(true);
}
}
}
$(document).on("click", "input[type=checkbox]", doFilter);
}
</script>
<style type="text/css">
#container {width:920px;height:500px; margin-left:auto;margin-right:auto;}
#map_canvas {width: 700px; height: 500px;float:left;border:1px gray solid;margin-right:10px;}
/* sidebar styling */
#locs {margin-top: 0px;padding-top: 0px;margin-left: 5px;height: 500px;width:200px;overflow-y: auto;overflow-x: hidden;}
#locs img {float:left;padding-right:5px;}
.loc {border: 0;width: 200px;padding: 2px;cursor: pointer;margin:0;text-transform:uppercase;font-family:Arial, Helvetica, sans-serif;font-size:0.90em;}
/* checkbox styling */
#form {margin-top: 0px;padding-top: 0px;height: 29px;}
.label {width: 32.5%;margin: 0;padding: 4px 0 4px 4px;display: inline-block;font-family: "Trebuchet MS", Verdana, Arial, sans-serif;text-transform: uppercase;font-size: 0.75em;font-weight: bold;}
.red {color:white;background-color:red;}
.yellow {color:yellow;background-color:black;}
.blue {color:white;background-color:blue;}
/* Map info styling */
#map_canvas div#iw {width:250px;height:65px;overflow:hidden;}
#map_canvas h3 {font-size:1em; margin:0; padding:3px;}
#map_canvas .iw2 a {text-decoration:none;border-bottom:thin blue dashed;}
#map_canvas .iw2 a:hover {text-decoration:none;border-bottom:thin red solid;}
</style>
</head>
<body onLoad="initialize()">
<div id="container">
<div id="map_canvas"></div> <!-- map //-->
<div id="locs"></div> <!-- sidebar //-->
<div id="form" style="margin:0;padding:0;border:thin gray solid;width:910px">
<span class="label blue"><input type="checkbox" name="bars" value="bar">Bars</span>
<span class="label yellow"><input type="checkbox" name="cafes" value="cafe">Cafes</span>
<span class="label red"><input type="checkbox" name="restaurants" value="restaurant">Restaurants</span>
</div>
</div> <!-- end of container //-->
</body>
</html>
EDIT 3 - 03/07 - Revised initialise marker / sidebar
// BB - revised each loop function to set markers and sidebar at the same time
var $locs = $("#locs");//Used repeatedly inside the loop.
$.each(data, function(idx, mapData) {
//BB - Note: critical assignment of the new marker as a property of `mapData`.
mapData.marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat,mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
var link = '' + mapData.title + '';
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";
//What was `marker` must now be referred to as `mapData.marker`
mapData.marker.infowindow = new google.maps.InfoWindow({
content: contentHtml
});
google.maps.event.addListener(mapData.marker, 'click', function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
//BB - `this` refers back to the clicked element; the appropriate marker
this.infowindow.open(map, this);
lastinfowindow = this.infowindow;
});
//BB - Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
var spot = '<img src="' + (getIcon(mapData.type)) + '" />';
$('<p class="loc" />')
.html(spot + mapData.title)
.appendTo($locs)
.data('mapData', mapData);//<<<<<<<< critical
//BB - At the bottom of the $.each() loop in initialize(), create and populate one array per category, as properties of markers ://
if(!markers[mapData.type]) markers[mapData.type] = [];
markers[mapData.type].push(mapData.marker);
});
// End of for/each loop
EDIT 4 - 07/07 - Revised full script for comment
<script type="text/javascript">
var map;
//var markers = [];
//object, not array. BB
var markers = {};
var lastinfowindow;
//var locIndex;
//Credit: MDN
if ( !Array.prototype.forEach ) {
Array.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}
}
}
//my data ~ cutdown and local for testing
var data = [
{address:'Rua Calheta',detail:'South African & Portuguese Restaurant',title:'Dolphin',type:'restaurant',lat:"37.08570947136275",long:"-8.731633722782135"},
{address:'Rua Calheta',detail:'Poruguese Cafe / Bar',title:'Cafe Calheta',type:'cafe',lat:"37.0858150589029",long:"-8.731550574302673"},
{address:'Rua Calheta',detail:'English Bar',title:'Kellys',type:'bar',lat:"37.08583217639933",long:"-8.731239438056945"},
{address:'Rua 2nd Abril',detail:'English Bar',title:'Godots',type:'bar',lat:"37.08602046860496",long:"-8.731470108032226"},
{address:'Rua 2nd Abril',detail:'Chinese Restaurant',title:'Royal Garden',type:'restaurant',lat:"37.086164896968405",long:"-8.731738328933715"},
{address:'Rua 2nd Abril',detail:'English Bar',title:'Clives',type:'bar',lat:"37.086125",long:"-8.731374"},
{address:'Rua 2nd Abril',detail:'English Restaurant',title:'The Lime Tree',type:'restaurant',lat:"37.086125877750365",long:"-8.731588125228881"},
{address:'Praia da Luz',detail:'Portuguese Restaurant',title:'Alloro',type:'restaurant',lat:"37.09158",long:"-8.724850"},
{address:'Rua Calheta',detail:'English Cafe / Bistro',title:'Jakes',type:'cafe',lat:"37.0862601125538",long:"-8.732671737670898"},
{address:'Av dos Pescadores',detail:'English & Portuguese Restaurant',title:'Chaplins',type:'restaurant',lat:"37.08550480361006",long:"-8.730005621910095"},
{address:'Av dos Pescadores',detail:'English & Portuguese Restaurant',title:'Atlantico',type:'restaurant',lat:"37.085425634814705",long:"-8.729941248893737"},
{address:'Av dos Pescadores',detail:'Indian Restaurant',title:'Saffron',type:'restaurant',lat:"37.08534432623613",long:"-8.729884922504425"},
{address:'Av dos Pescadores',detail:'Indian Restaurant',title:'Pashmina',type:'restaurant',lat:"37.08526729697597",long:"-8.729839324951171"},
{address:'Rua Calheta',detail:'English Bar',title:'The Bull',type:'bar',lat:"37.085652442494",long:"-8.73089075088501"},
{address:'Av dos Pescadores',detail:'English & Portuguese Restaurant',title:'Galley',type:'restaurant',lat:"37.08571577732778",long:"-8.729227781295776"},
{address:'Av dos Pescadores',detail:'English & Portuguese Bar',title:'Carlos',type:'bar',lat:"37.0856306176238",long:"-8.729227781295776"}
];
// translate data.type: to icon
function getIcon(type) {
switch(type) {
case "bar": return "../mapIcons/mini-blue.png";
case "restaurant": return "../mapIcons/mini-red.png";
case "cafe": return "../mapIcons/mini-green.png";
default: return "../mapIcons/mini-white.png"; // used as current selection indicator
//default: return "../mapIcons/marker-yellow-dot.png"; // will use instead of white later
}
}
function initialize() {
var latlng = new google.maps.LatLng(37.08597981464561, -8.730670809745788);
var myOptions = {
zoom: 17,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
//-----------------------------------------------------------------------------------------
/*
Start of for/each loop
*/
var $locs = $("#locs");//Used repeatedly inside the loop.
$.each(data, function(idx, mapData) {
//BB - Note: critical assignment of the new marker as a property of `mapData`.
// section 1 - Marker construct
mapData.marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat,mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
// section 1 end
// section 2 - Infowindow construct
var link = '' + mapData.title + ''; // make a link of name
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "</div>"; // style info window and add detail
// What was `marker` must now be referred to as `mapData.marker`
mapData.marker.infowindow = new google.maps.InfoWindow({
content: contentHtml,
});
// section 2 end
// section 3 - Map marker click function
google.maps.event.addListener(mapData.marker, 'click', function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close(); // close any open infowindows
map.setZoom(18); // zoom in
map.setCenter(mapData.marker.getPosition());// centre on selection
//BB - `this` refers back to the clicked element; the appropriate marker
this.infowindow.open(map, this); // open infowindow
lastinfowindow = this.infowindow; // remember for closing this infowindow, if still open when selection changes
});
// section 3 end
// section 4 - Display Map marker and sidebar item
// BB - Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
var spot = '<img src="' + (getIcon(mapData.type)) + '" />'; // sidebar copy of marker for display
$('<p class="loc" />')
.html(spot + mapData.title) // sidebar icon and name
.appendTo($locs) // add to sidebar container
.data('mapData', mapData); //<<<<<<<< critical
// BB Remove non matching from sidebar
marker.loc = $('<p class="loc" />').addClass(mapData.type).html(spot + mapData.title).data('marker', {m:marker, type:mapData.type}).appendTo($locs).get(0);
// BB - At the bottom of the $.each() loop in initialize(), create and populate one array per category, as properties of markers ://
if(!markers[mapData.type]) markers[mapData.type] = [];
markers[mapData.type].push(marker);
});
// End of for/each loop
//-----------------------------------------------------------------------------------------
} // end of initialise (?)
//-----------------------------------------------------------------------------------------
/*
Sidebar Functions
*/
$(document).on("mouseenter", ".loc", function() {
//if(lastinfowindow && lastinfowindow.close) lastinfowindow.close(); // moved to on click, avoids closing when accidently moving mouse
var mapData = $(this).data("mapData");
map.setZoom(18); // zoom in a bit
map.setCenter(mapData.marker.getPosition());// centre on selection
//map.panTo(mapData.marker.getPosition());// direct reference to the marker avoids the need to loop.
mapData.marker.setIcon(getIcon(''));//get the default icon ie. larger yellow spot.
}).on("mouseleave", ".loc", function() {
var mapData = $(this).data("mapData");
mapData.marker.setIcon(getIcon(mapData.type));//reset marker to correct type
map.setZoom(17); //rest zoom to default
map.setCenter(marker.getPosition()); // centre on map middle
}).on("click", ".loc", function() { // opens corresponding info window on map
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
var mapData = $(this).data("mapData");
//map.setZoom(19); // zoom in more maybe
mapData.marker.infowindow.open(map, mapData.marker); // open infowindow
lastinfowindow = mapData.marker.infowindow; // remember for closing this infowindow, if still open when selection changes
})
//-----------------------------------------------------------------------------------------
/*
Run on every change to any of the checkboxes
*/
function doFilter() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close(); // close last infowindow if still open
alert ("filter triggered");
$("input[type=checkbox]").each(function(i, checkbox) {
var markersArr = markers[checkbox.value];
if(markersArr) {
$.each(markersArr, function(i, marker) {
marker.loc.style.display = ['none', 'block'][Number(checkbox.checked)]; // don't display non selected types
marker.setVisible(checkbox.checked); // display selected type(s)
});
};
});
}
//-----------------------------------------------------------------------------------------
/*
Run on initial setup
*/
doFilter();
//-----------------------------------------------------------------------------------------
/*
fires on every checkbox change
*/
$("#form").on("click", ".cat", doFilter)
</script>
If, in the loop where the markers are created, you give each sidebar item a reference to its marker instead of the locid, then there's no need to loop to rediscover the marker on mouseenter/mouseleave.
ie, instead of setting :
//My best guess at what the statement must be.
thisloc.data("locid", id);
//Keep this line if thisloc.data("locid") is used elsewhere.
set (within the sidebar item/marker creation loop) :
var marker = ....;
var thisloc = ...;
...
data[i].marker = marker;//add a .marker property to the original dataset.
thisloc.data("itemData", data[i]);//associate the original dataset with the sidebar item
Now the mouseenter/mouseleave handlers should simplify as follows:
$(document).on("mouseenter", ".loc", function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
var data = $(this).data("itemData");
map.panTo(data.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
data.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
var data = $(this).data("itemData");
data.marker.setIcon(getIcon(data.type));
});
Note: You shouldn't need to pan to the marker again on mouseleave.
This should answer the question posed plus other advantages.
EDIT:
OK, now I've seen the main loop, it can be revised something like this, with various mods all through :
var $locs = $("#locs");//Used repeatedly inside the loop.
$.each(data, function(idx, mapData) {
//Note: critical assignment of the new marker as a property of `mapData`.
mapData.marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat,mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
var link = '' + mapData.title + '';
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";
//What was `marker` must now be referred to as `mapData.marker`
mapData.marker.infowindow = new google.maps.InfoWindow({
content: contentHtml
});
google.maps.event.addListener(mapData.marker, 'click', function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
//`this` refers back to the clicked element; the appropriate marker
this.infowindow.open(map, this);
lastinfowindow = this.infowindow;
});
var spot = '<img src="' + (getIcon(mapData.type)) + '" />';
//Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
$('<p class="loc" />')
.html(spot + mapData.title)
.appendTo($locs)
.data('mapData', mapData);//<<<<<<<< critical
});
doFilter();//It should be possible to execute this statement when the loop is finished.
And the corresponding mouseenter/mouseleave handlers would be :
$(document).on("mouseenter", ".loc", function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
var mapData = $(this).data("mapData");
map.panTo(mapData.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
mapData.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
var mapData = $(this).data("mapData");
mapData.marker.setIcon(getIcon(mapData.type));
});
All untested, so may well need debugging.
EDIT2 :
The way I left it above, the array markers wasn't being populated. Now I have seen the function doFilter() I can see that markers should be populated, however it is more convenient for it to be an object rather than an array.
markers = {};//in the same scope as before
At the bottom of the $.each() loop in initialize(), create and populate one array per category, as properties of markers :
if(!markers[mapData.type]) markers[mapData.type] = [];
markers[mapData.type].push(mapData.marker);
With the markers populated in this way, you have a very simple means of identifying all markers in each category, and doFilter() will simplify enormously to :
function doFilter() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
$("input[type=checkbox]").each(function(i, checkbox) {
var markersArr = markers[checkbox.value];
if(markersArr) {
$.each(markersArr, function(i, marker) {
marker.setVisible(checkbox.checked);
});
}
});
}
EDIT3
To get the sidebar entries to show/hide in sympathy with the category controls, initialize() should be as follows :
function initialize() {
var latlng = new google.maps.LatLng(37.08597981464561, -8.730670809745788);
var myOptions = {
zoom: 18,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var $locs = $("#locs");//Used repeatedly inside the loop.
var marker;
$.each(data, function(idx, mapData) {
//Note: critical assignment of the new marker as a property of `mapData`.
marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(mapData.lat, mapData.long),
title: mapData.title,
icon: getIcon(mapData.type)
});
var link = '' + mapData.title + '';
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";
marker.infowindow = new google.maps.InfoWindow({
content: contentHtml
});
google.maps.event.addListener(marker, 'click', function() {
closeLastInfoWindow();
//`this` refers back to the clicked element; the appropriate marker
this.infowindow.open(map, this);
lastinfowindow = this.infowindow;
});
var spot = '<img src="' + (getIcon(mapData.type)) + '" />';
//Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods.
marker.loc = $('<p class="loc" />')
.addClass(mapData.type)
.html(spot + mapData.title)
.data('marker', {m:marker, type:mapData.type})
.appendTo($locs).get(0);
if(!markers[mapData.type]) markers[mapData.type] = [];
markers[mapData.type].push(marker);
});
$(document).on("mouseenter", ".loc", function() {
closeLastInfoWindow();
var m = $(this).data("marker").m;
map.panTo(m.getPosition());//<<<< direct reference to the marker avoids the need to loop.
m.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
var data = $(this).data("marker");
data.m.setIcon(getIcon(data.type));
}).on("click",".loc",function() {
var m = $(this).data("marker").m;
m.infowindow.open(map, m);
lastinfowindow = m.infowindow;
});
$("#form").on("click", ".cat", doFilter);
doFilter();
}
markers[i].setIcon("currmark") won't work (sets the icon to the string "currmark"). Use the variable: markers[i].setIcon(currmark).
$(document).on("mouseleave",".loc",function() {
var thisloc = $(this).data("locid");
var currmark = getIcon("mapData.type");
for(var i=0; i<markers.length; i++) {
if(markers[i].locid == thisloc) { //get the latlong
map.panTo(markers[i].getPosition());
markers[i].setIcon(currmark);
}
}
});

How to dynamically add data to google maps API?

I have got a map that I want to have a feed of lat/long data getting pushed to the map data array. I have the function to get the data, but am having trouble getting that to be usable in the map data array.
The idea is to have a new marker drop in when a new coordinate is added to the array. Any ideas? Thanks in advance!
var ID='0';
var DATA=[];
function getData(){
var url = 'http://us7.fieldagent.net/api/newResponses/';
//url = 'http://us7.fieldagent.net/api/newResponses/;
$.post(url,{'id':ID},function(data){
if(data.status_id == 0){
ID = data.id;
console.log('Last Id: '+data.id);
var new_data = data.responses;
var count = 0
$.each(new_data,function(i,v){
count += 1;
var coord = 'new google.maps.LatLng('+v.lat+','+v.lon+'),';
DATA.push(coord);
})
console.log('Added '+count+' responses..')
}
});
}
$(document).ready(function(){
getData();
setInterval(getData,20*1000);
});
function drop() {
for (var i = 0; i < DATA.length; i++) {
setTimeout(function() {
addMarker();
}, i * 500);
}
}
function addMarker(){
markers.push(new google.maps.Marker({
position: DATA[iterator],
map: map,
draggable: false,
icon: 'fatie.svg',
animation: google.maps.Animation.DROP
}));
iterator++;
}
You need to actually add the item to the map. Right now, you're only adding an item to your DATA array. You need to call addMarker with the new data as well.
You seem to want to add these markers to the map at an interval so they drop onto the map over time, while also being able to query for new markers from your server.
Try code like this:
var ID='0';
var DATA=[];
function getData(){
var url = 'http://us7.fieldagent.net/api/newResponses/';
$.post(url,{'id':ID},function(data){
if(data.status_id == 0){
ID = data.id;
console.log('Last Id: '+data.id);
var new_data = data.responses;
var count = 0
$.each(new_data,function(i,v){
count += 1;
var coord = 'new google.maps.LatLng('+v.lat+','+v.lon+'),';
DATA.push(coord);
});
console.log('Added '+count+' responses..');
if (count > 0) addMarker(); //call addMarker if there are new markers
}
});
}
$(document).ready(function(){
getData();
setInterval(getData,20*1000);
});
function addMarker(){
if (DATA.length == 0) return; //exit if DATA is empty
markers.push(new google.maps.Marker({
position: DATA.shift(), //take the first item in DATA
map: map,
draggable: false,
icon: 'fatie.svg',
animation: google.maps.Animation.DROP
}));
if (DATA.length > 0) setTimeout(addMarker, 500); //call again if needed
}
Create a Method which does two things.
Add to the Array
Add the Item to the map

Resources