How in leaflet add custom data to polyline? - javascript

In jquery 3/leaflet / turf app
I use custom class extended from CircleMarker
as I need in any marker keep info about any point and info on nearby points.
Markers are connected with polylines and I want to keep simialr information polyline
and clicking on it get this info. I failed to make it. I do
customCircleMarker = L.CircleMarker.extend({
options: {
first_market: false,
last_market: false,
point_id: null,
prior_point_id: null,
}
});
var selectedPoint= {}
var points = [
{id: 1, title:'title #1 ', lat:52.509, lng:-3.08},
{id: 2, title:'title #2 ', lat:51.503, lng:-1.06},
{id: 3, title:'title #3 ', lat:49.51, lng:-2.47}
];
var mymap = L.map('mapid').setView([51.505, -0.09], 7);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1
}).addTo(mymap);
drawPoints()
function drawPoints() {
let polylinePoints= [] // I get all info about all Polylines
let loop_index = 0
points.forEach(point => {
let priorPoint= null
if(loop_index > 0) {
priorPoint= points[loop_index - 1]
}
var myMarker = new customCircleMarker([point.lat, point.lng], {
title: 'unselected',
radius: 20,
first_market: loop_index == 0,
last_market: loop_index == points.length-1,
point_id: point.id,
prior_point_id: priorPoint ? priorPoint.id : null,
});
myMarker.on('click', function (event) { // THAT WORKS OK
console.log('myMarker.event.target.options.point_id::')
console.log(event.target.options.point_id)
});
myMarker.addTo(mymap);
polylinePoints[polylinePoints.length]=[point.lat, point.lng]
loop_index++
})
var radius = 10;
var polyline = new L.Polyline(polylinePoints, {
color: 'green',
opacity: 1,
weight: 2,
customData:{ // BUT TAT DOES NOT WORK AS POINT IS OUT OF LOOP
point_id: point.id,
prior_point_id: priorPoint ? priorPoint.id : null,
}
// offset: radius
});
// Add click listener
polyline.on('click', function (event) {
event.originalEvent.stopPropagation();
window.event.cancelBubble = true; // CAN NOT STOP Propagation
showModal(event)
// alert('Polyline clicked!');
});
// Add polyline to featuregroup
polyline.addTo(mymap);
// zoom the map to the polyline
mymap.fitBounds(polyline.getBounds());
} // function drawPoints () {
How can I add custom data to polyline ?
Thanks!

You don't have to extend the CircleMarker class to add more options. You can do this at the default way:
var myMarker = L.circleMarker([point.lat, point.lng], {
title: 'unselected',
radius: 20,
first_market: loop_index == 0,
last_market: loop_index == points.length-1,
point_id: point.id,
prior_point_id: priorPoint ? priorPoint.id : null,
});
Also don't use polylinePoints[polylinePoints.length]= if it is not necessary. Use polylinePoints.push(
What do you want with the data on the polyline? Why you not adding the whole point array to the polyline?
var polyline = new L.Polyline(polylinePoints, {
customData:{
points: points
}
});
Else you can create a array of the point ids:
let polylinePoints= [] // I get all info about all Polylines
let loop_index = 0;
let pointIds = [];
points.forEach(point => {
pointIds.push(point.id);
//...
var polyline = new L.Polyline(polylinePoints, {
customData:{
points: pointIds
}
});
Or (what I recommand) to add the markers to the polyline:
let markersForPoly = [];
points.forEach(point => {
//... Loop ...
myMarker.addTo(mymap);
markersForPoly .push(myMarker);
});
//.. Code
var polyline = new L.Polyline(polylinePoints, {
customData:{
points: markersForPoly
}
});
And the you can get the points in the click listener:
polyline.on('click', function (event) {
var layer = event.target;
var points = layer.options.customData.points;
console.log(points);
});
Example
https://jsfiddle.net/falkedesign/61sjx3bv/

Related

`Uncaught Error: Map container is being reused by another instance` from second manual search

I am currently trying to make an IP address checker which checks user's ip on load and on the user's manual search using Leaflet and other apis like ipgeolocation and currencyconverer. Currently, the map loads perfectly on load and on the first manual search after load. But when I try to do the second manual IP search I get this -
Map.js:745 Uncaught Error: Map container is being reused by another instance
at i.remove (Map.js:745:10)
at HTMLButtonElement.mapOff (index.js:136:25)
and then the result shows up like the first search.
Why is it appearing and how do I solve it?
Github Repository Link - https://github.com/MustakAbsarKhan/ip-address-tracker
Code-
//loading map and it's features
mapload ();
function mapload (){
count++;
if(count===1){
//map initiation
var map = L.map('map').setView([latitude, longitude], 13);
}else if(count === 2){
var map = L.map('map').setView([latitude, longitude], 13);
count --;
}
//maptile setup
L.tileLayer('https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=Kiarb32YtKIgXk1i9lL1',{
tileSize: 512,
zoomOffset: -1,
minZoom: 1,
attribution: "\u003ca href=\"https://www.maptiler.com/copyright/\" target=\"_blank\"\u003e\u0026copy; MapTiler\u003c/a\u003e \u003ca href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\"\u003e\u0026copy; OpenStreetMap contributors\u003c/a\u003e",
crossOrigin: true
}).addTo(map);
//map icon
var blackIcon = L.icon({
iconUrl: 'images/icon-location.svg',
iconSize: [30, 40]
});
//marker & popup on marker
L.marker([latitude, longitude],{icon: blackIcon}).addTo(map)
.bindPopup('Your IP Shows You Here')
.openPopup();
//popup on map click
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
//leaflet-locatecontrol plugin
var lc = L.control.locate({
position: 'topleft',
tap: false,
strings: {
title: "Click here, to get your device's current location"
},
locateOptions: {
enableHighAccuracy: true
}
}).addTo(map);
count--;
function mapOff(){
map.off();
map.remove();
};
button.addEventListener('click',mapOff);
};
The issue is solved by separating the marker, making the code only reposition the marker if the map and layer are already defined.
The thing that did cause the problem was- The map and Its Layer Container were getting initialized over and over following the button click.
Hope someone in the future will find this solution useful.
The Code-
let count = 0;
ipgeolocationapiCALL(ipgeolocationAPI);
//if user clicks on the button
button.addEventListener("click", function () {
if (searchbox.value !== "") {
ipgeolocationAPI =
`https://api.ipgeolocation.io/ipgeo?${params}&ip=` + searchbox.value;
count++;
ipgeolocationapiCALL(ipgeolocationAPI);
};
});
//initialization of map on load cause the count value stays 0 at the beginning and when the search icon is pressed for the first time
function initializeMap(latitude, longitude) {
map = L.map("map").setView([latitude, longitude], 13);
L.tileLayer(
"https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=Kiarb32YtKIgXk1i9lL1",
{
tileSize: 512,
zoomOffset: -1,
minZoom: 1,
attribution:
'\u003ca href="https://www.maptiler.com/copyright/" target="_blank"\u003e\u0026copy; MapTiler\u003c/a\u003e \u003ca href="https://www.openstreetmap.org/copyright" target="_blank"\u003e\u0026copy; OpenStreetMap contributors\u003c/a\u003e',
crossOrigin: true,
}
).addTo(map);
var blackIcon = L.icon({
iconUrl: "images/icon-location.svg",
iconSize: [30, 40],
});
L.marker([latitude, longitude], { icon: blackIcon })
.addTo(map)
.bindPopup("Your IP Shows You Here")
.openPopup();
L.control
.locate({
position: "topleft",
tap: false,
strings: {
title: "Click here, to get your device's current location",
},
locateOptions: {
enableHighAccuracy: true,
},
})
.addTo(map);
}
function ipgeolocationapiCALL(api) {
//returns "ip": "103.145.74.149","continent_code": "AS","continent_name": "Asia","country_code2": "BD","country_code3": "BGD","country_name": "Bangladesh","country_capital": "Dhaka","state_prov": "Dhaka Division","district": "Savar Upazila","city": "Savar Union","zipcode": "","latitude": "23.86170","longitude": "90.25649","is_eu": false,"calling_code": "+880","country_tld": ".bd","languages": "bn-BD,en","country_flag": "https://ipgeolocation.io/static/flags/bd_64.png","geoname_id": "1200292","isp": "Master Net","connection_type": "","organization": "Master Net","currency": {"code": "BDT","name": "Bangladeshi Taka","symbol": "৳"},"time_zone": {"name": "Asia/Dhaka","offset": 6,"current_time": "2022-08-28 15:24:16.540+0600","current_time_unix": 1661678656.54,"is_dst": false,"dst_savings": 0
fetch(api)
.then((response) => response.json()) //collects data as json
.then((data) => {
//declaring contents of api as objects
const ip = data.ip; //103.145.74.149
const { city, country_name, isp, country_flag, latitude, longitude } =
data; //Dhaka, Bangladesh,Master Net
const { current_time, name } = data.time_zone; //"2022-08-27 23:25:49.527+0600";
const { code, symbol } = data.currency; //BDT,TAKA SYMBOL
let timezone = current_time.slice(current_time.length - 5); //+0600
let date = current_time.slice(0, current_time.search(" ")); // 2022-08-27
let time = current_time.slice(date.length + 1, date.length + 9); //23:01:28
let exactTimezone =
"UTC " +
timezone.slice(0, 3) +
":" +
timezone.slice(timezone.length - 2, timezone.length); //UTC +06:00
//assigning api values to html elements
ipAddress.textContent = ip;
cityData.textContent = city + ",";
countryData.textContent = country_name;
timezoneData.textContent = exactTimezone + ",";
timeData.textContent = time + ",";
dateData.textContent = date;
ispData.textContent = isp;
currencyData.textContent = code + ` (${symbol})`;
flagIcon.src = country_flag;
let currencyCODE = code; //assigining fetched value to this variable for being able to reassign value to following conditions
if (currencyCODE === "USD") {
currencyCODE = "EUR";
let xchangeRateAPI = `https://api.exchangerate.host/convert?from=USD&to=${currencyCODE}`;
xchangeRateAPICALL(xchangeRateAPI);
} else {
let xchangeRateAPI = `https://api.exchangerate.host/convert?from=USD&to=${currencyCODE}`;
xchangeRateAPICALL(xchangeRateAPI);
}
//calling exchange rate api. This one Converts USD to User's Currency and For users who lives in United States it would convert 1 USD to Euro.
function xchangeRateAPICALL(api) {
fetch(api)
.then((response) => response.json())
.then((data) => {
const { to } = data.query;
const { result } = data;
const convertedAmount = result.toFixed(2);
currencyconvertData.textContent =
"$ 1 = " + `${to} ${convertedAmount}`;
});
}
//default value of count is 0 which gets incremented in the previous if function(which checks if the input field has any value)
if (count === 0) {//initializing the map and the layout on load
initializeMap(latitude, longitude);
} else {//resetting the marker position as the map and layout is already initialized
var blackIcon = L.icon({
iconUrl: "images/icon-location.svg",
iconSize: [30, 40],
});
L.marker([latitude, longitude], { icon: blackIcon })
.addTo(map)
.bindPopup("Your IP Shows You Here")
.openPopup();
}
})
.catch((error) => {
console.log("Error is "+ error);
alert("Wrong IP. Please Try Again.");
searchbox.value = "";
});

add marker to middle of polyline in leaflet

I have a leaflet map with polyline data in. The polyline is styled how I want but what I would like is to have a marker at the centre of each line. Is this possible and if so what changes to the below do I need to make?
var pathstyling = {
stroke: true,
fillColor: "#b5b5b5",
color: "#b5b5b5",
weight: 5,
opacity: 1,
fillOpacity: 0.6,
dashArray: 10,
};
const path = L.geoJSON(path_line, {
style: pathstyling,
})
.bindPopup(function (layer) {
let cap_name = layer.feature.properties.name.replace(
/(^\w{1})|(\s+\w{1})/g,
(letter) => letter.toUpperCase()
);
return `<p>${cap_name}</p><a href="https://${layer.feature.properties.link}" target="_blank">View<a>`;
/******/
})
.addTo(map);
You can simply do this with leaflet core:
function calcMiddleLatLng(map, latlng1, latlng2) {
// calculate the middle coordinates between two markers
const p1 = map.project(latlng1);
const p2 = map.project(latlng2);
return map.unproject(p1._add(p2)._divideBy(2));
}
function createMiddleMarkers(line){
var latlngs = line.getLatLngs();
for(var i = 1; i < latlngs.length; i++){
var left = latlngs[i-1];
var right = latlngs[i];
var newLatLng = calcMiddleLatLng(map,left,right);
L.marker(newLatLng).addTo(map);
}
}
createMiddleMarkers(layer);
https://jsfiddle.net/falkedesign/g7e8w9tz/

How to use leaflet-indoor plugin and draggable object in leaflet.1.0.3

When using leaflet v0.7.7 then leaflet-indoor works perfectly.
After updating to leaflet v1.0.3 markers become draggable, but now leaflet-indoor is not working.
Moreover, leaflet itself is throwing an error:
TypeError: Cannot read property 'call' of undefined
const MapController = function() {
// Containers
var $window = $(window),
mapEl = document.getElementById('map-base'),
mapFileName = window.mapFile,
resourceSidebar = document.getElementById('resourceSidebar'),
detailSidebar = document.getElementById('detailSidebar');
// Links
var addResource = document.querySelectorAll('[data-add-resource]');
setHeight();
// Create map perimeter
// console.log(window);
var view = window.mapDefaultView.split(',');
var map = new L.Map(mapEl, {
center: new L.LatLng(parseFloat(view[0]),parseFloat(view[1])),
zoom: parseFloat(window.mapDefaultZoom),
zoomControl: true
});
makePerimeter()
L.marker([parseFloat(view[0]),parseFloat(view[1])], {
draggable: true,
icon: L.divIcon({
iconSize: null,
html: "<div style='padding:1rem'>Hi</div>"
})
}).addTo(map);
// Not draggable
// Just here for visual reference
// when dragging marker
var circle = L.circle([parseFloat(view[0]),parseFloat(view[1])],100000).addTo(map);
L.control.mousePosition().addTo(map);
// Set heights on the main containers
function setHeight() {
var winHeight = $window.height()+'px';
mapEl.style.height = winHeight;
resourceSidebar.style.height = winHeight;
detailSidebar.style.height = winHeight;
}
// Open the detail sidebar
function toggleDetailSidebar() {
var el = detailSidebar;
if (el.classList.contains('active')) {
el.classList.remove('active');
} else {
el.classList.add('active');
}
}
// Create Perimeter, Guides, and Sections
function makePerimeter() {
$.get(window.mapDataFilePath, function(data) {
var baseLayer = new L.Indoor(data, {
getLevel: function(feature) {
if (feature.properties.relations.length === 0)
return null;
return feature.properties.relations[0].reltags.level;
},
onEachFeature: function(feature, layer) {
layer.bindPopup(JSON.stringify(feature.properties, null, 4));
},
style: function(feature) {
var fill = '#fafafa',
stroke = '#4d4d4d',
part = feature.properties.tags.buildingpart;
switch (part) {
case 'guide':
fill = '#eee';
stroke = '#eee';
break;
case 'section':
fill = 'transparent';
stroke = 'transparent';
break;
}
return {
fillColor: fill,
weight: 1,
color: stroke,
fillOpacity: 1
};
}
});
baseLayer.setLevel("0");
baseLayer.addTo(map);
var levelControl = new L.Control.Level({
level: "0",
levels: baseLayer.getLevels()
});
// Connect the level control to the indoor layer
levelControl.addEventListener("levelchange", baseLayer.setLevel, baseLayer);
levelControl.addTo(map);
perimeterWasMade()
});
}
function perimeterWasMade() {
// Save map view/zoom in hash, so it presists on refresh
var saveView = new L.Hash(map);
// Some other plugins I was messing around with
// Leave commented for now
//L.control.polylineMeasure({imperial: true}).addTo(map);
//L.control.mousePosition().addTo(map);
// 6' booth example
// This is the correct w/h of a 6'w booth that is 4'h
// except it is rotated incorrectly.
// It should be wider than it is tall.
var bounds = [
[ -0.0807 , 12.8787 ],
[ -0.0807 , 13.2845 ],
[ 0.5284 , 13.2845 ],
[ 0.5284 , 12.8787 ]
];
var booth = L.polygon(bounds, {color: 'red', weight: 1})
booth.addTo(map);
// Load booths
loadObjects()
}
function loadObjects() {
// load booths and prizes onto the map
loadJSON("booths.json", function(response) {
var actual_JSON = JSON.parse(response);
$.each(actual_JSON,function(i,val){
if (val.coordinate != undefined && val.size != null)
{
var size = val.size.split('x');
var marker = L.marker([val.coordinate.x,val.coordinate.y], {
id: val.id,
draggable: true,
icon: L.divIcon({
iconSize: null,
html: "<div style='height:"+size[0]+"; width="+size[1]+";'><div style=' padding:5px 10px;'>"+val.vendor.industry +" </div><span style='text-align:center;display:block;border-top:4px solid #888;'>"+val.vendor.name+"</span></div>"
})
}).addTo(map);
// label.dragging.enable();
marker.on('drag', function(event){
console.log("position");
});
//also display booths using leaflet.label
// var label = new L.Label();
// label.setContent("<div style='height:"+size[0]+"; width="+size[1]+";'><div style=' padding:5px 10px;'>"+val.vendor.industry +" </div><span style='text-align:center;display:block;border-top:4px solid #888;'>"+val.vendor.name+"</span></div>");
// label.setLatLng([val.coordinate.x,val.coordinate.y]);
// map.showLabel(label);
}
})
});
}
map.on('click', function(e) {
//alert(e.latlng);
});
// Catch click on resource in sidebar
if (addResource.length > 0) {
addResource.each(function() {
this.addEventListener('click', function(e) {
e.preventDefault();
mapEl.classList.add('adding-booth');
});
});
}
}

How to add clustered markers to a group [Here Maps api]

I want to hide specific markers on checkbox click, that's why i tried to
add these markers to groups. But when i do this iconsGroup.getObjects();
it returns an empty array. What am i doing wrong ?
var iconsGroup = new H.map.Group();
var CUSTOM_THEME = {
getClusterPresentation : function (cluster) {
var randomDataPoint = getRandomDataPoint(cluster),
bubbleContent = getBubbleContent(cluster);
// Get a reference to data object that DataPoint holds
map.setCenter(cluster.getPosition());
data = randomDataPoint.getData();
var marker = new H.map.DomMarker(cluster.getPosition(), {
icon: data.icon,
min: cluster.getMinZoom(),
max: cluster.getMaxZoom()
});
iconsGroup.addObject(marker);
marker.setData(bubbleContent);
marker.addEventListener('tap', onClusterClick);
return marker;
},
getNoisePresentation : function (noisePoint) {
var data = noisePoint.getData();
var noiseMarker = new H.map.DomMarker(noisePoint.getPosition(), {
icon: noisePoint.getData().icon,
min: noisePoint.getMinZoom()
});
noiseMarker.setData(data);
noiseMarker.addEventListener('tap', onMarkerClick);
return noiseMarker;
}
}
var clusterProvider = new H.clustering.Provider(arr, {
clusteringOptions: {
minWeight: 1,
eps: 32
}
});
clusterProvider.setTheme(CUSTOM_THEME);
clusteringProviderArray.push(clusterProvider);
var clusterLayer = new H.map.layer.ObjectLayer(clusterProvider);
map.addLayer(clusterLayer);
clusteringLayerArray.push(clusterLayer);
groupLayers.push(iconsGroup);
I will just use arrays instead of groups

Point in Polygon using leaflet-pip

I'm trying to, given a .json containing a lot of points, determine how many there are in each region (probably returning a dictionary), which are defined in another .json file.
I'm doing this based on this example:
https://www.mapbox.com/mapbox.js/example/v1.0.0/point-in-polygon/
However, I can't get it to work.
This line:
var layer = leafletPip.pointInLayer(this.getLatLng(), states, true);
Returns empty for my test case.
Here is a jsfiddle reproducing my code:
http://jsfiddle.net/Pe5xU/346/
map = L.map('map').setView([40.658528, -73.952551], 10);
// Load a tile layer
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap',
maxZoom: 18,
minZoom: 10
}).addTo(map);
geojson = L.geoJson(data).addTo(map);
var all_markers = [];
var layers = {};
$.each(dots, function(index, rec) {
var markers = {}
if (rec.hasOwnProperty("latitude") && rec.hasOwnProperty("longitude")) {
var marker = L.circleMarker([rec.latitude, rec.longitude], marker_style()).addTo(map);
all_markers.push(marker);
}
});
var all_layers = L.featureGroup(all_markers);
map.fitBounds(all_layers.getBounds());
function marker_style() {
return {
radius: 4,
weight: 0,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
};
}
$.each(dots, function(index, rec) {
if (rec.hasOwnProperty("latitude") && rec.hasOwnProperty("longitude")) {
var layer = leafletPip.pointInLayer([rec.latitude, rec.longitude], geojson, true);
console.log(layer);
}
});
This code example provides coordinates in latitude, longitude order. As documented in the leaflet-pip readme, leaflet-pip expects coordinates in longitude, latitude order, the same as GeoJSON and other geospatial formats.

Categories

Resources