Use marker icon with only awesome fonts, no surrounding balloon - leaflet

I have this code that works fine, but I need to get only the icon to show and not the "balloon" with its shadow.
I have tried with removing "markerColor..." but that is only changing to the default blue marker / balloon.
How to only show the icon and its size and color?
pointToLayer: function(feature, latlng) {
var con = feature.properties.concept;
var hub;
// icons for XX, YY and ZZ
if (kon === 'XX') {
hub = new L.marker(latlng, {icon: L.AwesomeMarkers.icon({icon: 'truck', prefix: 'fa', markerColor: 'cadetblue'}) });
} else if (kon === 'YY') {
hub = new L.marker(latlng, {icon: L.AwesomeMarkers.icon({icon: 'envelope', prefix: 'fa', markerColor: 'blue'}) });
} else if (kon === 'ZZ') {
hub = new L.marker(latlng, {icon: L.AwesomeMarkers.icon({icon: 'bicycle', prefix: 'fa', markerColor: 'darkblue'}) });
} else {
hub = new L.marker(latlng, {icon: L.AwesomeMarkers.icon({icon: 'envelope-o', prefix: 'fa', markerColor: 'red'}) });
}
return hub;
}

Unfortunately, Leaflet.awesome-markers plugin does not offer you the option to display only the inner icon (from Font Awesome or whatever source) without the surrounding balloon.
Same for its cloned version and other variations like Leaflet.extra-markers plugin.
But you can very simply use a Leaflet DivIcon instead:
Represents a lightweight icon for markers that uses a simple <div> element instead of an image. Inherits from Icon but ignores the iconUrl and shadow options.
Then you simply fill that <div> container with your Font Awesome icon, the same way you would do it in a normal page, and what the Leaflet.awesome-markers plugin does under the hood for you:
L.marker(latlng, {
icon: L.divIcon({
html: '<i class="fa fa-truck" style="color: red"></i>',
iconSize: [20, 20],
className: 'myDivIcon'
})
});
Note that you also have to specify a bit of CSS to customize it as you wish:
.myDivIcon {
text-align: center; /* Horizontally center the text (icon) */
line-height: 20px; /* Vertically center the text (icon) */
}
Example:
var map = L.map('map').setView([48.86, 2.35], 11);
L.marker([48.86, 2.35], {
icon: L.divIcon({
html: '<i class="fa fa-truck" style="color: red"></i>',
iconSize: [20, 20],
className: 'myDivIcon'
})
}).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
.myDivIcon {
text-align: center; /* Horizontally center the text (icon) */
line-height: 20px; /* Vertically center the text (icon) */
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet.awesome-markers#2.0.4/dist/leaflet.awesome-markers.css" />
<script src="https://unpkg.com/leaflet.awesome-markers#2.0.4/dist/leaflet.awesome-markers.js"></script>
<link href="https://use.fontawesome.com/releases/v5.0.8/css/all.css" rel="stylesheet">
<div id="map" style="height: 180px"></div>

Related

Leaflet small divIcons less than 14px do not align at center of point

I want to add some very small custom divIcon markers to a Leaflet (v1.9.3) map. However, it seems icons smaller than [14,14] do not align at the center of the marker. Following image shows three markers at same location with a circle SVG as the divIcon and radii of 50,20,6. Notice the first two center but the 6px one is off center:
Some control can be had with iconAnchor but seems the value would need to vary depending on iconSize and the computation isn't obvious to me. Is there a better way to center align small divIcons like above?
Code for above image:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.9.3/dist/leaflet.css" integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.9.3/dist/leaflet.js" integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
<style type='text/css'>
html, body { margin: 0; padding: 0; border: 0; }
#map { height: 100vh; }
</style>
<script>
var map;
var myIcon = (color = 'red', b = 24) => `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${b} ${b}">
<circle fill="${color}" cx="${b/2}" cy="${b/2}" r="${b/2}" />
</svg>`;
function init() {
var map = L.map('map').setView([50.0, 0.0], 13);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
var marker = L.marker(
[51.5, -0.09], {
icon: L.divIcon({
html: myIcon('red', 50),
iconSize: [50,50],
className: ''
}),
}).addTo(map);
var marker = L.marker(
[51.5, -0.09], {
icon: L.divIcon({
html: myIcon('green', 20),
iconSize: [20,20],
className: ''
}),
}).addTo(map);
var marker = L.marker(
[51.5, -0.09], {
icon: L.divIcon({
html: myIcon('blue', 6),
iconSize: [6,6],
className: ''
}),
}).addTo(map);
map.panTo(marker.getLatLng());
}
</script>
</head>
<body onload='init();'>
<div id="map"></div>
</body>
</html>
Solution: set style="display: block" on the <svg> element. <svg> elements are use display: inline by default which uses some flow layouting algorithm.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" style="display: block">
<circle fill="blue" cx="3" cy="3" r="3"></circle>
</svg>

How to dynamically adjust symbology based on user generated table

I have created a simple leaflet web app that allows for the user to search for the countries that they have traveled.
The user can use a search box or click on the layer on the map. either action will highlight the country green and load the name and flag in an adjacent table.
This works fine. However, problems arise should the user decide to remove a country visited (for example they made a mistake when selecting)
I have included the ability to delete the table items by user click and enabled a function on double click of the layer itself to reset the colour.
What I need is for these two actions to relate to one another. so if the user removes the flag, the related country is reset and vis versa.
I think I need some sort of listener on the click remove or highlight function or perhaps defining the symbology based on the user-generated table of countries visited. Any suggestions?
<!DOCTYPE html>
<html>
{% load static %}
{% load leaflet_tags %}
<head>
{% leaflet_js %}
{% leaflet_css %}
<script type="text/javascript" src="{% static 'dist/leaflet.ajax.js' %}"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>hello world</title>
<link rel="stylesheet" href="{% static 'leaflet-groupedlayercontrol/leaflet.groupedlayercontrol.min.css' %}" />
<link rel="stylesheet" href="https://labs.easyblog.it/maps/leaflet-search/src/leaflet-search.css" />
<link rel="stylesheet" href="https://labs.easyblog.it/maps/leaflet-search/examples/style.css" />
<style type="text/css">
#findbox {
background: #eee;
border-radius: 0.125em;
border: 2px solid #1978cf;
box-shadow: 0 0 8px #999;
margin-bottom: 10px;
padding: 2px 0;
width: 600px;
height: 26px;
}
.search-tooltip {
width: 200px;
}
.leaflet-control-search .search-cancel {
position: static;
float: left;
margin-left: -22px;
}
#map {
width: 50%;
height: 650px;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<p style="text-align: center; font-size: 24px; color: #175b81; font-weight: bold;"></p>
<h3>My World Map</h3>
<div id="findbox"></div>
<div id="map" style="height: 680px; width: 48%; display: ;"></div>
<div class="sidebar" style="display: right;">
<p style="text-align: centre; font-size: 24px; color: #175b81; font-weight: bold;">My Flags:</p>
<table style="width: 90%;">
<tr>
<td id="name" hidden></td>
<td id="code" hidden></td>
</tr>
<ul id="Countries_Visted"></ul>
</table>
</div>
<script>
var img1 = "<img src=";
var im2 = ">";
var quotes = "'";
var flagimage_pt1 = "https://www.countryflags.io/";
var flagimage_pt2 = "/flat/64.png";
// creating mapbox basemap
var mapboxAccessToken =
"pk.eyJ1IjoiY2hyaXNyeWFuLXRlY2giLCJhIjoiY2thY2c3bDZyMDZsNDJ4cHJlZmhwZmFjaCJ9.3GuHRvRz-8fxi4r103z05w";
var mapbox = L.map("map").setView([46.0, 8.0], 2.2);
mapbox.doubleClickZoom.disable();
L.tileLayer(
"https://api.mapbox.com/styles/v1/chrisryan-tech/ckaxvc0bt10041iqfz5fb7dgj/tiles/256/{z}/{x}/{y}#2x?access_token=" +
mapboxAccessToken,
{
tileSize: 512,
zoomOffset: -1,
attribution:
'© Mapbox © OpenStreetMap',
}
).addTo(mapbox);
//loading data with style, click function and listener
var data_layer = new L.GeoJSON.AJAX("{% url 'borders_data' %}", {
style: countriesStyle,
onEachFeature: countriesOnEachFeature,
});
//listener - adding to map on load
data_layer.on("data:loaded", function (e) {
layer.addTo(mapbox);
});
function countriesStyle(feature) {
return {
fillColor: "grey",
weight: 2,
opacity: 0.2,
color: "grey",
dashArray: 3,
fillOpacity: 0.2,
};
}
function countriesOnEachFeature(feature, layer) {
layer.on({
click: function (e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: "#fff",
dashArray: "",
fillOpacity: 0.9,
fillColor: "green",
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
},
});
layer.on({
click: load_name_flag,
});
layer.on({
dblclick: clear,
});
}
///// clearing styling
var layer = data_layer;
function clear(e) {
layer.resetStyle(e.target);
}
///// adding to clicked countries to countries visted list
function load_name_flag(e) {
var field1 = document.getElementById("name");
field1.innerHTML = e.target.feature.properties.name;
var field2 = document.getElementById("code");
field2.innerHTML = e.target.feature.properties.iso2;
var selected_name_search = field1.innerHTML;
var selected_flag_search = img1 + quotes + flagimage_pt1 + field2.innerHTML + flagimage_pt2 + quotes + im2;
var selected_features_combine = selected_name_search + selected_flag_search;
$("#Countries_Visted").append("<li>" + selected_features_combine + "</li>").disabled = true;
}
</script>
<!- search box functionality - searches attribute data -->
<!- contained within Geojson from loaded event listener -->
<script src="https://unpkg.com/leaflet#1.3.0/dist/leaflet.js"></script>
<script src="https://labs.easyblog.it/maps/leaflet-search/src/leaflet-search.js"></script>
<script>
//pointing search function to map layer
var searchControl = new L.Control.Search({
container: "findbox",
layer: data_layer,
initial: false,
collapsed: false,
propertyName: "name",
marker: false,
});
// search box finds layer and adds country name and flag, changes layer symbol to green
searchControl.on("search:locationfound", function (e) {
var field1 = document.getElementById("name");
field1.innerHTML = e.layer.feature.properties.name;
var field2 = document.getElementById("code");
field2.innerHTML = e.layer.feature.properties.iso2;
var selected_name_search = field1.innerHTML;
var selected_flag_search = img1 + quotes + flagimage_pt1 + field2.innerHTML + flagimage_pt2 + quotes + im2;
var selected_features_combine = selected_name_search + selected_flag_search;
// styles for selected country
e.layer.setStyle({ fillColor: "green", color: "black", fillOpacity: 0.8 });
/// adding name and flag to countriesvisted list from searchbox select
$("#Countries_Visted").append("<li>" + selected_features_combine + "</li>").disabled = true;
});
mapbox.addControl(searchControl); //inizialize search control
// function to remove added layers
Countries_Visted.onclick = function remove(e) {
var li = e.target;
var layer = data_layer;
var listItems = document.querySelectorAll("li");
var ul = document.getElementById("ul");
li.parentNode.removeChild(li);
layer.resetStyle(e.target);
};
</script>
</body>
</html>

Measuring drawn line length with turf.js and mapbox

I'm trying to build out the functionality to measure a line that a user draws over a Mapbox map, using turf.js 'length' feature.
That said, I'm a coding newb - I know just barely enough to be dangerous.
Ultimately I'd like to be able to draw both areas and lines, and have their respective measures returned (area for polygons, lengths for line strings).
Can anyone offer insight as to why this code doesn't work?
https://jsfiddle.net/knxwu342
<html>
<head>
<meta charset=utf-8 />
<title>Line Test</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
.calculation-box-length {
height: 75px;
width: 100px;
position: absolute;
bottom: 40px;
left: 10px;
background-color: rgba(255, 255, 255, .9);
padding: 15px;
text-align: center;
}
</style>
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v3.0.11/turf.min.js'>
</script>
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.css' type='text/css'/>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.js'>
</script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.css' rel='stylesheet' />
</head>
<body>
<div id='map'></div>
<div class='calculation-box-length'>
<p>Length:</p>
<div id='calculated-length'></div>
</div>
<nav id="menu"></nav>
<script>mapboxgl.accessToken = 'pk.eyJ1IjoibWJsYWNrbGluIiwiYSI6ImNqaWMxcGk2MzAwd3YzbG1oeW4yOHppdnYifQ.xdb-2slu5LapzpuMCiKzQQ';
//*********-----------------------------------------------------------------------------------------------------------------**********
//Create new map object
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mblacklin/cjii8o7w91g9o2stcktaeixai', // stylesheet location
center: [-117.572737, 51.746916], // starting position [lng, lat]
zoom: 16 // starting zoom
});
//*********-----------------------------------------------------------------------------------------------------------------**********
// Add navigation controls to the map
map.addControl(new mapboxgl.NavigationControl());
//*********-----------------------------------------------------------------------------------------------------------------**********
// Add the ability to draw geometries and display their measurements
//
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
line_string: true,
polygon: true,
trash: true
}
});
map.addControl(draw);
map.on('draw.create.', updateLength);
map.on('draw.delete', updateLength);
map.on('draw.update', updateLength);
function updateLength(e) {
var data = draw.getAll();
var answer = document.getElementById('calculated-length');
if (data.features.length > 0) {
var length = turf.length(data);
// restrict to area to 2 decimal points
answer.innerHTML = '<p><strong>' + length + '</strong></p><p> meters</p>';
} else {
answer.innerHTML = '';
if (e.type !== 'draw.delete') alert("Use the draw tools in the upper right to calculate a distance");
}
};
//
//*********-----------------------------------------------------------------------------------------------------------------**********
</script>
</body>
</html>
I see two little problems in your code:
There is a typo in your 'draw.create' listener. Just remove the point after create:
map.on('draw.create', updateLength);
The version of Turf you are using is too old and does not seem to have the length function. Try using the most recent one: https://npmcdn.com/#turf/turf/turf.min.js

Mapbox GL JS: Change a map's style without hiding map layer?

I am using Mapbox GL JS and I would like to allow users to change a map's background style from streets to satellite, while showing a polygon layer above the map background.
I have adapted the Mapbox example, but I can't work out how to stop map.setStyle from setting the new style above (and thus hiding) my polygon layer. I would like it to change without hiding the polygon layer.
Before switching layers:
After switching layers - polygon overlay missing, would like to carry on showing it:
This is my code in full:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.33.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.33.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<style>
#menu {
position: absolute;
background: #fff;
padding: 10px;
font-family: 'Open Sans', sans-serif;
}
</style>
<div id='map'></div>
<div id='menu'>
<input id='basic' type='radio' name='rtoggle' value='basic' checked='checked'>
<label for='basic'>basic</label>
<input id='satellite' type='radio' name='rtoggle' value='satellite'>
<label for='satellite'>satellite</label>
</div>
<script>
mapboxgl.accessToken = 'pk.eyTOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/basic-v9',
zoom: 5,
center: [-3.0, 54.6]
});
map.on('load', function () {
map.addSource('xxx', {
type: 'vector',
url: 'mapbox://xxx.xxx'
});
map.addLayer({
'id': 'xxx',
'source': 'xxx',
'type': 'fill',
'paint': {
'fill-color': 'red'
},
'source-layer': mylayer
}, 'road'); // Note that I'd like this to display above the OSM 'roads' layer
var layerList = document.getElementById('menu');
var inputs = layerList.getElementsByTagName('input');
function switchLayer(layer) {
var layerId = layer.target.id;
map.setStyle('mapbox://styles/mapbox/' + layerId + '-v9');
}
for (var i = 0; i < inputs.length; i++) {
inputs[i].onclick = switchLayer;
}
});
</script>
</body>
</html>
Mapbox GL JS does not have the concept of a "base layer" and "overlays." All layers of all maps are drawn with the same basic primitives.
If you need to persist one or more custom layers while switching between the provided Mapbox styles, you may choose to
fork the Mapbox style and add your custom layers in Studio
re-add your custom layers after switching styles
download the Mapbox style as JSON in the browser and add your custom layers to the style in the browser

Mapbox search bar doesent load location

I have 2 toolbars in my scrpit one to add markers and another one to search locations but when i had both in the scrpit the location bar stop fiding location can you tell me why ? i am new at leaflet and mapbox
<style>
#geocoder-container {
position: absolute;
top: 0;
width: 100%;
margin-top: 10px;
}
#geocoder-container > div {
min-width:50%;
margin-left:25%;
}
</style>
<div id='map'></div>
<div id='geocoder-container'></div>
<script>
<!-- MAP Feature-->
L.mapbox.accessToken = 'pk.eyJ1Ijoib2JlY2VuZSIsImEiOiJjaXNicjdja3kwMDE3MnBvMGppMWpubDBqIn0.L4r0u_Em0utvCuFmqbsWQw';
var map = L.mapbox.map('map', 'mapbox.streets')
.setView([38.89399, -77.03659], 17);
var featureGroup = L.featureGroup().addTo(map);
<!-- MAP Feature-->
<!-- Tolbar eddit -->
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function(e) {
featureGroup.addLayer(e.layer);
});
var geocoder = new mapboxgl.Geocoder({
container: 'geocoder-container'
});
map.addControl(geocoder);
</script>
</body>
</html>