186 lines
6.9 KiB
HTML
186 lines
6.9 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8"/>
|
||
<title>Arenos – Map</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||
<style>
|
||
html,body,#map {height:100%;margin:0}
|
||
.coords{position:absolute;left:6px;bottom:6px;z-index:1000;background:rgba(255,255,255,.85);
|
||
padding:3px 6px;font-size:12px;border-radius:4px;font-family:system-ui,Segoe UI,Arial}
|
||
.center-dot{position:absolute;top:50%;left:50%;width:5px;height:5px;background:red;border-radius:50%;transform:translate(-50%,-50%);box-shadow:0 0 0 2px rgba(255,255,255,.95),0 0 2px rgba(0,0,0,.35);pointer-events:none;z-index:1001}
|
||
.copy-btn{position:absolute;left:6px;bottom:36px;z-index:1000;background:#ef4444;color:#fff;border:0;border-radius:6px;padding:6px 10px;font:600 12px/1 system-ui,Segoe UI,Arial;cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,.25)}
|
||
.copy-btn:active{transform:translateY(1px)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="map"></div>
|
||
<div class="center-dot" aria-hidden="true" title="Kartenmitte"></div>
|
||
<div class="coords">—</div>
|
||
<button class="copy-btn" title="Aktuelle Kartenmitte als Marker-Code kopieren">Marker Koords</button>
|
||
|
||
<script>
|
||
// === Parameter (wie Export) ===
|
||
const ORIG_W = 49152; // px
|
||
const ORIG_H = 32768; // px
|
||
const TILE = 256; // px
|
||
const MAX_Z = 7; // 0..7, 0=weit, 7=Originalgröße
|
||
|
||
// === CRS an Export anpassen: z=7 → scale=1 (1px = 1 Einheits-Pixel), z=0 → 1/128 ===
|
||
const CRS_TILES = L.Util.extend({}, L.CRS.Simple, {
|
||
transformation: new L.Transformation(1, 0, 1, 0), // y nach unten positiv
|
||
scale: z => Math.pow(2, z - MAX_Z), // 7→1, 6→1/2, …, 0→1/128
|
||
zoom: s => Math.log(s) / Math.LN2 + MAX_Z
|
||
});
|
||
|
||
// Karte
|
||
const bounds = [[0, 0], [ORIG_H, ORIG_W]]; // Pixelkoordinaten (oben/links → unten/rechts)
|
||
const map = L.map('map', {
|
||
crs: CRS_TILES,
|
||
minZoom: 0,
|
||
maxZoom: MAX_Z,
|
||
preferCanvas: true,
|
||
zoomControl: true,
|
||
inertia: false,
|
||
keepBuffer: 0
|
||
});
|
||
map.fitBounds(bounds);
|
||
map.setMaxBounds(bounds);
|
||
|
||
// Hilfsfunktion: Tile-Grid je Zoom (wie Exporter)
|
||
function gridForZoom(z){
|
||
const scale = Math.pow(2, MAX_Z - z); // z=7 → 1
|
||
const wZ = Math.ceil(ORIG_W / scale);
|
||
const hZ = Math.ceil(ORIG_H / scale);
|
||
const tilesX= Math.ceil(wZ / TILE);
|
||
const tilesY= Math.ceil(hZ / TILE);
|
||
const offX = Math.floor(tilesX / 2);
|
||
const offY = Math.floor(tilesY / 2);
|
||
return { tilesX, tilesY, offX, offY };
|
||
}
|
||
|
||
// Debug: zeige beim ersten Tile die berechneten Werte
|
||
let first = true;
|
||
|
||
const CenteredTiles = L.GridLayer.extend({
|
||
createTile: function (coords, done) {
|
||
const img = document.createElement('img');
|
||
img.decoding = 'async';
|
||
img.referrerPolicy = 'no-referrer';
|
||
img.loading = 'lazy';
|
||
|
||
const { tilesX, tilesY, offX, offY } = gridForZoom(coords.z);
|
||
const xC = coords.x - offX; // zentrierte Namen
|
||
const yC = coords.y - offY;
|
||
|
||
const minX = -offX, maxX = tilesX - offX - 1;
|
||
const minY = -offY, maxY = tilesY - offY - 1;
|
||
|
||
if (first) {
|
||
console.log(`[z=${coords.z}] tilesX=${tilesX} tilesY=${tilesY} offX=${offX} offY=${offY}`);
|
||
console.log('tile coords →', coords, '→ centered', xC, yC, 'validX', minX, maxX, 'validY', minY, maxY);
|
||
first = false;
|
||
}
|
||
|
||
// Außerhalb? → transparent (keine HTTP-Requests)
|
||
if (xC < minX || xC > maxX || yC < minY || yC > maxY) {
|
||
img.width = img.height = TILE;
|
||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
|
||
img.onload = () => done(null, img);
|
||
return img;
|
||
}
|
||
|
||
img.onload = () => done(null, img);
|
||
img.onerror = () => {
|
||
img.src = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256">\
|
||
<rect width="100%" height="100%" fill="%23ddd"/>\
|
||
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle"\
|
||
font-family="monospace" font-size="14">no tile</text></svg>';
|
||
done(null, img);
|
||
};
|
||
|
||
img.src = `tiles/${coords.z}/${xC}/${yC}.png`;
|
||
return img;
|
||
}
|
||
});
|
||
|
||
// === check URL hash ===
|
||
// Format: #X,Y,Z (z.B. #22884,8249,5)
|
||
const hash = window.location.hash.slice(1).split(',');
|
||
if (hash.length >= 2) {
|
||
const x = parseFloat(hash[0]);
|
||
const y = parseFloat(hash[1]);
|
||
const z = hash.length >= 3 ? parseInt(hash[2]) : 5;
|
||
if (!isNaN(x) && !isNaN(y)) {
|
||
map.setView([y, x], isNaN(z) ? 5 : z);
|
||
}
|
||
}
|
||
|
||
new CenteredTiles({
|
||
tileSize: TILE,
|
||
noWrap: true,
|
||
bounds: bounds,
|
||
minNativeZoom: 0,
|
||
maxNativeZoom: MAX_Z
|
||
}).addTo(map);
|
||
|
||
// Koordinatenanzeige
|
||
const box = document.querySelector('.coords');
|
||
function show(){
|
||
const c = map.getCenter();
|
||
box.textContent = `X: ${c.lng.toFixed(0)} Y: ${c.lat.toFixed(0)} Z: ${map.getZoom()}`;
|
||
}
|
||
map.on('move zoom', show); show();
|
||
|
||
// Button: Marker-Code mit aktuellen (Mitte-)Koordinaten in die Zwischenablage
|
||
const copyBtn = document.querySelector('.copy-btn');
|
||
|
||
function copyText(text){
|
||
if (navigator.clipboard && window.isSecureContext){
|
||
return navigator.clipboard.writeText(text);
|
||
} else {
|
||
// Fallback für HTTP/unsichere Kontexte: verstecktes Textfeld + execCommand
|
||
const ta = document.createElement('textarea');
|
||
ta.value = text;
|
||
ta.setAttribute('readonly','');
|
||
ta.style.position = 'fixed';
|
||
ta.style.left = '-9999px';
|
||
ta.style.top = '-9999px';
|
||
document.body.appendChild(ta);
|
||
ta.focus();
|
||
ta.select();
|
||
let ok = false;
|
||
try { ok = document.execCommand('copy'); } catch(e) { ok = false; }
|
||
document.body.removeChild(ta);
|
||
return ok ? Promise.resolve() : Promise.reject(new Error('copy failed'));
|
||
}
|
||
}
|
||
|
||
copyBtn.addEventListener('click', () => {
|
||
const c = map.getCenter();
|
||
const X = Math.round(c.lng); // Anzeige links unten: X zuerst
|
||
const Y = Math.round(c.lat); // dann Y
|
||
const snippet = ` L.marker([${Y},${X}])
|
||
.addTo(map)
|
||
.bindTooltip("NAME")
|
||
.on('click',()=>window.open(
|
||
'WIKI_LINK','_blank'
|
||
));`;
|
||
|
||
copyText(snippet).then(() => {
|
||
const old = copyBtn.textContent; copyBtn.textContent = 'Kopiert!';
|
||
setTimeout(()=> copyBtn.textContent = old, 1200);
|
||
}).catch(err => {
|
||
console.error(err);
|
||
// Letzter Ausweg: Prompt anzeigen, manuell STRG+C drücken
|
||
window.prompt('Kopieren fehlgeschlagen. Bitte STRG+C drücken und mit OK schließen:', snippet);
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<script src="markers.js"></script>
|
||
</body>
|
||
</html>
|