Leaflet offset PNG tile layer coordinates - leaflet

I am trying to combine several hand-drawn map images into an interactive HTML feature using leaflet.
I've been trying to work up from the most basic code. To begin with I added three pictures at different zoom levels. I am still experimenting with that, but here's what the code looks like now:
var map = L.map('map').setView([0, 0], 10);
L.tileLayer('Drawn/Pacific.png',
{tileSize: 800, noWrap: true, minZoom:10, maxZoom: 10}).addTo(map);
L.tileLayer('Drawn/Hong Kong.png',
{tileSize: 800, noWrap: true, minZoom: 11, maxZoom: 11}).addTo(map);
L.tileLayer('Drawn/Kowloon.png',
{tileSize: 800, noWrap: true, minZoom: 12, maxZoom: 12}).addTo(map);
How can I offset the starting coordinates of these layers so that they line (So that Hong Kong at zoom level 11 is actually where it should be in the Pacific Tile at zoom level 10)?
Thanks!

It looks like you have only 1 big image per "tile layer", so you would probably be interested in implementing your features as Image Overlays instead:
Used to load and display a single image over specific bounds of the map.
With Image Overlay, you can specify the geographical coordinates of your image, so that it is placed exactly where you want. You can adjust the relative coordinates of your 3 images, so that they line up as you need.

Related

Leaflet - Allow for switching to another base layer at higher than max zoom

I have 2 base layers (map and satellite) that users can switch between. The satellite layer's max zoom is higher by 2. I am using Leaflet's provided L.control.Layers() to manage the layers
var mapLayer = L.tileLayer('map-tiles.example.com/{z}/{x}/{y}.png', {
maxZoom: 18,
})
var satelliteLayer = L.tileLayer('satellite-tiles.example.com/{z}/{x}/{y}.png', {
maxZoom: 20,
})
var baseLayers = {
'Map': mapLayer,
'Satellite': satelliteLayer
}
var layerControls = L.control.layers(baseLayers).addTo(map);
If the user zooms to 19 or 20 in satellite view, he will not be able to switch back to the map layer (the radio button is disabled) until he zooms back to 18 or lower.
I want the user to be able to switch to the map layer even on zoom 19 or 20. And when the user switches to the map layer, the zoom will be set to 18.
Is there some way to achieve this with Leaflet's layer control? Or do I have to build a custom layer control?
Is there some way to achieve this with Leaflet's layer control?
Not with the default behaviour, no. If you check the relevant portion of the source code, you'll see that a L.Control.Layers always disables checkboxes/radio buttons when the layer is out of its min/maxzoom range.
Or do I have to build a custom layer control?
Yes, you can create your own subclass of L.Control.Layers disabling this functionality, replacing the relevant method with a function that does nothing:
L.Control.Layers.NeverDisable = L.Control.Layers.extend({
_checkDisabledLayers: function(){}
});
var myLayersControl = new L.Control.Layers.NeverDisable(
baselayers, overlayLayers, options);
myLayersControl.addTo(map);
You can check a working example in this plunkr.
An easy workaround to have your mapLayer selectionable on zooms 19-20 is to use the Tile Layer maxNativeZoom option at 18, and increase your maxZoom to 20.
Then if you still want to automatically decrease the zoom back to 18 when mapLayer is selected, use a listener on map "baselayerchange" event, check the map.getZoom() and modify it (map.setZoom(18)) if desired.

mbtiles files with leaflet

I am using Tileserver to host my mbtiles file. I am trying to open my mbtile sfile using leaflet in ionic. I am not able to see map. Following is the code that I am using:
leaflet.tileLayer('http://subdomain/styles/klokantech-basic/?vector#{z}/{x}/{y}').addTo(map);
I have also tried to use:
var mb = leaflet.tileLayer.mbTiles('http://subdomain/styles/klokantech-basic/?vector#{z}/{x}/{y}').addTo(this.map);
But I am just able to see grey screen on my device instead of map.
It sounds like leaflet is loading the tiles from your tile server, but the map you are serving doesn't have data for the location and zoom level you are looking at. Try this script.
Leaflet example:
<script>
var map = L.map('map').
setView([lat, lon], zoom );
//OpenMapTiles
L.tileLayer('http://subdomain/styles/klokantech-basic/{z}/{x}/{y}.png', {
//tms: true,
maxZoom: 20,
attribution: 'Map data © OpenStreetMap'
}).addTo(map);
</script>
An alternative is to use Mapbox GL JS, this pushes the rendering to your browser and allows you to use tileserver-gl-light as well:
<script src='http://subdomain/mapbox-gl.js'></script>
<link href='http://subdomain/mapbox-gl.css' rel='stylesheet' />
Mapbox GL JS
var map = new mapboxgl.Map({
container: 'map',
style: 'http://subdomain/styles/klokantech-basic/style.json',
center: [lon, lat],
zoom: 7
});
When creating the mbtiles file, make sure you create it to support the zoom level and location you set, OpenMapTiles defaults to a zoom level of 7, it may needs to be increased for your map, I use 14, which supports a zoom level to 20 for rendering.

Why is zoom level interpreted different in mapbox-gl-js?

Comparing maps from openlayers, leaflet and mapbox-gl-js, I can see that mapbox-gl-js has a different map image for the same center and zoom combination, and the same div size. It seems to zoom in one level extra compared to the openlayers and leaflet maps. Why is this?
yep that's correct. this one level difference is because the vector tiles used by mapbox-gl-js are 512px and in openlayers/leaflet/mapbox.js the raster tiles used are 256px. in other words, the appearance of a 512 tile at zoom level 0 is the same as the appearance of a 256 tile at zoom level 1. hope this helps!
512px tile 0/0/0
256px tile 1/0/0
You can set the zoomFactor of OpenLayerMap like this to make same as mapbox-gl
const view = new View({
center: [lng, lat],
zoom: zoom,
zoomFactor: 2.38
})
Also you should set the tileSize to 512 in openlayer map setting

Prevent tiles outside of polygon from loading

Is there a way to prevent tiles from loading that are outside of a polygon? The closest example of this I found is here
http://jsfiddle.net/LsvDk/23/
var polygon = L.multiPolygon(
[
[
[
[51.509, -0.08],
[51.503, -0.08],
[51.503, -0.047],
[51.51, -0.047],
],
[
[84.774798, -176.835937],
[-85.221998, -175.078125],
[-85.103422, 176.484375],
[84.3916, 178.242188]
]
]
], {
color: '#DBDBDB',
weight: 1,
opacity: 1,
fillOpacity: 1
}).addTo(map);
It does what I want except it looks crappy when panning or zooming out because it loads tiles outside of the polygon.
If my understanding is correct, you do not like the example you mention because it uses a vector shape (polygon) to hide the tiles, but Leaflet has to re-render the shape when panning / zooming, which makes the background tiles temporarily appear?
EDIT:
Please have a try with TileLayer.BoundaryCanvas plugin. Instead of using a vector shape as mask, it combines tiles and its mask into a canvas. Hence the mask becomes a raster image, which avoids Leaflet behaviour of vector shape re-rendering at each pan / zoom, but instead keeps previous tiles (combined with mask in this case) until new ones are received.
Allows you to draw tile layers with arbitrary polygonal boundary. HTML5 Canvas is used for rendering.
The mask is specified as a GeoJSON shape, so it should be as easy to set up as in your example.
Remaining of original answer:
You would probably be interested in the bounds option of Tile Layer, to prevent any displaying of tiles outside a specified area.
When this option is set, the TileLayer only loads tiles that are in the given geographical bounds.
However, if your visible area does not perfectly fit with tiles boundaries, you would still have to use a mask to cover some areas, and you might still get the same issue with vector shapes being re-rendered.

Mapbox.js - Fallback tile image from lower zoom level, when missing in requested zoom level

I serve map png files from disk and I have tile pngs for whole city in zoom level 15. I have also tiles in zoom levels 16-18 but only for certain areas.
I would like to set up the tile Layer, so that when the user is in zoom level 18 the map will display scaled tiles from level 15 as a fallback.
I tried setting option maxNativeZoom, but didn't work for me.
Here is my code:
offlineLayer = L.mapbox.tileLayer(tileJSON, {
minZoom: 8,
maxZoom: 18,
maxNativeZoom: 15
});
map.addLayer(offlineLayer, 'Offline', 1);
Can I make it work, that way using some options or do I need to hack it some way? Or is there some example code for that?
Just to let people know that I wrote Leaflet.TileLayer.Fallback plugin some time ago to address this exact use case:
Replaces missing Tiles (404 error) by scaled lower zoom Tiles.
Demo page
In OP's situation, you would just specify the maxZoom Tile Layer option as usual, and whenever a tile is found missing at the current zoom level, the plugin will try to replace it by the "parent" tile at the previous zoom level (scaled and clipped appropriately so that it fits the missing tile), and so on until a tile is found or minZoom is reached.
L.tileLayer.fallback(urlTemplate, {
minZoom: 8,
maxZoom: 18
});
Disclaimer: I am the author of that plugin.
When you say that it didn't work for you, I'm guessing that what you're seeing with the code above is level 15 tiles for everywhere even when at zoom levels 16-18 in the areas where you have tiles at those levels? That's the expected behavior of maxNativeZoom. TileLayer creates all of the tiles for a given zoom level and map bounds and sets the "src" to a URL containing the current zoom level. If the current zoom level is greater than "maxNativeZoom" then the zoom level in the URL is set to "maxNativeZoom". For example, src="http://a.tile.openstreetmap.org/15/16368/10896.png" would be used for zoom levels 16-18 if "maxNativeZoom" is set to 15.
There is no logic in the code that checks to see if an image actually exists for that tile at that zoom level.
If your tiles are in a single data set then you could modify the code in TileLayer to check for a HTTP 404 return code for the generated URL and if one is received then create a new URL using "maxNativeZoom". If your tiles are in multiple data sets (i.e. one for zoom level 15 and less, and another for zoom levels 16-18) then I think you'd have to write a TileLayer that supports multiple data sets.