Multiple popups opened at the same time in OL3 - javascript

I have some serious question. How can i set multiple popups to be opened at the same time? I am using this code to open a popup:
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
closer.onclick = function() {
overlay.setPosition(undefined);
closer.blur();
return false;
};
var overlay = new ol.Overlay({
element: container
});
And for viewing content for specific feature in cluster at specific coordinates i use this:
map.on('click', function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function (feature, layer) { return feature; });
var coordinate = evt.coordinate;
var prop;
var vyprop = "";
overlay.setPosition(coordinate);
var features = feature.get('features');
for(var i = 0; i < features.length; i++) {
prop = features[i].getProperties();
vyprop += prop.odbermisto + "<br>";
}
content.innerHTML = vyprop;
});
Because it uses div as a content and moves it from coordinates, hwere it is needed, I ain't able to open two popups at the same time. is there any solution to do that, without duplicating and deleting divs, which i dont wanna do? Thx for help guys

Related

check if a lat long is within an extent using open layers 3

I have a UK county shape file (Multipolygon) using EPSG:4326 in my geoserver. I'm using open layers 3 to load this shape file in my application as below :
source = new ol.source.XYZ({url: '/gmaps?zoom={z}&x={x}&y={y}&Layers=UKCounties', crossOrigin: "anonymous"});
countiesLayer = new ol.layer.Tile({source: source});
map.addLayer(countiesLayer);
This works well. I have a requirement to get users current location which is done as
var coordinate = geolocation.getPosition();
I'm able to retrieve the correct lat & long here. Ex : Lat = 53.797534899999995, Lng = -1.5449. now I need to check which of the counties (polygon) these points are in using open layers 3 & Javascript.
Using Geoserver WFS, I'm able to get the bounding box of each of the counties as
$.each(features, function(index, eachFeature) {
var bbox = eachFeature.properties.bbox;
if (bbox != null) {
var bottomLeft = ([bbox[0], bbox[1]]);
var topRight = ([bbox[2], bbox[3]]);
var extent = new ol.extent.boundingExtent([bottomLeft, topRight]);
if (ol.extent.containsXY(extent1,lat,long)) {
alert("got the feature");
}
}
});
The issue is my code doesn't print the alert statement.I've also tried using
if (ol.extent.containsXY(extent,long,lat))
and
var XY = ol.proj.transform([long, lat], 'EPSG:4326', 'EPSG:3857');
if (ol.extent.containsXY(extent,XY[0],XY[1]))
if (ol.extent.containsXY(extent,XY[1],XY[0]))
But none of these print the alert. Is there anything wrong in this?
Before answering your question, I did not know the method of "ol.extent.containsXY".
I used my poor logic! I detected a feature if in a polygon by follwing :
transform feature and container(polygon) to coordinate [lon, lat]
detect the container if contain the feature
extent array rule [minLon, minLat, maxLon, maxLat]
code snippet: (my destinationPro:'EPSG:3857', sourcePro:'EPSG:4326')
QyGIS.prototype.isInner = function(featureExtent, containerExtent) {
var featureLonLat = ol.proj.transformExtent(featureExtent, destinationPro, sourcePro);
var containerLonLat = ol.proj.transformExtent(containerExtent, destinationPro, sourcePro);
// in my condition, the feature is a point, so featureLonLat[0] = featureLonLat[2], featureLonLat[1] = featureLonLat[3]. what's more extent have four value in a array so the loop length is 4
for (var i = 0; i < featureLonLat.length; i++) {
/* actually:
featureLonLat[0] < containerLonLat[0] || featureLonLat[0] > containerLonLat[2]
featureLonLat[1] < containerLonLat[1] || featureLonLat[1] > containerLonLat[3]
featureLonLat[2] < containerLonLat[0] || featureLonLat[2] > containerLonLat[2]
featureLonLat[3] < containerLonLat[1] || featureLonLat[3] > containerLonLat[3]
*/
if (featureLonLat[i] < containerLonLat[i % 2] || featureLonLat[i] > containerLonLat[i % 2 + 2]) {
return false;
}
}
return true;
};
QyGIS.prototype.getInnerFeatures = function(layerName, extent) {
var self = this;
var layer = self.getLayer(layerName);
if (layer) {
var source = layer.getSource();
var features = source.getFeatures();
for (var i = 0; i < features.length; i++) {
var curFeatureExtent = features[i].getGeometry().getExtent();
if (self.isInner(curFeatureExtent, extent)) {
console.log(features[i].get('name') + 'in area');
}
}
}
};
At last, sorry for my poor english if my answer confuse you .
I had to use
var XY = ol.extent.applyTransform(extent, ol.proj.getTransform("EPSG:3857", "EPSG:4326"));
instead of
var XY = ol.proj.transform([long, lat], 'EPSG:4326', 'EPSG:3857');
and it works.

Leaflet open multiple popups without binding to a marker

I am busy writing a simple map implementation using leaflet however I have hit a bit of a snag. I am trying to setup my map and have added a custom control to show labels (which will show the popups) based on the selection of a checkbox.
My custom control is like so:
var checkBoxControl = L.Control.extend({
options: {
position: 'topright'
},
onAdd: function (map) {
var container = L.DomUtil.create('input', 'leaflet-control');
container.style.backgroundColor = 'white';
container.id = 'labels_checkbox';
container.style.width = '30px';
container.style.height = '30px';
container.label = "Labels";
container.type = 'checkbox';
container.onclick = function () {
var checkBox = $("#labels_checkbox");
var checkBoxValue = checkBox[0];
var labelsChecked = checkBoxValue.checked;
var bounds = mymap.getBounds();
for (var i = 0; i < markers.length; i++) {
marker = markers[i].mark;
if (bounds.contains(marker.getLatLng())) {
var previewLabel = markers[i].previewLabel;
if (labelsChecked == true) {
console.log('previewLabel', previewLabel);
mymap.addLayer(previewLabel).fire('popupopen');
} else {
previewLabel.close();
}
}
}
};
return container;
}
});
I can see as per my console that it is fetching all the surrounding markers however the map won't open those markers?
Is there a way for me to open a popup without binding it to a marker?
Thanks
You have to change L.Map behaviour to prevent automatic closing of popups.
It is discussed here.
// prevent a popup to close when another is open
L.Map = L.Map.extend({
openPopup: function (popup, latlng, options) {
if (!(popup instanceof L.Popup)) {
var content = popup;
popup = new L.Popup(options).setContent(content);
}
if (latlng) {
popup.setLatLng(latlng);
}
if (this.hasLayer(popup)) {
return this;
}
// NOTE THIS LINE : COMMENTING OUT THE CLOSEPOPUP CALL
//this.closePopup();
this._popup = popup;
return this.addLayer(popup);
}
});
See this example

How many points can a Cesium Map display?

I am trying to put thousands of points on a Cesium map and running into problems with Firefox crashing. I am required to use Firefox. The map seems to be able to display 15,000 points (as images). However, it is also almost unusable. Zooming and panning have huge delays and eventually crash. Does anyone know how many points the limit should be? Also, is there a better way to display these points then the way I am doing it? I really hope it's me and not Cesium. I heard that creating czml and then passing it in is slower so I have the following javascript test:
function test(){
for (var i=0; i<15000; i++){
tempLat +=1;
tempLon +=1;
if(tempLat>90){
tempLat=0;
tempLon=0;
}
addBillboard(scene, ellipsoid, tempLat,tempLon);
}
}
//this is from the sandcastle examples for cesium.
function addBillboard(scene, ellipsoid,tempLat,tempLon) {
var primitives = scene.primitives;
var image = new Image();
image.onload = function() {
var billboards = new Cesium.BillboardCollection();
var textureAtlas = scene.context.createTextureAtlas({image : image});
billboards.textureAtlas = textureAtlas;
billboard = billboards.add({
position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(tempLat, tempLon)),
imageIndex : 0
});
primitives.add(billboards);
};
image.src = '../images/Cesium_Logo_overlay.png';
}
Your code is creating 15,000 BillboardCollection primitives, each with one Billboard each. You will have much better performance if you create one BillboardCollection and add 15,000 Billboards to that collection.
Here's a working example you can use with the b28 release. Paste this into the b28 Sandcastle: http://cesiumjs.org/releases/b28/Apps/Sandcastle/index.html?src=Billboards.html&label=Showcases
Note that some of these APIs will be changing in the upcoming release. Always check CHANGES.md for a list of breaking changes.
require(['Cesium'], function(Cesium) {
"use strict";
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var primitives = scene.primitives;
var ellipsoid = viewer.scene.globe.ellipsoid;
var billboards = new Cesium.BillboardCollection();
var image = new Image();
image.onload = function() {
var textureAtlas = scene.createTextureAtlas({image : image});
billboards.textureAtlas = textureAtlas;
primitives.add(billboards);
};
image.src = '../images/Cesium_Logo_overlay.png';
function addBillboard(tempLat, tempLon) {
billboards.add({
position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(tempLat, tempLon)),
imageIndex : 0
});
}
var tempLat = 0;
var tempLon = 0;
for (var i = 0; i < 15000; i++){
tempLat += 1;
tempLon += 1;
if (tempLat > 90){
tempLat = 0;
tempLon = 0;
}
addBillboard(tempLat, tempLon);
}
Sandcastle.finishedLoading();
});

Gmaps4rails : How to change appearance of marker when user clicks link in sidebar?

I'm trying to get this behavior with gmaps4rails :
User clicks link in the sidebar, corresponding marker on the map changes image/color in order to make the selected one stand out from the others.
I've tried with this code
Gmaps.map.callback = function(){
var len = Gmaps.map.markers.length;
for(var i = 0; i < len; i++){
marker = Gmaps.map.markers[i];
google.maps.event.addListener(marker.serviceObject, 'click', (function(i){
return function(){
console.log($(Gmaps.map.markers[i].serviceObject.ne.ga).attr("src", "/assets/marker_sprite2.png"));
}
})(i));
}
}
But this changes every marker's appearance, which is not very useful for what I'm trying to do !
Is there anyway to achieve this ?
EDIT : Made it work see answer below
EDIT 2 : While this solution worked for what I wanted, I stumbled upon an other issue with this method, see comment to answer below
You've a javascript issue. Actually, you can't make a closure with a changing counter: it will always be referenced with it's last value.
Gmaps.map.callback = function(){
for(var i = 0; i < Gmaps.map.markers.length; i++){
marker = Gmaps.map.markers[i];
google.maps.event.addListener(marker.serviceObject, 'click',
(function(marker){
return function(){
console.log($(marker.serviceObject.ne.ga).attr("src", "/assets/marker_sprite2.png"));
}
})(marker)
)
}
}
Your main issue is a bit long, not complex but long. The idea is the following:
add an id to each marker, you could use the block argument of the to_gmaps4rails method and add some more json
create the sidebar yourself and add the id to each line to know which marker you want when you click the li
loop all markers to get the one with the proper id
change it's picture
I made it work with this code :
Gmaps.map.callback = function(e){
var len = Gmaps.map.markers.length;
var markers = Gmaps.map.markers;
for(var i = 0; i < len; i++){
m = Gmaps.map.markers[i];
google.maps.event.addListener(m.serviceObject, 'click',
(function(m, markers){
return function(){
for(a in markers){
markers[a].serviceObject.setIcon("/assets/marker_sprite.png");
}
m.serviceObject.setIcon("/assets/marker_sprite2.png");
}
}(m, markers))
)
}
}
What's happening is we add a click listener to each marker and pass to this listener function the marker itself. I also pass in the whole markers array to reset them all on each click and then make the one I'm interested stand out. This is the simple version, my final version gives this :
Gmaps.map.callback = function(){
var len = Gmaps.map.markers.length;
var markers = Gmaps.map.markers;
var anchor = new google.maps.Point(9.5, 34);
var sAnchor = new google.maps.Point(0, 34);
var origin = new google.maps.Point(0, 0);
var sOrigin = new google.maps.Point(28.5, 0);
var size = new google.maps.Size(28.5,34);
var markerNormal = new google.maps.MarkerImage("/assets/marker_sprite.png", size, origin, anchor);
var markerHighlight = new google.maps.MarkerImage("/assets/marker_sprite_bleu.png", size, origin, anchor);
var shadow = new google.maps.MarkerImage("/assets/marker_sprite.png", size, sOrigin, sAnchor);
for(var i = 0; i < len; i++){
m = Gmaps.map.markers[i];
google.maps.event.addListener(m.serviceObject, 'click',
(function(m, markers){
return function(){
console.log(m);
for(a in markers){
markers[a].serviceObject.setIcon(markerNormal);
markers[a].serviceObject.setZIndex(1);
markers[a].serviceObject.shadow = shadow;
}
Gmaps.map.map.setZoom(7);
m.serviceObject.setZIndex(99);
m.serviceObject.setIcon(markerHighlight);
}
}(m, markers))
)
}
}
If you see anything that could be improved feel free to comment :)

Javascript variables not propagating through

I'm using OpenLayers with Waze (Maps layers) and having some problems with variables not propagating.
I've got this code:
var lonlat = new Array(), infodiv = new Array();
for (var i = 0; i < stations.length; i++)
{
if (i == 0)
icon_temp = icon;
else
icon_temp = icon.clone();
lonlat[i] = new OpenLayers.LonLat(stations[i].lon,stations[i].lat);
infodiv[i] = "<div style='font-size: 14px;'><strong>" + stations[i].company + "</strong></div>";
marker = new OpenLayers.Marker(lonlat[i],icon_temp);
marker.setOpacity(0.8);
marker.events.register('mousedown', marker, function(evt) {
popup = new OpenLayers.Popup.FramedCloud(null,
lonlat[i],
null,
infodiv[i],
anchor=null,true,null);
map.addPopup(popup);
OpenLayers.Event.stop(evt);
});
markers.addMarker(marker);
}
The code should iterate through the 'stations' array and add markers to the map. It works just fine!
The problem is with the 'lonlat' and 'infodiv' arrays. The 'OpenLayers.Popup.FramedCloud' doesn't see them - it is returned null (checked using FireBug). If I lose the array and only assign each time lonlat = ... and infodiv = ... like this:
for (var i = 0; i < stations.length; i++)
{
if (i == 0)
icon_temp = icon;
else
icon_temp = icon.clone();
lonlat = new OpenLayers.LonLat(stations[i].lon,stations[i].lat);
infodiv = "<div style='font-size: 14px;'><strong>" + stations[i].company + "</strong></div>";
marker = new OpenLayers.Marker(lonlat,icon_temp);
marker.setOpacity(0.8);
marker.events.register('mousedown', marker, function(evt) {
popup = new OpenLayers.Popup.FramedCloud(null,
lonlat,
null,
infodiv,
anchor=null,true,null);
map.addPopup(popup);
OpenLayers.Event.stop(evt);
});
markers.addMarker(marker);
}
it is being propagated to the FrameCloud function and is being shown - but then the problem is it shows only the last lonlat and infodiv (it's like it doesn't hold a copy of them but holds the actual objects - so every iteration 'lonlat' and 'infodiv' are being replaced by the latest info).
How can this be overcome?
This is due to closure of variable i.
Introduce another scope like,
(function(i){
marker.events.register('mousedown', marker, function(evt) {
popup = new OpenLayers.Popup.FramedCloud(null,
lonlat[i],
null,
infodiv[i],
anchor=null,true,null);
map.addPopup(popup);
OpenLayers.Event.stop(evt);
});
})(i);

Categories