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

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.

Related

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.

Adding my own style is not working in MapBox Javascript

In a previous question, someone asked about image overlays with map styles:
How do I add a simple image overlay in Mapbox Javascript?
I got it to work with their example, but I want to use my own style.
Here's a link to my map style.
This is the style they use that works:
mapbox://styles/mapbox/satellite-v9
This is my style, it doesn't work:
mapbox://styles/nittyjee/ck0fasve30an21cpalmwct518
Below is the code that works, you can run it yourself. My style is commented out.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add an image</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<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' />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoibml0dHlqZWUiLCJhIjoid1RmLXpycyJ9.NFk875-Fe6hoRCkGciG8yQ';
var map = new mapboxgl.Map({
container: 'map',
maxZoom: 5.99,
minZoom: 4,
zoom: 5,
center: [-75.789, 41.874],
//Style from Stack Overflow:
style: 'mapbox://styles/mapbox/satellite-v9'
//My style does not work:
//style: 'mapbox://styles/nittyjee/ck0fasve30an21cpalmwct518'
});
map.on('load', function() {
map.addSource("myImageSource", {
"type": "image",
"url": "https://docs.mapbox.com/mapbox-gl-js/assets/radar.gif",
"coordinates": [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
});
map.addLayer({
"id": "overlay",
"source": "myImageSource",
"type": "raster",
"paint": {
"raster-opacity": 0.85
}
});
});
</script>
</body>
</html>
You need to bump up mapbox-gl version. You're using a way older SDK.
Change your script/css definition to this:
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.3.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.3.1/mapbox-gl.css' rel='stylesheet' />
That doesn't look like a style URL https://docs.mapbox.com/help/glossary/style-url/
You'd need to create a style in Mapbox Studio and grab the style ID.
When you're in Studio looking at your style, you can click on Share in the top right corner.
Then from there, you should see a panel that will have a section called Your style url. If you copy that link and paste it into your code, your style should come through.
You can also click on the 3 dots by your style and copy the style id at the bottom of the panel that appears:

ArcGis layers on mapbox gl

I'm trying to add a layer from an api on ArcGis:
https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Facility_and_Structure/MapServer/1
In leaflet it is posible with:
L.esri.featureLayer({
url:"https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Facility_and_Structure/MapServer/1",
style: function () {
return { color: "#70ca49", weight: 2 };
}
}).addTo(map);
Is there a way to do this on mapboxgl?
Hi Jorge Monroy - Mapbox GL JS expects the data sources as such. In your case where you're wanting to load building footprints from an ArcGIS REST Service, your best bet is to load them as a geojson.
It looks like you're publishing the services from ArcServer 10.31. In that case, the way that I've loaded ArcGIS REST services is by exposing them through AGOL as explained there. If you have that option, that seems easiest. Otherwise, there are other (work-arounds)[https://gis.stackexchange.com/questions/13029/converting-arcgis-server-json-to-geojson] that I've no experience with.
Using Washington D.C. as an example, if you navigate to: http://opendata.dc.gov/datasets/building-footprints and then click on APIs, you can copy the link to the geojson service.
You can then load into MapboxGL JS through the data property of the geojson source shown there.
You can use leaflet-mapbox-gl.js to integrate leaflet and mapbox. Get token from mapbox and add it to below example to make it work.
References: https://github.com/mapbox/mapbox-gl-leaflet
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js'></script>
<script src="https://unpkg.com/mapbox-gl-leaflet/leaflet-mapbox-gl.js"></script>
<script src="https://unpkg.com/esri-leaflet/dist/esri-leaflet.js"></script>
<style>
html, body, #map {
margin:0; padding:0; width : 100%; height : 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var token = "";//add token before running this example
const INITIAL_VIEW_STATE = {
latitude: 45.528,
longitude: -122.680,
zoom: 13
};
var map = L.map('map').setView([45.528, -122.680], 13);
L.esri.basemapLayer("NationalGeographic").addTo(map);
var parks = L.esri.featureLayer({
url: "https://services.arcgis.com/rOo16HdIMeOBI4Mb/arcgis/rest/services/Portland_Parks/FeatureServer/0",
style: function () {
return { color: "#70ca49", weight: 2 };
}
}).addTo(map);
var gl = L.mapboxGL({
accessToken: token,
style: 'mapbox://styles/mapbox/dark-v10'
}).addTo(map);
//To add anything on mapbox, first access the mapbox using getMapboxMap()
gl.getMapboxMap().on('load', () => {
//To load any layer on mapbox
//gl.getMapboxMap().addLayer();
});
var popupTemplate = "<h3>{NAME}</h3>{ACRES} Acres<br><small>Property ID: {PROPERTYID}<small>";
parks.bindPopup(function(e){
return L.Util.template(popupTemplate, e.feature.properties)
});
</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.

Constrain Mapbox panning north/south only?

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>