I have one function called loadmap(){} where im creating map.Im loading this function with
ionViewDidEnter() {
this.loadmap();
}
Inside loadmap i have
this.map = leaflet.map("map").fitWorld();
thats how i initialize map
This is how i remove map when user changes tab.
ionViewDidLeave(){
this.map.remove();
}
This is my .locate function:
var usermarker;
this.map.locate({
setView: true,
maxZoom: 120,
watch:true,
enableHighAccuracy:true
}).on("locationfound", e => {
if (!usermarker) {
usermarker = new L.marker(e.latlng).addTo(this.map);
} else {
usermarker.setLatLng(e.latlng);
}
}).on("locationerror", error => {
if (usermarker) {
this.map.removeLayer(usermarker);
usermarker = undefined;
}
});
The problem is in first time .locate function works.but if i change tab and go back to map tab .locate function doesnt work.if i remove watch option it works.
Thanks
You have to call map.stopLocate() besides map.remove():
Stops watching location previously initiated by map.locate({watch: true})
Live demo: https://plnkr.co/edit/PKMPjfX3zD3QdWmEI0iX?p=preview (use the "Toggle map" button to simulate your changing tabs)
That being said, it is true that Leaflet could automatically do this when using the remove map method. => Merged in PR Leaflet/Leaflet#5893
Related
I am trying to switch between a leaflet map and a list. It is working fine untill the code is compiled by webpack. After clicking on an item the map shows up as it should. But when I close the map with the close button, I have map.remove() which removes the map but the class toggles do not toggle but instead they add the already existing class.
The function below runs to open the map and toggles correctly. And it runs when closing the map, but then the same toggles do not run correctly and adds the same class, as you can see in the photo below. The menu class is also duplicated which it should not.
Before I compile with webpack it does run correctly. Thanks!
const mapHandler = async id => {
let brewery;
if (id) {
brewery = await fetchBreweries.get('/' + id);
if (!brewery.latitude || !brewery.longitude) {
alert('Sorry, this entry has no coordinates.');
return;
};
};
if (!id) {
//no id means we are clicking the closehandler, so enable buttons in footer and remove the map
footerBtns.map(btn => btn.disabled = false);
//remove it before toggling classes or it will throw the can't find it error
if (map) map.remove();
};
mapEl.classList.toggle('not-visible');
list.classList.toggle('not-visible');
mapEl.classList.toggle('visible');
list.classList.toggle('visible');
if (!id) return;
//there is an id, so we are going to build the map to show it
//disable buttons in the footer so we cannot click on them when the map is shown
footerBtns.map(btn => btn.disabled = true);
if (aroundMyLocation) {
//if above flag is true than we want to build the map, show own location and then fly to brewery location
buildMap(myLocation);
setTimeout(() => {
map.flyTo([brewery.latitude, brewery.longitude], 12, {duration: 3})
}, 800);
setTimeout(() => {
L.marker([brewery.latitude, brewery.longitude])
.addTo(map)
.bindPopup(brewery.name)
.openPopup();
}, 1200);
} else {
//flag is false so we only want to show the brewery location
buildMap({lat: brewery.latitude, lng: brewery.longitude});
L.marker([brewery.latitude, brewery.longitude])
.addTo(map)
.bindPopup(brewery.name)
.openPopup();
};
//show brewery details in bottom right box
details.innerHTML = `
<h3>${brewery.name}</h3>
<p>${brewery.phone || 'number unknown'}</p>
<p>${brewery.street || 'street unknown'}</p>
<p>${brewery.city || 'city unknown'}</p>
`;
};
My script was being added twice with webpack. After adding inject: false to HtmlWebpackPlugin in my config file it was running as it should. I was trying to do a simple project so I could focus on learning webpack, serviceworkers and testing. A bit too much all at once probably...
I have set up an app using Electron. For the most part, all of my code is in the index.html file. The app takes some data from the user, and uses it to plot areas on a Leaflet map. The map is plotted onto a <div>
The Leaflet map can be drawn with the taken geolocation, but, when the areas are plotted, the app reloads, causing the data to be lost.
I have rearranged most of the javascript, to find any issues there. I have tried global and local variables.
//Geoloaction for now using dev tools to spoof for test location.
const geo = navigator.geolocation;
let lat;
let lon;
let map;
document.getElementById("geo").addEventListener("click", () => {
geo.getCurrentPosition((position) => {
lat = position.coords.latitude;
lon = position.coords.longitude;
document.getElementById('location').value = (lat + ', ' + lon);
mapIt();
}, error);
});
document.getElementById('submit').addEventListener("click", () => {
let rad = document.getElementById('radius');
let squa = document.getElementById('square');
if (rad.checked) {
radius();
} else if (squa.checked) {
square();
}
});
//Mapping function using Leaflet
function mapIt() {
console.log("RUNNING");
map = L.map('maps').setView([lat, lon], 8);
const attribution = '© OpenStreetMap';
const tileURL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
const tiles = L.tileLayer(tileURL, {
attribution
});
tiles.addTo(map);
}
function error(error) {
console.log(error.POSITION_UNAVAILABLE);
}
At the end of either of these two functions, the app is caused to reload:
function square() {
//Some Calculations...
let bounds = [
[lat,lon],[lat,lon]
];
L.rectangle(bounds, {
color: "#ff7800",
weight: 1
}).addTo(map);
map.fitBounds(bounds)
}
function radius() {
//Some Calculations...
L.circle([lat, lon], {
radius: (km_R * 1000)
}).addTo(map);
}
There should be no need for the app to reload. Everything worked fine, until I added those last two functions. I have debugged, and the functions run until the end. No error messages are ejected.
Is this just an issue with an Electron App's format? Will it go away, after I build and package the App?
Smells like you have an HTML <form> with a <button> or <input type="submit"/>, since you target an element with id "submit".
If that is the case, the HTML specified behaviour is to send the form data to the page indicated by the action attribute. If there no such indication, it sends the data to the current page, effectively reloading it.
See also javascript redirect not working anyway
The easy solution is simply to add event.preventDefault() at the start of your event listener.
Leaflet itself has no mean to reload your page.
For refererence for the case of a button: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
If your buttons are not for submitting form data to a server, be sure to set their type attribute to button. Otherwise they will try to submit form data and to load the (nonexistent) response, possibly destroying the current state of the document.
My current code is
addpolygon: function(e) {
var vm = this;
var point = {
lat: parseFloat(e.latLng.lat()),
lng: parseFloat(e.latLng.lng())
};
vm.coord.push(point);
vm.replot();
vm.marker = new google.maps.Marker({
position: point,
map: vm.map,
icon: "/fred.png"
});
vm.infowindow = new google.maps.InfoWindow({
content:"<a class=\"btn btn-danger\" #click.native=\"removePoint("+vm.markerid+)\">Remove</a>",
maxWidth: 300
});
vm.bindInfoWindow(vm.marker, vm.map, vm.infowindow);
vm.markers[vm.markerid] = {
marker: vm.marker,
point: point
};
vm.markerid++;
},
When I click on Remove, I need to trigger another function remove Point.
I defined it as
removePoint: function(id) {
alert("adsf")
},
But I am not able to trigger the same using the above code. Nothing happens when I click on the button remove. What is the problem regarding the same. Please help me to have a solution?
New Solution
Call a global method from InfoWindow using plain-old click handler.
`onclick="removePoint(${vm.markerId})"`
Then use a closure to access your vm from the global method.
const vm = this
window.removePoint = function(id) {
vm.removePoint(id)
}
IF you have multiple instances, you will need to extend this approach.
Old Solution
There are 2 issues here.
First, fix the syntax error concerning the quote.
vm.markerid + ")\">Remove</a>"
Even better, take advantage of template strings to avoid this kind of quote insanity.
vm.infowindow = new google.maps.InfoWindow({ content:`
<a class="btn btn-danger" #click.native="removePoint(${vm.markerid})">Remove</a>`, maxWidth: 300 });
Second, any function inside a vue template is always within the scope of the component. Assume a this. object is placed in front. So calling removePoint is really calling this.removePoint.
Define function inside instance.
vm.removePoint = function(id) {
console.log(`removing point ${id}...`)
}
Or make sure your component options defines removePoint in the methods section.
You can also define removePoint globally (on the window object) and call $window.removePoint(" + vm.markerId + ")" from the template if using a plugin such as https://www.npmjs.com/package/window-plugin.
#click.native=\"$window.removePoint(" + vm.markerid ...
function removePoint(id) {
console.log(`removing point ${id}...`)
}
#StevenSpungin solution worked like a charm. Thanks.. Just to make it simple.
while creating marker content,
markerContent += `<button onclick='editVehicle(${vehicle.id});'>EDIT</button>`;
and on created(any component)
created() {
let that = this;
window.editAppointment = function(vehicleId) {
console.log("vehicleId", vehicleId);
}
}
in your mounted method map the window method to the vue method:
mounted(){
this.initMap();
window.linkToKey = this.linkToKey; // vue method wired to window method
},
and in the html for your infoWindow:
const contentString =`<img onClick="linkToKey('${video.key}')" src="images/${video.key}.png">`;
const infowindow = new google.maps.InfoWindow({
content: contentString,
});
and then you can define your vue method as expected:
methods: {
linkToKey(key) {
console.log('key', key);
this.$router.push(`/video/${key}`);
},
then the window method is wired to your vue method and you can do everything as expected on the click of any items in the InfoWindow
I have a JavaScript Listener within a Google Maps object:
map.data.addListener('click', (event) => {
var mapElement = event.feature.getProperty('type');
switch(mapElement){
case 'cameras':
var cameraID = event.feature.getProperty('trafficID');
this.cameraService.createCamera(cameraID);
break; ...
This 'map' Listener calls a function within a service called 'cameraService' which initialises the camera. I have a DOM object which uses Angular 2's ngIf directive to determine whether it exists:
<div *ngIf="cameraService.camera1 != null">
<app-camera></app-camera>
</div>
My problem: ngIf doesn't run after the click event happens. Console logs show that the camera object is created, and if I click on another element (outside of the map), the view updates (ngIf change detection starts up again).
I assume this issue to do with the Angular 2 zone's - so when I click on the Google Map, it is outside of the Angular 2 zone and when I click on another element (within Angular 2), I re-enter the Angular 2 zone [Correct me if I am wrong!]... How do I manually trigger ngIf or re-enter the Angular 2 zone at the end of the click event to restart the Angular 2 change detection?
You can either use zone.run() to force the execution back into Angulars zone or you can invoke change detection manually:
constructor(private zone:NgZone, private cdRef:ChangeDetectorRef) {}
map.data.addListener('click', (event) => {
this.zone.run(() => {
var mapElement = event.feature.getProperty('type');
switch(mapElement){
case 'cameras':
var cameraID = event.feature.getProperty('trafficID');
this.cameraService.createCamera(cameraID);
break; ...
});
}
or
map.data.addListener('click', (event) => {
var mapElement = event.feature.getProperty('type');
switch(mapElement){
case 'cameras':
var cameraID = event.feature.getProperty('trafficID');
this.cameraService.createCamera(cameraID);
this.cdRef.detectChanges(); // <<<=== added
break; ...
}
i am using the ARCGIS Javascript API and trying to override the default right click behavior of the vertex points of a shape.
in ESRI's help it does list the onVertexClick event however from here it seems there is no way to determine if this is a right or left click event so i cannot override just the rightclick.
https://developers.arcgis.com/javascript/jsapi/edit.html
I am trying to set the right click behavour to just delete the current node/vertex instead of showing a menu with the option Delete.
EDIT
Here is the current event that exists within the ARCGIS api.
this.eventsList.push(dojo.connect(this._editToolbar, 'onVertexClick', $.proxy(this.addCustomVertexClickEvent, this)));
this event is already in the api however it does not return any way for me to determine left/right click.
your comment "listen for the click event then test the button attribute of the MouseEvent object" would work however i cant actually add a click event to the vertex points directly as these are inside the ARCGIS api code.
For anyone else who is looking for a way to do this without hacking around. You can listen to "contextmenu" (right click) events on the body, set a flag in the "contextmenu" handler to let the application know the current state. Simulate a click event to the "vertex handle" with a "mousedown", "mouseup" combination. In the "vertex-click" handler check for the right click flag set in the "contextmenu" handler
var editToolbar = new Edit(map, options);
var rightClick;
$('body').on('contextmenu', function(e) {
var target = e.target;
if(target.tagName === 'circle') {
// We only care about this event if it targeted a vertex
// which is visualized with an SVG circle element
// Set flag for right click
rightClick = true;
// Simulate click on vertex to allow esri vertex-click
// to fill in the data for us
var mouseDownEvt = new MouseEvent('mousedown', e.originalEvent);
target.dispatchEvent(mouseDownEvt);
var mouseUpEvt = new MouseEvent('mouseup', e.originalEvent);
target.dispatchEvent(mouseUpEvt);
// Since this event will be handled by us lets prevent default
// and stop propagation so the browser context menu doesnt appear
e.preventDefault();
e.stopPropagation();
}
});
editToolbar.on('vertex-click', function(e) {
if(rightClick) {
// Handle the right click on a vertex
rightClick = null;
}
});
after hearing back from ESRI it seems they do not provide this detail in their API so this is not possible yet.
I ended up doing this differently. I wanted to add a UI so the user could enter the XY of the point
// setup to allow editing
this.editToolbar = new EditToolbar(this.map, { allowDeleteVertices: false });
const rcMenuForGraphics = new RightClickVertexContextMenu();
const menu = rcMenuForGraphics.createMenu();
// bind to the map graphics as this is where the vertex editor is
this.map.graphics.on("mouse-over", (evt)=> {
// bind to the graphic underneath the mouse cursor
menu.bindDomNode(evt.graphic.getDojoShape().getNode());
});
this.map.graphics.on("mouse-out", (evt)=> {
menu.unBindDomNode(evt.graphic.getDojoShape().getNode());
});
this.editToolbar.on("vertex-click", (evt2) => {
rcMenuForGraphics.setCurrentTarget(evt2);
// evt2.vertexinfo.graphic.geometry.setX(evt2.vertexinfo.graphic.geometry.x - 1000);
})
// when the graphics layer is clicked start editing
gl.on("click", (evt: any) => {
this.map.setInfoWindowOnClick(false);
// tslint:disable-next-line: no-bitwise
const t: any = EditToolbar.MOVE | EditToolbar.EDIT_VERTICES;
this.editToolbar.deactivate();
this.editToolbar.activate(t, evt.graphic);
})
The code for the menu uses esri's vertex editor to grab the point, change its XY and then manually call the events to refresh the geometry. Only tested with polygon
import Menu = require("dijit/Menu");
import MenuItem = require("dijit/MenuItem");
import Graphic = require("esri/graphic");
import Edit = require("esri/toolbars/edit");
import Point = require("esri/geometry/Point");
class RightClickVertexContextMenu {
private curentTarget: { graphic: Graphic; vertexinfo: any; target: Edit; };
public createMenu() {
const menuForGraphics = new Menu({});
menuForGraphics.addChild(new MenuItem({
label: "Edit",
onClick: () => {
// this is a bit hooky. We grab the verx mover, change the x/y and then call the _moveStopHandler
console.log(this.curentTarget.vertexinfo);
const e: any = this.curentTarget.target;
const mover = e._vertexEditor._findMover(this.curentTarget.vertexinfo.graphic);
const g: Graphic = mover.graphic;
// add in a UI here to allow the user to set the new value. This just shifts the point to the left
g.setGeometry(new Point(mover.point.x - 1000, mover.point.y ))
e._vertexEditor._moveStopHandler(mover, {dx: 15});
this.curentTarget.target.refresh();
}
}));
menuForGraphics.addChild(new MenuItem({
label: "Delete",
onClick: () => {
// call the vertex delete handler
const ct: any = this.curentTarget.target;
ct._vertexEditor._deleteHandler(this.curentTarget.graphic)
}
}));
return menuForGraphics;
}
public setCurrentTarget(evt: { graphic: Graphic; vertexinfo: any; target: Edit; }) {
this.curentTarget = evt;
}
}
export = RightClickVertexContextMenu;