Map Transformation Operations in Arnis: Translate, Scale, and Coordinate Systems
Arnis implements map transformation operations through Leaflet's L.Transformation class and CSS transforms, supporting linear translation and uniform scaling but not rotation.
The Arnis repository leverages Leaflet combined with Proj4Leaflet to render raster tiles in arbitrary coordinate reference systems. All geometric transformations rely on the L.Transformation class for coordinate math and CSS transforms for DOM rendering, with no rotation capabilities implemented in the core pipeline.
Core Transformation Engine: L.Transformation
The foundation of Arnis map operations is Leaflet's L.Transformation class, which implements a linear affine transformation defined by:
x' = a·x + b
y' = c·y + d
In this model, a and c represent scaling factors while b and d represent translation offsets. This strictly affine approach limits transformations to scaling and translation, excluding rotation matrices.
Transformation Methods
The L.Transformation class exposes two primary methods for coordinate conversion:
transform(point, scale?)– Applies the linear transformation to anL.Pointobject, optionally factoring in a zoom scale.untransform(point, scale?)– Performs the inverse operation, converting screen coordinates back to map coordinates.
Constructor Usage in Arnis
The repository defines three distinct transformation patterns in src/gui/js/maps/proj4leaflet.js:
1. Default CRS Transformation
new L.Transformation(1, 0, -1, 0)
Located at lines 71-73, this creates an identity scale on the x-axis (a=1), flips the y-axis (c=-1) to match tile origin conventions, and applies zero translation.
2. Custom Origin Adjustment
new L.Transformation(1, -this.options.origin[0], -1, this.options.origin[1])
Found at lines 97-99, this translates the map so that user-specified origin coordinates align with the tile grid, effectively shifting the map center while maintaining scale.
3. Scale-Dependent Transforms
new L.Transformation(1, -crsBounds[0], -1, upperY)
At lines 168-169, Arnis builds zoom-level specific transformations (scaleTransforms[zoom]) that translate to the upper-left corner of projected bounds, enabling precise tile alignment across different zoom scales.
CSS-Level Transformations for Rendering
Beyond coordinate math, Arnis applies CSS transforms to DOM elements during rendering animations. In src/gui/js/maps/leaflet.js (lines 8-9), the codebase utilizes:
this._pathRoot.style[o.DomUtil.TRANSFORM] =
o.DomUtil.getTranslateString(i) + " scale(" + e + ") ";
This implementation:
- Generates a
translate(x, y)string from pointiusinggetTranslateString - Applies a uniform
scale(e)factor for zoom animations - Targets SVG path roots for hardware-accelerated rendering
These CSS operations perform translation and uniform scaling exclusively, matching the affine constraints of the coordinate system.
How Transformations Are Wired Together
The transformation pipeline in Arnis follows a three-stage process:
-
CRS Definition – When initializing a custom coordinate reference system via
new L.Proj.CRS(...), theoptions.transformationfield receives anL.Transformationinstance (default, origin-adjusted, or scale-dependent). -
Tile Coordinate Conversion – The tile layer (
L.Proj.TileLayer.TMS) invokesthis.crs.transformation.transform(point, scale)to convert projected coordinates into pixel positions for tile retrieval. -
SVG Path Rendering – During zoom animations, Leaflet calculates pixel offsets and zoom factors, then writes CSS
transform: translate(...) scale(...)declarations to SVG container elements.
This pipeline ensures consistent linear transformation application from data coordinates through to screen rendering.
Why Rotation Is Not Supported
Arnis deliberately excludes rotation operations from its transformation pipeline. Leaflet's core projection model assumes a north-up orientation, and the L.Transformation class implements strictly affine transformations (scale + translate) without rotation matrices.
Introducing rotation would require:
- Non-affine transformation matrices (requiring
sin/coscoefficients) - Re-implementation of the tile coordinate calculation pipeline
- CSS rotation transforms that would invalidate the TMS (Tile Map Service) alignment assumptions
The repository maintains this constraint to ensure compatibility with standard tile server conventions and hardware-accelerated CSS transforms.
Practical Code Examples
Creating a Custom Transformation
// Define translation and scale parameters
var tx = 100; // translate 100px on X
var ty = 200; // translate 200px on Y
var scale = 2; // double size
// Instantiate transformation
var myTransform = new L.Transformation(scale, tx, scale, ty);
// Apply to map point
var pt = L.point(10, 20);
var px = myTransform.transform(pt); // → L.Point { x: 120, y: 240 }
// Inverse operation (screen to map)
var mapPt = myTransform.untransform(px); // → L.Point { x: 10, y: 20 }
Integrating with CRS Definition
// Configure custom CRS with origin translation
var myCrs = new L.Proj.CRS('EPSG:3857', null, {
origin: [500000, 2000000],
transformation: new L.Transformation(1, -500000, -1, 2000000)
});
Observing CSS Transforms During Zoom
// Monitor zoom animation events
map.on('zoomanim', function (e) {
// e.center – new center in map coordinates
// e.zoom – target zoom level
// Leaflet internally calculates:
// var translate = map.latLngToLayerPoint(e.center);
// var scale = map.getZoomScale(e.zoom);
// Resulting CSS: transform: translate(translate) scale(scale);
});
Summary
- Core Class:
L.Transformationinsrc/gui/js/maps/proj4leaflet.jsimplements linear affine transformations using coefficientsa·x + bandc·y + d. - Available Operations: Translation (via
banddoffsets) and uniform scaling (viaaandccoefficients). Rotation is not supported. - Application Points: Three variants exist—default CRS (flip Y-axis), custom origin (align user origin to tile grid), and scale-dependent (per-zoom-level bounds alignment).
- Rendering Layer: CSS
transform: translate(...) scale(...)applied insrc/gui/js/maps/leaflet.jshandles hardware-accelerated SVG/Canvas animations. - Inverse Operations: The
untransform()method converts screen coordinates back to map coordinates for user interaction handling.
Frequently Asked Questions
Does Arnis support rotating the map view?
No, Arnis does not implement map rotation. The transformation pipeline relies on Leaflet's L.Transformation class, which only supports affine transformations (scaling and translation). Rotation would require non-affine matrix operations that break the tile coordinate calculations in src/gui/js/maps/proj4leaflet.js.
How do I apply a custom origin offset to my Arnis map?
Define a custom L.Transformation with negative origin values when creating your CRS. In src/gui/js/maps/proj4leaflet.js at lines 97-99, the pattern new L.Transformation(1, -origin[0], -1, origin[1]) translates the coordinate system so your specified origin aligns with the tile grid's upper-left corner.
What is the difference between transform() and untransform()?
transform() converts map coordinates to screen pixels by applying the linear equation x' = a·x + b, while untransform() performs the inverse operation, solving for x = (x' - b) / a to convert screen coordinates back to map coordinates. Both methods accept an optional scale parameter for zoom-level adjustments.
Where are CSS transforms applied in the Arnis codebase?
CSS transforms are written to SVG path elements in src/gui/js/maps/leaflet.js (lines 8-9) during zoom animations. The code constructs a transform string combining translate(x, y) from DomUtil.getTranslateString() and scale(zoomFactor) to hardware-accelerate the visual transition of vector layers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →