leaflet.draw trash button delete all polygons and save - javascript

Using javascript, how can I alter the leaflet.draw "Trash" button to delete all polygons that have been drawn and automatically save. Below is the code I've implemented but it is a complete hack. It removes the active polygon, but after I delete an object once I begin to get errors in the console when I click the "Trash" icon like NotFoundError: Node was not found and TypeError: this._deletedLayers is null
map.on('draw:editstart', function (e) {
if(e.handler == 'remove' && typeof drawnItem != 'undefined' && drawnItem !== null){
if(window.console) window.console.log('Drawing deleted...');
if(typeof drawnItem != 'undefined' && drawnItem !== null){
drawnItems.removeLayer(drawnItem);
}
$('.leaflet-draw.leaflet-control .leaflet-draw-actions').hide();
$('.leaflet-popup-pane .leaflet-draw-tooltip').remove();
}
});

Solved my own problem with a custom control (thanks to stackexchange - https://gis.stackexchange.com/questions/60576/custom-leaflet-controls):
UPDATED! added #SpiderWan suggestions (thanks!) so no need for custom CSS. Also, the event was previously attached to the entire control bar. Instead attached to just the controlUI button itself.
Javascript:
L.Control.RemoveAll = L.Control.extend({
options: {
position: 'topleft',
},
onAdd: function (map) {
var controlDiv = L.DomUtil.create('div', 'leaflet-control leaflet-bar');
var controlUI = L.DomUtil.create('a', 'leaflet-draw-edit-remove', controlDiv);
controlUI.title = 'Remove all drawn items';
controlUI.setAttribute('href', '#');
L.DomEvent
.addListener(controlUI, 'click', L.DomEvent.stopPropagation)
.addListener(controlUI, 'click', L.DomEvent.preventDefault)
.addListener(controlUI, 'click', function () {
drawnItems.clearLayers();
if(window.console) window.console.log('Drawings deleted...');
});
return controlDiv;
}
});
removeAllControl = new L.Control.RemoveAll();
map.addControl(removeAllControl);

Like jduhls's answer but using Leaflet.draw CSS classes :
L.Control.RemoveAll = L.Control.extend(
{
options:
{
position: 'topleft',
},
onAdd: function (map) {
var controlDiv = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
L.DomEvent
.addListener(controlDiv, 'click', L.DomEvent.stopPropagation)
.addListener(controlDiv, 'click', L.DomEvent.preventDefault)
.addListener(controlDiv, 'click', function () {
drawnItems.clearLayers();
});
var controlUI = L.DomUtil.create('a', 'leaflet-draw-edit-remove', controlDiv);
controlUI.title = 'Remove All Polygons';
controlUI.href = '#';
return controlDiv;
}
});
var removeAllControl = new L.Control.RemoveAll();
map.addControl(removeAllControl);

You can also overwrite the delete tool's enable method to simply delete all layers instead of opening the delete menu:
L.EditToolbar.Delete.include({
enable: function () {
this.options.featureGroup.clearLayers()
}
})

Related

Extending L.control.zoom with custom button

I'm looking for a way to add an extra button to L.control.zoom. Leaflet is being loaded from a CDN and I'm using vanilla Javascript (no preprocessors or anything).
I was hoping there'd be something like L.control.zoom.extend({}), but unfortunately that doesn't exist. Trying L.Control.extend({...L.control.zoom}) didn't work either.
For context, doing it the long way by copy-pasting the original code and adding the code for my custom button at line 42 would look like this:
let zoomControls = L.Control.extend({
// #section
// #aka Control.Zoom options
options: {
position: 'topleft',
// #option zoomInText: String = '+'
// The text set on the 'zoom in' button.
zoomInText: '+',
// #option zoomInTitle: String = 'Zoom in'
// The title set on the 'zoom in' button.
zoomInTitle: 'Zoom in',
// #option zoomOutText: String = '−'
// The text set on the 'zoom out' button.
zoomOutText: '−',
// #option zoomOutTitle: String = 'Zoom out'
// The title set on the 'zoom out' button.
zoomOutTitle: 'Zoom out'
},
onAdd: function (map) {
var zoomName = 'leaflet-control-zoom',
container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
options = this.options;
let locationLink = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container);
L.DomEvent.disableClickPropagation(locationLink);
locationLink.title = 'My location';
let locationIcon = L.DomUtil.create('span', 'fa-lg fas fa-map-marker-alt', locationLink);
L.DomEvent.on(locationLink, 'click', () => {
alert('BUTTON CLICKED');
});
this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle,
zoomName + '-in', container, this._zoomIn);
this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
zoomName + '-out', container, this._zoomOut);
this._updateDisabled();
map.on('zoomend zoomlevelschange', this._updateDisabled, this);
return container;
},
onRemove: function (map) {
map.off('zoomend zoomlevelschange', this._updateDisabled, this);
},
disable: function () {
this._disabled = true;
this._updateDisabled();
return this;
},
enable: function () {
this._disabled = false;
this._updateDisabled();
return this;
},
_zoomIn: function (e) {
if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
}
},
_zoomOut: function (e) {
if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
}
},
_createButton: function (html, title, className, container, fn) {
var link = L.DomUtil.create('a', className, container);
link.innerHTML = html;
link.href = '#';
link.title = title;
/*
* Will force screen readers like VoiceOver to read this as "Zoom in - button"
*/
link.setAttribute('role', 'button');
link.setAttribute('aria-label', title);
L.DomEvent.disableClickPropagation(link);
L.DomEvent.on(link, 'click', L.DomEvent.stop);
L.DomEvent.on(link, 'click', fn, this);
L.DomEvent.on(link, 'click', this._refocusOnMap, this);
return link;
},
_updateDisabled: function () {
var map = this._map,
className = 'leaflet-disabled';
L.DomUtil.removeClass(this._zoomInButton, className);
L.DomUtil.removeClass(this._zoomOutButton, className);
if (this._disabled || map._zoom === map.getMinZoom()) {
L.DomUtil.addClass(this._zoomOutButton, className);
}
if (this._disabled || map._zoom === map.getMaxZoom()) {
L.DomUtil.addClass(this._zoomInButton, className);
}
}
});
While not being explicit in the Leaflet class customization tutorial, there is a subtle distinction between factories, which are lowerCased and that you cannot extend, and Classes, which are PascalCased and on which you can use Leaflet extend mechanism:
var MyNewZoomControl = L.Control.Zoom.extend({
onAdd: function (map) {
// your new method content
}
}
That being said, if your new button does not really share functionality with the zoom buttons or is not "merged" with them, you could simply make a separate Control and insert it in the same corner position. There is also Leaflet EasyButton plugin which can help in this regard.

leaflet mouse hover popup through array list

i want to display a popup on mouse hover,i want to show names on popup,which will be select from the array list,i placed multiple markers on map at different latlon,now i want to display a popup(which contain name) for particular latlon,this is my code,where i want to show my district name on mouse hover,now i am getting the popup text on mouse hover but i don't know how can i call my array list in popupcontent,any one can suggest what i should do?
var planes = [
["Jodhpur",26.28, 73.02],
["Bikaner",28.0229,73.3119],
["Churu",28.3254,74.4057],
["Ganga Nagar",29.9038,73.8772],
["Hanumangarh",29.1547,74.4995],
["Jaisalmer", 26.9157,70.9083],
["Jalore",25.1257,72.1416],
["Jhunjhunu",28.1289,75.3995],
["Nagaur",27.1854,74.0300],
["Pali",25.7711, 73.3234],
["Sikar",27.6094,75.1399],
["Sirohi",24.7467,72.8043],
["Barmer",25.7532,71.4181],
];
for (var i = 0; i < planes.length; i++) {
marker = new L.marker([planes[i][1],planes[i][2]],{icon: myIcon}).addTo(map).bindPopup('<div id="chart" class="chart"></div>');
marker.on('click', onMarkerClick, this);
/*var currentMarker = planes[i][0];
currentMarker.on('mouseover', currentMarker.openPopup.bind(currentMarker));
*/
marker.on('mouseover', function(e) {
//open popup;
var popup = L.popup()
.setLatLng(e.latlng)
.setContent('Popup')
.openOn(map);
});
}
Filter your array to return the name based on lat and/or lng
marker.on('mouseover', function(e) {
var name = "";
$.each(planes,function(i,v){
if (v.indexOf(e.latlng[0]) > 0) {//test if the lat is in the array
name = v[0];//get the name
}
})
var popup = L.popup()
.setLatLng(e.latlng)
.setContent('District: '+name)
.openOn(map);
})
Note: i am assuming e.latlng is a array of [lat,lng]
You have to change the marker1 name as per your marker name.
var marker1 = L.marker(23.0225, 72.5714).addTo(mymap)
.bindPopup("Demo Content of Popup");
let isClicked = false
marker1.on({
mouseover: function() {
if(!isClicked) {
this.openPopup()
}
},
mouseout: function() {
if(!isClicked) {
this.closePopup()
}
},
click: function() {
isClicked = true
this.openPopup()
}
})
mymap.on ({
click: function() {
isClicked = false
},
popupclose: function () {
isClicked = false
}
})

Cannot click within dynamically inserted content

I have a form that is dynamically inserted into the Google Map. However I cannot click any of the inputs. I believe I need to add a listener somewhere but I'm not sure.
Fiddle
function googlemap() {
// google map coordinates
var posY = 37.765700,
posX = -122.449134,
location = new google.maps.LatLng(posY,posX),
// offset location
posY = posY + 0.055;
offsetlocation = new google.maps.LatLng(posY,posX);
var mapOptions = {
panControl: false,
zoomControl: false,
mapTypeControl: false,
scaleControl: false,
streetViewControl: false,
overviewMapControl: false,
draggable: true,
disableDoubleClickZoom: false,
scrollwheel: false,
zoom: 12,
center: offsetlocation,
// ROADMAP; SATELLITE; HYBRID; TERRAIN;
mapTypeId: google.maps.MapTypeId.ROADMAP
};
overlay.prototype = new google.maps.OverlayView();
// create overlay marker
overlay.prototype.onAdd = function() {
blip = document.createElement('div'),
pulse = document.createElement('div');
blip.className = 'blip';
pulse.className = 'pulse';
// createa dialog and grab contents from #mapcontents
boxText = document.createElement("div");
boxText.className = "dialog";
mapContents = $('#mapcontents').html();
boxText.innerHTML = mapContents;
$('#mapcontents').remove();
blip.appendChild(boxText);
// append 'blip' marker
this.getPanes().overlayLayer.appendChild(blip).appendChild(pulse);
}
// update blip positioning when zoomed
overlay.prototype.draw = function(){
var overlayProjection = this.getProjection(),
bounds = new google.maps.LatLngBounds(location, location),
sw = overlayProjection.fromLatLngToDivPixel(bounds.getSouthWest()),
ne = overlayProjection.fromLatLngToDivPixel(bounds.getNorthEast());
blip.style.left = sw.x + 'px';
blip.style.top = ne.y + 'px';
// shift nav into view by resizing header
var w = $('.dialog').width(),
w = (w / 2) + 25,
w = '-' + w + 'px';
h = $('.dialog').height(),
h = (h) + 100,
h = '-' + h + 'px';
$('.dialog').css({
'margin-top' : h,
'margin-left' : w
});
};
var map = new google.maps.Map(document.getElementsByClassName('map')[0], mapOptions);
// explicitly call setMap on this overlay
function overlay(map) {
this.setMap(map);
}
// center map when window resizes
google.maps.event.addDomListener(window, 'resize', function() { map.setCenter(location) });
// center map when zoomed
google.maps.event.addListener(map, 'zoom_changed', function() { map.setCenter(location) });
// I have nfi what I'm doing but I think this click listener is part of the solution.
google.maps.event.addListener('.dialog', 'click', function() {
alert('ok');
});
// process contact form
google.maps.event.addListener(map, 'domready', function() {
$('button').click(function(e) {
(e).preventDefault();
alert('ok');
return false;
var name = $(".contactform input[name='name']"),
email = $(".contactform input[name='email']"),
message = $(".contactform textarea[name='message']"),
error = false;
// clear validation errors
$('#contact input, #contact textarea').removeClass('error');
if(name.val().length < 1)
name.addClass("error");
if(!/^[a-zA-Z0-9._+-]+#[a-zA-Z0-9-]+\.[a-zA-Z]{2,4}(\.[a-zA-Z]{2,3})?(\.[a-zA-Z]{2,3})?$/.test(email.val()))
email.addClass("error");
if(message.val().length < 1)
message.addClass("error");
// if error class exists
if($(".error").length) return false;
$(this).attr('disabled', true).prepend('<i class="load animate-spin"></i>');
$.ajax({
type: "post",
dataType: "json",
url: "lib/sendmail.php",
data: $("#contactform").serialize()
})
.always(function(data) {
$('h5').animate({opacity:0},function(){
$('h5').text("Email Sent!!")
.animate({opacity:1});
});
$('.contactform').animate({opacity:0},function(){
$('.contactform').html("<p class='success'>Thank You for your form submission. We will respond as soon as possible.</p>")
.animate({opacity:1});
})
});
});
return false;
});
// add overlay
overlay = new overlay(map);
}
Any idea why I can't click the inputs?
You just need to block propagation of mousedown map event to make inputs clickable:
google.maps.event.addDomListener(blip, 'mousedown', function (e) {
e.cancelBubble = true;
if(e.stopPropogation) {
e.stopPropagation();
}
});
And you can do the same for dbclick to prevent map zooming: http://jsfiddle.net/gfKWz/1/
The click-events fire fine for all these inputs, the issue here at first is that your code will never execute, because there is no domready-event for a google.maps.Map
Change this:
google.maps.event.addListener(map, 'domready', function () {
into this:
google.maps.event.addListenerOnce(map, 'tilesloaded', function () {
for observation of the events you may use $.on(), e.g.:
$(map.getDiv()).on('click','button',function (e) {/*some code*/});
Demo: http://jsfiddle.net/doktormolle/jcfDu/
You use $('button').click which is triggered before the button is present on the dom. .click() binds the handler to all the current elements on the dom.
Better use $('button').on('click', function(){}); which binds the click event handler to all the current and future instances of button on your page. This is especially handy if you dynamically add content on the page. Through ajax or otherwise.
Read more about jQuery .on() in here http://api.jquery.com/on/
Your event has to be added in the onAdd function.
Currently, the event handler is created before the element. So it doesn't catch the click on this particular element.
http://jsfiddle.net/NeekGerd/duEEt/4/
Or you could create a new function for your overlay's bindings, just for the sake of clean-code :
overlay.prototype.onAdd = function() {
[...]
this.bindings();
}
overlay.prototype.bindings = function(){
$("button").on("click",function(){
alert("Click");
return false;
}
}
For now I have no real solution to your inputs problem.
Maybe by reattaching mousedownevents to them, and force them to focus():
$("input,textarea").on("mousedown",function(){$(this).focus();});
Same thing with your checkboxes.
On another note, since you use jQuery, why not use it all the way?
Like you can do:
$('#mapcontents')
.clone()
.wrap('<div class="dialog" />')
.wrap('<div class="blip />')
.appendTo( *selector* );
In order to quickly build some html and append it to the selected element. Much more readable (thus easier to maintain) than the DOM code you got there. Since you already use jQuery anyway.

Google Maps API v3 : Click events not triggered in firefox for custom marker

have created a map that I'm trying to have function similar to 'My Maps'. I have two dropdownlists on the right side, based on the selection in those ddl's, you can add a custom marker / icon. You select a marker type, then click the '+' button in the top right corner of the map, and then click where you want the marker added. My issue is, this works fine in IE, Safari, and Chrome, but not in firefox. The click event doesn't seem to fire.
Here is the location of the map : https://ait.saultcollege.ca/Michael.Armstrong/Index.html
The button to add the marker in the top right has an onclick event pointing to my 'placeMarker()' function. Here is the code for placeMarker(), createMarker() ...
function placeMarker() {
select("placeMarker");
var infowindow = new google.maps.InfoWindow({});
var catID = document.getElementById('category');
var typeID = document.getElementById('ddlType');
var category = catID.options[catID.selectedIndex].value;
var markerType = typeID.options[typeID.selectedIndex].value;
if (!markerType) {
alert("You must select an icon type.");
}
else {
var moveListener = google.maps.event.addListener(customMap, 'mousemove', function(event) {
if (mapMarker) {
mapMarker.setPosition(event.latLng);
} else {
mapMarker = createMarker(event.latLng, "test", markerType, "test");
}
});
var clickListener = google.maps.event.addListener(customMap, 'click', function(event) {
if (mapMarker) {
select("hand_b");
google.maps.event.clearListeners(customMap, 'mousemove');
google.maps.event.removeListener(listener);
mapMarker = createMarker(event.latLng, "test2", markerType, "test");
var htmlInfo = "" +
"Category:" + category + "" +
"Item:" + markerType + "" +
"Notes:" +
"Location:" + mapMarker.getPosition().toString() + "" +
"" +
"";
//infowindow.setContent(htmlInfo);
//infowindow.open(customMap, mapMarker);
}
});
}
}
function createMarker(latlng, title, icon, html) {
var mapMarker = new google.maps.Marker({
position: latlng,
map: customMap,
title: title,
icon: 'Images/' + icon + '.png'
});
return mapMarker;
}
function select(buttonId) {
document.getElementById("hand_b").className = "unselected";
document.getElementById("placeMarker").className = "unselected";
document.getElementById(buttonId).className = "selected";
}
Any help or suggestions would be awesome. Could this perhaps be a bug in ff?
I did something very similar for an open-source disaster software package. In this case, lets assume I selected "Fire" in my dropdown menu and this triggers addFire(). The listener on the markers will delete the point on a click or allow you to drag it. The map can only have one listener at a time, but each marker can still have its own listener at the same time.
Here is the code that worked on Chrome, Firefox and IE8:
//This function sets up the map for adding a fire icon
function addFire() {
//Kill old listener
if(listening)
google.maps.event.removeListener(listenerhandle);
//Start new listener
listenerhandle = google.maps.event.addListener(disasterMap, 'click', addFirePoint);
listening = true;
}//end addFire
//This function adds new fire points to the map and controls dragging and clicking
function addFirePoint(event) {
//Create the marker
var fireMarker = new google.maps.Marker({
icon: "./mapimgs/fire.png", position: event.latLng, map: disasterMap, draggable: true });
newFireMarkers.push(fireMarker);
fireMarker.setTitle("Fire");
//Listen for clicks on the new marker
google.maps.event.addListener(fireMarker, 'click', function() {
fireMarker.setMap(null);
//remove the marker from the array
for(i=0;i<newFireMarkers.length;i++) {
if(fireMarker.getPosition() == newFireMarkers[i].getPosition()) {
newFireMarkers.splice(i,1);
break;
}
}
}
); //end click listener
}//end addFirePoint

ckeditor - onshow overiding custom definition

when using the ckeditor link dialog, I have custom code for some extra options. I would also like to grab the selected text to use - so I have called:
selectedContents = CKEDITOR.instances['my_editor'].getSelection().getSelectedText();
I want this to happen when the dialog is loaded. So I wrote an "onShow()" handler function... but that messes up the customizations that I have made to the dialog. I'm guessing that my onShow is grabbing the normal process for that event - how can I continue with the normal processing at that point?
dialogDefinition.onShow = function(evt)
{
contents = CKEDITOR.instances['my_editor'].getSelection().getSelectedText();
// now here, continue as you were...
}
Ok, I still have some issues, but the answer to this question is to grab the existing "onShow" handler before overwriting it. Use a global, then it can be called within the new handler:
var dialogDefinition = ev.data.definition;
var oldOnShow = dialogDefinition.onShow;
dialogDefinition.onShow = function(evt) {
// do some stuff
// do some more stuff
// call old function
oldOnShow();
}
Depending on Andy Wallace code:
var oldOnShow = dialogDefinition.onShow;
var newOnShow = function () {
//your code
}
and then:
dialogDefinition.onShow = function(){
oldOnShow.call(this, arguments);
newOnShow.call(this, arguments);
}
It helps me!
Correct syntax is:
/* if new picture, then open the Upload tab */
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
var dialog = dialogDefinition.dialog;
if (dialogName == 'image2') {
dialogDefinition.onShow = CKEDITOR.tools.override(dialogDefinition.onShow, function(original) {
return function() {
original.call(this);
CKEDITOR.tools.setTimeout( function() {
if (dialog.getContentElement('info', 'src').getValue() == '') {
dialog.selectPage('Upload');
}
}, 0);
}
});
}
});

Categories