ESRI TileLayer with non-standard 0 zoom does not load in Leaflet - 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.

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.

how to apply 'generateId:true' on composite source features?

I want to find out buildings inside a runtime-drawed polygon.
And fill with green color.
I try to archive it with feature state and case expression. When I find out those features that inside the polygon in some way, I still can't execute the map.setFeatureState, cause the first parameter need (feature object) need a specified id, however, the feature that created by composite source hasn't set id, also looks there is no way to set 'generateId:true'.
Is there any alternate way to achieve the purpose?
in the follow snippet,there is a polygon draw tool on the top-right,draw a polygon and double click to end of draw, then you will find the error alerted in the console.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
<script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.0/mapbox-gl-draw.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5/turf.min.js"></script>
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.0/mapbox-gl-draw.css" type="text/css" />
<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.eyJ1IjoibWluem9qaWFuIiwiYSI6ImNrbGFsem92MjAxaHAycG1sbGg3MXFsODAifQ.Kclz1IBxyU0iDiVgIjhSYQ";
var map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11"
});
var map = new mapboxgl.Map({
style: "mapbox://styles/mapbox/light-v10",
center: [-73.991, 40.735],
zoom: 15.5,
pitch: 45,
bearing: -17.6,
container: "map",
antialias: true
});
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
polygon: true,
trash: true
}
});
map.addControl(draw);
map.on("draw.create", updateArea);
map.on("draw.delete", updateArea);
map.on("draw.update", updateArea);
function updateArea(e) {
if (map.getLayer("maine")) map.removeLayer("maine");
if (map.getSource("data-area")) map.removeSource("data-area");
map.addSource("data-area", {
type: "geojson",
data: draw.getAll()
});
//get features in polygon
lastInAreaFeatures = map
.queryRenderedFeatures({
layers: ["3d-buildings"]
})
.filter(function(t) {
return turf.booleanContains(
turf.polygon(draw.getAll().features[0].geometry.coordinates),
t.geometry.type == "MultiPolygon" ?
turf.polygon([t.geometry.coordinates[0][0]]) :
turf.polygon([t.geometry.coordinates[0]])
);
});
//try to update state,and set in-area equals true. so that the condition paint logic will work in the bellow "3d-buildings" layer
//////however,the follow code can't work because the id of feature is undefined
///// 'Error: The feature id parameter must be provided.' will alerted in console
lastInAreaFeatures.forEach(function(f) {
map.setFeatureState(f, {
"in-area": true
});
});
console.log(lastInAreaFeatures);
draw.deleteAll();
}
var lastInAreaFeatures;
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on("load", function() {
map.addLayer({
id: "3d-buildings",
source: "composite",
"source-layer": "building",
filter: ["==", "extrude", "true"],
type: "fill-extrusion",
minzoom: 15,
paint: {
"fill-extrusion-color": [
"case", ["==", ["feature-state", "in-area"], true],
"green",
"#aaa"
],
"fill-extrusion-height": [
"interpolate", ["linear"],
["zoom"],
15,
0,
15.05, ["get", "height"]
],
"fill-extrusion-base": [
"interpolate", ["linear"],
["zoom"],
15,
0,
15.05, ["get", "min_height"]
],
"fill-extrusion-opacity": 0.6
}
});
});
</script>
</body>
</html>

How to Implement Open Route Service in Leaflet

Can I use Open Route Service api in leaflet map? I can't find working example to show how to integrate api key on the map. Now I'm using graphhopper and it's working flawless but now it have restrictions to use up to 5 points.
When I try to make waypoints via open route service I'm showing this error: Uncaught TypeError: L.Routing.openrouteservice is not a constructor
My code:
var mymap = L.map('mapid').setView([50.27264, 7.26469], 13);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors and ORS'
}).addTo(this.mymap);
var control = L.Routing.control({
waypoints: [
L.latLng(3.102739, 101.598077),
L.latLng(3.101861, 101.599037)
],
router: new L.Routing.openrouteservice('5b3ce3597851110001cf6248e3cd48b3c44c4e529f8fac67408d4257')
// routeWhileDragging: true
}).addTo(this.mymap);
I don't know does openrouteservice works with leaflet routing machine but I tried with MapBox and everything works fine. So now my map support walking directions.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Plain Leaflet API</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.css' rel='stylesheet' />
<!-- Leaflet Map -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet-routing-machine/3.2.12/leaflet-routing-machine.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-routing-machine/3.2.12/leaflet-routing-machine.min.js"></script>
<!-- end Leaflet map -->
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiZmFyYWRheTIiLCJhIjoiTUVHbDl5OCJ9.buFaqIdaIM3iXr1BOYKpsQ';
var mapboxTiles = L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=' + L.mapbox.accessToken, {
attribution: '© Mapbox © OpenStreetMap',
tileSize: 512,
zoomOffset: -1
});
var map = L.map('map')
.addLayer(mapboxTiles)
.setView([42.3610, -71.0587], 15);
L.Routing.control({
router: L.Routing.mapbox(L.mapbox.accessToken,{
profile : 'mapbox/walking',
language: 'en',
}),
waypoints: [
L.latLng(40.779625, -73.969111),
L.latLng(40.767949, -73.971855)
],
}).addTo(map);
</script>
</body>
</html>
This is my HTML snippet for including the stuff I need for maps and routing:
<script src="scripts/maps/leaflet.js"></script> <!-- Include Leaflet JS -->
<script src="scripts/maps/leaflet-routing-machine.js"></script> <!-- Include the Leaflet Routing Machine -->
<script src="scripts/maps/polyline.min.js"></script> <!-- for Leaflet Routing Machine -->
<script src="scripts/maps/lodash.min.js"></script> <!-- for Leaflet Routing Machine -->
<script src="scripts/maps/corslite.js"></script> <!-- for Leaflet Routing Machine -->
<script src="scripts/maps/L.Routing.OpenRouteService.js"></script> <!-- Include the Open Route Service for Leaflet Routing Machine -->
<script src="scripts/maps/leaflet-providers.js"></script>
Then in typescript:
let router = (L as any).Routing.control({
router: new (L as any).Routing.openrouteservice(orsKey),
waypoints: [
L.latLng(startLatitude, startLongitude),
L.latLng(endLatitude, endLongitude)
],
routeWhileDragging: false,
show: false,
fitSelectedRoutes: false,
createMarker: function (i, waypoint, n) {
return null;
},
lineOptions: {
styles: [{ color: '#9f150b', opacity: 1, weight: 4 }]
}
});
router.addTo(map);
Only problem is the Open route service use POST requests in the latest API. So the L.Routing.OpenRouteService.js file needs updating

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:

Tile layers content is repeating on each tile

I am new to leaflet and mapbox. I built a map with with mapbox with my own tiled layers that I made with tilemill exported as mbtiles and extracted with mb-util. Tested from my localhost everything is fine, but when I built the map from my webserver, the layer contents are repeating on each tile when zooming the map. Below is my code. I don't understand this different behaviour and how to avoid this. Can anybody help me, please??
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Layers Control</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox.js/v1.6.2/mapbox.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/v1.6.2/mapbox.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 type="text/javascript">
var map = L.map('map').setView([50.11, 8.86], 13);
map.setMaxBounds([[50.09, 8.797], [50.134, 8.889]]);
L.control.layers({
'Base Map': L.mapbox.tileLayer('examples.map-9ijuk24y', { noWrap: true }).addTo(map),
'OSM': L.tileLayer('http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png', {
attribution: '© ' + 'OpenStreetMap' + ' Contributors', noWrap: true
})
}, {
'NEG Dietesheimer Steinbrüche': L.tileLayer('tiles/leafNegGrenz/{z}/{x}/{y}.png', { maxZoom: 19, minZoom: 0, noWrap: true }).addTo(map),
'NEG Ausstattung': L.tileLayer('tiles/leafNegAus/{z}/{x}/{y}.png', { maxZoom: 19, minZoom: 0, noWrap: true })
}).addTo(map);
Something is wrong with your server, or the export you created: tiles redirect to each other. For instance, open
http://geo-information.de/tiles/leafNegGrenz/15/17190/11098.png
In a browser, and it redirects to
http://geo-information.de/tiles/leafNegGrenz/15/17190/11096.png
This is why tiles are repeating.