Constrain Mapbox panning north/south only? - leaflet

Anyone know how to constrain the panning in a Mapbox map only using north/south bounds? What I would like to do is use the world copy so that users can continuously pan east to west, but restrict the north and south panning so that you can't pan outside of the map tiles (which I find super annoying). I'm currently using a maxbounds when initializing the map to prevent panning into the white tiles above and below the poles, but that's a no-go with world copying.

You can use -Infinity west, and Infinity east (thnx Ivan) in the L.LatLngBounds you use for setting the maxBounds option: (using Leaflet in the snippet but should work for Mapbox.js too)
var map = new L.Map('leaflet', {
'center': [0, 0],
'zoom': 2,
'worldCopyJump': true,
'layers': [
new L.TileLayer('//{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
'attribution': '© OpenStreetMap contributors, © CartoDB'
}),
new L.Marker([0,0])
],
'maxBounds': [
[-90, -Infinity],
[90, Infinity]
]
});
body {
margin: 0;
}
html, body, #leaflet {
height: 100%;
}
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="//cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
</head>
<body>
<div id="leaflet"></div>
<script type="application/javascript" src="//cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
</body>
</html>

Related

Vector tiles are showing in the wrong place and in the wrong scale in esri-leaflet-vector plugin

I'm using esri-leaflet#3.0.0 and leaflet 1.7.1 and esri-leaflet-vector plugin.
When I'm adding VectorTileServer layer, rendered tiles are showing in the wrong place and in the wrong scale. Where I doing wrong?
VectorTileServer is published in Web Mercator "spatialReference":{"wkid":102100,"latestWkid":3857}.
Taka a look on the Norway.
it is shown in the wrong place and in the wrong scale?
const map = L.map('map').setView([50, 18], 3);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
L.esri.Vector.vectorTileLayer("https://services.geodataonline.no/arcgis/rest/services/GeocacheVector/GeocacheGraatone_WM/VectorTileServer", {
style: (style) => {
style.layers.forEach(function (layer) {
if (layer.layout['text-rotate']) {
layer.layout['text-rotate'].stops = [[0, 0]]
}
});
return style
}
}
).addTo(map);
body { margin:0; padding:0; }
#map {
position: absolute;
top:0;
bottom:0;
right:0;
left:0;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
color: #323232;
}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Esri Leaflet</title>
<!-- Load Leaflet from CDN -->
<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>
<!-- Load Esri Leaflet from CDN -->
<script src="https://unpkg.com/esri-leaflet#3.0.0/dist/esri-leaflet.js"></script>
<script src="https://unpkg.com/esri-leaflet-vector#3.0.0/dist/esri-leaflet-vector.js"></script>
</head>
<body>
<div id="map"></div>
</body>
</html>
Anyone can help ?
Regards
Mik
One thing I noticed with this layer you're using is that the scale properties of each zoom level seem to be off. From the esri-leaflet docs
Your map service must be published using the and the default scale options used by Google Maps, Bing Maps and ArcGIS Online. Esri Leaflet will not support any other spatial reference for tile layers.
If you go to arcgis's sample tutorial for vector tile layers, they have a sample example with a sample vector tile layer there: Santa Monica Mountain Parcels. If you open up that tile server url, you'll see the JSON with property lods:
"lods": [
{
"level": 0,
"resolution": 78271.51696399994,
"scale": 295828763.795777
},
{
"level": 1,
"resolution": 39135.7584820001,
"scale": 147914381.897889
},
{
"level": 2,
"resolution": 19567.87924099992,
"scale": 73957190.948944
},
...
}
I'm going to assume that these are the default scales accepted by esri-leaflet, considering these are the ones used in their sample vector tile layer. They're also listed here: What ratio scales do Google Maps zoom levels correspond to?
If you open up the tile server url for the layer you're trying to use for norway, you'll see the same, but the scale numbers seem to be off by one zoom level:
"lods": [
{
"level": 0,
"resolution": 156543.03392800014,
"scale": 591396864
},
{
"level": 1,
"resolution": 78271.51696399994
"scale": 295698432,
},
{
"level": 2,
"resolution": 39135.75848200009,
"scale": 147849216
},
...
This is why your norway layer is wrong, but at least is consistently wrong across all zoom layers (meaning it always shows up in the same, wrong spot, at half the size it should be).
If you have any control over / connection with the people who server that layer, I'd let them know their zoom levels are wrong.
In the meantime, there may be a way to override the JSON that esri-leaflet is using when loading that layer, but it would be a lot of work of digging into their source code, and very hacky.

Leaflet only renders tiles with 0 y coordinate for game map

I have just started out with Leaflet, and I wanted to make a map for a game I play (GTA V)
I have all the necessary tiles for building a map. However, when I run my code, only 2 tiles show up, that have 0 as their y coordinate.
My code:
Javascript:
const map = L.map('map', {}).setView([0.0, 0.0], 0);
const tile = L.tileLayer('tiles/minimap_sea_{y}_{x}.png', {
minZoom: 0,
maxZoom: 2,
tileSize: 1024,
bounds: [[0, 0],[3072, 2048]],
maxNativeZoom: 0,
minNativeZoom: 0,
noWrap: true
}).addTo(map);
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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>
<link rel="stylesheet" href="style.css">
<script src="./script.js" type="text/javascript" defer></script>
<title>Test Map</title>
</head>
<body>
<div id="map"></div>
</body>
</html>
CSS:
body {
margin: 0;
height: 100vh;
width: 100vw;
}
#map {
width: 100%;
height: 100%;
background-color: #888888;
}
Tile picture names:
minimap_sea_0_0.png
minimap_sea_0_1.png
minimap_sea_1_0.png
minimap_sea_1_1.png
minimap_sea_2_0.png
minimap_sea_2_1.png
I have already tried setting the bounds but that did not work either.
Solution:
I specified the coordinate system to be simple at map creation, and negated the y length of my bounds.
New code:
Javascript:
const map = L.map('map', {
crs: L.CRS.Simple
}).setView([0.0, 0.0], 0);
const tile = L.tileLayer('tiles/minimap_sea_{y}_{x}.png', {
minZoom: 0,
maxZoom: 2,
tileSize: 1024,
bounds: [[0, 0],[-3072, 2048]],
maxNativeZoom: 0,
minNativeZoom: 0,
tms: true
}).addTo(map);
If you make a map not covering a ~sphere (like the Earth), make sure to specify L.CRS.Simple in the map crs option:
A simple CRS that maps longitude and latitude into x and y directly. May be used for maps of flat surfaces (e.g. game maps).
const map = L.map('map', {
crs: L.CRS.Simple
}).setView([0.0, 0.0], 0);
Otherwise at zoom 0, max and min latitudes of default EPSG3857 CRS will clamp to 256px height, 512px at zoom 1, and 1024px at zoom 2, therefore still within your y = 0 tile (1024px size).
See also the Leaflet tutorial covering this case: Non-geographical maps
This is probably related to leaflet confusing the zoom with the y value. I recommend following the standard slippymap tile structure. The tiles need to be in a directory structure that follows a zoom/x/y pattern. It looks like you only have one zoom layer (as far as I can tell). Lets say its zoom level 0. Your tiles should be organized like this:
/myproj
myjavascriptfile.js
/tiles
/0
/0
0.png
1.png
/1
0.png
1.png
/2
0.png
1.png
Now you can tell the tilelayer where to find these like this:
const tile = L.tileLayer('tiles/{z}/{y}/{x}.png')
I highly recommend checking out this article on naming map tiles.

Leaflet show multiple markers and setView

I have sample code that uses Leaflet and open street map. Nevertheless, i am not sure how could i add more markers. Besides i don't really understand the sense of setView function i read that it's for centering map, but what is the sense to have it since there is only one marker which should be automaticly as map center point, and from the other hand if there will be more markers what is a sense of setView?
Additional question: Is Leaflet and open street map free for commercial use?
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet.js" integrity="sha512-/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw==" crossorigin=""></script>
</head>
<body>
<div id="mapDiv" style="width: 800px; height: 500px"></div>
<script>
// position we will use later
var lat = 40.73;
var lon = -74.00;
// initialize map
map = L.map('mapDiv').setView([lat, lon], 13);
// set map tiles source
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors',
maxZoom: 18,
}).addTo(map);
// add marker to the map
marker = L.marker([lat, lon]).addTo(map);
// add popup to the marker
marker.bindPopup("<b>ACME CO.</b><br />This st. 48<br />New York").openPopup();
</script>
</body>
</html>
</pre>
there is only one marker which should be automaticly as map center point
There is nothing in Leaflet itself that would automatically center a map view on your only Marker.
On the other hand, the sample code you show does achieve this, using setView on the same coordinates as your only Marker.
if there will be more markers what is a sense of setView?
setView gives you the ability to define which part of the map you want to initially display in your viewport, independently from your map content / layers (in your case: your markers). Obviously you can define a view that shows all your Markers at once as well.
i am not sure how could i add more markers.
Do L.marker([lat, lon]).addTo(map); as many times as needed, with lat and lon different each time as needed.
Is Leaflet and open street map free for commercial use?
Leaflet is distributed under a BSD 2-clause license, commercial use is permitted.
OpenStreetMap data is free, but not the tiles generated by OSM servers. There are many services generating similar tiles from OSM data or other sources that you can check out. E.g. you can search for "leaflet providers".
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet.js" integrity="sha512-/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw==" crossorigin=""></script>
</head>
<body>
<div id="mapDiv" style="width: 800px; height: 500px"></div>
<script>
// position we will use later
var lat = 40.73;
var lon = -74.00;
// initialize map
map = L.map('mapDiv');
// set map tiles source
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors',
maxZoom: 18,
}).addTo(map);
/*
L.tileLayer('http://{s}.google.com/vt?lyrs=s&x={x}&y={y}&z={z}', {
maxZoom: 13,
subdomains:['mt0','mt1','mt2','mt3']
}).addTo(map);
*/
map.locate({setView: false, maxZoom: 8});
// add marker to the map
map.setView(new L.LatLng(lat, lon), 18);
marker = L.marker([lat, lon]).addTo(map);
// add popup to the marker
marker.bindPopup("<b>ACME CO.</b><br />This st. 48<br />New York").openPopup();
</script>
</body>
</html>
</pre>
Map Tiles

Leaflet GeoJSON is is possible to crop a line feature before it reaches its destination?

Is there a simple way to shorten lines on a GeoJSON layer?
I have a line, it goes from point A to point B. I want the line to stop the radius of a marker short of it's terminus. Is that possible? Kind of like an offset from the line terminus/origin.
Here is an example:
I have icons that are 50 x 50, but semi transparent (see image) and I have lines that go to the Lat/Long of the icons, but I want to try cropping or offsetting the line before it enters the icon, so you can't see the line under the icon. Is this possible?
Please comment if this question is unclear.
That can be done using the dashArray and dashOffset options:
var map = new L.Map('leaflet', {
center: [0, 0],
zoom: 0
});
new L.CircleMarker([0, 90], {radius: 30}).addTo(map);
new L.CircleMarker([0, -90], {radius: 30}).addTo(map);
var polyline = new L.Polyline([[0, -90], [0, 90]]).addTo(map);
// Get length of the line
var totalLength = polyline.getElement().getTotalLength();
polyline.setStyle({
// Set dashArray to the length of the line minus radius * 2
dashArray: totalLength - 60,
// Offset by radius
dashOffset: -30
});
body {
margin: 0;
}
html, body, #leaflet {
height: 100%;
}
<!DOCTYPE html>
<html>
<head>
<title>Leaflet 1.2.0</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/leaflet#1.2.0/dist/leaflet.css" />
</head>
<body>
<div id="leaflet"></div>
<script src="//unpkg.com/leaflet#1.2.0/dist/leaflet.js"></script>
</body>
</html>

ESRI TileLayer with non-standard 0 zoom does not load in Leaflet

appreciate some assistance with figuring out why ESRI Tile layer does not load in Leaflet? It loads in OpenLayers and of course when using the ESRI JS API, but I'd like to use Leaflet...
This is the standard Leaflet "Quickstart" example with the Tile Layer url. I've tried many options of both the layer and map constructor and forcing the map to resize and redraw etc, but I can't get it to work...
<html>
<head>
<meta charset=utf-8 />
<title>Esri Leaflet Quickstart</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<!-- Load Leaflet from CDN-->
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.0.3/dist/leaflet-src.js"></script>
<!-- Load Esri Leaflet from CDN -->
<script src="https://unpkg.com/esri-leaflet#2.0.8"></script>
<style>
body { margin:0; padding:0; }
#map { position: absolute; top:0; bottom:0; right:0; left:0; }
</style>
</head>
<body>
<div id="map"></div>
<script>
function initmap() {
map = new L.Map('map');
//var osmUrl = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}.png'; <- example from Esri-Leaflet that works fine
var osmUrl = 'https://citymaps.capetown.gov.za/agsext1/rest/services/Background_Maps/Relief_Map/MapServer/tile/{z}/{y}/{x}';
var osm = new L.esri.tiledMapLayer({url:osmUrl, noWrap: true});
map.addLayer(osm);
}
var lat = Number(18.5296);
var lng = Number(-33.9597);
var startLatLng = new L.LatLng(lat, lng);
initmap();
map.setView(startLatLng, 0);
</script>
</body>
</html>
OpenLayers example of exactly the same that works:
<!DOCTYPE html>
<html>
<head>
<title>Tiled ArcGIS MapServer</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.1.0/css/ol.css" type="text/css">
<script src="https://openlayers.org/en/v4.1.0/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var urlRelief = 'https://citymaps.capetown.gov.za/agsext1/rest/services/Background_Maps/Relief_Map/MapServer';
var layers = [
/* new ol.layer.Tile({
source: new ol.source.OSM()
}), */
new ol.layer.Tile({
source: new ol.source.TileArcGISRest({
url: urlRelief,
projection: 'EPSG:4326',
wrapX: false
})
})
];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: [2062612, -4026418],
zoom: 10
})
});
</script>
</body>
</html>
i see several different problems going on here:
you shouldn't append /tile/{z}/{y}/{x} to urls when instantiating a tiledMapLayer. we don't do that in our samples.
Leaflet (and consequently Esri Leaflet) only know about the explicit tiling scheme used by Google, Bing etc. your service may reference the same base projection, but it utilizes a non standard tiling scheme (ie: the resolutions and scales differ). in Esri Leaflet we attempt to remap LODs, but only when we recognize the scale and resolution.
standard:
{
"level": 10,
"resolution": 152.87405657041106,
"scale": 577790.554289
},
{
"level": 11,
"resolution": 76.43702828507324,
"scale": 288895.277144
},
{
"level": 12,
"resolution": 38.21851414253662,
"scale": 144447.638572
},
yours:
{
"level": 0,
"resolution": 135.46693760054188,
"scale": 512000
},
{
"level": 1,
"resolution": 67.73346880027094,
"scale": 256000
},
{
"level": 2,
"resolution": 33.86673440013547,
"scale": 128000
}
your best option is to stick with Google's standard tiling scheme even if you want to restrict the area of interest. this can be accomplished pretty easily in ArcGIS Desktop whether you end up publishing tiles to ArcGIS Online, ArcGIS Server or creating a custom tile package.