MediaWiki:Common.js: Difference between revisions

From Rise of Agon Wiki
Admin (talk | contribs)
Fix CRS transformation to prevent negative tile Y
Admin (talk | contribs)
Align tile zooms and bounds
Line 1: Line 1:
// Custom Agon World Map with Leaflet (tiled)
// Custom Agon World Map with Leaflet (tiled)
(function() {
(function() {
    // Only run on map pages
     if (!document.getElementById('agon-map-container')) return;
     if (!document.getElementById('agon-map-container')) return;


    // Load Leaflet CSS
     var link = document.createElement('link');
     var link = document.createElement('link');
     link.rel = 'stylesheet';
     link.rel = 'stylesheet';
Line 10: Line 8:
     document.head.appendChild(link);
     document.head.appendChild(link);


    // Load Leaflet JS
     var script = document.createElement('script');
     var script = document.createElement('script');
     script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
     script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
     script.onload = function() {
     script.onload = function() { initAgonMap(); };
        initAgonMap();
    };
     document.head.appendChild(script);
     document.head.appendChild(script);
})();
})();
Line 23: Line 18:
     if (!mapContainer) return;
     if (!mapContainer) return;


    // Image dimensions and tiling
     var imageWidth = 16384;
     var imageWidth = 16384;
     var imageHeight = 16384;
     var imageHeight = 16384;
     var tileSize = 256;
     var tileSize = 256;
     var maxZoom = 6; // 16384 / 256 = 64 => log2(64) = 6
     var maxZoom = 6; // tiles generated 0..6


     // Custom CRS: same as L.CRS.Simple but with y increasing downwards (so tile y >= 0)
     // CRS with downward-positive Y; scale is 2^zoom, so pixel coords are divided by scale
     var crs = L.extend({}, L.CRS.Simple, {
     var crs = L.extend({}, L.CRS.Simple, {
         transformation: new L.Transformation(1, 0, 1, 0)
         transformation: new L.Transformation(1, 0, 1, 0)
     });
     });


     // Bounds in pixel space projected via CRS at max zoom
     // Bounds in lat/lng units (pixels scaled by 2^maxZoom)
     var southWest = crs.unproject(L.point(0, imageHeight), maxZoom);
     var southWest = crs.unproject(L.point(0, imageHeight), maxZoom);
     var northEast = crs.unproject(L.point(imageWidth, 0), maxZoom);
     var northEast = crs.unproject(L.point(imageWidth, 0), maxZoom);
     var bounds = new L.LatLngBounds(southWest, northEast);
     var bounds = new L.LatLngBounds(southWest, northEast);


    // Create map centered on bounds
     var map = L.map('agon-map-container', {
     var map = L.map('agon-map-container', {
         crs: crs,
         crs: crs,
Line 45: Line 38:
         maxZoom: maxZoom,
         maxZoom: maxZoom,
         center: bounds.getCenter(),
         center: bounds.getCenter(),
         zoom: 2,
         zoom: maxZoom, // start at full detail (tile z=6)
         maxBounds: bounds,
         maxBounds: bounds,
         maxBoundsViscosity: 1.0
         maxBoundsViscosity: 1.0
     });
     });


    // Tile layer using generated pyramid under /resources/assets/tiles/AgonBigMap/{z}/{x}/{y}.jpg
     var tileUrl = mw.config.get('wgScriptPath') + '/resources/assets/tiles/AgonBigMap/{z}/{x}/{y}.jpg';
     var tileUrl = mw.config.get('wgScriptPath') + '/resources/assets/tiles/AgonBigMap/{z}/{x}/{y}.jpg';
     L.tileLayer(tileUrl, {
     L.tileLayer(tileUrl, {
Line 61: Line 53:
     }).addTo(map);
     }).addTo(map);


    // Fit map to bounds
     map.fitBounds(bounds);
     map.fitBounds(bounds);


    // Add scale
     L.control.scale().addTo(map);
     L.control.scale().addTo(map);


    // Click to get coordinates (report image pixel coords Y, X)
     map.on('click', function(e) {
     map.on('click', function(e) {
         var point = map.project(e.latlng, maxZoom); // pixel space
         var point = map.project(e.latlng, maxZoom); // pixel space
Line 74: Line 63:
         var text = 'Coordinates: ' + y + ', ' + x;
         var text = 'Coordinates: ' + y + ', ' + x;
         console.log(text);
         console.log(text);
         L.popup()
         L.popup().setLatLng(e.latlng).setContent(text).openOn(map);
            .setLatLng(e.latlng)
            .setContent(text)
            .openOn(map);
     });
     });


    // Load markers from page data; stored as 'Y, X' pixel coordinates
     if (window.agonMapMarkers) {
     if (window.agonMapMarkers) {
         window.agonMapMarkers.forEach(function(marker) {
         window.agonMapMarkers.forEach(function(marker) {
Line 91: Line 76:
                 popupAnchor: [1, -34]
                 popupAnchor: [1, -34]
             });
             });
 
             L.marker(latlng, { icon: icon }).addTo(map).bindPopup('<b>' + marker.name + '</b><br>' + marker.description);
             L.marker(latlng, { icon: icon })
                .addTo(map)
                .bindPopup('<b>' + marker.name + '</b><br>' + marker.description);
         });
         });
     }
     }
}
}

Revision as of 09:09, 25 January 2026

// Custom Agon World Map with Leaflet (tiled)
(function() {
    if (!document.getElementById('agon-map-container')) return;

    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
    document.head.appendChild(link);

    var script = document.createElement('script');
    script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
    script.onload = function() { initAgonMap(); };
    document.head.appendChild(script);
})();

function initAgonMap() {
    var mapContainer = document.getElementById('agon-map-container');
    if (!mapContainer) return;

    var imageWidth = 16384;
    var imageHeight = 16384;
    var tileSize = 256;
    var maxZoom = 6; // tiles generated 0..6

    // CRS with downward-positive Y; scale is 2^zoom, so pixel coords are divided by scale
    var crs = L.extend({}, L.CRS.Simple, {
        transformation: new L.Transformation(1, 0, 1, 0)
    });

    // Bounds in lat/lng units (pixels scaled by 2^maxZoom)
    var southWest = crs.unproject(L.point(0, imageHeight), maxZoom);
    var northEast = crs.unproject(L.point(imageWidth, 0), maxZoom);
    var bounds = new L.LatLngBounds(southWest, northEast);

    var map = L.map('agon-map-container', {
        crs: crs,
        minZoom: 0,
        maxZoom: maxZoom,
        center: bounds.getCenter(),
        zoom: maxZoom, // start at full detail (tile z=6)
        maxBounds: bounds,
        maxBoundsViscosity: 1.0
    });

    var tileUrl = mw.config.get('wgScriptPath') + '/resources/assets/tiles/AgonBigMap/{z}/{x}/{y}.jpg';
    L.tileLayer(tileUrl, {
        tileSize: tileSize,
        minZoom: 0,
        maxZoom: maxZoom,
        maxNativeZoom: maxZoom,
        bounds: bounds,
        noWrap: true
    }).addTo(map);

    map.fitBounds(bounds);

    L.control.scale().addTo(map);

    map.on('click', function(e) {
        var point = map.project(e.latlng, maxZoom); // pixel space
        var y = point.y.toFixed(2);
        var x = point.x.toFixed(2);
        var text = 'Coordinates: ' + y + ', ' + x;
        console.log(text);
        L.popup().setLatLng(e.latlng).setContent(text).openOn(map);
    });

    if (window.agonMapMarkers) {
        window.agonMapMarkers.forEach(function(marker) {
            var coords = marker.coordinates.split(',').map(parseFloat); // [Y, X]
            var latlng = map.unproject([coords[1], coords[0]], maxZoom); // [x, y]
            var icon = L.icon({
                iconUrl: marker.icon || mw.config.get('wgScriptPath') + '/resources/assets/marker-icon.png',
                iconSize: [25, 41],
                iconAnchor: [12, 41],
                popupAnchor: [1, -34]
            });
            L.marker(latlng, { icon: icon }).addTo(map).bindPopup('<b>' + marker.name + '</b><br>' + marker.description);
        });
    }
}