Mapbox search bar doesent load location - leaflet

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>

Related

Add a div element on Leaflet map

I need to add my favorite text to middle-top of my map div, so I have:
<div id="map" style="height:300px;">
<div style="width:150px;background-color:red; z-index:1000;"> Hello World !!</div>
</div>
And this is my simplified JS code:
<script>
$(document).ready(function () {
var Lat = 32.646540;
var Lon = 51.667743;
map = L.map('map').setView([Lat, Lon], 19);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Thanks to OSM',
maxZoom: 19
}).addTo(map);
});
</script>
And the problem is that the Red Text goes behind the map and I can't manage it.
Before loading the map :
After loading the map :
add your Hello World div a higher z-index like z-index: 9999
Update
Now I see it. You add the hello World div as child to the map. You have to add it as sibling.
<div class="box"> Hello World</div>
<div id="map" style="height:300px;"></div>
CSS:
.box{
position: absolute;
top: 0;
z-index: 9999;
text-align: center;
width: 150px;
left: 50%;
margin-left: -75px; /* half of the width */
background-color:red;
}

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

Use marker icon with only awesome fonts, no surrounding balloon

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>

Can't load Leaflet inside Vue component

I am trying to create a Leaflet map as a Vue component but I am having some difficult getting started. I installed Leaflet through npm
Where am I going wrong? console.log(Leaflet) is returning a Leaflet object but I am having trouble getting the map to expand and render.
Some direction would be appreciated
<template>
<div id="map"></div>
</template>
<script>
// import leaflet here?
import Leaflet from 'leaflet';
export default {
components: {
Leaflet
},
created() {
console.log(this);
console.log(Leaflet);
},
ready() {
this.map = L.map('map').setView([51.959, -8.623], 14);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(this.map);
}
}
</script>
<style>
#map {
height: 100%;
width: 100%;
margin-top: -24px;
}
/* default legend, layer styling from leaflet template */
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
.legend {
text-align: left;
line-height: 18px;
color: #555;
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
</style>
There are a couple problems in your code:
There is not a ready lifecycle hook in Vue. Use mounted().
You are trying to pass the Leaflet library as a component to Vue, which does nothing since Leaflet is not a Vue component and does not need any sort of registering with Vue.
A map data property was not declared on the Vue component.
There is no problem with importing the library as Leaflet from 'leaflet', but it might be more consistent for yourself or others to declare the Leaflet object as L from 'leaflet'. You can also use: import { Map } from 'leaflet', but then must initialize your Map appropriately: this.map = new Map("mapContainer")
Prevent potential memory leaks and/or cleanup appropriately by using the remove() method on a Leaflet Map Class. A good spot is within the Vue beforeDestroy lifecycle hook.
Also, don't forget to import the Leaflet CSS, for example:
import "leaflet/dist/leaflet.css";
<template>
<div id="mapContainer"></div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
export default {
name: "LeafletMap",
data() {
return {
map: null
};
},
mounted() {
this.map = L.map("mapContainer").setView([51.959, -8.623], 12);
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors'
}).addTo(this.map);
},
beforeDestroy() {
if (this.map) {
this.map.remove();
}
}
};
</script>
<style scoped>
#mapContainer {
width: 100vw;
height: 100vh;
}
</style>
The correct answer was already provided by jhickok but was insightful for the vue3 composition API as well. The leaflet map can't be instantiated in setup() for the same reasons explained.
<template>
<div id="mapContainer"></div>
</template>
<script>
import { map, tileLayer } from "leaflet";
import { onMounted, onBeforeUnmount } from "vue";
export default {
name: "Map",
setup() {
let container = null;
onMounted(() => {
container = map("mapContainer").setView([51.959, -8.623], 12);
tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors'
}).addTo(container);
});
onBeforeUnmount(() => {
container.remove();
});
}
};
</script>
<style scoped>
#mapContainer {
width: 40vw;
height: 40vh;
}
</style>
first install leafletjs and vue2-leaflet with npm
second copy the code to your component
<template>
<div id="app" v-if="isLoad">
<l-map style="height: 350px" :zoom="zoom" :center="center" #click="addMarker" :max-zoom="maxZoom" :min-zoom="minZoom">
<l-tile-layer :url="url" :attribution="attribution"></l-tilelayer>
<l-marker :lat-lng="markerLatLng" ></l-marker>
</l-map>
</div>
</template>
<script>
import {LMap, LTileLayer, LMarker} from "vue2-leaflet";
import "leaflet/dist/leaflet.css";
import {Icon} from "leaflet";
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});
export default {
components: {
LMap,
LTileLayer,
LMarker,
},
mounted() {
this.mapIsReady()
},
data() {
return {
isLoad: false,
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: '© OpenStreetMap contributors',
zoom: 15,
maxZoom:18,
minZoom:5,
center: [35.6892, 51.3890],
markerLatLng: [35.6892, 51.3890],
};
},
methods: {
mapIsReady() {
let self = this
setTimeout(() => self.isLoad = true, 1500)
},
addMarker(event) {
console.log(event)
this.markerLatLng = [event.latlng.lat,event.latlng.lng];
},
},
};
</script>
this code has add marker , max zoom and min zoom for your map
this code used open street map because it's free. you can change it to api.mapbox.com if you want but you need api key or accessToken
I just did this, but I needed basemaps too (bing).
leafletWrapper.js
const L = require('leaflet')
require('leaflet-bing-layer')
export default L
LeafletMap.vue
<template>
<div id="map">
</div>
</template>
<script>
import L from 'leafletWrapper'
export default {
name: 'leaflet-map',
props: ['center', 'zoom', 'minZoom', 'maxZoom', 'markers'],
data () {
return {
mapObject: {},
markerLayer: {}
}
},
mounted () {
const bingKey = 'foo'
this.mapObject = L.map(
"map", {
center: this.center,
zoom: this.zoom,
minZoom: this.minZoom,
maxZoom: this.maxZoom
}
)
L.tileLayer.bing({bingMapsKey: bingKey, imagerySet: 'Road'}).addTo(this.mapObject)
},
beforeDestroy () {
this.mapObject.clearAllEventListeners()
}
}
</script>