MS RFC 142: Scalebar Measurement Modes¶
- Author:
Tamas Szekeres
- Contact:
szekerest at gmail.com
- Last Updated:
2026-05-16
- Version:
MapServer 8.8
- Status:
Draft
Overview¶
This RFC proposes adding explicit measurement modes to the MapServer
SCALEBAR object. The goal is to preserve the current Cartesian scalebar
behavior by default while allowing users to request local geodesic ground
distance measurement for projections where projected map units are not equal to
ground distance.
The main motivating case is Web Mercator / EPSG:3857. In that projection, coordinates are expressed in meters, but projected meters increasingly differ from local ground meters away from the equator. The current scalebar implementation treats projected coordinate units as a flat Cartesian plane, so metric scalebars can overstate local ground distance in EPSG:3857 and similar projections.
This RFC introduces an explicit MEASURE keyword:
SCALEBAR
UNITS KILOMETERS
MEASURE CARTESIAN
END
SCALEBAR
UNITS KILOMETERS
MEASURE GEODESIC
END
MEASURE CARTESIAN remains the default and preserves existing output.
MEASURE GEODESIC computes a local ground distance for the sampled scalebar
pixel span.
Current Implementation¶
The current scalebar implementation is centered in mapscale.c.
The main functions are:
msDrawScalebar(): computes the interval length, creates the scalebar image, and renders the scalebar graphics and labels.msEmbedScalebar(): renders the scalebar into a temporary image and embeds it as a generated symbol through a synthetic layer.msInchesPerUnit(): returns the unit conversion factor used by the Cartesian calculation.msCalculateScale(): computes the map scale denominator. This is related map state, but it is not the scalebar measurement algorithm itself.
The current distance calculation in msDrawScalebar() is approximately:
distance =
map.cellsize * desired_pixel_width /
(inches_per_scalebar_unit / inches_per_map_unit)
The implementation then rounds the interval value, converts the rounded interval back to pixels, and shrinks the desired pixel width until the bar and label fit the requested image width.
This algorithm is fast and stable, but it assumes a Cartesian coordinate plane. MapServer documentation already describes the scalebar as Cartesian and not geodesic.
Problem Statement¶
The current implementation is not suitable when users expect a scalebar to represent local ground distance in a distorted projected CRS.
For example, with MAP PROJECTION set to EPSG:3857 and SCALEBAR UNITS
METERS or KILOMETERS, the current implementation treats a horizontal pixel
span in projected meters as if it were a ground-distance span. This is only
approximately true near the equator.
Issue https://github.com/MapServer/MapServer/issues/7397 reports this behavior for EPSG:3857 and compares MapServer output against distance calculations in a client map library.
Proposed Solution¶
Add a new MEASURE keyword to the SCALEBAR object.
Supported values:
CARTESIAN: existing projected-plane behavior.GEODESIC: local ellipsoidal ground-distance behavior.
An optional SPHERICAL mode may be considered later as an explicitly
approximate mode, but it is not required for the first implementation.
The default value is:
MEASURE CARTESIAN
The default must not change existing scalebar output.
Mapfile Syntax¶
Example:
SCALEBAR
STATUS EMBED
POSITION LL
UNITS KILOMETERS
INTERVALS 4
SIZE 200 3
MEASURE GEODESIC
LABEL
SIZE 10
COLOR 0 0 0
END
END
MEASURE should be parsed, serialized, and exposed consistently with other
scalebarObj properties.
The following paths must round-trip the new setting:
mapfile parsing through
loadScalebar()mapfile writing through
writeScalebar()runtime string updates through
msUpdateScalebarFromString()string serialization through
msWriteScalebarToString()MapScript bindings for
scalebarObj
Architecture¶
The implementation should introduce a measurement layer between
msDrawScalebar() and the existing interval fitting logic.
msDrawScalebar()
measure desired pixel span
choose rounded interval
convert interval to pixels
fit width
render existing scalebar styles
The measurement layer should replace only the distance estimate. The existing rounding, fit-shrinking, style rendering, label placement, and image creation structure should remain as close to the current implementation as practical.
A possible helper API is:
static int msScalebarMeasurePixelSpan(mapObj *map,
const scalebarObj *scalebar,
double pixel_width,
scalebarDistanceObj *distance);
where scalebarDistanceObj contains a numeric value and output unit.
The helper dispatches to the configured backend:
Cartesian backend
Geodesic backend
Optional spherical backend, if later accepted
Cartesian Measurement¶
The Cartesian backend preserves the current calculation:
distance =
map.cellsize * pixel_width /
(inches_per_scalebar_unit / inches_per_map_unit)
This backend is the default and must keep current behavior unchanged.
Geodesic Measurement¶
The geodesic backend computes a local distance for the horizontal pixel span used by the scalebar fitting loop.
Algorithm:
Choose a deterministic representative sample pixel from
SCALEBAR POSITIONandSCALEBAR OFFSET.Convert the pixel endpoints to map coordinates.
If the map CRS is geographic, use the endpoint coordinates directly as longitude/latitude.
Otherwise transform endpoints from
map->projectiontomap->latlon.Compute inverse geodesic distance on the appropriate ellipsoid.
Convert the measured distance in meters to
scalebar->units.
This work must reuse MapServer’s existing projection context lifecycle and thread-safety model. It should not introduce an independent projection context management path.
If endpoint transformation or geodesic distance calculation fails, the scalebar
draw operation must fail with a clear error. MEASURE GEODESIC must not
silently fall back to Cartesian output.
Sample Position¶
Geodesic scalebars are local measurements. A deterministic sample-position policy is therefore required.
Initial policy:
lower positions (
LL,LC,LR) sample near the lower map edgeupper positions (
UL,UC,UR) sample near the upper map edgeunsupported positions fall back to the map vertical centerline
OFFSETis treated as a simple vertical adjustment of the sample rowgenerated scalebar image height is ignored for the first implementation
the horizontal sample coordinate is always the map horizontal center
the final sample row is clamped to the valid map image extent
the same policy applies to standalone and embedded scalebars
This intentionally uses existing POSITION and OFFSET settings as
measurement hints instead of adding a separate SAMPLEPOSITION or
MEASUREPOSITION keyword. For standalone scalebars, these settings describe
where the user intends to place the generated scalebar relative to the map in
the surrounding user interface.
A representative implementation shape is:
static void msScalebarSamplePixel(const mapObj *map, double *px,
double *py) {
double x = map->width * 0.5;
double y;
switch (map->scalebar.position) {
case MS_LL:
case MS_LR:
case MS_LC:
y = map->height - map->scalebar.offsety - 1.0;
break;
case MS_UL:
case MS_UR:
case MS_UC:
y = map->scalebar.offsety;
break;
default:
y = map->height * 0.5;
break;
}
*px = x;
*py = MS_MAX(0.0, MS_MIN(y, map->height - 1.0));
}
Documentation must clearly explain that a geodesic scalebar reports local scale at the selected sample position. It is not a globally valid scale for every part of the map.
Unit Conversion¶
The local measurement backends should produce a canonical distance in meters. The result is then converted to the configured scalebar output unit.
Existing supported scalebar output units must continue to work:
inches
feet
miles
meters
kilometers
nautical miles
Decimal degrees should remain invalid as scalebar output units unless a separate RFC changes that behavior.
Scale Denominator¶
This RFC does not change msCalculateScale() semantics.
Scale denominator calculation and scalebar distance measurement must remain separate concerns. Any broader change to scale denominator behavior should be covered by a separate RFC.
Backwards Compatibility Issues¶
The default behavior remains MEASURE CARTESIAN. Existing mapfiles that do
not use the new keyword should render the same scalebars as before.
The new behavior is opt-in:
SCALEBAR
MEASURE GEODESIC
END
No implicit behavior change is proposed for existing metric scalebars. This is intentional: some users may rely on projected-plane scalebars, even when the output unit is meters or kilometers.
Security Implications¶
No new security implications are expected.
The geodesic backend may perform additional projection operations during scalebar rendering. These should use existing MapServer projection handling and error reporting paths.
MapScript Implications¶
MapScript must expose the new measurement mode on scalebarObj.
MapScript should be able to:
read the default measurement mode
set the measurement mode
preserve the setting when maps are serialized or written
Documentation Needs¶
The following documentation should be updated:
MapServer-documentation/en/mapfile/scalebar.txtMapServer-documentation/en/utilities/scalebar.txtMapScript documentation where
scalebarObjproperties are listed
Documentation must cover:
MEASURE CARTESIANas the defaultMEASURE GEODESICas opt-in local ground-distance measurementhow
POSITIONandOFFSETselect the representative geodesic sample locationfailure behavior when geodesic measurement cannot be computed
the local nature of geodesic scalebars
Testing¶
Numeric measurement tests should be added before relying on rendered-image comparisons.
Required test cases:
default Cartesian behavior remains unchanged
EPSG:3857 at a higher latitude where local ground distance differs from projected distance
invalid projection or endpoint transformation failure
parser and writer round-trip for
MEASURE CARTESIANandMEASURE GEODESICruntime string update through
msUpdateScalebarFromString()string serialization through
msWriteScalebarToString()MapScript read/write access to the measurement mode
Rendering tests should include:
Test explicit cartesian scalebar measurement in EPSG:3857
Test local geodesic scalebar measurement in EPSG:3857
Test geodesic scalebar failure without a usable map projection
Implementation Plan¶
Refactor scalebar distance calculation into an internal measurement helper. Route existing Cartesian behavior through the helper without changing output.
Add
MEASUREtoscalebarObj, the parser, writer, string update path, string serialization path, and MapScript bindings.Implement the geodesic backend using endpoint sampling, existing MapServer projection context management, and ellipsoidal inverse distance.
Add numeric tests for measurement behavior.
Add rendering tests for default behavior and opt-in geodesic behavior.
Update user and MapScript documentation.
Out of Scope¶
The following topics are explicitly out of scope:
changing scalebar visual styles
changing
msCalculateScale()
Affected Files¶
Expected affected source files:
mapserver.hmapfile.cmapscale.cmapcopy.cifscalebarObjcopy handling requires updatesmapscript/swiginc/scalebar.irelevant MapScript generated files, depending on the binding update workflow
msautotesttest files
Expected affected documentation files:
en/mapfile/scalebar.txten/utilities/scalebar.txtrelevant MapScript documentation
Ticket ID and References¶
EPSG:3857 scalebar report: https://github.com/MapServer/MapServer/issues/7397
Voting History¶
No vote yet. Draft RFC for discussion.
