Adding an extra zoom levels in Leaflet Maps - maps

Im using leaflet to create a photo map, with my own tiles, which works as expected.
Im trying to work out how I can prevent the zoom from following this Quadtree type pattern:
Zoom Level 0 - Entire map width = 256px;
Zoom Level 1 - Entire map width = 512px;
Zoom Level 2 - Entire map width = 1024px;
And so on...
I would like to be able to zoom in say increments of 25% or 100px.
An example of 100px increments:
Zoom Level 0 - Entire map width = 200px;
Zoom Level 1 - Entire map width = 300px;
Zoom Level 2 - Entire map width = 400px;
And so on...
Question:
What is the logic for doing this? If it is at all possible?
My reason for wanting to do this is so that my photo map (which doesnt wrap like a normal map) can be more responsive and fit the users screen size nicely.
I made a demonstration of my issue which can be seen here

The short answer is that you can only show zoom levels for which you have pre-rendered tiles. Leaflet won't create intermediary zoom levels for you.
The long answer is that in order to use do this, you need to define your own CRS scale method and pass it to your map, for example:
L.CRS.CustomZoom = L.extend({}, L.CRS.Simple, {
scale: function (zoom) {
// This method should return the tile grid size
// (which is always square) for a specific zoom
// We want 0 = 200px = 2 tiles # 100x100px,
// 1 = 300px = 3 tiles # 100x100px, etc.
// Ie.: (200 + zoom*100)/100 => 2 + zoom
return 2 + zoom;
}
});
var map = L.map('map', { crs: L.CRS.CustomZoom }).setView([0, 0], 0);
In this example, I've extended L.CRS.Simple, but you can of course extend any CRS from the API you'd like, or even create your own from scratch.
Using a zoom factor which results in a map pixel size that is not a multiple of your tilesize, means your right/bottom edge tiles will only be partially filled with map data. This can be fixed by making the non-map part of such tiles 100% transparent (or same the colour as your background).
However, it is, in my opinion, a much better idea to set the tilesize to match the lowest common denominator, in this case 100px. Remember to reflect this by using the tileSize option in your tile layer. And, of course, you will need to re-render your image into 100x100 pixels tiles instead of the 256x256 tiles you are using currently.
One caveat, the current version of LeafletJS (0.5) has a bug that prevents a custom scale() method from working, due to the TileLayer class being hardcoded to use power-of-2 zoom scaling. However, the change you need to do is minor and hopefully this will be addressed in a future release of Leaflet. Simply change TileLayer._getWrapTileNum() from:
_getWrapTileNum: function () {
// TODO refactor, limit is not valid for non-standard projections
return Math.pow(2, this._getZoomForUrl());
},
To:
_getWrapTileNum: function () {
return this._map.options.crs.scale(this._getZoomForUrl());
},

Related

leaflet.js: set max zoom level per layer?

I am using CartoDB.js (3.15.9, based on Leaflet.js) with two map base layers, a street layer from CartoDB and a satellite layer from MapQuest:
var options = {
center: [53.2, -2.0],
zoom: 6
};
var map = new L.Map('map', options);
var streetLayer = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors</a>'
}).addTo(map);
L.control.layers({
'Map': streetLayer,
'Satellite': MQ.satelliteLayer()
}, null, {'collapsed': false, 'position': 'bottomleft'}).addTo(map);
Can I set per-layer max zoom levels? I would like a max zoom of 18 on the street layer, and 21 on the satellite layer (this is because they have different max zoom levels available).
I tried setting maxZoom: 18 on the streetLayer object, but this doesn't seem to do anything. The same option on options sets a global maximum zoom, but that obviously isn't what I want.
As you figured out, map's maxZoom option limits the navigation (the user cannot zoom higher than the specified level).
On a Tile Layer, the maxZoom option defines up to which level the Tile Layer is updated on the map. Passed that level, the Tile Layer no longer updates, i.e. tiles are no longer downloaded and displayed.
You might be interested in using it in conjunction with the maxNativeZoom option:
Maximum zoom number the tile source has available. If it is specified, the tiles on all zoom levels higher than maxNativeZoom will be loaded from maxNativeZoom level and auto-scaled.
For example, for your street layer, you could set maxNativeZoom at 18 and maxZoom at 21.
Now if you want different navigation limits depending on what the map currently displays (for example if you have your 2 Tile Layers as basemaps in a Layers Control, so that they are not simultaneously displayed), you could use map.setMaxZoom() to dynamically change that limit when the user switches the basemap.

How to add multiple layers using TileLayer and TileLayer.WMS in leaflet

I am new to leafletjs. I am working on a map that renders just the united states map with few layers options such as roads and major roads ecc.. I have a base layer that uses WMS protocol to get the first layer's tiles with the following code:
L.tileLayer.wms(......)
Then I have another layer that comes from a different server which does not use WMS protocol. This server accepts parameters like bbox, height, width, lat, lng and few others. I can query this server for one tile at a time, so i need to provide multiple ajax calls to get multiple tiles to cover my current view and also update all layers when the map is moved.
The problem I have is how to make both layers work together? and how to get the Bounding Box of each tile the leafletJS way? and how to continue to update all tiles on "moveend" event as the user moves the map?
Using version 0.7.3 of leafletJS!
Thanks for your help
There is undocumented function in L.TileLayer class:
L.TileLayer.getTileUrl(tilePoint)
This function is called for each tile on a screen. It receives hash with x, y and z keys (tile number) and returns image URL for the tile. You can rewrite this function in your L.TileLayer instance:
var layer = L.tileLayer(url);
layer.getTileUrl = function(tilePoint) {
return 'http://example.com/' + tilePoint.z + '/' + tilePoint.y + '/' + tilePoint.x + '.png';
}
So, you don't need to track map movements, appearing and disappearing of tiles, etc.

Esri Silverlight control Pan/Zoom from code

I have trouble getting Map behave properly when calling ZoomToResolution and PanTo
I need to be able to Zoom into specific coordinate and center map.
The only way I got it working is by removing animations:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
Otherwise if I make call like this:
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
It does one or another (i.e. pan or zoom, but not both). If (after animation) I call this code second time (map already zoomed or panned to needed position/level) - it does second part.
Tried this:
control.MapControl.ZoomToResolution(ZoomLevel, MapPoint());
Same issue, internally it calls above commands
So, my only workaround right now is to set Zoom/Pan duration to 0. And it makes for bad UX when using mouse.
I also tried something like this:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
this.MapControl.ZoomDuration = new TimeSpan(750);
this.MapControl.PanDuration = new TimeSpan(750);
Which seems to be working, but then mouse interaction becomes "crazy". Mouse scroll will make map jump and zoom to random places.
Is there known solution?
The problem is the second operation replaces the previous one. You would have to wait for one to complete before starting the next one. But that probably doesn't give the effect you want.
Instead zoom to an extent, and you'll get the desired behavior. If you don't have the extent but only center and resolution, you can create one using the following:
var zoomToExtent = new Envelope(point.X - resolution * MapControl.ActualWidth/2, point.Y, point.X + resolution * MapControl.ActualWidth/2, point.Y);
Btw it's a little confusing in your code that you call your resolution "ZoomLevel". I assume this is a map resolution, and not a level number right? The esri map control doesn't deal with service-specific levels, but is agnostic to the data's levels and uses a more generic "units per pixels" resolution value.

How can I change the location center of a map using Leaflet API?

My map (Mapbox) takes up the whole background of the site so the center is in the middle of the site. But the focus of the map for the user is on the right side because I have content overlapping the map on the left side. When leaflet grabs the location, it's from the center of the map, but it would be more convenient if I could set it to grab the location from the center of the right third of the site, so that way the user won't be centering the map on targets bordering content on the left half of the site.
Is there a way I could set the center or location focus of the leaflet API for the map?
Here's how I have it set up currently,
mapOptions: {
maxZoom: 18,
zoomControl: false,
worldCopyJump: true
},
createMap: function() {
Map.map = L.map('map', Map.mapOptions);
Map.layer = L.mapbox.tileLayer(Map.mapID, {
unloadInvisibleTiles: true,
}).addTo(Map.map);
Map.map.locate({setView: true});
Map.map.addControl(L.mapbox.geocoderControl(Map.mapID));
new L.Control.Zoom({ position: 'topright' }).addTo(Map.map);
},
You can use a combo of panBy() and getSize() to offset the map the width of your overlay.
Here's a snippet that should get you a started:
// Calculate the offset
var offset = map.getSize().x*0.15;
// Then move the map
map.panBy(new L.Point(-offset, 0), {animate: false});
In my example, my overlay is 33% of the width of the map. So I grab the size of the map area, then multiple by 0.15 (this was based on some experimenting to see what the best offset amount was) and use panBy() to offset the map center.
Here's a full example.
If you have multiple markers and want to center the map in their bounds and at the same time you have overlapping container, you can use the fitBounds options (paddingTopLeft, paddingBottomRight, padding).
http://leafletjs.com/reference.html#map-paddingtopleft
First you will need to know the lat and long of the point on the map you want to center on. Then it is simple, just call Map.map.setView passing in your coordinates and zoom level.
Api reference: http://leafletjs.com/reference.html#map-set-methods
If you don't know your coordinates then you can find it by trial and error, just create a marker and add it to the map.
Found this solution by ghybs over on GIS which helped me solve this problem:
Leaflet-active-area
This plugin allows you to use a smaller portion of the map as an active area. All positioning methods (setView, fitBounds, setZoom) will be applied on this portion instead of the all map.

Configuring maxExtent and restrictExtent coordinates in OpenLayers

I'm very new to OpenLayers and working with GeoData; as such, I think I have a pretty noob question about configuring map bounds with OpenLayers. First, here's the map code I've made up...
function createMap(containerId){
return new OpenLayers.Map(containerId, {
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
maxResolution:156543.0339,
numZoomLevels:4,
controls: [],
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
restrictExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34)
});
}
I have my map and I've loaded a GeoJSON layer of vector country shapes on top of it. So far so good. However, if I call zoomToMaxExtent on the map, the map zooms out to be a little tiny thumbnail-sized graphic in the center of my viewport rather than filling the frame. Then if I zoom in on the map, I can (seemingly) pan the map indefinitely in any direction rather than being constrained at the edges of the map shapes.
So I assume I'm doing something wrong with my maxExtent and restrictExtent settings, although I have no idea what it is. To be honest, I'm not sure what those huge bounding numbers are (found them in sample code). Essentially, by Lon/Lat coordinates, I think I'm just trying to restrict bounding to -180, -90, 180, 90 – which should provide a tight frame around the map geography, right? Unfortunately setting those Lon/Lat's to the bounding params don't seem to do anything. Any insight would be greatly appreciated!

Resources