show popups correctly with external hyperlinks in openlayers 4 - javascript

I have an openlayers map that loads a couple of kml files containing about 120 polygon placemarks each. As they're too many to show a popup for each, I had to create an outside-map menu, so the user can click on any one of these features and see it's info / location.
I use this function to create the outside-map menu, containing all the features:
vEnergeticos.getSource().on('change', function(evt){
var source = evt.target;
if (source.getState() === 'ready') {
var energeticos = source.getFeatures();
for (var i in energeticos) {
var figura = energeticos[i].getGeometry().getExtent();
var myCenter = ol.extent.getCenter(figura);
$("#containerLeft").append("<a href=javascript:showMenuPopup(" + myCenter + "," + energeticos[i].get('ID') + ")>" + energeticos[i].get('name') + "</a><br>");
}
}
});
and then when the user clicks on any of these options, this function is called:
function showMenuPopup(xx, yy, theID){
var myPixel = map.getPixelFromCoordinate([xx, yy]);
var elNombre = "";
var laDescripcion = "";
map.forEachFeatureAtPixel(myPixel, function(feature, layer) {
if (feature.get('ID') == theID){
elNombre = feature.get('name');
laDescripcion = feature.get('description');
}
});
popupTitle.innerHTML = elNombre;
popupContent.innerHTML = laDescripcion;
overlay.setPosition([xx,yy]);
}
This works in some situations, however, when the selected feature is outside of the current map view, the map relocates successfully (overlay.setPosition([xx,yy]);), the popup is shown, but the popup is empty. If the feature is visible when the user clicks from the left menu, then the popup is shown correctly.
Just to be clear enough, imagine you're seeing a map where you can see part of Europe, and then you click on some item located in Canada (using the off-map menu), you'll see the map relocates in Canada, but the popup that is shown is empty. If you click again on that very same off-map link, or any other feature that is visible at that location/zoom view, then the popup is shown correctly.
I tried to use the "moveend (ol.MapEvent)" in order to fix this, so the popup was loaded after the map is relocated, but it didn't work for me. The moveend event is called before the map starts to move using overlay.setPosition([xx,yy]), and I haven't been able to find some other "after-relocation" event that I could use.
I've been struggling with this for many days now, so any help will be really appreciated.
Regards!!

The problem is that the features outside of the current map view are not "AtPixel", so you won't catch them with map.forEachFeatureAtPixel.
I suggest you to avoid passing coordinates to showMenuPopup: you just need the feature id, than you can retrieve the feature's coordinates inside showMenuPopup.
$("#containerLeft").append("<a href=javascript:showMenuPopup('" + energeticos[i].getId() + "')>" + energeticos[i].get('name') + "</a><br>");
Then
function showMenuPopup(featureId){
var feature = vEnergeticos.getSource().getFeatureById(featureId);
var elNombre = feature.get('name');
var laDescripcion = feature.get('description');
var figura = feature.getGeometry().getExtent();
var myCenter = ol.extent.getCenter(figura);
popupTitle.innerHTML = elNombre;
popupContent.innerHTML = laDescripcion;
overlay.setPosition(myCenter);
}

Related

How to select which feature overlay in case of overlapping or same geometry in OpenLayers

I created a map in which an overlay is opened clicking on the features. But I have problem if more than one feature are in overlapping, or worse, when features have the same geometry.
I use this code to show the overlay. But when two features have the same geometry only one overlay is shown.
const overlayContainerElement = document.querySelector('.overlay-container');
const overlayLayer = new ol.Overlay ({
element: overlayContainerElement
});
map.addOverlay(overlayLayer);
const overlayFeatureName = document.getElementById('feature-name');
const overlayFeatureAdditionalinfo = document.getElementById('feature-additionalinfo');
map.on('click', function(e){
overlayLayer.setPosition(undefined);
map.forEachFeatureAtPixel(e.pixel, function (feature, layer){
let clickedCoordinate = e.coordinate;
let clickedFeatureName = feature.get('Number')
let clickedFeatureInfo = feature.get('Text');
overlayLayer.setPosition(clickedCoordinate);
overlayFeatureName.innerHTML = clickedFeatureName;
overlayFeatureAdditionalinfo.innerHTML = clickedFeatureInfo;
},
{
layerFilter: function(layerCandidate){
if (layerCandidate.get('title') === "Title") {
return 1;
}
}
})
});
Any idea how to configure a way in order to select which feature detail I want to see? So after clicking, if two features are under the mouse, a sort of list is shown to select what overlay I want to open.
Instead of updating your overlay inside the map.forEachFeatureAtPixel method, you could use the map.forEachFeatureAtPixel method to collect all features that were clicked, and then work with an Array of features.
Something like this (untested / unfinished):
var features = [];
map.forEachFeatureAtPixel(e.pixel, function (feature, layer){
features.push(feature);
},
{
layerFilter: function(layerCandidate){
if (layerCandidate.get('title') === "Title") {
return 1;
}
}
});
// And then, you could iterate on your features and show information as a list in your overlay.
// todo - loop here
/*
let clickedCoordinate = e.coordinate;
let clickedFeatureName = feature.get('Number')
let clickedFeatureInfo = feature.get('Text');
overlayLayer.setPosition(clickedCoordinate);
overlayFeatureName.innerHTML = clickedFeatureName;
overlayFeatureAdditionalinfo.innerHTML = clickedFeatureInfo;
*/
You could have your overlay show a list of results in a "summary" fashion, and clicking on one of the feature name (for example) could show its full details within the overlay.
HTH

leaflet popup not opening for a particular marker

Using leaflet, I call below function when I add a layer to the map. This function adds popups to each feature.
They work when I click on them but I can't get the bindPopup.openPopup() to work so it opens without being clicked on.
There aren't any errors but the popup doesn't open without being clicked on.
var popupToOpen = null;
var clickedLocationId = 0;
function onEachFeature(feature, layer) {
if (feature.properties && feature.properties.UserName) {
if (feature.properties.MarkerId == clickedLocationId) {
layer.bindPopup("<div id='unlockLocationId'>" + feature.properties.MarkerId + "</div><div>" + feature.properties.UserName + "</div>").openPopup();
} else {
layer.bindPopup("<div id='unlockLocationId'>" + feature.properties.MarkerId + "</div><div>" + feature.properties.UserName + "</div>");
}
}
}
I've tried the way you're trying but the popup doesn't open. Well, you can try opening popup another way.
Create a function where you iterate your geojson layer and open the popup if the id matches. Here is the function
function openMarkerPopup(id){
geojson.eachLayer(function(feature){
if(feature.feature.properties.id==id){
feature.openPopup();
}
});
}
Simply pass the required id to this function, and it'd work.
openMarkerPopup(108);
Here is a working fiddle

Open InfoWindow using KeyDown/KeyPress

I'm just wondering if there is a way to open Google Map InfoWindow by keyboard. So far, I can only make it open by click.
I found the following: http://www.visionaustralia.org/digital-access-googlemap
but it only handles the controls and navigation.
$(function(){
//use mapselector="div" if you don't know the map id
var mapSelector = "#map_canvas div";
var attemptInterval = 2;
var maxAttempts = 18;
var mA = 0;
var notYet = true;
var titles = {"pan up":1,"pan down":1,"pan right":1,"pan left":1, "zoom in":1,"zoom out":1,"show street map":1,"show satellite imagery":1};
function addKey(){
mA++;
if(mA > maxAttempts){return;}
$(mapSelector).each(function(index){
var title = this.getAttribute("title")
if(title) title=title.toLowerCase().trim();
if(title in titles){
jqel = $(this);
titles[title] = jqel;
jqel.attr("tabindex","0");
jqel.attr("role","button");
jqel.keydown(function(ev){
if(ev.which==13) {
$(this).trigger("click")
}else if(ev.which==40) {
titles["pan down"].trigger("click");
}else if(ev.which==38) {
titles["pan up"].trigger("click");
}else if(ev.which==37) {
titles["pan left"].trigger("click");
}else if(ev.which==39) {
titles["pan right"].trigger("click");
}else if(ev.which==61 || ev.which == 187) {
titles["zoom in"].trigger("click");
}else if(ev.which==173 || ev.which == 189) {
titles["zoom out"].trigger("click");
}else{
return
}
ev.preventDefault();
});
(function(){
var mo = false;
var bo = jqel.css("border");
var ma = jqel.css("margin");
var bc = jqel.css("background-color");
var op = jqel.css("opacity");
jqel.mouseover(function(){mo=true;});
jqel.mouseout(function(){mo=false;});
jqel.focus(function(){
if(mo)return;
$(this).css({"border":"2px solid blue","margin":"-2px","background-color":"transparent","opacity":"1"});
});
jqel.blur(function(){$(this).css({"border":bo,"margin":ma,"background-color":bc,"opacity":op});});
notYet = false;
})();
}
});
if(notYet){setTimeout(addKey,attemptInterval*1000);}
}
addKey();
});
It works by getting the title of the controls so I tried to add a title to the marker and adding it to the titles array but it doesn't work.
I'm quite new with Google Maps API and JavaScript, any suggestions on where to get started? Thanks!
I'm a bit rusty on my Google Maps API knowledge, but if I remember correctly, the API does indeed give a way to attach a listener to your marker. However, it does not work for keypress events, as you have noticed. For completeness, the basic syntax is this:
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map,marker);
});
The addListener function accepts three parameters:
The item on which to attach the event (the marker in this case)
The type of event to listen for
The lamba or function to execute when the event occurs (opening an infoWindow)
The important bit here is the second parameter, the event for which to listen. These are not actual DOM events, but Google's own abstracted events (for better cross-browser support) and thus only a select amount is available - there is no keypress event.
JavaScript, on the other hand, does recognize several key events. I'm cowardly assuming you have jQuery available (from your posted example code), so you could exploit jQuery's keypress function to capture, well, a key press and opening an infoWindow with that:
$(document).keypress(function (event) {
switch (event.which) {
case 102:
// lowercase f
infowindow.open(map, marker);
break;
}
});
This should open your defined infoWindow on your defined marker position. Note that this example does only contain code to open one infoWindow for one marker. But it should give you a general idea.
See the following Fiddle I made for a very rough and simple example, using the 'f' key to open your infoWindow (after clicking in the "result" frame).

Clicking a polygon using Google Maps API 3 - Need to change to MouseOver (Geoxmlv3 & KML Layer)

I have a Google Maps (API 3) running a custom KML (geoxml3) with polygons containing titles and descriptions.
It all works fine, however I need to change the polygon click which reveals the infowindow to work on hover instead. It is easy enough to create a mouseover listener which runs the click function, however I need the click to run another function, so the click function would get overwritten using this method.
How can I find/copy the code that runs for the polygon click function and apply it to onmouseover instead? Is this possible?
Update: I found this section in the geoxmlv3.js file:
google.maps.event.addListener(gObj, 'click', function (e) {
var iW = this.infoWindow;
iW.close();
iW.setOptions(this.infoWindowOptions);
if (e && e.latLng) iW.setPosition(e.latLng);
else if (this.bounds) iW.setPosition(this.bounds.getCenter());
iW.setContent("<div id='geoxml3_infowindow'>" + iW.getContent() + "</div>");
google.maps.event.addListenerOnce(iW, "domready", function () {
var node = document.getElementById('geoxml3_infowindow');
var imgArray = node.getElementsByTagName('img');
for (var i = 0; i < imgArray.length; i++) {
var imgUrlIE = imgArray[i].getAttribute("src");
var imgUrl = cleanURL(doc.baseDir, imgUrlIE);
if (kmzMetaData[imgUrl]) {
imgArray[i].src = kmzMetaData[imgUrl].dataUrl;
} else if (kmzMetaData[imgUrlIE]) {
imgArray[i].src = kmzMetaData[imgUrlIE].dataUrl;
}
}
});
iW.open(this.map, this.bounds ? null : this);
});
I have tried changing the 'click' event to 'mouseover' but that causes no mouseover or click to work
Here's the solution (after much trial and error!)
google.maps.event.addListener(poly,"mouseover",function(e) {
var iW = this.infoWindow;
iW.close();
iW.setOptions(this.infoWindowOptions);
if (e && e.latLng) iW.setPosition(e.latLng);
else if (this.bounds) iW.setPosition(this.bounds.getCenter());
iW.setContent("<div id='geoxml3_infowindow'>"+iW.getContent()+"</div>");
iW.open(this.map, this.bounds ? null : this);
});
Then just change your click function to something else. Remember to also set your mouseout function to close the infowindow too

OpenLayers Markers with custom Text / Label

I have "N" markers on an Openlayers map and I need to "label" these markers (Meaning: Put a text in/on them)
I have tried several ways but still couldn't achieve what I need.
My JS code snippet (Removed some irrevelant stuff from the code):
function getWeatherInfo(){
if(wheatherOfCitiesMarkerLayer == null){
wheatherOfCitiesMarkerLayer = new OpenLayers.Layer.Markers("WeatherMarkerLayer");
map.addLayer(wheatherOfCitiesMarkerLayer);
}
$.getJSON(qryPointResultListForAllCities(), function(data) {
if(data!= null){
var size = new OpenLayers.Size(60,45);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
for(var i = 0; i < data.pr.length; i ++){
// Create markers by using the returned data from the server
//...
//... Removed some irrevelant stuff
var lat = data.pr[i].la;
var lon = data.pr[i].lo;
var infos = data.pr[i].info.infos;
var infop = data.pr[i].info.infop;
var infocc = data.pr[i].info.infocc;
var icon = new OpenLayers.Icon('my_marker_img.png',size,offset);
location = new OpenLayers.LonLat(lon, lat);
location = transformFromWGS1984ToSphericalMercator(location.clone());
marker = new OpenLayers.Marker(location,icon.clone());
wheatherOfCitiesMarkerLayer.addMarker(marker);
}
}
}
}
What I need to do is put a label or text in/on each marker on the map.
Since you are taking marker data from server anyway, what you can do is, create a layer in a map file with just the label class defined and add it on your marker layer as a overlay.
Hope this helps.
Instead of adding text (or label) to a marker, you can add a marker to a label.
This is done by creating a new text file with the location, title, description and path to your icon (not required, it will use the default if none is set). For example,
if you want to place two markers on the map, the text file may look like this
point title description icon
10,99 An labeled marker with default image
12,34 Another marker This is my label text http://example.com/marker.png
Please note that there should be tabs between the names and parameters, not spaces. There is also a tab at the end of the second line where no icon is given.
Save this file somewhere on your site and put this code where your layers are being initialized.
var textl = new OpenLayers.Layer.Text("text", {location : "data_dile.txt"});
map.addLayer(textl);
The text file may be created dynamically though server-side languages, such as PHP.
There is an online example here.

Categories

Resources