Leaflet polygon precision loss on zoom out - leaflet

I'm creating multipolygon using geojson data.
While zooming in/out the precision of the polygon fades, the corner points move.
Any suggestion how i can make the polygon more precise when zoomed out?
This is how my current code looks. I went through the documentation and tried multiple things including smoothFactor, but nothing seems to have a effect.
I'm kinda stuck now and need your help.
var map = L.map('map', {
zoomControl: false,
zoomSnap: 0.0,
scrollWheelZoom: true,
keyboard: false,
dragging: false,
boxZoom: false,
doubleClickZoom: false,
tap: false,
touchZoom: false,
maxNativeZoom: 1,
animate: false,
easeLinearity: 0
});
paths =
[[[[6.858829796182704, 46.95251393022377],
[6.858822143959083, 46.95252165923424],
[6.858803353389316, 46.95251293626237],
[6.858811005614312, 46.952505207253175],
[6.858829796182704, 46.95251393022377]]],
[[[6.858817924471068, 46.95249692043298],
[6.858810272248277, 46.95250464944266],
[6.858791481686369, 46.95249592646894],
[6.858799133910535, 46.95248819746051],
[6.858817924471068, 46.95249692043298]]],
[[[6.858837595845731, 46.95250605229153],
[6.8588299436243805, 46.952513781302564],
[6.858811153056015, 46.95250505833199],
[6.858818805278741, 46.95249732932224],
[6.858837595845731, 46.95250605229153]]]]
geojsonLayer = L.polygon(paths, {
smoothFactor: 0.0,
color: "grey",
weight: 1,
opacity: 1,
fillColor: "white",
fillOpacity: 1,
})
geojsonLayer.addTo(map);
geojsonLayer.smoothFactor = 0.0
console.log(geojsonLayer.getBounds())
map.setZoom(24.851706204825632)
console.log(map.getZoom())
var latlng = L.latLng(6.858817924471068, 46.95249692043298);
map.panTo(latlng)
map.zoomSnap
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css">
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
<style>
body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 1000px;
width: 1000px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div id="map"></div>
</body>
</html>

Related

How can I read Latlng under a filled Polyline?

I need to know (clicking with the mouse) the coordinate under the polyline.
The polyline cannot be hidden (NO mymap.removeLayer(LayA / B)), the polyline must remain filled (if necessary, may temporarily hide its popup).
Thanks to someone who can help me.
Here an example to work on:
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
</head>
<body style="margin:0; border: 0; padding:0; content: 0;">
<div id="mapid" style="width: 100%; height: 666px;"></div>
<script>
"use strict";
var mymap = L.map('mapid', { zoomControl:false, doubleClickZoom:false, minZoom: 2, maxZoom: 18, keyboard: false}).setView([43, 11], 7);
var LayMap = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {zIndex:1, minZoom: 2, maxZoom: 18, zoom: 8, doubleClickZoom: false, id: 'mapbox/streets-v11', tileSize: 512, zoomOffset: -1, accessToken: '...'}).addTo(mymap);
mymap.createPane('PanA').style.zIndex = 501;
var LayA = new L.layerGroup();
var Poly1 = L.polygon([[43,11],[43.5,11],[43.5,11.5]], {pane: 'PanA' , weight: 9, color: 'Indigo', opacity: 1, fill: true, fillOpacity: 0.1, dashArray: '2,5'}).bindPopup('HI from Poly1');
LayA.addLayer(Poly1);
var Poly2 = L.polygon([[44,11],[44.5,11],[44.5,11.5]], {pane: 'PanA' , weight: 9, color: 'Red', opacity: 1, fill: true, fillOpacity: 0.1, dashArray: '2,5'}).bindPopup('HI from Poly1');
LayA.addLayer(Poly2);
mymap.addLayer(LayA);
mymap.on('click', function(e) {
console.log(e.latlng);
});
</script>
</body>
</html>

Leaflet .png image-map with flipped coordinates?

i have a 4096x4096px .png that represents 15000x15000m. With this code it will be displayed perfectly the way I want it and I could also determine the pixel positions (0/0 top left corner):
<head>
<link rel="stylesheet" href="scripts/leaflet.css"></script>
<style>
#image-map {
width: 1024px;
height: 1024px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div id="image-map"></div>
<script src="scripts/leaflet.js"></script>
<script>
// Using leaflet.js to pan and zoom a big image.
// See also: http://kempe.net/blog/2014/06/14/leaflet-pan-zoom-image.html
// create the slippy map
var map = L.map('image-map', {
minZoom: -1,
maxZoom: 2,
center: [0, 0],
zoom: -1,
crs: L.CRS.Simple
});
// dimensions of the image
var w = 4096,
h = 4096,
url = 'images/Bild.png';
// calculate the edges of the image, in coordinate space
var southWest = map.unproject([0, h], map.getMaxZoom()-1);
var northEast = map.unproject([w, 0], map.getMaxZoom()-1);
var bounds = new L.LatLngBounds(southWest, northEast);
// add the image overlay,
// so that it covers the entire map
L.imageOverlay(url, bounds).addTo(map);
// tell leaflet that the map is exactly as big as the image
map.setMaxBounds(bounds);
</script>
</body>
But I don't want the pixel position, i want to get coordinates with a mouse click (0-point in the middle of the graphic). The limits are upper left corner + 7500 / + 7500, lower right corner -7500 / -7500 (and +/- lower left, -/+ upper right) I'm a beginner and I have no idea how to do this. Many of the questions here have similar problems, but I couldn't transfer them to my scenario.
Thanks for your help
EDIT: I have now worked my way towards this code. However, this does not take into account the limits of the overlay and the display is not as good in the example above. You can also read in the XY values outside, cause the picture no longer slides back to the map. The coordinates are ok for that - but they should point in the other direction. How can this be solved? I also think I'm making a mistake with crs: L.CRS.Simple, but when I remove it the graphic is only shown as a narrow strip in the map.
<html>
<head>
<link rel="stylesheet" href="scripts/leaflet.css">
<style>
#map {
width: 1024px;
height: 1024px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="scripts/leaflet.js"></script>
<script>
var map = L.map('map', {
minZoom: -3,
maxZoom: 0,
center: [0, 0],
zoom: -3,
crs: L.CRS.Simple
});
url = 'images/Bild.png';
L.imageOverlay(url, [
[4096, -4096],
[-4096, 4096]
]).addTo(map);
var popup = L.popup();
map.on("click", function(e) {
popup
.setLatLng(e.latlng)
.setContent('<p>Lng (x): '+e.latlng.lng+' / Lat (y): '+e.latlng.lat+'</p>')
.openOn(map);
});
</script>
</body>
</html>

HexagonLayer - Get aggregated data or it's value bounds

I am building mapbox & deck.gl application and I want to filter some bars at runtime based on value ranges
I want to only show bars, which's aggregated value falls in some range [min,max]
So, my simplified question is
How can I get deck.gl hexagonLayer aggregated bar data after hexagons are drawn on map?
So,then I can correctly calculate upperPercentile and lowerPercentile, which are responsible for graphical filtering...
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
<html>
<head>
<title>Road AccidentsIncidents</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/deck.gl#^6.2.0/deckgl.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
#map { position:absolute; top:0; bottom:0; width:100%; }
body {
font-family: Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0
}
#control-panel {
position: absolute;
background: #fff;
top: 0;
left: 0;
margin: 12px;
padding: 20px;
font-size: 12px;
line-height: 1.5;
z-index: 1;
}
label {
display: inline-block;
width: 140px;
}
</style>
</head>
<body>
<div id="control-panel">
<div>
<label>Radius</label>
<input id="radius" type="range" min="500" max="10000" step="500" value="1000"></input>
<span id="radius-value"></span>
</div>
<div>
<label>Coverage</label>
<input id="coverage" type="range" min="0" max="1" step="0.1" value="1"></input>
<span id="coverage-value"></span>
</div>
<div>
<label>Upper Percentile</label>
<input id="upperPercentile" type="range" min="90" max="100" step="1" value="100"></input>
<span id="upperPercentile-value"></span>
</div>
</div>
</body>
<script type="text/javascript">
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6ImNqbjdsemZybzFtc3MzcnFvNXFucXQweTEifQ.2N_6Nd1rZG-I2pyA9I1xfA';
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
//Add the deck.gl Custom Layer to the map once the Mapbox map loads
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
});
// Add sliders to change the layer's settings based on user input
OPTIONS.forEach(key => {
document.getElementById(key).onchange = (evt) => {
var value = Number(evt.target.value);
document.getElementById(key + '-value').innerHTML = value;
if (hexagonLayer) {
hexagonLayer.setProps({
[key]: value });
}
};
});
</script>
</html>
getColorValue function will be called for each cluster with points belonging to that cluster. You can modify this function like:
// other props
// will be called num_cluster times
getColorValue: (points) => {
// points that belong to a particular cluster
// do stuff with points
return points.length;
},
// other props
Finally, you can use the onSetColorDomain callback (think onClusteringComplete callback) to fill in the values into your html. Added advantage here is that this function calls the callback with [min, max], so you don't have to compute them if it's only for showing a bar.
I've altered your example to show number of clusters and min and max.
<html>
<head>
<title>Road AccidentsIncidents</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/deck.gl#^6.2.0/deckgl.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
#map { position:absolute; top:0; bottom:0; width:100%; }
body {
font-family: Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0
}
#control-panel {
position: absolute;
background: #fff;
top: 0;
left: 0;
margin: 12px;
padding: 20px;
font-size: 12px;
line-height: 1.5;
z-index: 1;
}
label {
display: inline-block;
width: 140px;
}
</style>
</head>
<body>
<div id="control-panel">
<div>
<label>Radius</label>
<input id="radius" type="range" min="500" max="10000" step="500" value="1000"></input>
<span id="radius-value"></span>
</div>
<div>
<label>Coverage</label>
<input id="coverage" type="range" min="0" max="1" step="0.1" value="1"></input>
<span id="coverage-value"></span>
</div>
<div>
<label>Upper Percentile</label>
<input id="upperPercentile" type="range" min="90" max="100" step="1" value="100"></input>
<span id="upperPercentile-value"></span>
</div>
<div>
<div style="padding: 7px 0">
<span>Total Clusters: </span>
<span id="total-clusters-value">Loading</span>
</div>
<div>
<span>[Min, Max]: </span>
<span id="min-max-value">Loading</span>
</div>
</div>
</div>
</body>
<script type="text/javascript">
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6ImNqbjdsemZybzFtc3MzcnFvNXFucXQweTEifQ.2N_6Nd1rZG-I2pyA9I1xfA';
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
let clusters = [];
const minMax = document.getElementById('min-max' + '-value');
const totalClusters = document.getElementById('total-clusters' + '-value');
//Add the deck.gl Custom Layer to the map once the Mapbox map loads
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
getColorValue: points => { clusters.push(points.length); return points.length; },
onSetColorDomain: ([min, max]) => { minMax.innerHTML = `[${min}, ${max}]`; totalClusters.innerHTML = clusters.length; },
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
});
// Add sliders to change the layer's settings based on user input
OPTIONS.forEach(key => {
document.getElementById(key).onchange = (evt) => {
clusters = [];
var value = Number(evt.target.value);
document.getElementById(key + '-value').innerHTML = value;
if (hexagonLayer) {
hexagonLayer.setProps({
[key]: value });
}
};
});
</script>
</html>

Measuring drawn line length with turf.js and mapbox

I'm trying to build out the functionality to measure a line that a user draws over a Mapbox map, using turf.js 'length' feature.
That said, I'm a coding newb - I know just barely enough to be dangerous.
Ultimately I'd like to be able to draw both areas and lines, and have their respective measures returned (area for polygons, lengths for line strings).
Can anyone offer insight as to why this code doesn't work?
https://jsfiddle.net/knxwu342
<html>
<head>
<meta charset=utf-8 />
<title>Line Test</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
.calculation-box-length {
height: 75px;
width: 100px;
position: absolute;
bottom: 40px;
left: 10px;
background-color: rgba(255, 255, 255, .9);
padding: 15px;
text-align: center;
}
</style>
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v3.0.11/turf.min.js'>
</script>
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.css' type='text/css'/>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.js'>
</script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.css' rel='stylesheet' />
</head>
<body>
<div id='map'></div>
<div class='calculation-box-length'>
<p>Length:</p>
<div id='calculated-length'></div>
</div>
<nav id="menu"></nav>
<script>mapboxgl.accessToken = 'pk.eyJ1IjoibWJsYWNrbGluIiwiYSI6ImNqaWMxcGk2MzAwd3YzbG1oeW4yOHppdnYifQ.xdb-2slu5LapzpuMCiKzQQ';
//*********-----------------------------------------------------------------------------------------------------------------**********
//Create new map object
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mblacklin/cjii8o7w91g9o2stcktaeixai', // stylesheet location
center: [-117.572737, 51.746916], // starting position [lng, lat]
zoom: 16 // starting zoom
});
//*********-----------------------------------------------------------------------------------------------------------------**********
// Add navigation controls to the map
map.addControl(new mapboxgl.NavigationControl());
//*********-----------------------------------------------------------------------------------------------------------------**********
// Add the ability to draw geometries and display their measurements
//
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
line_string: true,
polygon: true,
trash: true
}
});
map.addControl(draw);
map.on('draw.create.', updateLength);
map.on('draw.delete', updateLength);
map.on('draw.update', updateLength);
function updateLength(e) {
var data = draw.getAll();
var answer = document.getElementById('calculated-length');
if (data.features.length > 0) {
var length = turf.length(data);
// restrict to area to 2 decimal points
answer.innerHTML = '<p><strong>' + length + '</strong></p><p> meters</p>';
} else {
answer.innerHTML = '';
if (e.type !== 'draw.delete') alert("Use the draw tools in the upper right to calculate a distance");
}
};
//
//*********-----------------------------------------------------------------------------------------------------------------**********
</script>
</body>
</html>
I see two little problems in your code:
There is a typo in your 'draw.create' listener. Just remove the point after create:
map.on('draw.create', updateLength);
The version of Turf you are using is too old and does not seem to have the length function. Try using the most recent one: https://npmcdn.com/#turf/turf/turf.min.js

leaflet map grey screen for geoJSON Layer

Wondering if anyone can help, it's a bit of a strange one. My leaflet map works in isolation, but when added to some existing code the geoJSON (world-map.geojson) appears not to be added to a layer, I just get a grey screen with the leaflet map controls. However, when the leaflet.css is commented out you can see the geoJSON layer has been added... I get no errors and If I comment out references to other JS files it makes no difference.
As this appears to be conflict of sorts i'm not expected a hard and fast answer, but if anyone has come across this before or could suggest any pointers it would be much appreciated.
HTML
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css">
<script type="text/javascript" src="leaflet.js"></script>
<script type="text/javascript" src="world-map.geojson"></script>
<style type="text/css">
#map {
height: 600px;
width: 100%;
}
</style>
<div id="map"></div>
<script src="map.js"></script>
JS - stripped right back
var geojson,
layer,
map = L.map('map').setView([43.8476, 18.3564],3);
// style each continent
function countriesStyle(feature) {
return {
fillColor: "#FED976",
weight: 1,
opacity: 1,
color: "white",
dashArray: 1,
fillOpacity: 0.7
};
}
// add layer
geojson = L.geoJson(countries, {
style: countriesStyle,
maxZoom: 2,
minZoom: 2
}).addTo(map);
// works. countries var is in the DOM
for(var i = 0; i < countries.features.length; i++) {
console.log(countries.features[i].properties.name)
}