Get Google Maps v3 to resize height of InfoWindow - google-maps-api-3

When I click a marker and the InfoWindow appears, the height does not adjust if the length of the content is longer that the InfoWindow default height (90px).
I am using text-only, no images.
I have tried maxWidth.
I have checked for inherited CSS.
I have wrapped my content in a div
and applied my CSS to that, including
a height.
I have even tried forcing the
InfoWindow to resize with jQuery
using the domready event on the
InfoWindow.
I only have a few hairs left. Here is my JS:
var geocoder;
var map;
var marker;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(41.8801,-87.6272);
var myOptions = {
zoom: 13,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
function codeAddress(infotext,address) {
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var image = '/path-to/mapMarker.png';
var infowindow = new google.maps.InfoWindow({ content: infotext, maxWidth: 200 });
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
icon: image
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
}
});
}
function checkZipcode(reqZip) {
if ( /[0-9]{5}/.test(reqZip) ) {
$.ajax({
url: 'data.aspx?zip=' + reqZip,
dataType: 'json',
success: function(results) {
$.each(results.products.product, function() {
var display = "<span id='bubble-marker'><strong>"+this.name+"</strong><br>"+
this.address+"<br>"+
this.city+", "+this.state+" "+this.zip+"<br>"+
this.phone+"</span>";
var address = this.address+","+
this.city+","+
this.state+","+
this.zip;
codeAddress(display,address);
});
},
error: function() { $('#information-bar').text('fail'); }
});
} else { $('#information-bar').text('Zip codes are five digit numbers.'); }
}
$('#check-zip').click(function() { $('#information-bar').text(''); checkZipcode($('#requested-zipcode').val()); });
initialize();
InfoText and Address come from an AJAX query of an XML file. Data is not the issue, as it always comes through correctly. codeAddress() is called after the data has been retrieved and formatted.
HTML in the file:
<div id="google_map"> <div id="map_canvas" style="width:279px; height:178px"></div> </div>
CSS for my InfoWindow content (no other CSS applies to the map):
#bubble-marker{ font-size:11px; line-height:15px; }

I finally found a working solution for the problem. Is not as flexible as I wished, but it's pretty good. Fundamentally the key point is: don't use a string as window content but instead a DOM node.
This is my code:
// this dom node will act as wrapper for our content
var wrapper = document.createElement("div");
// inject markup into the wrapper
wrapper.innerHTML = myMethodToGetMarkup();
// style containing overflow declarations
wrapper.className = "map-popup";
// fixed height only :P
wrapper.style.height = "60px";
// initialize the window using wrapper node
var popup = new google.maps.InfoWindow({content: wrapper});
// open the window
popup.open(map, instance);
the following is the CSS declaration:
div.map-popup {
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
ps: "instance" refers to the current custom subclass of google.maps.OverlayView (which I'm extending)

Just wrap your content with a div and specify it's height: <div style="height:60px">...</div>, e.g.
myMarker.setContent('<div style="height:60px">' + txt + '</div>');
- In my case it was fairly enough.

Your map canvas is too small. Increase the width/height of your <div id="map_canvas"> element and you should see larger InfoWindows automatically.
That said, I had the same problem on a site I was building. I solved it by creating a cloned div containing the InfoWindow content, measuring that div's width and height, and then setting the InfoWindow content div to have that measured width and height. Here's my code ported into the middle of your codeAddress function (also note that I removed the maxWidth: 200 from your InfoWindow declaration):
function codeAddress(infotext,address) {
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
// Create temporary div off to the side, containing infotext:
var $cloneInfotext = $('<div>' + infotext + '</div>')
.css({marginLeft: '-9999px', position: 'absolute'})
.appendTo($('body'));
// Wrap infotext with a div that has an explicit width and height,
// found by measuring the temporary div:
infotext = '<div style="width: ' + $cloneInfotext.width() + 'px; ' +
'height: ' + $cloneInfotext.height() + 'px">' + infotext +
'</div>';
// Delete the temporary div:
$cloneInfotext.remove();
// Note no maxWidth defined here:
var infowindow = new google.maps.InfoWindow({ content: infotext });
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
}
});
}

Just wrap you InfoBox content with DIV with padding-bottom: 30px;
JS:
map_info_window = new google.maps.InfoWindow();
var $content = $('<div class="infobox">').html(content);
map_info_window.setContent($content.get(0));
map_info_window.open(map, marker);
CSS:
.infobox{
padding-bottom: 30px;
}

It is not really an answer (daveoncode's solution to create a DOM node and use it as content is right), but if you need to dynamically change the content once set (e.g. with jQuery) then you can force gmail to resize the infoWindow with:
infoWindowLinea.setContent(infoWindowLinea.getContent());

Related

How to close an open google maps infowindow by clicking marker again

I'm trying to close a open google maps infowindow by clicking a marker again. Currently there are only questions about how to close all other infowindows when clicking the map or other markers.
How to close an open google maps infowindow by clicking the same marker again? Currently I can only close an infowindow by clicking the cross on the top right corner of the infowindow.
This is what I tried but it doesn't even open the infowindow:
EncoreMarker.addListener('click', function () {
if (EncoreInfoCard.open) {
EncoreInfoCard.close(map, EncoreMarker);
}
else {
EncoreInfoCard.open(map, EncoreMarker);
}
});
Your code won't work because open is a function that opens the InfoWindow, not a boolean that tells whether it is open or not.
This works for me:
EncoreMarker.addListener('click', function () {
// create a custom property of the InfoWindow, defaults to a value that evaluates as false
if (EncoreInfoCard.isOpen) {
EncoreInfoCard.close(map, EncoreMarker);
EncoreInfoCard.isOpen = false;
}
else {
EncoreInfoCard.open(map, EncoreMarker);
EncoreInfoCard.isOpen = true;
}
});
proof of concept fiddle
code snippet:
// This example displays a marker at the center of Australia.
// When the user clicks the marker, an info window opens.
// When the user clicks the makrer again, the info window closes.
function initMap() {
var uluru = {
lat: -25.363,
lng: 131.044
};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: uluru
});
var EncoreInfoCard = new google.maps.InfoWindow({
content: "<b>This is a Test</b>"
});
google.maps.event.addListener(EncoreInfoCard, 'closeclick', function() {
EncoreInfoCard.isOpen = false;
});
var EncoreMarker = new google.maps.Marker({
position: uluru,
map: map,
title: 'Uluru (Ayers Rock)'
});
EncoreMarker.addListener('click', function() {
if (EncoreInfoCard.isOpen) {
EncoreInfoCard.close(map, EncoreMarker);
EncoreInfoCard.isOpen = false;
} else {
EncoreInfoCard.open(map, EncoreMarker);
EncoreInfoCard.isOpen = true;
}
});
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>

Google maps API getting infowindow on click with geojson file

I am using the google maps API and I am displaying polygons on a map using a GeoJSON file. When the user presses inside the polygon, I would like an InfoWindow to appear and display data that is stored in the properties. Seems easy enough but when I am clicking on the polygons, nothing is popping up. Can anyone explain what I am doing wrong?
Below is what I am currently attempting:
map.data.loadGeoJson('plant_bounds_2011.json');
map.data.setStyle({
fillColor: 'red',
strokeWeight: 1
});
var infowindow = new google.maps.InfoWindow({
content: "hello"
});
map.data.addListener('click', function(event) {
let id = event.feature.getProperty('ID');
let name = event.feature.getProperty('HORZ_ORG');
let html = id + " " + name;
infowindow.setContent(html); // show the html variable in the infowindow
infowindow.setPosition(event.feature.getGeometry().get()); // anchor the infowindow at the marker
infowindow.setOptions({pixelOffset: new google.maps.Size(0,-30)}); // move the infowindow up slightly to the top of the marker icon
infowindow.open(map);
});
There is a javascript error with the posted code: Uncaught TypeError: event.feature.getGeometry(...).get is not a function on the line:
infowindow.setPosition(event.feature.getGeometry().get()); // anchor the infowindow at the marker`
A Data.Polygon geometry doesn't have a .get() method. It has a .getArray() method (which returns an array of LineStrings)
One location to place the InfoWindow at would be the point clicked (which is in the polygon):
infowindow.setPosition(event.latLng);
(if you want to either add an fixed point for the infowindow to the GeoJson or you want to compute a fixed point from the polygon you can do that as well)
proof of concept fiddle
code snippet:
function initialize() {
var map = new google.maps.Map(
document.getElementById("map_canvas"), {
zoom: 4,
center: {
lat: -28,
lng: 137
},
mapTypeId: google.maps.MapTypeId.ROADMAP
});
map.data.loadGeoJson('https://storage.googleapis.com/mapsdevsite/json/google.json');
map.data.setStyle({
fillColor: 'red',
strokeWeight: 1
});
var infowindow = new google.maps.InfoWindow({
content: "hello"
});
map.data.addListener('click', function(event) {
let id = event.feature.getProperty('ID');
let name = event.feature.getProperty('HORZ_ORG');
if (typeof id == "undefined") id = event.feature.getProperty('letter');
if (typeof name == "undefined") name = event.feature.getProperty('color');
let html = id + " " + name;
infowindow.setContent(html); // show the html variable in the infowindow
infowindow.setPosition(event.latLng);
infowindow.setOptions({
pixelOffset: new google.maps.Size(0, 0)
}); // move the infowindow up slightly to the top of the marker icon
infowindow.open(map);
});
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map_canvas"></div>

How to close POI's InfoWindow?

I'm trying to make POI's InfoWindow close when click a link.
I overrided the setContent of InfoWindow like this:
//override the built-in setContent-method
google.maps.InfoWindow.prototype.setContent = function (content) {
content = content.innerHTML + '<br/> Save place';
//run the original setContent-method
fx.apply(this, arguments);
};
Note: I didn't create any InfoWindow object to reference to use close() method.
In your override function capture a global reference to the infowindow so you can reference it to close it.
Your override of the infowindow doesn't work. I took a working version from this question: How to get a click event when a user clicks a (business) place on the map
proof of concept fiddle
code snippet:
var geocoder;
var map;
var infowindow;
//keep a reference to the original setPosition-function
var fx = google.maps.InfoWindow.prototype.setPosition;
// from https://stackoverflow.com/questions/24234106/how-to-get-a-click-event-when-a-user-clicks-a-business-place-on-the-map/24234818#24234818
//override the built-in setPosition-method
google.maps.InfoWindow.prototype.setPosition = function() {
//logAsInternal isn't documented, but as it seems
//it's only defined for InfoWindows opened on POI's
if (this.logAsInternal) {
// save reference in global infowindow variable.
infowindow = this;
google.maps.event.addListenerOnce(this, 'map_changed', function() {
var map = this.getMap();
//the infoWindow will be opened, usually after a click on a POI
if (map) {
//trigger the click
google.maps.event.trigger(map, 'click', {
latLng: this.getPosition()
});
}
});
}
//call the original setPosition-method
fx.apply(this, arguments);
};
function initialize() {
var map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
google.maps.event.addDomListener(document.getElementById('btn'), 'click', function(e) {
// close the last opened POI infowindow
infowindow.close();
});
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<input id="btn" type="button" value="close IW" />
<div id="map_canvas"></div>

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);
}
}
});

Suppress fusion table infowindow with multiple layers

I have a map with two fusion tables layers and am using suppressInfoWindows so that an infowindow from one layer is not left open when a user clicks on a marker from the other layer. This works fine using the below code:
<script type="text/javascript">
var map;
var infowindow;
var layer;
var tableid = MY FUSION TABLE ID;
var layer2;
var tableid2 = MY FUSION TABLE ID;
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
center: new google.maps.LatLng(10, 30),
zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
infoWindow = new google.maps.InfoWindow();
google.maps.event.addListener(map, "click", function() { infoWindow.close(); });
layer = new google.maps.FusionTablesLayer(tableid, {suppressInfoWindows:true});
layer.setQuery("SELECT 'Country Geometry' FROM " + tableid);
layer.setMap(map);
google.maps.event.addListener(layer, "click", openIW);
layer2 = new google.maps.FusionTablesLayer(tableid2, {suppressInfoWindows:true});
layer2.setQuery("SELECT 'Site Location' FROM " + tableid2);
layer2.setMap(map);
google.maps.event.addListener(layer2, "click", openIW);
}
function openIW(FTevent) {
// infoWindow.setContent(FTevent.infoWindowHtml);
// infoWindow.setPosition(FTevent.latLng);
infoWindow.setOptions(
{
content: FTevent.infoWindowHtml,
position: FTevent.latLng,
pixelOffset: FTevent.pixelOffset
});
infoWindow.open(map);
}
</script>
How can I now add custom html to the infowindow rather than rely on the infowindow settings from Fusion Tables? The code I was using before adding the suppressInfoWindows option was as follows, but I'm not sure how to now add this back in, in the right format. Also, is it possible to use different html code for the infowindows on different layers, or must both layers use the same infowindow? Thanks.
e.infoWindowHtml = "<div id='SiteInfo' class='googft-info-window' style='font-family: sans-serif; width: 500px; height: 300px; overflow: auto;'>\
<b>" + e.row['Site Name'].value + "</b><br />\
</div></div>";
See my example in my answer to your last question on this.
Example of your map with 2 layers, 1 infowindow
To implement:
Suppress the infowindows on both layers
Write code to open a shared infowindow when either layer is clicked.

Resources