I'm using OpenLayers to view a map and I'm having an issue with the marker's popup. When the Markers are loaded, I'm assigning them two events the moouseover and mouseout but when any of the markers are triggered with these events only the first created marker's popup is shown, even when I mouseover other markers. Its like I'm only creating these events for the first marker and not for all of them.. Any ideas? Thanks
var listMarkers = getMarkers();
for (var i = 0; i < listMarkers.length; i++) {
var size = new OpenLayers.Size(21, 25);
var offset = new OpenLayers.Pixel(-(size.w / 2), -size.h);
var icon;
if (listMarkers[i].Icon.trim() === "red") {
icon = new OpenLayers.Icon
('http://www.openlayers.org/dev/img/marker.png', size, offset);
}
else {
icon = new OpenLayers.Icon
('http://www.openlayers.org/dev/img/marker-' + listMarkers[i].Icon.trim() + '.png', size, offset);
}
var mark = new OpenLayers.Marker(new OpenLayers.LonLat(listMarkers[i].Longitude,
listMarkers[i].Latitude).transform(new OpenLayers.Projection("EPSG:4326"),
map.getProjectionObject()), icon);
//here add mouseover event
mark.events.register('mouseover', mark, function (evt) {
popup = new OpenLayers.Popup.FramedCloud("Popup",
new OpenLayers.LonLat(listMarkers[i].Longitude,
listMarkers[i].Latitude).transform(new OpenLayers.Projection("EPSG:4326"),
map.getProjectionObject()),
null,
'<div><b>' + listMarkers[i].Title + '</b><br/>' + listMarkers[i].Description + '</div>',
null,
false);
map.addPopup(popup);
});
//here add mouseout event
mark.events.register('mouseout', mark, function (evt) { popup.hide(); });
markers.addMarker(mark);
}
In the mouseover event while creating popup you're referring to listMarkers[i], which in javascript scope would remember last value of given variable i, so for every popup it would get information from listMarkers[listMarkers.length-1]. To fix this, add details (Title, Latitude, Longitude) into the mark object (mark.data.Title = listMarkers[i]), and then read them in event handler from evt or this object (as you're setting it in register call).
Related
I'm using a camera and barcode markers. In order for my cannon to shoot I need to spawn a sphere when a clicked. I tried to get the marker position by using .getAttribute('position') but I'm getting disappointing results e.g. null and [object, object]. Is there a real way to to access the coordinates of a marker in AFrame? So far it creates a sphere but right in the camera because its unable to find the location of the marker.
Javascript
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('#cannonMarker');
// trigger event when button is pressed.
document.addEventListener("click", function (e) {
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
console.log('Sphere coordinates:' + pos);
});
Provided the marker is being recognized and the references are correct
markerEl.getAttribute('position')
should return the current marker position.
If your script is in the <head> element, the marker may not yet exist when the code is executed.
It's a good idea to throw your code into an a-frame component:
HTML:
<a-scene ball-spawner></a-scene>
js:
AFRAME.registerComponent('ball-spawner', {
init: function() {
// your code here - the scene should be ready
}
})
I've made a slight modification to your code (working glitch here):
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('a-marker');
// trigger event when button is pressed.
sceneEl.addEventListener("click", function (e) {
if (markerEl.object3D.visible === false) {
return;
}
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('radius', 0.5)
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
});
}
When the marker gets out of vision, it's position is 'last remembered'. So it's the same place on the screen. That's why there's a return if the marker element is not visible.
It seems to be working as you want since the ball is falling out of a transparent box on the marker (the box is a nice way to be sure the marker is recognized):
I am new to here maps and need to show a div on marker hover. I have been able to put markers with icons but now need to show a div with some extra information. Does HERE maps API provide this functionality?
Any doc URL or piece of code will be appreciated.
NOTE: I am using HERE maps JS API for web.
You can create a tooltip effect by adding various event listeners to the map to check if the mouse pointer is over an object.
(function (ctx) {
// ensure CSS is injected
var tooltipStyleNode = ctx.createElement('style'),
css = '#nm_tooltip{' +
' color:white;' +
' background:black;' +
' border: 1px solid grey;' +
' padding-left: 1em; ' +
' padding-right: 1em; ' +
' display: none; ' +
' min-width: 120px; ' +
'}';
tooltipStyleNode.type = 'text/css';
if (tooltipStyleNode.styleSheet) { // IE
tooltipStyleNode.styleSheet.cssText = css;
} else {
tooltipStyleNode.appendChild(ctx.createTextNode(css));
}
if (ctx.body) {
ctx.body.appendChild(tooltipStyleNode);
} else if (ctx.addEventListener) {
ctx.addEventListener('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
}, false);
} else {
ctx.attachEvent('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
});
}
})(document);
Object.defineProperty(Tooltip.prototype, 'visible', {
get: function() {
return this._visible;
},
set: function(visible) {
this._visible = visible;
this.tooltip.style.display = visible ? 'block' : 'none';
}
});
function Tooltip(map) {
var that = this;
that.map = map;
that.tooltip = document.createElement('div');
that.tooltip.id = 'nm_tooltip';
that.tooltip.style.position = 'absolute';
obj = null,
showTooltip = function () {
var point = that.map.geoToScreen(obj.getPosition()),
left = point.x - (that.tooltip.offsetWidth / 2),
top = point.y + 1; // Slight offset to avoid flicker.
that.tooltip.style.left = left + 'px';
that.tooltip.style.top = top + 'px';
that.visible = true;
that.tooltip.innerHTML = obj.title;
};
map.getElement().appendChild(that.tooltip);
map.addEventListener('pointermove', function (evt) {
obj = that.map.getObjectAt(evt.currentPointer.viewportX,
evt.currentPointer.viewportY);
if(obj && obj.title){
showTooltip();
} else {
that.visible = false;
}
});
map.addEventListener('tap', function (evt){
that.tooltip.visible = false;
});
map.addEventListener('drag', function (evt){
if (that.visible) {
showTooltip();
}
});
};
This is initialised by passing the map object as shown:
function addTooltipControlToMap(map) {
tooltip = new Tooltip(map);
}
The code as written is looking for a .title attribute to be added to the map objects - this could be updated to use .getData() if preferred. Tooltips can be initialised as shown below, taking either text or html:
function addMarkersWithTooltips(map) {
// Simple Marker with tooltip
var brandenburgerTorMarker = new H.map.Marker(
{lat:52.516237, lng: 13.35}),
fernsehturmMarker = new H.map.Marker(
{lat:52.520816, lng:13.409417});
brandenburgerTorMarker.title = 'Brandenburger Tor';
// Marker with HTML Tooltip
fernsehturmMarker.title ='<div>' +
'<h2>Tooltip with HTML content<\/h2>' +
'<img width=\'120\' height=90 src=' +
'\'http://upload.wikimedia.org/wikipedia/commons/' +
'8/84/Berlin-fernsehturm.JPG\' ' +
'alt=\'\'/><br/><b>Fernsehturm, Berlin<\/b>' +
'<\/div>';
// Add the markers onto the map
map.addObjects([brandenburgerTorMarker, fernsehturmMarker]);
}
I have been able to find proper mouse over events for HERE map's markers which are pointerenter and pointerleave and sample code to use these events is:
// After Initializing map with your own credentials.
var map = new H.Map(document.getElementById('map'),
defaultLayers.normal.map,{
center: {lat: LAT_VAL, lng: LNG_VAL},
zoom: 12
});
var domMarker = new H.map.DomMarker(coords, {
icon: domIcon
});
var bubble;
domMarker.addEventListener('pointerenter', function(evt) {
bubble = new H.ui.InfoBubble({lat:"SOME_VALUE",lng:"SOME_VALUE"}, {
content: "Your content come here"
});
ui.addBubble(bubble);
}, false);
domMarker.addEventListener('pointerleave', function(evt) {
bubble.close();
}, false);
map.addObject(domMarker);
Depending on the api version you are using, you may find what you are looking for inside the documentation pdf (or at least start from there).
Supposing you need do make some HTML styled marker, you may need:
DomMarker (instead of a Marker, because it allows you to use ->2)
DomIcon (which can embed html)
An example can be found here https://developer.here.com/apiexplorer-v2-sample-data/template-web-default/examples/map-with-dom-marker/index.html
Anyway, if you need to show informations about the marker, I would suggest to use InfoBubbles, which have been developed for this purpose.
From the 3.0.5 docs:
// Create an info bubble object at a specific geographic location:
ui = H.ui.UI.createDefault(self.map, defaultLayers);
var bubble = new H.ui.InfoBubble({ lng: 13.4, lat: 52.51 }, {
content: '<b>Hello World!</b>'
});
// Add info bubble to the UI:
ui.addBubble(bubble);
To show them, you should attach an event to the marker tap event:
marker.addEventListener('tap', function (evt) {
//create and add the bubble
}
In any case, you can find the documentation of your api version here: https://developer.here.com/documentation/versions
You do not have "hover" listener for marker,
but you can show infoBubble on click
http://heremaps.github.io/examples/explorer.html#infobubble-on-marker-click
If this doesn't work for you, you will have to use jquery and to bind "hover" on HTML marker element. (This is not very easy task)
I'm having problems, trying to display the mouse position x/y when they click on an image, im using one of the click on and image example that phaser provides.
here is the code
var game = new Phaser.Game(800, 500, Phaser.AUTO, 'phaser-example', { preload: preload, create: create });
var text;
var counter = 0;
function preload () {
// You can fill the preloader with as many assets as your game requires
// Here we are loading an image. The first parameter is the unique
// string by which we'll identify the image later in our code.
// The second parameter is the URL of the image (relative)
game.load.image('Happy-face', 'happy.png');
}
function create() {
// This creates a simple sprite that is using our loaded image and
// displays it on-screen and assign it to a variable
var image = game.add.sprite(game.world.centerX, game.world.centerY, 'Happy-face');
// Moves the image anchor to the middle, so it centers inside the game properly
image.anchor.set(0.5);
// Enables all kind of input actions on this image (click, etc)
image.inputEnabled = true;
this.position = new Phaser.Point();
text = game.add.text(250, 16, '', { fill: '#ffffff' });
image.events.onInputDown.add(listener, this);
}
function listener () {
counter++;
text.text = "Position x/y " + counter + "!";
}
if you want x and y position o f input
game.input.x;
game.input.y;
if you want for mouse specifically
game.input.mousePointer.x;
game.input.mousePointer.y;
the listener function will be like
function listener () {
counter++;
text.text = game.input.mousePointer.x +"/"+game.input.mousePointer.y + counter + "!";
}
Just to add that the listener function will be sent 2 parameters: sprite and pointer. So you can do:
function listener (sprite, pointer) {
var x = pointer.x;
var y = pointer.y;
...
}
This will be the most accurate method to use as it accounts for multi-touch devices, where-as accessing input.x/y directly doesn't, it only contains the most recent touch event coordinates (which in a mouse only environment is fine, but not anywhere else).
I'm working with the Google Maps v3 API, and I have a custom overlay layer based on the ImageMapType class. I would like to show a loading indicator of some sort while the overlay's tiles are loading, but I don't see any way to know when they are finished.
The code to create the overlay looks similar to the following:
var myOverlay = new google.maps.ImageMapType({
getTileUrl: myGetTileUrl,
tileSize: new google.maps.Size(256, 256),
isPng: true
});
myMap.overlayMapTypes.push(myOverlay);
The above works just fine, and the overlay successfully loads; it just seems that no events are emitted by the map to indicate anything about the ImageMapType overlay's status.
I would expect the map to at least emit an "idle" event when the tiles are finished loading, but as far as I can tell it does not.
How may I know when the ImageMapType overlay is finished loading?
EDIT
I wrote a test case on jsFiddle: http://jsfiddle.net/6yvcB/ — Watch your console output for the word "idled" to see when the idle event fires. Notice that it never fires when you click the button to add an overlay.
Also, kittens.
It would seem there's no "out of the box" way to know when an ImageMapType overlay has finished loading, but thanks to a suggestion from Martin over on the Google Maps API v3 Forums I was able to add in my own custom event that is emitted when the layer finishes loading.
The basic approach is:
Every time a URL is requested, add the URL to a list of pending URLs
Override ImageMapType.getTile() so that we can add "onload" event listeners to each <img> element.
When each image's "load" event fires, remove that image from the list of pending URLs.
When the list of pending URLs is empty, then emit our custom "overlay-idle" event.
I've copied the code below for posterity, but you can see it in action on jsFiddle: http://jsfiddle.net/6yvcB/22/
// Create a base map
var options = {
zoom: 3,
center: new google.maps.LatLng(37.59, -99.13),
mapTypeId: "terrain"
};
var map = new google.maps.Map($("#map")[0], options);
// Listen for the map to emit "idle" events
google.maps.event.addListener(map, "idle", function(){
console.log("map is idle");
});
// Keep track of pending tile requests
var pendingUrls = [];
$("#btn").click(function() {
var index = 0;
var urls = [ "http://placekitten.com/256/256",
"http://placekitten.com/g/256/256",
"http://placekitten.com/255/255",
"http://placekitten.com/g/255/255",
"http://placekitten.com/257/257",
"http://placekitten.com/g/257/257" ];
var overlay = new google.maps.ImageMapType({
getTileUrl: function() {
var url = urls[index % urls.length];
index++;
// Add this url to our list of pending urls
pendingUrls.push(url);
// if this is our first pending tile, signal that we just became busy
if (pendingUrls.length === 1) {
$(overlay).trigger("overlay-busy");
}
return url;
},
tileSize: new google.maps.Size(256, 256),
isPng: true,
opacity: 0.60
});
// Listen for our custom events
$(overlay).bind("overlay-idle", function() {
console.log("overlay is idle");
});
$(overlay).bind("overlay-busy", function() {
console.log("overlay is busy");
});
// Copy the original getTile function so we can override it,
// but still make use of the original function
overlay.baseGetTile = overlay.getTile;
// Override getTile so we may add event listeners to know when the images load
overlay.getTile = function(tileCoord, zoom, ownerDocument) {
// Get the DOM node generated by the out-of-the-box ImageMapType
var node = overlay.baseGetTile(tileCoord, zoom, ownerDocument);
// Listen for any images within the node to finish loading
$("img", node).one("load", function() {
// Remove the image from our list of pending urls
var index = $.inArray(this.__src__, pendingUrls);
pendingUrls.splice(index, 1);
// If the pending url list is empty, emit an event to
// indicate that the tiles are finished loading
if (pendingUrls.length === 0) {
$(overlay).trigger("overlay-idle");
}
});
return node;
};
map.overlayMapTypes.push(overlay);
});
Use the event tilesloaded
Doc: About event tilesloaded
Example :
let layer = new window.google.maps.ImageMapType({
getTileUrl: getTileUrl,
tileSize: new window.google.maps.Size(256, 256),
minZoom: 0,
maxZoom: 24,
opacity: 1.0,
isPng: true,
});
map.overlayMapTypes.setAt(0, layer);
layer.addListener("tilesloaded", () => {
console.log("Overlay tiles loaded");
});
Based on the response by #David, I have created a pure Javascript alternative (especially taking into account that the Op did not specify jQuery).
var pendingUrls = [];
function addPendingUrl(id, url)
{
// Add this url to our list of pending urls
pendingUrls[id].push(url);
//console.log("URL " + url + " added (" + pendingUrls[id].length + ")");
// if this is our first pending tile, signal that we just became busy
if (pendingUrls[id].length === 1) {
console.log("overlay is busy");
}
}
function addTileLoadListener(id, mapType, timeout)
{
// Initialise the sub-array for this particular id
pendingUrls[id] = [];
// Copy the original getTile function so we can override it, but still make use of the original function
mapType.baseGetTile = mapType.getTile;
// Override getTile so we may add event listeners to know when the images load
mapType.getTile = function(tileCoord, zoom, ownerDocument)
{
// Get the DOM node generated by the out-of-the-box ImageMapType
var node = mapType.baseGetTile(tileCoord, zoom, ownerDocument);
//console.log("URL " + node.firstChild.__src__ + " confirmed (" + pendingUrls[id].length + ")");
function removePendingImg(node, src, result)
{
var index = pendingUrls[id].indexOf(src);
if (index == -1)
{
//console.log("URL " + src + " " + "not found" + " (" + pendingUrls[id].length + ")");
}
else
{
pendingUrls[id].splice(index, 1);
//console.log("URL " + src + " " + result + " (" + pendingUrls[id].length + ")");
// If the pending url list is empty, emit an event to indicate that the tiles are finished loading
if (pendingUrls[id].length === 0) {
console.log("overlay is idle");
}
}
}
// Listen for any images within the node to finish loading
node.getElementsByTagName("img")[0].onload = function() {
//console.log("URL " + node.firstChild.src + " maybe loaded (" + node.firstChild.__src__ + ")");
// Check that we have loaded the final image. We detect this because the node.src ends with what is in node.__src__
var str = node.firstChild.src;
var suffix = node.firstChild.__src__;
if (str.indexOf(suffix, str.length - suffix.length) !== -1)
{
removePendingImg(node, node.firstChild.__src__, "loaded"); // Remove the image from our list of pending urls
}
};
// Limit the wait
var imgsrc = node.firstChild.__src__;
setTimeout(function() {
if (node.firstChild) // if the map has already changed and the image is not going to be loaded, the node is destroyed
{
//var index = pendingUrls[id].indexOf(node.firstChild.__src__);
//if (index != -1)
// If the image is not loaded yet (node.src changes to the same value as node.firstChild.__src__ when loaded)
var str = node.firstChild.src;
var suffix = node.firstChild.__src__;
if (!(str.indexOf(suffix, str.length - suffix.length) !== -1))
{
node.getElementsByTagName("img")[0].onload = null; // Disable the event handler for this node
removePendingImg(node, node.firstChild.__src__, "timed out"); // Remove the image from our list of pending urls
}
}
else removePendingImg(node, imgsrc, "discarded"); // Remove the image from our list of pending urls
}, timeout);
return node;
};
}
And these functions can be easily invoked from any getTileUrl function.
myMapType = new google.maps.ImageMapType({
getTileUrl: function(coord, zoom)
{
var url = '//a.tile.server.com/' + zoom + '/' + coord.x + '/' + coord.y + '.png';
// Add this url to our list of pending urls, and enable the loading image if appropriate
addPendingUrl("myLayer", url);
return url;
},
tileSize: new google.maps.Size(256, 256),
opacity: 0.5
});
// Listen for all the images having been loaded
addTileLoadListener("myLayer", myMapType, 15000);
Bonus features: support for multiple layers and timeouts (in case the server is slow or sloppy).
I am trying to get the infoWindow to display in just the right place on a Google Maps integration I'm working on (just above the top right/top left corner of an image). Whenever I try to adjust the "infoWindowAnchor" it doesn't seem to be working? However, the "iconAnchor" seems to be working as expected. I am wondering if anyone has ever run into a similar scenario before? I am making use of a custom icon, but even when I revert back to the standard icons provided by Google, the infoWindow seems to be displaying incorrectly (The tail is centered on the icon as opposed to being placed in it's top right position).
Below is the code I'm using to get this all working. The Important bits are here, but I've removed some of the other functions that may get in your way when diagnosing.
$(document).ready(function() {
var centerLatitude = 37.782112;
var centerLongitude = -122.419281;
var sanFran = new GLatLng(centerLatitude, centerLongitude);
var startZoom = 12;
var map;
var icon;
// Creates a default icon using our tuberent image
var myIcon = new GIcon();
myIcon.image = baseurl + 'my/imageFolder/markerFolder/image.png';
myIcon.shadow = baseurl + 'my/imageFolder/markerFolder/shadow.png';
myIcon.iconSize = new GSize(25,25);
myIcon.shadowSize = new GSize(38,25);
myIcon.iconAnchor = new GPoint(13,25);
myIcon.infoWindowAnchor = new GPoint(13,0);
myIcon.printImage = baseurl + 'my/imageFolder/markerFolder/printImage.gif';
myIcon.mozPrintImage = baseurl + 'my/imageFolder/markerFolder/mozPrintImage.gif';
myIcon.printShadow = baseurl + 'my/imageFolder/markerFolder/printShadow.gif';
myIcon.transparent = baseurl + 'my/imageFolder/markerFolder/transparent.png';
myIcon.imageMap = [22,0,22,1,22,2,21,3,21,4,21,5,21,6,21,7,23,8,24,9,24,10,22,11,22,12,22,13,22,14,22,15,22,16,22,17,22,18,22,19,22,20,22,21,22,22,22,23,22,24,2,24,1,23,1,22,1,21,1,20,1,19,1,18,1,17,1,16,1,15,1,14,1,13,1,12,1,11,0,10,0,9,1,8,3,7,4,6,5,5,7,4,8,3,9,2,10,1,11,0];
map = new GMap2(document.getElementById('map'));
map.addControl(new GMapTypeControl());
map.addControl(new GLargeMapControl3D());
map.setCenter(sanFran, startZoom);
/* Some Random Code and Functions here .... */
var point = new GLatLng(location.lat, location.lng);
var marker = new GMarker(point, myIcon);
map.addOverlay(marker);
/* Some Random Code and Functions here .... */
GEvent.addListener(marker, "click", function(){
var randomText = "Just some random test text";
var myHtml = "<b>#" + location.id + "</b><br/>" + location.neighborhood + "<br/>" + randomText;
map.openInfoWindowHtml(point, myHtml);
});
});
$(document.body).unload(function() {
if (GBrowserIsCompatible()) {
GUnload();
}
});
You could also try setting the pixel offset in the GInfoWindowOptions of openInfoWindowHTML().
Can you move the info window at all? By setting very large values just to test?
Otherwise, I doubt this is the answer, but I'd try it just in case.
From Google Maps API:
"Once we've created a map via the GMap2 constructor, we need to initialize it. This initialization is accomplished with use of the map's setCenter() method. The setCenter() method requires a GLatLng coordinate and a zoom level and this method must be sent before any other operations are performed on the map, including setting any other attributes of the map itself."
You're adding a couple of controls before calling setCenter().