I am preparing a printable map in leaflet. For this purpose I would like to make a map zoom to a particular scale eg. 1:500 or 1:1000. But Leaflet zooms a map based on the extend and zoom level only. Is there any way the map can be zoomed to the scale as desired?
First, a note about map scales.
A map scale represented as a unitless factor (e.g. 1:50000) means that a map unit represents X map units (e.g. a meter in the map = 50 km in reality).
This kind of scale is simply impossible to achieve in web maps, as you cannot be sure of the physical screen size. Think about a laptop connected to a projector screen, having the same image in the small laptop screen and the huge projector wall at the same time.
Now, the usual assumption is that computer screens have a resolution of 72 or 96 pixels per inch, and then one can calculate the size of a CSS pixel as 0.26mm to 0.29mm.
What Leaflet (and any other web map engine) does is have a scale factor between (CSS) pixels and map units in the display CRS. In most web maps, this means a relationship between pixels (in CSS) and EPSG:3857 CRS map units (represented as meters, only makes sense in the equator because projections are complicated, which will add to the problem).
The magic of the "usual" tiling scheme is that the circumference of the earth at the equator is represented in 256 pixels at zoom 0. This means a scale factor of approximately 40075km/256px ~= 156543m/px.
This is meters in the display CRS. As the common CRS (EPSG:3857) implies a cylindrical projection, which is conformal but not equidistant, the pixel-to-meter scale only has meaning at the equator (this is why the scale bar in web maps shifts as you pan the map north or south, by a factor depending on the cosine of the latitude).
So, a Leaflet map does have a map units to pixel scale per map unit (156543units/px). By default, the scale halves at every zoom level.
So now the question becomes: «How can I manually specify a map units-to-pixels scale to Leaflet?»
There are several approaches.
One is to create a custom CRS, specifying scale and zoom functions which fits your needs. This requires a bit of knowledge about extending Leaflet, in order to not make a mess of the code. You'll also need to generate raster data (tiles) according to this scale/zoom relationship in order for the map to appear crisp.
Another approach is to use the fractional (i.e. non-integer) zoom capabilities in Leaflet 1.0.0. Given a map units-to-pixels scale factor, calculate the zoom level (using a base-2 logarithm), set the
zoomSnapoption of the map, and set the zoom level to the desired value. Note that raster tiles might appear blurry (as one raster pixel will not map to one screen pixel).
A third approach is using Proj4Leaflet. This is basically the user-friendlier way of defining a custom CRS, without having to delve in the particulars of the
L.CRSimplementation manually. Note that Proj4Leaflet allows you to specify a list of pixels-per-projection-unit to be used as zoom levels, making your life a bit easier. Again, you might need to generate your own raster data if you want it to look crisp.
Leaflet doesn't have this functionality.
There is a election in the Leaflet site to choose if this function will be developed.
Can't help you with the exact problem, but unless you rendered your multi-scale map at high PPI / retina resolution, and used the exact scales you specify here as the zoom scales to create the map service, the printed map is likely to suffer from poor printing quality due to the mismatch in pixel resolution, because the data will need to be interpolated and the source map tiles may lack the required resolution for good print. The needed interpolation will of course exacerbate the latter.
So, either you will need to accept lesser print quality, or create custom zoom scales at retina resolution, or you will need to use Vector Tiles in Leaflet and find a way to print those (note though that even Vector Tiles are often necessarily simplified for a given zoom level).