leaflet popup width on markers loaded with JSON - javascript

I have a leaflet map with markers loaded dynamically with geoJSON data:
mapLayers.markets = L.geoJSON(geoData, {
pointToLayer: function (feature, latlng) {
switch (feature.properties.status) {
case "stress":
return L.marker(latlng, { icon: iconA });
case "alert":
return L.marker(latlng, { icon: iconB });
case "crisis":
return L.marker(latlng, { icon: iconC });
}
},
onEachFeature: onEachFeature,
}).addTo(map);
I bind the popup inside the onEachFeature function:
function onEachMarketFeature(feature, layer) {
var tableCommon = '<table>' +
'<thead><tr><th>Agency</th><th>Personnel</th></tr></thead>' +
'<tbody>' +
'<tr><td>Alfa</td><td>12</td></tr>' +
'<tr><td>Bravo</td><td>123</td></tr>' +
'</tbody></table>';
var tableSingle='<table>' +
'<thead><tr><th>Agency</th><th>Personnel</th></tr></thead>' +
'<tbody>' +
'<tr><td>Juliet</td><td>62</td></tr>' +
'<tr><td>Lima</td><td>41</td></tr>' +
'</tbody></table>';
var container = '<div><div class="row"><div class="col-xl-6">' + tableCommon + '</div><div class="col-xl-6">' + tableSingle + '</div></div></div>';
layer.bindPopup(feature.properties.type +
'<br/> Market Code=' + feature.properties.code + '<br/>' + feature.properties.name + '</a><br/>' + container);
}
The problem is that the content inside the popup, defined with row-col classes of bootstrap4, is greater than the popup and this one is not resized to fit it, and results of a smaller width.
I've tried to set in the bindPopup function the {maxWidth: "none"} but it's even worst and the popup is extra small.
This is what I get:
and this is what I'm trying to have:

Related

url.content not loading image javascript and asp.net mvc

I have my javascript in my view and I am trying to load an image from my database into an info window for my google maps but the image does not load.
Javascript code in view:
$.get("#Url.Action("GetMarkers","Donator")", function (json, status) {
//Global infoWindow object that will be reused by all markers
var infoWindow = new google.maps.InfoWindow();
var marker, data;
//Loop through the json data
for (var i = 0; i < json.length; i++) {
data = json[i];
var latLng = new google.maps.LatLng(data.Latitude, data.Longitude)
//Creating marker and putting it on the map
marker = new google.maps.Marker({
position: latLng,
map: map,
title: data.NpoName + ' says Click Me!'
});
// Creating a closure to retain the correct data, notice how I pass the current data in the loop into the closure (marker, data)
(function (marker, data) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker, "click", function (e) {
var imageUrl = '<%=Url.Content(' + data.VerificationIcon + ')%>';
infoWindow.setContent('<h3 class="info-window-title">' + data.NpoName + '</h3>' + '<hr />' + '<p>Npo type: ' + data.NpoType + '</p>' + '<p>' + data.VerificationStatus + '<img src="' + imageUrl + '" alt="verification icon" class="verification-icon" />' + '</p>' + 'Click here to donate!');
infoWindow.open(map, marker);
});
})(marker, data);
};
});
Code in controller:
public JsonResult GetMarkers()
{
//Disable lazy loading
db.Configuration.ProxyCreationEnabled = false;
//Map marker section
var DynamicMapMarkers = db.tblNpo.Include(zz => zz.tblVerification).Include(xx => xx.tblNpo_Type);
//Set map marker values
foreach (var item in DynamicMapMarkers)
{
DonatorUserViewModel MapMarkers = new DonatorUserViewModel();
MapMarkers.NpoID = item.npo_id;
MapMarkers.NpoName = item.npo_name;
MapMarkers.NpoType = item.tblNpo_Type.description;
MapMarkers.VerificationStatus = item.tblVerification.description;
MapMarkers.VerificationIcon = item.tblVerification.verification_icon;
MapMarkers.Longitude = Convert.ToDouble(item.longitude);
MapMarkers.Latitude = Convert.ToDouble(item.latitude);
MapMarkerList.Add(MapMarkers);
}
var json = MapMarkerList;
return Json(json, JsonRequestBehavior.AllowGet);
}
Main focus area from the code above:
var imageUrl = '<%=Url.Content(' + data.VerificationIcon + ')%>';
infoWindow.setContent('<h3 class="info-window-title">' + data.NpoName + '</h3>' + '<hr />' + '<p>Npo type: ' + data.NpoType + '</p>' + '<p>' + data.VerificationStatus + '<img src="' + imageUrl + '" alt="verification icon" class="verification-icon" />' + '</p>' + 'Click here to donate!');
The format of the way the image is stored in the database ~/Images/DbImages/verified.png
I am not sure why the image isn't loading, or how to work around it. Any help would be appreciated.

Leaflet: .bindPopup with layer groups opens popup in the same spot for all features

I'm building a Leaflet map of recent earthquakes that pulls from three APIs to create three layer groups on a map. I'm also building a sidebar with a list of earthquakes from just one of the three API links. When a user clicks on a map marker, a popup appears with more information about the quake. And when they click on a list item in the sidebar, I'd like for the popup for the corresponding quake to open, the same as if the user had clicked on the marker.
The popups work fine when the user clicks on the map markers. However, when I click on the list item, the popups with the correct information do open, but they all appear in the same place, and not at the quake's correct lat/lng.
I tried using FeatureGroup instead of LayerGroup, as suggestedhere, but the same thing happens. I also tried adding the popup to a specific layer with addTo(layers[key]);, but this changes nothing, either.
Here is the relevant code:
URLs = {
PAST_MONTH: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson",
PAST_WEEK: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_week.geojson",
PAST_DAY: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson",
};
Object.entries(URLs).forEach(([key, url]) => {
d3.json(url).then(function (response){
//Add features to the map.
response.features.forEach(feature => {
var calif = feature.properties.title.split(",");
if (calif[1] === " CA") {
console.log(feature.properties.title);
quake = L.geoJSON(feature, {
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, {
radius: getRadius(feature.properties.mag),
fillColor: getColor(feature.properties.mag),
color: getColor(feature.properties.mag),
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
},
});
//populate the sidebar with JUST the list of earthquakes in the past week
if (url === URLs.PAST_WEEK) {
var list = document.getElementsByClassName("quake-list");
var listItem = document.createElement("li");
listItem.innerHTML = feature.properties.title + " " + moment(feature.properties.time).startOf('day').fromNow();
list[0].appendChild(listItem);
listItem.addEventListener('click', function() {
quake.bindPopup("<h3>" + feature.properties.title + "</h3><hr>" +
"<strong>Magnitude: </strong>" + feature.properties.mag +
"<br><strong>Date and time: </strong>" + convertUnixTime(feature.properties.time))
quake.openPopup();
})
}
// Add popup with info, when user clicks on map markers
quake.bindPopup("<h3>" + feature.properties.title + "</h3><hr>" +
"<strong>Magnitude: </strong>" + feature.properties.mag +
"<br><strong>Date and time: </strong>" + convertUnixTime(feature.properties.time));
// Add to specific layer
quake.addTo(layers[key]);
};
});
});
});
And this is the specific part where I can't figure out how to bind the popups to the list items so that they appear at the correct lat/lng:
//populate the sidebar with JUST the list of earthquakes in the past week
if (url === URLs.PAST_WEEK) {
var list = document.getElementsByClassName("quake-list");
var listItem = document.createElement("li");
listItem.innerHTML = feature.properties.title + " " + moment(feature.properties.time).startOf('day').fromNow();
list[0].appendChild(listItem);
listItem.addEventListener('click', function() {
quake.bindPopup("<h3>" + feature.properties.title + "</h3><hr>" +
"<strong>Magnitude: </strong>" + feature.properties.mag +
"<br><strong>Date and time: </strong>" + convertUnixTime(feature.properties.time))
quake.openPopup();
})
}
I'm new to Leaflet and would appreciate any help. I'm sure I'm missing something obvious but I've been racking my brain trying to see what I'm missing. Thank you!
Instead adding the popup to the layergroup bind the popup to each layer.
if (url === URLs.PAST_WEEK) {
var list = document.getElementsByClassName("quake-list");
var listItem = document.createElement("li");
listItem.innerHTML = feature.properties.title + " " + moment(feature.properties.time).startOf('day').fromNow();
list[0].appendChild(listItem);
listItem.addEventListener('click', function() {
quake.eachLayer(function(layer){
layer.bindPopup("<h3>" + feature.properties.title + "</h3><hr>" +
"<strong>Magnitude: </strong>" + feature.properties.mag +
"<br><strong>Date and time: </strong>" + convertUnixTime(feature.properties.time),
{autoClose: false})
layer.openPopup();
});
})
}
The problem turns out to be an issue with scope for the "quake" variable. This code works:
var quake = L.geoJSON(feature, {
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, {
radius: getRadius(feature.properties.mag),
fillColor: getColor(feature.properties.mag),
color: getColor(feature.properties.mag),
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
},
});

Change Mapbox marker image on click

I'm currently using a png as markers but I would like to change the one that get clicked on & active to a different png.
How do I achieve that?
var icon = [
{
"className": "my-icon icon-dc", // class name to style
"html": '<img class="style-svg" src="wp-content/uploads/2016/06/circle.svg" alt="">', // add content inside the marker
"iconSize": [60,60] // size of icon, use null to set the size in CSS
}
];
myLayer.on('layeradd', function(e) {
var marker = e.layer,
feature = e.layer.feature;
marker.setIcon(L.divIcon(feature.properties.icon));
});
// Listen for individual marker clicks.
myLayer.on('click',function(e) {
// Force the popup closed.
e.layer.closePopup();
var feature = e.layer.feature;
var content = '<div><div id="close1" class="absolute_p"><img class="exit" src="/wp-content/uploads/2016/06/X#2x.png"></div>' +
'<img src="' + feature.properties.img + '" alt="">' +
'<h4>' + feature.properties.title + '</h4>' +
'<p>' + feature.properties.description + '</p>' +
'<p class="num">' + feature.properties.num + 'Properties now showing</p>' +
'find your second home here</div>';
info.innerHTML = content;
map.panTo(e.layer.getLatLng());
});
At the end of your click event handling, why not just use setIcon on the e.layer with a reference to an "active"/"clicked" icon you declare somewhere else?
var myNewIcon = L.icon({
iconUrl: 'my-new-icon.png'
});
e.layer.setIcon(myNewIcon)
http://leafletjs.com/reference.html#icon

Prevent plotting scaled point with zero value, Mapbox, leaflet

I'm making a bubble map with Mapbox js.
The problem is: I'm trying to stop points appearing if they are below a certain value. Currently values of 0 appear.
The function that sets the points looks like this. I've commented out an if statement that was unsuccessful.
function layer(selecta){
geoJson = L.geoJson(geoJsonData, {
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, {
color: '#c10000',
// if (parseInt(feature.properties[selecta]) < 50000){
radius: (Math.sqrt(feature.properties[selecta] / 3.141592) / 50)
// }
}).bindPopup('<h3>' + feature.properties.country + '</h3><p>' + 'Refugees, asylum seekers and IDPs in ' + currentYear + ': ' + numeral(feature.properties['yr' + currentYear]).format('0,0') + '</p>',{
closeButton: true,
minWidth: 320
});
}
}).addTo(map);
};
layer('yr2013');
The data is loaded from a geoJSON file, which is an array of objects.
Every object has properties for each year.
Eg.
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
64.585262,
41.377491
]
},
"properties":{
"country":"Uzbekistan",
"yr2000":"39598",
"yr2001":"40923",
"yr2002":"46014",
"yr2003":"45653",
"yr2004":"44932",
"yr2005":"44537",
"yr2006":"1422",
"yr2007":"1060",
"yr2008":"821",
"yr2009":"555",
"yr2010":"311",
"yr2011":"214",
//"yr2012":"176",
//"yr2013":"138"
}
},
If I get rid of the values that have a value below 200, like the above example, this returns an error in the console. The points still display, but it's a massive hack.
Can someone please tell me a cleaner way of doing this?
X EDIT X
I never did find a solution, but I ended up finding a work-around.
Leaflet has CircleMarker options opacity and fillOpacity.
Here's the docs
So I made a function that returns zero if the value is too small, so that the marker will not appear.
It is still technically there though.
Have you tried using Mapbox.js's .setFilter()? It should remove the features you don't want rather than just making them transparent.
https://www.mapbox.com/mapbox.js/example/v1.0.0/markers-with-multiple-filters/
How about this:
function layer(selecta){
geoJson = L.geoJson(geoJsonData, {
pointToLayer: function(feature, latlng) {
//I suppose this is an integer
var number_refugees = numeral(feature.properties['yr' + currentYear]);
if(number_refugees > 0){
var popup_content = '<h3>' + feature.properties.country + '</h3>'
+ '<p>' + 'Refugees, asylum seekers and IDPs in ' + currentYear + ': '
+ number_refugees.format('0,0') + '</p>';
return L.circleMarker(latlng, {
color: '#c10000',
radius: (Math.sqrt(feature.properties[selecta] / 3.141592) / 50)
}).bindPopup(popup_content),{
closeButton: true,
minWidth: 320
});
}
}
}).addTo(map);
};
layer('yr2013');

Marker click event always opens to the last pushed infowindow

I have a json object infoCentros which I use to construct the map, like this:
for ( var i = 0; i < infoCentros.length; i++ ) {
var centro = infoCentros[i];
var lat = centro.cordenadas.lat;
var lon = centro.cordenadas.long;
if (lat && lon) {
c++;
latlon = new google.maps.LatLng(lat, lon);
var moptions = {
position: latlon,
map: $project.gmap
}
moptions.icon = theme_uri + '/images/marker.png';
var marker = new google.maps.Marker(moptions);
$project.mapMarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
$project.mapInfoWindow.setContent(
'<div class="sescam-info-window">' +
'<h3>' + centro.nombre + '</h3>' +
'<p>' + centro.lugar + '</h3>' +
'<p>Coordinador</p>' +
'<p>' + centro.coordinador.nombre + '</p>' +
'<p>' + centro.coordinador.email + '</p>' +
'<p>Responsable</p>' +
'<p>' + centro.responsable.nombre + '</p>' +
'<p>' + centro.responsable.email + '</p>'
+ '</div>'
);
$project.mapInfoWindow.open($project.gmap, marker);
});
$project.mapBounds.extend(latlon);
}
}
It seems to work fine, but if I have 5 markers, It doesn't matter which one I click, the infowindow always corresponds to the last item (position and content),
Any idea what am I missing? I thought that passing marker to the addListener would Do the trick..
Problem is with third argument of google.maps.event.addListener. That anonymous function contains the variable from the parent scope which when evaluated with marker click will always take the last value assigned. Look for closure for more details. However you may able to get the desired behaviour by using "bind" feature of javascript function prototype in below manner:
//inside for loop
google.maps.event.addListener(marker, 'click', handleMarkerClick.bind(undefined, marker, i));
//other codes if any...
defining handleClick
function handleMarkerClick(marker, index) {
if (typeof infowindow === 'undefined') {
infowindow = new google.maps.InfoWindow({});
}
var data = infoCentros[index]//helpful data
//create content with dynamic data
infowindow.setContent("dynamic content");
infowindow.open(marker.getMap(), marker);//modify as per your requirement
}
Duncan is right, he means you have to attach the listener outside of your for loop. It's not specific to Google Maps, it's how javascript works. Try searching for "javascript closures for loop" or visit this link - Closures in Javascript for a simple explanation.

Categories