Editing scale-bar in non-geographic maps

Viewed 325

I am working on non-geographic maps in Leaflet. Since the coordinates are Cartesian with no geographic reference, it should not be m by default as follows (I have disabled the imperial units in the scale bar):

enter image description here

Is there any solution to convert 100 m to 100 in the scale bar or even replace the m with something else?

1 Answers

You want to be aware of this piece of code from Leaflet's L.Control.Scale:

_updateMetric: function (maxMeters) {
    var meters = this._getRoundNum(maxMeters),
        label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';

    this._updateScale(this._mScale, label, meters / maxMeters);
},

Now, to change that behaviour there's an ugly hacky way and a bit cleaner way.

The hacky way is to replace either the (private, undocumented) _updateMetric method from either the prototype or the instance of L.Control.Scale, e.g.:

L.Control.Scale.prototype._updateMetric = function (maxMeters) {
    var meters = this._getRoundNum(maxMeters),
        label = meters + ' units';

    this._updateScale(this._mScale, label, meters / maxMeters);
};
var myScaleControl = L.control.scale().addTo(map);

or

var myScaleControl = L.control.scale();
myScaleControl._updateMetric = function (maxMeters) {
    var meters = this._getRoundNum(maxMeters),
        label = meters + ' units';

    this._updateScale(this._mScale, label, meters / maxMeters);
};
myScaleControl.addTo(map);

Replacing non-documented internal methods is usually seen as a dirty hack, and can lead to confusion. In my experience, nobel programmers tend to trust the hack without understanding how JS prototypal inheritance works.

The "a bit cleaner" way is to read Leaflet's tutorial on subclassing controls, and then create a subclass of L.Control.Scale, e.g.:

L.Control.MeterlessScale = L.Control.Scale.extend({
    _updateMetric: function (maxMeters) {
        var meters = this._getRoundNum(maxMeters),
            label = meters + ' units';

        this._updateScale(this._mScale, label, meters / maxMeters);
    }
});

var scale = (new L.Control.MeterlessScale()).addTo(map);

One should not be afraid to read Leaflet's source code. At the same time, please read up on JS prototypal inheritance and read Leaflet's own tutorials before jumping into it.


It is also true that the distance method of L.CRS is assumed to return distance in meters, and cannot inform any caller of the units of that CRS; hence, L.Control.Scale assumes that the distances are in meters. Architecturally speaking, there is no good simple way of abstracting distance units depending on the CRS (unfortunately).

Related