how to trigger the google map event from inside the overlayview - google-maps-api-3

In a application, I am using google map to display stations with google marker, because the google marker is static with icon not animated, so I decided to inherit OverlayView and use canvas to draw a station dynamically. And this works, however, I want this overlay to receive the google events like the marker, such as click, mouse over, mouse out...
For example,
function StationCanvas(map, position, name) {
this.map_ = map;
this.position_ = position;
this.name_ = name;
this.canvas_ = null;
this.labelDiv_ = null;
this.canvasWidth_ = 12;
this.canvasHeight_ = 50;
this.setMap(map);
console.log('canvas '+this.position_);
}
StationCanvas.prototype = new google.maps.OverlayView();
StationCanvas.prototype.onAdd = function() {
var canvas = document.createElement("canvas");
canvas.setAttribute("width", this.canvasWidth_);
canvas.setAttribute("height", this.canvasHeight_);
canvas.style.position = "absolute";
this.canvas_ = canvas;
var panes = this.getPanes();
panes.floatPane.appendChild(canvas);
this.labelDiv_ = document.createElement("div");
this.labelDiv_ .setAttribute("width", this.canvasWidth_);
this.labelDiv_ .setAttribute("height", this.canvasHeight_);
this.labelDiv_ .style.position = "absolute";
this.labelDiv_ .innerHTML = this.name_;
panes.floatPane.appendChild(this.labelDiv_ );
/////////////////////////////////////////////////////////////
this.listeners_ = [
google.maps.event.addListener(this.canvas_, "mouseover", function (e) {
//this.style.cursor = "pointer";
//google.maps.event.trigger(this, "mouseover", e);
console.log('mouse mover');
}),
google.maps.event.addListener(this.canvas_, "mouseout", function (e) {
//this.style.cursor = this.getCursor();
//google.maps.event.trigger(this, "mouseout", e);
console.log('mouse out');
}),
google.maps.event.addListener(this.canvas_, "click", function (e) {
google.maps.event.trigger(this, "click", e);
console.log('click');
}),
google.maps.event.addListener(this.canvas_, "dblclick", function (e) {
//google.maps.event.trigger(this, "dblclick", e);
}),
];
}
Intially, I use google.maps.event.addListener as showed above to listen the event, nothing happens, so it seems canvas doesn't work with google.maps.eventListener.
Then I found google has provided a addDomListener(instance:Object, eventName:string, handler:Function), but since it only support dom rather then canvas, so when I used that listener, the browser breaks down.
At last, I have tried to use
canvas.onmouseout = function() {
console.log("on mouse out");
}
}
It is supposed to work, but still no, I guess something wrong within the code. even this works, the next question is how can I trigger the event to outside, so that I can work this overlayview like the google marker
var test1 = new StationCanvas(map, new google.maps.LatLng(53.3234,-2.9178), "abc",13);
google.maps.event.addListener(test1, 'click', function(event){
console.log('test 1 click');
});

addDomListener works for me, even with <canvas/>
What would break your code is e.g. this:
google.maps.event.addListener(this.canvas_, "click", function (e) {
google.maps.event.trigger(this, "click", e);
console.log('click');
})
this , when used in a event-callback, refers to the object that triggers the event(here: the canvas-node), your code produces a recursion. When you want to trigger the click-event for the StationCanvas-instance, you may store the instance as a property of the canvas-element, so it will be easy accessible inside the click-callback
StationCanvas.prototype.onAdd = function() {
var canvas = document.createElement("canvas");
canvas.overlay=this;
//more code
}
this.listeners_ = [
google.maps.event.addDomListener(this.canvas_, "click", function (e) {
google.maps.event.trigger(this.overlay,'click')
}),
google.maps.event.addListener(this, "click", function (e) {
alert('click on the StationCanvas-instance');
})
];

Related

Change infowindow marker based on variable in realtime

How do I change the icon of a marker based on if a value is true or false.
I created an if function that checks the value of CameraStatus. I set it to false on default but the marker still won't change to RedStatus. It does change to RedStatus when I try a timer like this:
setTimeout(function () { MiamiMarker.setIcon(RedStatus) }, 10 * 1000);
It doesn't change to RedStatus when I try this:
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
How do I change the marker based on my if function?
Eventually I want to change all my markers with boolean values I get from a home controller. The value of the boolean should decide if the marker has a GreenStats or RedStatus icon. First I'm trying to change one marker based on a hardcoded value. (See code below)
My code:
<script>
var map;
function initMap() {
var CenterLoc = { lat: 51.34, lng: 5.53 };
map = new google.maps.Map(document.getElementById('map'),
{
center: CenterLoc,
disableDefaultUI: true,
zoom: 3,
});
google.maps.event.addDomListener(window, 'load', initMap);
var Miami = { lat: 25.774266, lng: -80.193659 };
var MiamiMarker = new google.maps.Marker
({
position: Miami,
map: map,
icon: GreenStatus
});
//Replace standard google maps markers with colored dots
var GreenStatus = "#ViewBag.GreenStatus";
var OrangeStatus = "#ViewBag.OrangeStatus";
var RedStatus = "#ViewBag.RedStatus";
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
var MiamiInfoCard = new google.maps.InfoWindow
({
content: '<div id="map-dialog"><h3>Miami</h3></div>'
});
MiamiMarker.addListener('click', function () {
MiamiInfoCard.open(map, MiamiMarker);
});
var position = new google.maps.LatLng(52.2305, 5.9924);
}
</script>
You can use an if else statement to change your Icon. Please note that it is .setIcon() and not *.SetIcon() just like in your code.
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
You can check this sample code that reproduces what you want in your use-case.
I used a toggle switch to set values for CameraStatus and call the CheckStatus() function passing the variables CameraStatus and MiamiMarker to be processed in the function.
var switchStatus = document.getElementById("mySwitch");
var CameraStatus;
switchStatus.addEventListener('change', function() {
if (switchStatus.checked) {
CameraStatus = false;
} else {
CameraStatus = true;
}
CheckStatus(CameraStatus, MiamiMarker);
});
I put the CheckStatus() function outside the initMap() function and passed the CameraStatus and MiamiMarker to change the marker's icon base on the value of CameraStatus.
function CheckStatus(CameraStatus, MiamiMarker) {
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
Hope this helps!

ionic for multi marker google map opens last markup

I am using ionic to display benefits data on Google map. It works fine except on click of any marker, it opens the last markup content. Follownig is my google map js code
.controller('BenefitsMapCtrl', function ($scope, LocationBenefits, Utilities, $ionicLoading, $compile) {
$scope.init = function () {
var userId = Utilities.getUserId();
LocationBenefits.getLocationBenefits(userId, function (userBenefits) {
console.log("Got location benefits data for Google mp for user id "+userId);
$scope.userBenefits = userBenefits;
var centerLatlng;
if (typeof $scope.userBenefits !== "undefined" && $scope.userBenefits.length > 0) {
var firstBenefitLocation = $scope.userBenefits[0];
centerLatlng = new google.maps.LatLng(firstBenefitLocation.location.lat, firstBenefitLocation.location.lng);
}
var mapOptions = {
center: centerLatlng,
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
$scope.markers=[];
//Loop in each benefits and place on Google map
if (typeof $scope.userBenefits !== "undefined" && $scope.userBenefits.length > 0) {
for (var i = 0; i < $scope.userBenefits.length; i++) {
var benefit = $scope.userBenefits[i];
//Marker + infowindow + angularjs compiled ng-click
var contentString = "<div><div><img class='shop-icon' src='" + benefit.shopicon + "' alt='" + benefit.shopName + "'/><span class='item-text-wrap'>" + benefit.shopName + "</span></div><div class='shop-offer'>"+benefit.benefits.short_benefitText+"</div><div class='card'><img class='card-art' src='"+benefit.cardart+"' alt='"+benefit.card+"'/></div></div>";
var compiled = $compile(contentString)($scope);
var infowindow = new google.maps.InfoWindow({
content: compiled[0]
});
//Get location
var locationLatLng = new google.maps.LatLng(benefit.location.lat, benefit.location.lng);
var marker = new google.maps.Marker({
position: locationLatLng,
map: map,
title: benefit.shopName
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
$scope.markers.push(marker);
}
}
//Finally set the map
$scope.map = map;
});
};
// google.maps.event.addDomListener(window, 'load', initialize);
$scope.centerOnMe = function () {
if (!$scope.map) {
return;
}
$scope.loading = $ionicLoading.show({
content: 'Getting current location...',
showBackdrop: false
});
navigator.geolocation.getCurrentPosition(function (pos) {
$scope.map.setCenter(new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude));
$scope.loading.hide();
}, function (error) {
alert('Unable to get location: ' + error.message);
});
};
$scope.clickTest = function () {
alert('Example of infowindow with ng-click')
};
});
Issue: On click of any markup on Google map, it always opens the last markup.
Please help.
I had the same problem and spend a lot of time to figure out what's going on so I'd like to add the answer for the future generations,lol.
First of all it's a good idea to refer to official Google maps API docs and take a look at "Events section". I found there one interface of adding event listeners to markers that I never seen before(even after googling this issue for few hours).
marker.addListener('click', function() {});
google.maps.event.addListener(marker, 'click', function () {});
It pointed me out to the idea that when you are trying to do it in the loop using an "old" way, your marker variable is obviously equals to the last element of your markers array. And when event really can be triggered your initialization process is finished what means that your variable has always wrong value in anonymous function at the moment it can be really called. So, again, you didn't passed marker as a parameter to that anonymous event handler.
But you still can do what you want. Just use this.inside your event handler. Bellow is my code sample
marker.addListener('click', function() {
var it = this;
$scope.$apply(function() {
$scope.activeEvent = EventService.getShort($scope.events[it.id]);
});
});
I believe that you can try to use this. in "old-style" interface as well.
I had the same problem and I found the solution here. Apparently, you just have to create a function to create the markers and call that function inside the for loop:
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
$scope.map = map; //Attach the map to the scope before adding the markers
$scope.markers=[];
var infowindow = new google.maps.InfoWindow();
var createMarkers = function (benefit){
//Info window's content
var contentString = "<div><div><img class='shop-icon' src='" + benefit.shopicon + "' alt='" + benefit.shopName + "'/><span class='item-text-wrap'>" + benefit.shopName + "</span></div><div class='shop-offer'>"+benefit.benefits.short_benefitText+"</div><div class='card'><img class='card-art' src='"+benefit.cardart+"' alt='"+benefit.card+"'/></div></div>";
var compiled = $compile(contentString)($scope);
//Get location
var locationLatLng = new google.maps.LatLng(benefit.location.lat, benefit.location.lng);
//Create marker
var marker = new google.maps.Marker({
position: locationLatLng,
map: map,
title: benefit.shopName
});
google.maps.event.addListener(marker, 'click', function () {
infoWindow.setContent(compiled[0]);
infoWindow.open($scope.map, marker);
});
$scope.markers.push(marker);
}
//Loop in each benefits and place on Google map
if (typeof $scope.userBenefits !== "undefined" && $scope.userBenefits.length > 0) {
for (var i = 0; i < $scope.userBenefits.length; i++) {
var benefit = $scope.userBenefits[i];
createMarkers(benefit);
}
}

How to increase surface size using a click event within famo.us?

I am attempting to increase the scale or the size of the surface so that it takes up the window when I click on it. Assume that a surface is created and is called surface3.
I have a boolean marked as flag that changes its value everytime the surface3 is clicked. true will cause a 'growSurface' event to be emitted that the eventHandler will respond to.
I do not know how to smoothly animate using a Famous.Transitionable to tween the increase in scale or size. I am able to successfully place a surface3.setSize([undefined, undefined]); to get it to jump to take up the window. How do I get it to animate in size or scale using a Transitionable?
Template.projects.rendered = function() {
Famous.Engine = famous.core.Engine;
Famous.Surface = famous.core.Surface;
Famous.Transform = famous.core.Transform;
Famous.Transitionable = famous.transitions.Transitionable;
Famous.Modifier = famous.core.Modifier;
Famous.StateModifier = famous.modifiers.StateModifier;
Famous.Easing = famous.transitions.Easing;
Famous.EventHandler = famous.core.EventHandler;
var mainContext = Famous.Engine.createContext();
var eventHandler = new Famous.EventHandler();
var surface3 = new Famous.Surface({
size: [300, $(window).height()],
content: "surface 3",
properties: {
color: '#FFF',
backgroundColor: 'green'
}
});
var flag = false;
var scaleModifier = new Famous.Modifier({
size: [300, $(window).height()]
});
scaleModifier.sizeFrom(function(){
return transitionable.get();
});
eventHandler.on('growSurface', function(){
// can do surface3.setSize( $(window).width() );
var transitionable = new Famous.Transitionable( 300 );
return transitionable.set( $(window).width(), {duration: 1500} );
});
eventHandler.on('shrinkSurface', function(){
console.log('shrink surface init');
// code to shrink size back to [300, $(window).height()]
// should reverse 'growSurface' event
// can do surface3.setSize([300, undefined]);
});
// Handles Clicks
surface3.on('click', function(event) {
if (flag === false) {
eventHandler.emit('growSurface');
flag = !flag
} else {
eventHandler.emit('shrinkSurface');
flag = !flag
}
});
mainContext.add(scaleModifier).add(alignSurface3Modifier).add(surface3);
I got it both animating on the growSurface and shrinkSurface events:
var eventHandler = new Famous.EventHandler();
var flag = false;
var transitionable = new Famous.Transitionable([300, undefined]);
var scaleModifier = new Famous.Modifier({
size: [300, $(window).height()]
});
scaleModifier.sizeFrom(function(){
return transitionable.get();
});
eventHandler.on('growSurface', function(){
surface3.setSize([undefined, $(window).height()]);
transitionable.set([$(window).width(), $(window).height()], { duration: 1500 });
});
eventHandler.on('shrinkSurface', function(){
transitionable.set([300, $(window).height()], { duration: 1500 });
});
// Handles Clicks
surface3.on('click', function(event) {
if (flag === false) {
eventHandler.emit('growSurface');
flag = !flag
} else {
eventHandler.emit('shrinkSurface');
flag = !flag
}
});

Check if infowindow is opened Google Maps v3

Please, I need a help.
I want to check if my infowindow is opened.
For example:
if (infowindow.isOpened)
{
doSomething()
}
or
if (infowindow.close)
{
doAnotherthing();
}
I dont have any idea, how to do this
This is an undocumented feature, and is therefore subject to change without notice, however the infoWindow.close() method sets the map on the object to null (this is why infoWindow.open(map, [anchor]) requires that you pass in a Map), so you can check this property to tell if it is currently being displayed:
function isInfoWindowOpen(infoWindow){
var map = infoWindow.getMap();
return (map !== null && typeof map !== "undefined");
}
if (isInfoWindowOpen(infoWindow)){
// do something if it is open
} else {
// do something if it is closed
}
Update:
Another potentially useful way to write this is to add an isOpen() method to the InfoWindow prototype.
google.maps.InfoWindow.prototype.isOpen = function(){
var map = this.getMap();
return (map !== null && typeof map !== "undefined");
}
Until google doesn't give us any better way of doing this, you can add a property to the infoWindow objects. Something like:
google.maps.InfoWindow.prototype.opened = false;
infoWindow = new google.maps.InfoWindow({content: '<h1> Olá mundo </h1>'});
if(infoWindow.opened){
// do something
infoWindow.opened = false;
}
else{
// do something else
infoWindow.opened = true;
}
I modified the prototype for google.maps.InfoWindow and changed open/close to set/clear a property:
//
// modify the prototype for google.maps.Infowindow so that it is capable of tracking
// the opened state of the window. we track the state via boolean which is set when
// open() or close() are called. in addition to these, the closeclick event is
// monitored so that the value of _openedState can be set when the close button is
// clicked (see code at bottom of this file).
//
google.maps.InfoWindow.prototype._open = google.maps.InfoWindow.prototype.open;
google.maps.InfoWindow.prototype._close = google.maps.InfoWindow.prototype.close;
google.maps.InfoWindow.prototype._openedState = false;
google.maps.InfoWindow.prototype.open =
function (map, anchor) {
this._openedState = true;
this._open(map, anchor);
};
google.maps.InfoWindow.prototype.close =
function () {
this._openedState = false;
this._close();
};
google.maps.InfoWindow.prototype.getOpenedState =
function () {
return this._openedState;
};
google.maps.InfoWindow.prototype.setOpenedState =
function (val) {
this._openedState = val;
};
You also need to monitor the closeclick event because clicking on the close button does not call close().
//
// monitor the closelick event and set opened state false when the close
// button is clicked.
//
(function (w) {
google.maps.event.addListener(w, "closeclick", function (e) {
w.setOpenedState(false);
});
})(infowindow);
Calling InfoWindow.getOpenedState() returns a boolean which reflects the state (opened/closed) of the infowindow.
I chose to do it this way instead of the using the InfoWindow.getMap() or MVCObject.get('map') method because of the well known pitfalls of using undocumented behavior. However google uses MVCObject.set('map', null) to force the removal of the InfoWindow from the DOM, so it is unlikely that this will change...
infowindow.getMap() returns null if infowindow is closed.
So you can use simply:
if (infowindow.getMap());
You can simply set key and value for infoWindow: infoWindow.set('closed', true);
example:
const infoWindow = new google.maps.InfoWindow({
content: 'foo',
position: {
lat: some_number,
lng: some_number
}
});
infoWindow.set('closed', true);
// Clicking on polyline for this example
// Can be marker too
polyline.addListener(
'click',
() => {
if (infoWindow.get('closed')) {
infoWindow.open(map);
infoWindow.set('closed', false);
} else {
infoWindow.close();
infoWindow.set('closed', true);
}
}
);

Google Maps API with backbone, how to bind an event

I've seen several other posts about this, but the answers from those responses don't work for me.
The other responses:
How do I bind a google maps geocoder.geocode() callback function
Backbone.js with Google Maps - problems with this and listeners
My code:
var ns = namespace('camelcase.geomanager.map');
ns.Site = Backbone.Model.extend({
url: '/site'
});
ns.Sites = Backbone.Collection.extend({
model: ns.Site
});
ns.MapView = Backbone.View.extend({
initialize: function() {
this.markers = new Array();
// Create the Google Map
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.googleMap = new google.maps.Map(this.$(".mapCanvas")[0], mapOptions);
// Register events
this.collection.on('add', this.addSite, this);
this.collection.on('remove', this.removeSite, this);
},
addSite: function(model) {
// Get model attributes
var elementId = model.get('elementId');
var latitude = model.get('latitude');
var longitude = model.get('longitude');
var id = model.get('id');
var notes = model.get('notes');
var title = ""+id;
// Create icon and marker
var icon = '/resources/img/elements/' + elementId + '_marker.png';
var latLng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
position: latLng,
title: title,
map: this.googleMap,
icon: icon
});
// Load info window
var siteBubbleTemplate = _.template($('#siteBubbleTemplate').html());
var siteContent = $(siteBubbleTemplate({
siteId: id,
siteNotes: notes
}))[0];
var infoWindow = new google.maps.InfoWindow({
content: siteContent
});
// Show info window when clicking on marker
_.bindAll(this, this.openSite);
google.maps.event.addListener(marker, 'click', this.openSite(id));
this.markers.push({
id: id,
marker: marker,
infoWindow: infoWindow
});
},
openSite: function(id) {
var marker;
for (var c=0; c<this.markers.length; c++) {
marker = this.markers[c];
// Open the appropriate marker info window
if (marker.id == id) {
marker.infoWindow.open(googleMap, marker.marker);
}
// Close the rest
else {
marker.infoWindow.close();
}
}
}
});
The offending line:
google.maps.event.addListener(marker, 'click', this.openSite(id));
The error being reported in firebug:
TypeError: func is undefined
underscore.js (line 482)
I suspect this.marker is the problem, since you should be able to just refer to it by name.
google.maps.event.addListener(marker, 'click', this.openSite(id));
Looks like it was a scoping issue. I solved my problem with the following code:
// Show info window when clicking on marker
_.bindAll(this);
var _self = this;
var doSomething = function(event) {
_self.openSite({
event: event
});
};
google.maps.event.addListener(marker, 'click', doSomething);
I'll give the answer to whoever can best explain why this works.
In a model/collection event handler, Backbone sets 'this' to the model/collection which raised the event. If you call _.bindAll(this) in your view's initialize(), 'this' will be set to the view in your event handlers. Check out this jsfiddle: http://jsfiddle.net/9cvVv/339/ and see what happens when you uncomment _.bindAll(this);
var MyView = Backbone.View.extend({
initialize: function() {
// TODO: uncomment this line
// _.bindAll(this);
this.collection.bind('myEvent', this.onDoSomething);
},
updateCollection: function() {
this.collection.doSomething();
},
onDoSomething: function() {
if (this.models && typeof this.models.length === 'number') {
alert('"this" is a collection.');
}
else if (this.collection) {
alert('"this" is the view.');
}
else {
alert('"this" is something else.');
}
}
});

Resources