I'm using the ng-map directive to display a map. The map has markers that show an infowindow whenever there's a mouseover on the marker. Sometimes however, the infowindow doesn't appear.
Other than this, I haven't been able to identify any pattern to what's happening, as the problem occurs for a different marker each time. I'm outputting data to the infowindow however the issue doesn't seem to be 'data related' as all data for the selected location seems to be correct at the point where the issue occurs.
I have a showInfo method that is being called on a mouseover like this:
showInfo(event, loc, infoWindowTemplate, map, mapsController) {
loc - data for the clicked location
infoWindowTemplate - the info window template to use (this is always the same for a particular map, however this is configurable, so if I'm showing a map for mobile, I use one infowindow template, if I'm showing a desktop map, I use a different one)
map - a reference to the NgMap object on the controller
mapsController - the controller itself (I strongly suspect that this is a bad code smell - it was the easiest way I could figure out to get reference back to the controller following the mouseover)
Here is the body of the method:
map.getMap(mapsController.mapId).then(function (myMap) {
var selectedMarker = myMap.markers["L: " + loc.position[0] + ", " + loc.position[1]];
selectedMarker.locationInfo = loc;
console.log("about to show infowindow - infoWindowTemplate = " + infoWindowTemplate);
// console output = "cached-myTemplate.html"
myMap.showInfoWindow(infoWindowTemplate, selectedMarker);
selectedMarker is definitely referring to the correct marker object. My template looks like this:
<script id="cached-myTemplate.html" type="text/ng-template">
<a class="map-location__link" href="/locations/{{anchor.locationInfo.locationId}}" target="_blank">
<img src="{{anchor.locationInfo.locationImageThumbnail}}" />
</a>
</script>
The issue seems to be that calling 'showInfoWindow' is intermittently failing somehow (although there are no errors in the console). Any comments or answers with ideas of what may be causing the issue or what else I can do to diagnose it will be appreciated!
I discovered that this is a timing issue. Delaying the 'turn' in which showInfoWindow is called (by adding a short timeout) fixed the issue:
map.getMap(mapsController.mapId).then(function (myMap) {
....
this.$timeout(function () {
dealmap.showInfoWindow(infoWindowTemplate, selectedMarker);
}, 100)
}.bind(this));
Related
My aim is to create a map for my work in OpenLayers 3 with several layers. One takes a basic feed from OpenStreetMaps. Another will be a transparent layer showing outlines of regions (not done yet). The third one, which is the one I'm having trouble with, shows a series of icons representing indivudal sites of interest on the map. I load the sites from a JS data stucture included as a separate script file. I have managed to get my code to add features that appear at the correct lat/lon. My next step is to get a HTML box (div) to appear in front of the map when they click on an icon (to display details of the site). However, I cannot get this to work. Apologies for my noobish coding, but it's really got me stumped and I'd really appreciate any help.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="ol.css" type="text/css">
<link rel="stylesheet" href="map.css" type="text/css">
<script src="ol.js" type="text/javascript"></script>
<script src="sites.js" type="text/javascript"></script>
<script type="text/javascript">
function buildLayerFromSites(allSites)
// allSites is just an array of data structures
{
var count, site;
// for each site in the array, make a feature
for (count=0;count<allSites.length;count++)
{
geom = new ol.geom.Point(ol.proj.fromLonLat([allSites[count].longitude,allSites[count].latitude]));
site = new ol.Feature(geom);
site.Name = allSites[count].siteName; // <-- can I assign further details in the structure like this?
var siteStyle = new ol.style.Style({
image: new ol.style.Icon ({
src: 'icon-blue.png',
scale: 0.1,
opacity: 0.9,
})
})
site.setStyle(siteStyle);
siteFeatures[count] = site;
}
siteSource = new ol.source.Vector ({
features: siteFeatures
})
siteLayer = new ol.layer.Vector({
source: siteSource
});
map.addLayer(siteLayer);
}
</script>
<title>Map</title>
</head><body>
...
<div id="map" class="map">
...
<script type="text/javascript">
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([115.51, -31.57]),
zoom: 8
})
});
buildLayerFromSites(includeSites); // adds the second layer with the sites
// click event handler, basically same as http://openlayers.org/en/v3.12.1/examples/icon.html
map.on('click', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature,layer) {
return feature;
});
if (feature) {
console.log("feature got clicked on"); // do stuff
} else {
console.log("didn't click a feature");
}
});
</script>
</body>
</html>
The map and the icons load, but when I click the icons, no matter how zoomed in or out I am, it doesn't detect a match with the icon I'm clicking on. The thing is, the event handler code is the same as in the official example, which make me think it's the way I'm creating the features. On the other hand, the features display fine as icons, its just the click event that doesn't seem to work. Similar weird stuff happens for the mouse cursor change from the example. I sorta guess it's because I don't understand the way the data/functions are structured in OL, and I find the documentation doesn't fully explain how it all works, so I've been writing snippets like this trying to take a look:
var myLayers = map.getLayers();
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
var mySource = myLayers.getSource;
console.log("source" + mySource.getProjection); // doesn't work
var features = mySource.getFeatures; // says mySource is undefined
Of course it fails totally.
How do I get it to detect the clicks on the icons/features so I can tell a div to appear and display my data at the right time? Am I doing this right? What don't I understand about the way the data/functions work? I'm a big JS noob so I hope I'm not doing something stupid, but it's causing me massive problems and I could really use your help! Thanks all!
If someone else comes upon this issue: my solution was changing the Feature icon PNG image to not have transparency (no color).
If your image is PNG and has transparency,
where it is transparent it won't be clickable
(I'm on OpenLayers v5). For example, I had an image that was a circle and it was transparent inside and just colored on the outside border.
When I changed the image to something without transparency in the middle (it can still be PNG of course) everything was okay. It seems this is how OpenLayers handles pixels for images - if the map can be seen through the image, it's not a 'hover' even though you are on a Feature.
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
It means you're trying to print the function itself, not the result of it's execution. Should be
console.log("keys - " + myLayers.getKeys())
Same with
mySource.getProjection();
mySource.getFeatures();
Usually if the attribute name starts with "get" and is written in camelCase - it's a function. Keep that hint in mind :)
After long hours staring at my screen and hitting my head on my desk, I've stumbled across the solution to my problem. It's a little obscure but mind-numbingly simple/stupid/silly. I thought I'd post the solution here for others.
I wrote a couple of lines of code to resize the viewport when the page is loading and again when the window is resized, so the map would adjust to the available user's browser window space. Unfortunately, as far as I can work out, OL doesn't know about it so the icons and the features are no longer in the same place after the resize. I didn't know this though, until after countless hours I was randomly clicking and resizing it detected a feature click.
Luckily there is an easy way to solve it once you figure out this is the problem. Basically just add map.updateSize() to the part of your code that resizes the viewport. So far this seems to have solved the problem.
The functions is only work on pixel of features so to solve this you can add some style with your image with fill with something like rgb(255,0,0,0.001) propert. I have tried it and i worked for me.
I am trying to show the infowindow on top of a marker and I am struggling with it.
Currently I have the following HTML:
<map data-ng-model="mymap" zoom="4" center="[38.50, -95.00]">
<marker ng-repeat="item in list"
position="[{{item.coordinates}}]" title="{{item.name}}"
icon="./images/i.png"
on-click="onMarkerClicked(mymap, this)" />
<control name="overviewMap" opened="true" />
</map>`
Then, in the corresponding controller, I have:
$scope.onMarkerClicked = function(mymap, marker, event){
infowindow = new google.maps.InfoWindow("Hi I am the MARKER" + event.target.title);
infowindow.open( mymap, event.target );
//infowindow.open( mymap, marker);
$scope.$apply();
}
However, mymap is has the correct value, while event.target does not seem to work, as well as marker (commented in the reported code)
Cannot read property 'target' of undefined
When using marker, it is undefined.
How can I manage to fix this?
I have come across this GitHub where they discuss this issue - https://github.com/allenhwkim/angularjs-google-maps/issues/95
They then go on to share an example of using ng-repeat with infoWindow - https://rawgit.com/allenhwkim/angularjs-google-maps/master/testapp/infowindow_compiled.html
Take a look at the source code on that page, I think it will help you
You should actually be passing the marker instance as the second parameter to the open call.
See here https://developers.google.com/maps/documentation/javascript/examples/infowindow-simple
You are sending the object this as an argument and naming that event later? On top of that if you are using Angular why not doing it with ng-click="onMarkerClicked(myMap, this, event)". I have never used Google Maps this way but it seems to me that your parameters somehow are incorrect.
See this simple fiddle to see what I mean
http://jsfiddle.net/ozbafyhh/
<button onclick="myFunction(this, event)">Click me</button>
function myFunction (x, event){
alert(x);
alert(event);
}
I am developing an application which uses google maps api v3 to show markers and infowindows.
Well, I have N markers stored within an array and a global infowindow used to show some information.
The infowindow contents are shown simply by clicking a marker, created in this way:
/* global js stuff */
var g_map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var g_current_popup = new google.maps.InfoWindow({ content: "" });
var g_markers = [];
/* create marker function */
function addMarker(p_infowindow_contents)
{
var l_marker = new google.maps.Marker( all the stuff needed );
google.maps.event.addListener(l_marker, 'click', function()
{
g_current_popup.close(); // if already open, it must be closed and reloaded
g_current_popup.setContent(p_infowindow_contents);
g_current_popup.open(g_map, l_marker);
});
g_markers.push(l_marker);
}
Everything works as expected, except for a little graphical glitch: when the infowindow is appearing, I see the infowindow 'tip' positioned at an unknown location for a tenth of a second, then it disappears and I see the correct infowindow.
Look at this screenshot took just before the tip disappears:
Does anyone experienced something like this?
Could it be some CSS issue?
Thanks
It does not look like this is a problem with your code, I think it more likely a browser issue. I was able to validate the same thing looking at the infowindow example that Google provides in Firefox, but not in Chrome:
https://developers.google.com/maps/documentation/javascript/examples/infowindow-simple
it seems to happen more visibly when the map has to scroll to fit the infowindow, but I would say that it is not a requirement for it to do so. It is likely just an artifact with the screen taking a few clock cycles to catch up with the DOM.
I use Mapbox for a dynamic map on a website. It works really well except, I have a sidebar which list the pins with a little more description and an image. I want to make it so when I click on that sidebar, it would fire the click event of that marker on the map.
I used to do this all the time with Google Maps but now I'm stuck because even in the case that I can keep the instance of the marker, I cannot fire the click on it for some reason. It simply does nothing (maybe I need to re-bind the click event on it but I don't know how with mapbox)
I've encountered a few questions about this on Google and SO but none bring a real answer to that question except "keep the instance" which isn't always possible in some cases.
So basically I have a jQuery click event like this:
var marker = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [lng, lat]
},
properties: {}
};
if (isPin) {
marker.properties = pinStyles.pin;
} else if (isWinery) {
marker.properties = pinStyles.winery;
} else {
marker.properties = pinStyles.user;
}
marker.properties.title = locationName;
marker.properties.description = pin.description;
var markerObject = L.mapbox.markerLayer(marker);
// Add to cluster
markers.addLayer(markerObject);
$('#marker_list a.marker_item:last').click(function() {
var geoJson = markerObject.getGeoJSON();
markerObject.fire('click'); // does nothing (openPopup makes "Uncaught TypeError: Object [object Object] has no method 'openPopup' " so I guess I'm not doing it right)
});
And I have this (click event for mapbox marker):
map.markerLayer.on('click', function(e) {
map.setView(e.layer.getLatLng(), map.getZoom());
});
Anyone has an idea about wether 1) fix the non-firing event OR 2) make an HTML link fire a mapbox marker click event OR .openPopup?
Thanks and have a nice day!
MapBox's marker layer is a collection of Leaflet markers. You can create an href to a function that look for a particular marker based on it's layer id.
map.markerLayer.getLayers() returns an array of layer objects that contain both a _leaflet_id and the method togglePopup.
Try matching your href call to the leaflet id and then fire map.markerLayer.getLayers()[i].togglePopup()
Let me know if this helps.
I'm using Twitter's Bootstrap, and want to show a Google Map in a popover.
The way it works right now I'm doing something like this
$ ->
$('.thumbnails a.js-popover').popover
html: true,
content: ->
uid = $(this).data('profileUid')
popover_container = $('.popover-contents:data(profileUid=' + uid + ')')
_.each window.Maps, (map) ->
google.maps.event.trigger map, 'resize' // I hoped this would re-draw the map
popover_container.html()
The popover loads it's content from a .popover-contents div which is hidden, and connected to the a with data attributes (so that I can find the correct popover to show)
The map works perfectly when not in a popover, and I think it's connected with being copied via html() in jQuery to another DOM element. Twitter's bootstrap doesn't provide a modal opened callback, and I'm genuinely not sure how to make the maps work.
As you can see the map works correctly on the full profile page, the markup is the same (rails partial), and the javascript is shared, too - I can only assume the GoogleMaps API really doesn't like having it's dom messed with, and is thus causing issues.
If you're using popovers, your best bet might be to use google's static API and avoid the headaches associated with an interactive map. Borrowing a very simple case from the documentation, you might do something like this:
var options = { content: '<img src="http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=14&size=512x512&maptype=roadmap&sensor=false">' };
$('#example').popover(options)
Wrapping it up into a reusable function yields:
var getMap = function(opts) {
var src = "http://maps.googleapis.com/maps/api/staticmap?",
params = $.extend({
center: 'New York, NY',
zoom: 14,
size: '512x512',
maptype: 'roadmap',
sensor: false
}, opts),
query = [];
$.each(params, function(k, v) {
query.push(k + '=' + encodeURIComponent(v));
});
src += query.join('&');
return '<img src="' + src + '" />';
}
var content = getMap({center: 'Fritz-Walter Stadion, Kaiserslautern'});
$('#example').popover({ content: content })
*Another valid answer might be found here, but it's not the solution I took. *
It seems to be widely accepted that rendering a Google map in an invisible DOM element leads to rendering bugs, and the solution (cribbed from http://sethmatics.com/articles/classipress-hidden-map-tab-and-redraw-google-map-canvas/ ) appears to look something like this:
jQuery('.tabprice ul.tabnavig li a').click(function() {
if(jQuery(this).attr('href') == '#priceblock1') {
//the element has to be visible on the page for google maps to render properly
jQuery('#priceblock1').show();
//rebuild the google map canvas to the proper size
google.maps.event.trigger(map, 'resize');
//ClassiPress javascript function to build map at address
codeAddress();
}
});
I do think it might be worth moving the dom element a long way off the left/right/bottom of the screen in order to avoid it flashing to the user, or doing something with Z-Indexing to make sure that the user doesn't see an unwelcome popup.
In my case, however the static maps API as suggested by rjz was perfect.
The problem is that google maps api requires visible element for container, so you should call the api inside the shown.bs.modal event handler. Something like this:
$picker.popover({
html: true,
content: '<div class="superPopover">'
});
$picker.on('shown.bs.popover', function () {
var $container = $('.superPopover');
new google.maps.Map($container[0]);
});