Mapbox allow user to click on map and pin? - mapbox

As the question says I want to allow users to click on the map and add a pin, I need to do that in order to display to users where is the business located and because I don't know the location. I need to allow them to insert it. However, documentation is a bit confusing for me and either I cannot find it or I just missed it.
So far I have:
<div id="right" class="map">
<div id='map' style='width: 100%; height: 100%;'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibGl2ZS1vbGRoYW0iLCJhIjoiY2ozbXk5ZDJ4MDAwYjMybzFsdnZwNXlmbyJ9.VGDuuC92nvPbJo-qvhryQg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v10'
});
</script>
</div>

You could add a marker using the mapbox gl js API.
Example is already there:
https://www.mapbox.com/blog/custom-markers-mapboxgl/
https://www.mapbox.com/mapbox-gl-js/example/custom-marker-icons/
If you want to add the marker on a clicked position you could use something like this:
map.on('click', (e) => {
var coords = `lat: ${e.lngLat.lat} <br> lng: ${e.lngLat.lng}`;
// create the popup
var popup = new mapboxgl.Popup().setText(coords);
// create DOM element for the marker
var el = document.createElement('div');
el.id = 'marker';
// create the marker
new mapboxgl.Marker(el)
.setLngLat(e.lngLat)
.setPopup(popup)
.addTo(map);
});
.mapboxgl-marker {
height: 20px;
width: 20px;
z-index: 5;
border: 1px solid black;
border-radius: 50%;
background-color: #305bad;
}

You can add a click event handler to the map surface. In the handler, add a layer with your "pin". In the example below, I added a circle layer but you could just as easily add a symbol layer for a custom "pin".
self.map.on("click", function(e){
console.log("background click", e.lngLat);
var geojson = {
type: "FeatureCollection",
features: [{
type:"Feature",
geometry: { type: "Point", coordinates: [ e.lngLat.lng, e.lngLat.lat ]}
}]
};
self.map.addSource("pins", {
"type": "geojson",
"data": geojson
});
self.map.addLayer({
id: "pinsLayer",
type: "circle",
source: "pins",
paint: {
"circle-color": "red",
"circle-radius": 5
}
});
});

Related

How can I control the zindex of a leaflet layer?

I have a codepen at https://codepen.io/ericg_off/pen/qBoPQGX which demonstrates the issue.
I would like the marker to be drawn underneath the line.
How can I change the z-index of the layers to accomplish this?
Searching has suggested several potential solutions, but nothing appears to work.
HTML
<div id="map"></div>
CSS
#map {
height: 100vh;
}
#map >>> .my-icons {
background-color: rgba(0, 255, 0, 0);
}
JS
var map = L.map("map", {
// Set latitude and longitude of the map center (required)
center: [38, -77],
// Set the initial zoom level, values 0-18, where 0 is most zoomed-out (required)
zoom: 5
});
// Create a Tile Layer and add it to the map
var tiles = new L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
attribution:
'© OpenStreetMap contributors',
minZoom: 3,
maxZoom: 8
}
).addTo(map);
divIcon = L.divIcon({
//html: '<i class="fa fa-map-marker fa-1x"></i>',
html: '<i class="fa fa-star fa-1x"></i>',
className: 'my-icons'
})
const markerLayer = L.layerGroup().addTo(map);
const lineLayer = L.layerGroup().addTo(map);
// markerLayer.setZIndex(-1000)
// markerLayer.bringToBack();
const from = [38, -77];
const to = [38, -100];
const fromMarker = L.marker(from, { icon: divIcon } ).addTo(lineLayer);
const line = L.polyline([from, to], { color: "green", weight: 1 }).addTo(markerLayer);
The tutorial on panes explains how to change the zIndex of a pane.
After reading the documentation on Panes and learning that the marker pane has a zindex of 600 and the overlay pane (where the lines are drawn) has a index of 400, I just needed to change the marker pane to a index of 300.
map.getPane('markerPane').style.zIndex = 300;
Updated the codepen with the answer.

Using leaflet.FreeDraw with leaflet.path.drag

I am wondering if it's possible to use Leaflet.FreeDraw with leaflet.path.drag to be able to drag the polygon created by FreeDraw plugin.
jsfiddle
I tried to enable dragging plugin like this, but it doesn't work.
const map = new L.Map(document.querySelector('section.map'), { doubleClickZoom: false }).setView(LAT_LNG, 14);
L.tileLayer(TILE_URL).addTo(map);
const freeDraw = new FreeDraw({ mode: FreeDraw.ALL });
map.addLayer(freeDraw);
freeDraw.dragging.enable();
You could extract the bounds from the FreeDraw by listening to the markers event to create a polygon or other map object using leaflet enabled with dragging. See working example below.
You should consider whether you would like to disable the FreeDraw after this, using the option leaveModeAfterCreate:true as the user may get additional polygons when dragging
const LAT_LNG = [51.505, -0.09];
const TILE_URL = 'https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}#2x.png';
const map = new L.Map(document.querySelector('section.map'), { doubleClickZoom: false }).setView(LAT_LNG, 14);
L.tileLayer(TILE_URL).addTo(map);
const freeDraw = new FreeDraw({
mode: FreeDraw.ALL,
leaveModeAfterCreate:true //recommended to prevent undesired creation of multiple polygons
});
map.addLayer(freeDraw);
//freeDraw.dragging.enable();
//STEP 1: Listen to markers event raised by free draw whenever edits (create/edit/deletions are made to the map)
freeDraw.on("markers",function(event){
//we are only interested in create events
//we aim to extract the bounds and remove the existing
// freedraw references. If it is that you would like your
// user to edit the polygon, then you may keep these and do the // additional work to manage and update these references
if(event.eventType=='create' && event.latLngs.length > 0){
//capture the current polygon bounds (store in 1st position)
var latLngs = event.latLngs[0];
freeDraw.clear(); //clear freedraw markers
//create polygon from lat lng bounds retrieved
var polygon = L.polygon(
latLngs.map(function(latLng){
return [latLng.lat,latLng.lng];
}), {
color: 'red',
draggable: true //make polygon draggable
}).addTo(map);
}
})
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.map {
width: 100vw;
height: 100vh;
}
.map.mode-create {
cursor: crosshair;
}
.leaflet-edge {
background-color: #95bc59;
box-shadow: 0 0 0 2px white, 0 0 10px rgba(0, 0, 0, .35);
border-radius: 50%;
cursor: move;
outline: none;
transition: background-color .25s;
}
.leaflet-polygon {
fill: #b4cd8a;
stroke: #50622b;
stroke-width: 2;
fill-opacity: .75;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.2/leaflet.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.2/leaflet-src.js"></script>
<script src="https://rawgit.com/Wildhoney/Leaflet.FreeDraw/master/dist/leaflet-freedraw.iife.js"></script>
<script src="https://npmcdn.com/leaflet.path.drag/src/Path.Drag.js"></script>
<section class="map"></section>
NB. Also see working example on js-fiddle here https://jsfiddle.net/ytevLbgs/

Adding circle polygon to Map on click

I'm looking to create an interactive map where a user can simply click to place various polygons/circles of varying size. Once they place these shapes, it would need to be saved so that the next time they visit the page it remembers where the user placed these polygons.
How can I use Mapbox to draw a circle like polygon on a map, of a predefined area/size, with a single click?
There are no circles in mapbox, any shape must be defined as a list of points to form a polygon... so option 1 would be to create your own 'rounded' polygon as a GeoJson feature that you can use as source for a layer... here you have a sample
{"geometry":{"coordinates":[[[-122.12994080132313,47.644482519898276],[-122.12990373140416,47.64449397672769],[-122.12986013420647,47.64449595450196],[-122.12982116483462,47.644490418558235],[-122.1297839964571,47.644476603480825],[-122.12975679969954,47.64445714476511],[-122.12973954095614,47.64443733202884],[-122.12973247670453,47.64440904091012],[-122.12973871662135,47.644379708121534],[-122.1297573021927,47.64435229906627],[-122.12978488013835,47.64433576206736],[-122.12980033418789,47.644325791349985],[-122.12985011085712,47.64431544969429],[-122.12989071574376,47.644316214791644],[-122.12993313243254,47.64432463813935],[-122.12996427964354,47.64434114057201],[-122.12998759690701,47.644364252993626],[-122.13000010533318,47.64438901705185],[-122.1300017490552,47.64441623463529],[-122.12999180010362,47.64444202406153],[-122.12997246871359,47.64446439674887],[-122.12994080132313,47.644482519898276]]],"type":"Polygon"},"type":"Feature","properties":{"name":"circle"}}
You can try using https://studio.mapbox.com/datasets/ to understand how to draw this manually. That would enable to create some predefined 'almost perfect circles' and then allow the users to drag and drop them through the Mapbox-gl-js draw
An option 2 would be to create these shapes is to use turf which is a geospatial js engine which allows you to create some predefined shapes, including circles
But if what you want is to just place the shape of a circle in a concrete point as a marker, that the users can drag and drop, option 3 is in this sample fiddle I have created to show you how to create a custom circle marker, that consist in creating a marker based in an svg circle shape. Obviously this shape doesn't scale as a mapbox polygon vector based.
Just defining a style for the marker (image from wikipedia, you'll need to get your own svg shape)
<style>
.marker {
background-image: url('https://upload.wikimedia.org/wikipedia/commons/a/a0/Circle_-_black_simple.svg');
background-size: cover;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
}
</style>
and then the relevant js code
mapboxgl.accessToken = 'PUT YOUR TOKEN HERE';
var geojson = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-77.032, 38.913]
},
'properties': {
'title': 'Mapbox',
'description': 'Washington, D.C.'
}
},
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-122.414, 37.776]
},
'properties': {
'title': 'Mapbox',
'description': 'San Francisco, California'
}
}
]
};
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
center: [-96, 15.8],
zoom: 2
});
// add markers to map
geojson.features.forEach(function(marker) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add it to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(
new mapboxgl.Popup({
offset: 25
}) // add popups
.setHTML(
'<h3>' +
marker.properties.title +
'</h3><p>' +
marker.properties.description +
'</p>'
)
)
.addTo(map);
});
EDITED
I forgot an option 4 to have circles on mapbox, in this case to paint circles but it would require some advanced coding to make them draggable. You can create a circles layer in this way...
https://jsfiddle.net/jscastro76/vjkt7wyx/14/

How can I add an icon to switch the mapboxgl style dynamically?

I want to add an icon as below in the mapboxgl view. Working with Angular2
When I click the icon it should automatically switch the styles (streets-v9, satelllite-v9)
I am following the link mapboxgl example
Did you see this API method?
https://www.mapbox.com/mapbox-gl-js/api/#map#setstyle
With that you can set a new style for the map when you e.g. click an icon or press a button or what ever you wish.
Take this as a reference to build upon:
https://jsfiddle.net/andi_lo/706pot8L/
mapboxgl.accessToken = 'pk.eyJ1IjoiZmFyYWRheTIiLCJhIjoiTUVHbDl5OCJ9.buFaqIdaIM3iXr1BOYKpsQ';
let map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-1.77, 52.73],
zoom: 3,
});
let icon = document.getElementById('icon');
icon.addEventListener('click', function(e) {
map.setStyle('mapbox://styles/mapbox/light-v9');
}, false)
#icon {
position: absolute;
top: 15px;
left: 15px;
color: black;
}
#map {
height: 500px;
}
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.css" rel="stylesheet"/>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.js"></script>
<div id="map"></div>
<button id="icon">
Switch layers
</button>
What you want is a "control" that switches layers. Mapbox-GL-JS doesn't include such a thing, nor is there one listed as a plugin (yet).
You should use Mapbox-GL-JS's iControl class to create the control, then add styling and behaviour following Mapbox's instructions:
function LayerSwitchControl() { }
LayerSwitchControl.prototype.onAdd = function(map) {
this._map = map;
this._container = document.createElement('div');
this._container.className = 'mapboxgl-ctrl';
// change this next line to include a layer-switching icon
this._container.textContent = 'Hello, world';
return this._container;
};
LayerSwitchControl.prototype.onRemove = function () {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
};
You'll then need to add code to:
Add the control
Respond to click events as appropriate.
This is my working code,
This is zoom level control

How to draw a polyline using the mouse and leaflet.js

I would like to draw a polyline on a map with leaflet. The basic gesture that I would like to apply is:
User clicks and holds on the mouse button -> that defines the first marker. If the user holds the mouse button, and moves the mouse, a corresponding "rubber band" is displayed.
User releases the mouse button -> a second marker is added to the map and the 2 markers are linked by a line.
Starting from the second marker, the user can continue building a second line using the the same procedure as above.
The final result should be the set of coordinates/markers, linked by a polyline.
As Julien V and IvanSanchez said, you can implement some of the draw-like plugins
In example below:
You can see usage of Leaflet.draw plugin. Hope it helps :)
// center of the map
var center = [46.165164, 15.750443];
// Create the map
var map = L.map('map').setView(center,15);
// Set up the OSM layer
L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Data © OpenStreetMap',
maxZoom: 18
}).addTo(map);
// Initialise the FeatureGroup to store editable layers
var editableLayers = new L.FeatureGroup();
map.addLayer(editableLayers);
var options = {
position: 'topleft',
draw: {
polygon: {
allowIntersection: false, // Restricts shapes to simple polygons
drawError: {
color: '#e1e100', // Color the shape will turn when intersects
message: '<strong>Oh snap!<strong> you can\'t draw that!' // Message that will show when intersect
},
shapeOptions: {
color: '#97009c'
}
},
polyline: {
shapeOptions: {
color: '#f357a1',
weight: 10
}
},
// disable toolbar item by setting it to false
polyline: true,
circle: true, // Turns off this drawing tool
polygon: true,
marker: true,
rectangle: true,
},
edit: {
featureGroup: editableLayers, //REQUIRED!!
remove: true
}
};
// Initialise the draw control and pass it the FeatureGroup of editable layers
var drawControl = new L.Control.Draw(options);
map.addControl(drawControl);
var editableLayers = new L.FeatureGroup();
map.addLayer(editableLayers);
map.on('draw:created', function(e) {
var type = e.layerType,
layer = e.layer;
if (type === 'polyline') {
layer.bindPopup('A polyline!');
} else if ( type === 'polygon') {
layer.bindPopup('A polygon!');
} else if (type === 'marker')
{layer.bindPopup('marker!');}
else if (type === 'circle')
{layer.bindPopup('A circle!');}
else if (type === 'rectangle')
{layer.bindPopup('A rectangle!');}
editableLayers.addLayer(layer);
});
html, body, #map { margin: 0; height: 100%; width: 100%; }
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.2.3/leaflet.draw.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.2.3/leaflet.draw.css" rel="stylesheet" />
<meta charset="utf-8">
<title>TEST</title>
</head>
<body>
<div id='map'></div>
</body>
</html>