How to add permanent labels to each feature in a geoJSON layer? - leaflet

I have some geoJSON objects brought in from a database and want to display them on a map using Leaflet.
As I have multiple objects visible with the same style icon/marker I don't know which is which until I click on each one at the moment.
I have working code to pop up a box with full information when I click on each object.
What I really want is to have each item with a label visible all the time so I know which one to click on to get more information.
I can see from the .bindTooltip method how to get a fixed label, but when I run my code, only the first object from the geoJSON file gets the label.
I can't find any examples of anyone doing anything similar - any maps with multiple labels are created from manually created points.
The code segment in particular looks like this:
// This shows correct geoJSON name but shows fixed label only for 1 (first) geoJSON object!
function fixedLabel(layer) {
return layer.feature.properties.name
};
function clickLabel (feature,layer) {
layer.bindPopup("ID: " + feature.properties.id + "<br>Name: " + feature.properties.name + "<br>DateTime: " + feature.properties.vdatetime + "<br>Speed: " + feature.properties.speedknots + " knots<br>CMG: " + feature.properties.cmg + "°");
};
var vesselsLayer = L.geoJson(vessels,{
onEachFeature: clickLabel
})
.addTo(map).bindTooltip(fixedLabel, {permanent: true, direction: 'right'}).openTooltip();
map.fitBounds(vesselsLayer.getBounds());
The full code & geoJSON code are in github here:
https://github.com/DPB61/leafletjs_test01
Am I going completely the wrong way about getting labels on each object?

You are binding the tooltip to the GeoJSON layer, not to each individual layer contained within it. Try something like this:
L.geoJson(vessels,{
onEachFeature: function (feature, layer) {
layer.bindPopup("ID: " + feature.properties.id + "<br>Name: " + feature.properties.name + "<br>DateTime: " + feature.properties.vdatetime + "<br>Speed: " + feature.properties.speedknots + " knots<br>CMG: " + feature.properties.cmg + "°");
layer.bindTooltip(feature.properties.name, {permanent: true, direction: 'right'}).openTooltip();
}
}).addTo(map)

Related

How to add a tooltip to a selection?

When the users selects some text, I want to be able to show a tooltip, right below the selected text?
Any ideas how can I do that?
You could add a component that creates the tooltip, such as paper-tooltip, or create one, even with css only, depends on your usecase.
Here is a W3 example of a CSS tooltip
As far as I can tell, react-draft-wysiwyg does not support arbitrary plugins in the same way that draft-js-plugins does.
Searching on NPM, the only text selection related plugin I found is draft-js-delete-selection-plugin. You could use that as a starting point, as well as look at the documentation for SelectionState.
Without any idea of what you have so far it is hard to provide more info. I have created a JS fiddle that shows a simple tool tip with an event listener that gets the selected text by element id
https://jsfiddle.net/03Lu28qb/1/
$(document).ready(function () {
const textSelectionTooltipContainer = document.createElement("div");
textSelectionTooltipContainer.setAttribute(
"id",
"textSelectionTooltipContainer"
);
textSelectionTooltipContainer.innerHTML = `<p id="textSelected">Selected! </p>`;
const bodyElement = document.getElementsByTagName("BODY")[0];
bodyElement.addEventListener("mouseup", function (e) {
var textu = document.getSelection().toString();
if (!textu.length) {
textSelectionTooltipContainer.remove();
}
});
document
.getElementById("textToSelect")
.addEventListener("mouseup", function (e) {
let textu = document.getSelection().toString();
let matchu = /\r|\n/.exec(textu);
if (textu.length && !matchu) {
let range = document.getSelection().getRangeAt(0);
rect = range.getBoundingClientRect();
scrollPosition = $(window).scrollTop();
containerTop = scrollPosition + rect.top - 50 + "px";
containerLeft = rect.left + rect.width / 2 - 50 + "px";
textSelectionTooltipContainer.style.transform =
"translate3d(" + containerLeft + "," + containerTop + "," + "0px)";
bodyElement.appendChild(textSelectionTooltipContainer);
}
});
});
If you trying to do it in react try this.
If you trying to do it in js try this.

Material-UI Table pagination translation

I'm trying to translate the table pagination of a material ui table. The guy from MUI said it's somehow possible (issue).
So does anybody knows how to translate to of from the displayed rows label ${from}-${to} of ${count}
You can set 'labelDisplayedRows' of TablePagination
<TablePagination
...
labelDisplayedRows={
({ from, to, count }) => {
return '' + from + '-' + to + ' из ' + count
}
}
/>
More properties in docs: https://material-ui.com/ru/api/table-pagination/

Leaflet map completely grey programmatically opening a popup tofa marker

I declare a leaflet map with
<div id="map" class="map-div"></div>
end initialize it with
var map = L.map('map').setView([51.178882, -1.826215],16);
$scope.map = map;
// OSM Mapnik
var osmUrl = "<a href='http://www.openstreetmap.org'>Open StreetMap</a>";
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + osmUrl,
maxZoom: 18,
}).addTo(map);
I grab some data from my server, and and markers to the map, in a loop, by calling this function (it's AngularJS, but I doubt that that plays a role):
$scope.AddMarkerToMap = function(companyData, index, array)
{
var companyName = companyData.company_name;
var latitude = companyData.latitude;
var longitude = companyData.longitude;
var cssClassname = 'comapny_has_no_present_workers';
if (companyData['currentWorkers'] > 0)
cssClassname = 'comapny_has_present_workers';
var pubLatLng = L.latLng(latitude,longitude);
// see https://leafletjs.com/reference-1.4.0.html#marker
var marker = L.marker(pubLatLng,
{
// this is the tooltip hover stuff
title: companyData['currentWorkers'] + ' current matches ' + companyData['previousWorkers'] + ' previous matches',
// see https://leafletjs.com/reference-1.4.0.html#icon
// this is a permanent label.
icon: new L.DivIcon({
className: cssClassname,
////html: '<img class="my-div-image" src="http://png-3.vector.me/files/images/4/0/402272/aiga_air_transportation_bg_thumb"/>'+
//// '<span class="my-div-span">RAF Banff Airfield</span>'
html: '<span>' + companyName + '</span>'
})
}).addTo($scope.map);
// see https://leafletjs.com/reference-1.4.0.html#popup
marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
}; // AddMarkerToMap()
And the entire map is suddenly grey - with no problems reported in the developer console.
If I comment out the line
marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
then everything displays as expected.
The code seems correct, as per the Leaflet documentation.
[Updtae] I just checked and if I only marker.bindPopup("<b>Hello world!</b><br>I am a popup."), the the map displays and I can click on the marker to display the popup. But when I try to programmatically open it with .openPopup(); the map is all grey.
[Update++] the map and its markers display just fine, with any one of
marker.bindPopup("<b>Hello world!</b><br>I am a popup.");
$scope.map.fitBounds(bounds, {padding: [50, 50]});
but with both, the map is grey :-(
What am I doing wrongly?
I think the issue comes from trying to change the map view (possibly through openPopup with autoPan, which is on by default) too often, typically in a loop without giving any delay for the map to actually set the view between each call.
IIRC, this is already identified as a limitation in Leaflet, but I could not find the exact thread in the issue tracker unfortunately.
Normally, a very simple fix is simply to remove the map view changes within your loop, and keep only the very last one.
In your case, if you have the default behaviour of only 1 Popup being opened at a time, then that would definitely be a valid solution: just open the popup of your last Marker.
If you did configure your map to keep several Popups open simultaneously, and you do want to open all of them through your loop, then make sure to disable autoPan (at least during your loop).

Leaflet - only one tile is loaded [duplicate]

I have a problem with Leaflet that actually holds up my whole work. For some reasons I can not explain, the UI of Leaflet is correctly loaded in my Intel XDK app, but there is only one map tile loaded - the same code works in another test app! Now, that I tried everything I could do, I hope that someone here can solve my problem.
For better understanding, here is the code in my leaflet.js (it isn't the leaflet.js, because I'm using the leaflet-src.js as script) and a screenshot of the map window of the app.
function initLeaflet() {
document.getElementById("map").setAttribute("style", "height:" + window.innerHeight + "px; width:" + window.innerWidth + "px;");
var map = L.map('map');
L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'examples.map-i875mjb7'
}).addTo(map);
map.on('locationfound', onLocationFound);
map.on('locationerror', onLocationError);
map.locate({setView: true, maxZoom: 16});
map.on('click', onMapClick);
}
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map)
.bindPopup("Position: " + e.latlng + " Genauigkeit " + radius ).openPopup();
L.circle(e.latlng, radius).addTo(map);
}
function onLocationError(e) {
alert(e.message);
}
function onMapClick(e) {
marker = new L.marker(e.latlng, {id:uni, icon:redIcon, draggable:'true'});
marker.on('dragend', function(event){
var marker = event.target;
var position = marker.getLatLng();
alert(position);
marker.setLatLng([position],{id:uni,draggable:'true'}).bindPopup(position).update();
});
map.addLayer(marker);
}
//var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
//x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
//x.innerHTML = "Latitude: " + position.coords.latitude +
//"<br>Longitude: " + position.coords.longitude;
}
http://imgur.com/exOUZuT
I would guess that the size of the map upon initialization is the culprit.
Leaflet needs to know the size of the element it is embedded in when initializing. Leaflet uses that information to know how much tiles to load etc. Furthermore any programmatic changes (or changes that cannot be easily detected by Leaflet) to the size of the map have to be followed by map.invalidateSize(..) link.
I suspect that after you set the size, Leaflet fails to read properly the new size of the #map element. Try invalidating the size afterwards or run initialization asynchronously. I would add:
setTimeout(function () {
map.invalidateSize();
}, 0);
and check if it gets any better.
I used that command to fix my missing tiles problem:
map.getSize();
Look like Leaflet needs to know the size of the element map in advance as Michal said.

leafletjs - move popup to a side panel

I've been playing around with popups, in conjunction with a geojson wrapped inside JavaScript and have mastered what I need to do on the bindpopup front. Now I'd like to effectively unbind the popup from its marker and get the popup to appear either in a side panel or below the map in its own div.
This is the code for my current popup and I'm guessing that I need to change my code around the area of layer.bindPopup(popupContent) and reference it to its own div?
<script>
var map = L.map('map').setView([51.4946, -0.7235], 11)
var basemap =
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'OSM data',
}).addTo(map);
function onEachFeature(feature, layer) {
var popupContent = "<b>" + feature.properties.ward_names +
" </b><br>Population 2011 = <b>" + feature.properties.census_11 +
" </b><br>Population 2001 = <b>"+ feature.properties.census_01 +
" </b><br>You can find out more about population data on the <a href='http://www.somewhere.com' target='_blank'>somewhere.com</a> website ";
if (feature.properties && feature.properties.popupContent) {
popupContent += feature.properties;
}
layer.bindPopup(popupContent);
}
L.geoJson([wardData], {
style: function (feature) {
return { weight: 1.5, color: "#000000", opacity: 1, fillOpacity: 0 };
return feature.properties;
},
onEachFeature: onEachFeature,
}).addTo(map);
</script>
However I'm not really sure how to do this and need some guidance.
Answer shared under another question, https://gis.stackexchange.com/a/144501/44746
If you're looking for populating another element entirely that you've
defined outside of the map, then you actually don't need even the
whole info control. You can just as easily do what you need in the
onEachFeature >> layer.on >> click part too.
function onEachFeature(feature, layer) {
layer.on({
click: function populate() {
document.getElementById('externaldiv').innerHTML = "BLAH BLAH BLAH " + feature.properties.name + "<br>" + feature.properties.description;
}
}); }
In your html body, you just have to make sure your <div id="externaldiv"> or whatever is placed where you want it.
This demo populates an external when the user clicks on a map
feature : http://labs.easyblog.it/maps/leaflet-geojson-list/   (Edit: link reported broken as of dec 2022)
The L.Control class is the appropriate tool for what you want to do.
I suggest you follow this tutorial, it will give you a quick understanding of what you can do with it.