I've been trying to get a flat image fantasy map working within LeafletJS. I have pretty much everything working well except for the map scale legend where two things are not working:

  1. I can't seem to change the scale of the control to reflect the scale of the map. The scale is trying to set itself for earth LatLng scales yet the map is of a very localised place.

  2. As the map is repositioned in the window the scale changes to reflect the curvature of the earth (so the scale increases the closer you get to the poles).

All I want is a flat scale across all areas of the map and wanting to have the scale accurately reflect the scale in the image. For example, if in the image 1km = 100 pixels how would I go about making that work within the map?

My basic code is as follows:

// ---- VARIABLES ---- //
// Map bounds (pixels)
var mapSW = [0, 8192]; // Deepest Z/X/Y.png +1 x 256.
var mapNE = [8192, 0]; // Deepest Z/X +1 x 256.

// ---- MAP SETUP ---- //
// Setup the map view
// Initial coordinates as well as starting zoom factor

var map = L.map('map').setView([0, 0], 2);

// Reference the map tiles output from Photoshop with properties
// for zoom factors and attribution.
// Use CRS.Simple so that the map can be referenced in terms of
// pixels rather than latitude and longitude

L.tileLayer('map/{z}/{x}/{y}.png', {
    minZoom: 2,
    maxZoom: 5,
    nowrap: true,
    crs: L.CRS.Simple,

// Set the map bounds

map.setMaxBounds(new L.LatLngBounds(
      map.unproject(mapSW, map.getMaxZoom()),
      map.unproject(mapNE, map.getMaxZoom())

// Scale legend
// TODO. Not working properly yet



I don't consider this a valid answer because this is just a hack over existing code. I know, I know - I'm lazy. ;)

Rather than just use Leaflet's Scale control I hacked this into the nicer looking graphicscale add-on (https://github.com/nerik/leaflet-graphicscale).

  1. Added in a couple of options (as different maps will require different scales). Sorry, not sure why the code isn't wanting to format.

    L.Control.GraphicScale = L.Control.extend({ options: { ..., unitsPer1000px: 1000, // x units per 1000 pixels at max zoom. Metres is default scaleUnit: null, // override with your own unit designation. null is default and will revert to m / km }, ...

  2. Replaced the _update method to just use the zoom factor to tweak the scale set in the options.

    _update: function () { var mapScale = Math.pow(2, this._map.getZoom()) / Math.pow(2, this._map.getMaxZoom()), dist = this.options.unitsPer1000px / mapScale;

    this._updateScale(dist, this.options);


  3. Replaced the _getDisplayUnit method to allow custom units. If custom units not specified revert to m / km.

    _getDisplayUnit: function(units) { if(this.options.scaleUnit) { // Defined scale unit return { unit: this.options.scaleUnit, amount: units }; } else { // Not defined. Use metres/km var displayUnit = (units<1000) ? 'm' : 'km'; return { unit: displayUnit, amount: (displayUnit === 'km') ? units / 1000 : units }; } }

  4. Then just call it with the options in the javascript.

    var graphicScale = L.control.graphicScale({ doubleLine: true, fill: 'hollow', showSubunits: true, unitsPer1000px: 6.8, scaleUnit: 'parsecs', }).addTo(map);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.