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.
Related
Even though markers are being groups into clusters, there are still a large of markers that arent included.
i've gone through the options but cannot figure out a way to ... increase the area in which markers get included into the cluster.
You should be able to address this issue by increasing the gridSize option from its default 60 pixels to a higher value when instantiating your MarkerClusterer.
For example, the following instantiation code changes the grid square size of a cluster up from 60 pixels to 120 pixels:
var mc = new MarkerClusterer(map, markers, {
gridSize: 120
});
This should result in a larger cluster catchment area and fewer individual markers.
If not, I'd recommend checking that all of your markers are being included in the clustering process.
The other options you could check are the maxZoom and minimumClusterSize settings. Whilst the following defaults for these options are intended to keenly cluster your markers, if you've adjusted these defaults you may have inadvertently reduced the degree of clustering:
maxZoom: The maximum zoom level at which clustering is enabled or null if
clustering is to be enabled at all zoom levels. The default value is
null.
minimumClusterSize: The minimum number of markers needed in a cluster before the markers are hidden and a cluster marker appears. The default value is 2.
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.
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.
I have an app that displays recent jobs on a map as pinpoints using Leafletjs.
With Leafletjs, when you want to zoom to the user's detected location, you call something like:
map.locate({'setView' : true, 'timeout' : 10000, maxZoom: 10});
However, for some locations, the zoom level 10 does not contain any job points, so I'd like to dynamically set the zoom so that at least on job is visible to the users.
I know that I can listen for the locate function's success and then check with something like:
map.on('locationfound', function() {
//for marker in markers{
//is point within currently visible bounds
//break on first positive
//else,
//zoom up a level, repeat previous checks
}
}
but that's quite inefficient, especially if I have a large number of points.
Does Leaflet have any built in functions/methods to provide information about layers within the current map view?
If you do some things on the server side, you can probably do the calculations fast enough.
Store the locations in pixel coordinates in your database at some way-zoomed-in zoom level (I use zoom level 23). I call this coordinate system "Vast Coordinate System". Then, to get the tile coordinates for a point at a specific location is IIRC one bitwise shift -- very fast, and something you can do in SQL.
Convert your users' location to pixel coords at that way-zoomed in level.
Iterate on zoom level. Get the tile coord for the user's location at that zoom level, then do an SQL query which counts the number of points on that tile. If > 0, stop.
Your SQL will be something like (sorry, I'm being lazy and doing it from memory/thinking instead of actually trying it out)
SELECT count(*) WHERE (vcsX>>(zoom+8)==userX>>(zoom+8)) AND (vcsY>>(zoom+8)==userY>>(zoom+8));
where vcsX and vcsY are the pixel coordinates in Vast Coordinate System.
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());
},