I have list of markers that want to render in the map, but I want it one by one. In first click I want to make new marker. Then when I click to another location, I want my marker to just move to the new latLng not to create another marker. Here is my code:
function (licon, coord, data) {
var self = jQuery(this);
var map = self.data("map");
var latlng = new L.LatLng(coord[0], coord[1]);
//Create Marker
if (licon) {
var leafIcon = L.icon(licon);
console.log(typeof (marker));
if (typeof (marker) === 'undefined') {
var marker = L.marker(latlng, {
icon: leafIcon,
"markerData": data,
draggable: true
});
} else {
console.log('not undefined');
map.removeLayer(marker);
marker = L.marker(latlng, {
icon: leafIcon,
"markerData": data,
draggable: true
});
}
} else {
var marker = L.marker(latlng, {
"markerData": data,
draggable: true
});
}
marker.addTo(map);
return marker;
}
A quick example of the result: http://jsfiddle.net/ve2huzxw/43/
var currentMarker;
map.on("click", function (event) {
if (currentMarker) {
currentMarker.setLatLng(event.latlng);
return;
}
currentMarker = L.marker(event.latlng, {
draggable: true
}).addTo(map).on("click", function () {
event.originalEvent.stopPropagation();
});
});
document.getElementById("done").addEventListener("click", function () {
currentMarker = null;
});
You can also add a smooth transition to show the marker moving to the new position:
if (currentMarker) {
currentMarker._icon.style.transition = "transform 0.3s ease-out";
currentMarker._shadow.style.transition = "transform 0.3s ease-out";
currentMarker.setLatLng(event.latlng);
setTimeout(function () {
currentMarker._icon.style.transition = null;
currentMarker._shadow.style.transition = null;
}, 300);
return;
}
a slightly more consolidated solution some years later.
var currentMarker;
map2.on('click', function(e) {
if (currentMarker){
currentMarker.setLatLng(e.latlng)
} else {
currentMarker = new L.marker(e.latlng).addTo(map2);
};
//console.log('lat, lon: ', e.latlng.lat, e.latlng.lng);
});
leaflet now defaults to smoothly dragging the point over to the new coords.
Related
I am trying to add a custom transform control to geoman, to do certain transformations with polylines and polygons. I see that on edit, geoman draws hint lines above vertices etc. I would like my tool to highlight polylines/polygons with the same type of hints. Below is the skeleton of my action:
const ConvertAction = L.Toolbar2.Action.extend({
options: {
toolbarIcon: {
html:
'<div class="icon-maps icon-convert" title="Convert point"></div>',
tooltip: 'Convert point'
}
},
addHooks: () => {
// draw polygon
// map.pm.enableDraw();
changeConvert();
}
});
function changeConvert() {
convert = true;
map.eachLayer(function (layer) {
if (layer.feature && layer.feature.geometry.type === 'Point') {
layer._icon.style['pointer-events'] = 'auto';
}
});
}
Is there an internal function or something that I could use to outline shapes? When I enable Edit layers tool already built into the geoman, shapes are outlined for me. How could I achieve this from my code without having to reimplement the entire thing?
Thus far, after quickly reviewing geoman code, I was able to come up with:
const ConvertAction = L.Toolbar2.Action.extend({
options: {
toolbarIcon: {
html:
'<div class="icon-maps icon-convert" title="Convert point"></div>',
tooltip: 'Convert point'
}
},
addHooks: () => {
// draw polygon
// map.pm.enableDraw();
if (!convert) changeConvert();
else disableConvert();
}
});
function changeConvert() {
convert = true;
map.eachLayer(function (layer) {
if (
layer?.feature?.geometry.type === 'Polygon' ||
layer?.feature?.geometry.type === 'LineString'
) {
const coords = layer.getLatLngs();
const markerGroup = new L.LayerGroup();
markerGroup._pmTempLayer = true;
const createMarker = (latlng) => {
const marker = new L.Marker(latlng, {
draggable: true,
icon: L.divIcon({ className: 'marker-icon' })
});
layer.options.pane =
(map.pm.globalOptions.panes &&
map.pm.globalOptions.panes.vertexPane) ||
'markerPane';
marker._pmTempLayer = true;
markerGroup.addLayer(marker);
return marker;
};
const handleRing = (coordsArr) => {
// if there is another coords ring, go a level deep and do this again
if (Array.isArray(coordsArr[0])) {
return coordsArr.map(handleRing, this);
}
// the marker array, it includes only the markers of vertexes (no middle markers)
const ringArr = coordsArr.map(createMarker);
return ringArr;
};
const markers = handleRing(coords);
map.addLayer(markerGroup);
}
});
}
function disableConvert() {
convert = false;
map.eachLayer(function (layer) {
if (
layer.dragging &&
layer.options?.draggable === true &&
layer._pmTempLayer === true
) {
console.log('temp layer:', layer);
map.removeLayer(layer);
}
});
update();
}
Seems like an excessive amount of code, and reimplementation [and probably not as good the geoman version as I don't fully understand geoman code by far] of existing functionality.
How do I simplify/fix this?
How do I stop cursor flickering over my Mapbox icons? Here is the live site where you can see the problem when hovering over the marker icons: https://rustic-waters-group.thesparksite.com/lakes/
Here is my code:
mapboxgl.accessToken = 'pk.eyJ1IjoiZG1pdHJpbWFydGluIiwiYSI6ImNreHRobHRmcjVqM3cydmt5NWkxdWNibTcifQ.CuN5Dwc963TW-BKRcowxBA';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/dmitrimartin/ckxtj5aur4fvv15mrhmihala8',
center: [-89.2, 44.33],
zoom: 9.2
});
map.on('load', () => {
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.on('mouseenter', 'lakes', (event) => {
map.getCanvas().style.cursor = 'pointer';
const features = map.queryRenderedFeatures(event.point, {
layers: ['lakes']
});
if (event.features.length === 0) return;
popup.setLngLat(features[0].geometry.coordinates);
popup.setHTML('<h3 class="lake-popup">' + features[0].properties.title + '</h3>');
popup.setMaxWidth("calc(100vw - 40px);");
popup.addTo(map);
});
map.on('mouseleave', 'lakes', () => {
map.getCanvas().style.cursor = '';
popup.remove();
});
map.on('click', 'lakes', (event) => {
const features = map.queryRenderedFeatures(event.point, {
layers: ['lakes']
});
if (event.features.length === 0) return;
window.location.href = ('/lakes/' + features[0].properties.url + '/');
});
document.getElementById("lakes-header").onmouseover = function() {
MyMouseOver();
};
function MyMouseOver() {
document.getElementById("wrapper").style.display = "none";
document.getElementById("lakes-header").style.display = "none";
}
});
The issue stems from the popup box on top of the layer and can be fixed by adding the CSS property 'cursor-events: none' to the popup. That should stop the flickering and shouldn't interfere with the layers.
Lines and polygons are added without problems, but when trying to add markers on top of a polygon layer ("lyrPoly"), it interprets that I want to click on the polygon (e.g. opens popup) instead of adding marker. How can I avoid this? I am using leaflet.pm to add markers, linestrings and polygons.
This is the code for the marker:
map.on('pm:create', function(e) {
e.marker;
var type = e.layerType,
layer = e.layer;
$(document).ready(function() {
var jsnMarker = layer.toGeoJSON().geometry;
jsnMarker = {
type: "MultiPoint",
coordinates: [jsnMarker.coordinates]
};
})
});
$(function() {
$('.check').change(function() {
var data = $(this).val();
if (data == "versionOne") {
var markerStyle = {
draggable: false,
icon: customIcon,
};
var options = {
markerStyle,
}
map.pm.enableDraw('Marker', options);
} else if (data == "versionTwo") {
var markerStyle = {
draggable: false,
icon: otherIcon,
};
var options = {
markerStyle,
}
map.pm.enableDraw('Marker', options);
}
$('.check').trigger('change');
});
And inside the ajax function I use this:
lyrMarker = L.geoJSON(jsnMarker, {
pointToLayer: function (feature, latlng) {
if(feature.properties.marker_type=="versionOne"){
return L.marker(latlng, {
icon: customIcon
})
}else if
(feature.properties.marker_type=="versionTwo"){
return L.marker(latlng, {
icon: otherIcon
})
} ,
onEachFeature: forEachMarker
});
For the polygon-layer I have a corresponding "onEachFeature"-function that looks like this:
function forEachFeature(feature, layer) {
var att = feature.properties;
var popupContent =
"Popup-content" ;
layer.bindPopup(popupContent, {
offset: [0, -120]
});
layer.on("click", function (e) {
lyrPoly.setStyle(style); //resets layer colors
layer.setStyle(highlight); //highlights selected.
});
};
Adding marker to the background/basemap works fine, but not onto the polygon layer. Why is that, and what would be a good solution? I don't want to add the marker to the same layer as the polygons, but avoid polygons "getting in the way".
I've had ideas about making the polygon layer interactive: false while in adding-marker-mode, but haven't succeeded.
Update your click event and test if drawing mode is enabled:
function forEachFeature(feature, layer) {
var att = feature.properties;
var popupContent =
"Popup-content" ;
layer.bindPopup(popupContent, {
offset: [0, -120]
});
layer.on("click", function (e) {
if(!map.pm.Draw.Marker.enabled()){
lyrPoly.setStyle(style); //resets layer colors
layer.setStyle(highlight); //highlights selected.
}
});
};
Update
To disable the popup open event add following code at the top of your file. Before you create a Layer with a Popup:
L.Layer.include({
_openPopup: function (e) {
var layer = e.layer || e.target;
//This IF is new
if(map.pm.Draw.Marker.enabled()){
return;
}
if (!this._popup) {
return;
}
if (!this._map) {
return;
}
// prevent map click
L.DomEvent.stop(e);
// if this inherits from Path its a vector and we can just
// open the popup at the new location
if (layer instanceof L.Path) {
this.openPopup(e.layer || e.target, e.latlng);
return;
}
// otherwise treat it like a marker and figure out
// if we should toggle it open/closed
if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
this.closePopup();
} else {
this.openPopup(layer, e.latlng);
}
},
});
Example: https://jsfiddle.net/falkedesign/dg4rpcqe/
In my Ionic 2 application, I'm able to see my current location on the map, but how can I add the location (longtitude and latitude) to Firebase?
initMap(): Promise<any> {
this.mapInitialised = true;
return new Promise((resolve) => {
Geolocation.getCurrentPosition().then((position) => {
let latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
//let latLng = new google.maps.LatLng(40.713744, -74.009056);
let mapOptions = {
center: latLng,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
this.map = new google.maps.Map(this.mapElement, mapOptions);
resolve(true);
google.maps.event.addListener(this.map, 'click', (event) => {
this.clearMarkers();
let geocoder = new google.maps.Geocoder;
let infowindow = new google.maps.InfoWindow;
let distanceToYou = this.getDistanceBetweenPoints(
event.latLng,
position,
'miles'
).toFixed(2);
this.geocodeLatLng(event.latLng,geocoder,infowindow,distanceToYou);
});
});
});
}
I'm able to get the current position of me and if I double click the map I can see the distance between me and the marker.
Yes it's possible. But since double clicking a map zooms it, i'll use press. But it would be better if you set a button on a info window with a "save location" text or something like this to call the saving function.
YOUR HTML
<div #mapID (press)="saveLocation()">
YOUR .TS
import * as firebase from 'firebase';
public lat;
public long;
initMap(): Promise<any> {
this.mapInitialised = true;
return new Promise((resolve) => {
Geolocation.getCurrentPosition().then((position) => {
this.lat = position.coords.latitude;
this.long = position.coords.longitude;
// YOUR CODE CONTINUES HERE...
});
});
});
saveLocation(){
firebase.database().ref('PATH/YOU/WANT/TO/SAVE').update({
lat: this.lat,
long: this.long
}).then(res =>{
// THE LOCATION IS SAVED, DO YOUR STUFF
})
}
Like this you can save you location, use update() instead of set() so you don't subscribe other data.
If you want to retrieve that data just use firebase.database().ref('PATH/YOU'VE/SAVED').once('value', snapshot =>{ // CODE });
Hope it helps
I have error about for info window data. I can't get the Please help me to check my code.
function initialize() {
map = new google.maps.Map(document.getElementById(map), {
center: new google.maps.LatLng(1.352083, 103.819836),
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
//var infowindow;
if (markers) {
for (var level in markers) {
for (var i = 0; i < markers[level].length; i++) {
var details = markers[level][i];
//var infowindow;
markers[level][i] = new google.maps.Marker({
title: details.name,
position: new google.maps.LatLng(
details.location[0], details.location[1]),
clickable: true,
draggable: false,
icon: details.icon
});
var infowindow = new google.maps.InfoWindow({
content: details.description,
//content : markers[level][i].description,
position: new google.maps.LatLng(details.location[0], details.location[1])
//position: markers[level][i].position
});
google.maps.event.addListener(markers[level][i], 'click', function() {
infowindow.setPosition(this.position);
alert(this.position);
//infowindow.setContent(markers[level][i].description);
infowindow.open(map,markers[level][i]);
});
}
}
}
I can't get the description data. Please help me to check my code.