MinedMap/viewer/MinedMap.js

214 lines
4.3 KiB
JavaScript

// bsearch-based array element check
function contains(array, elem) {
let min = 0, max = array.length;
while (min < max) {
const i = min + Math.floor((max-min)/2);
const cur = array[i];
if (cur === elem)
return true;
else if (cur < elem)
min = i + 1;
else
max = i;
}
return false;
}
const MinedMapLayer = L.TileLayer.extend({
initialize: function (mipmaps, layer) {
L.TileLayer.prototype.initialize.call(this, '', {
detectRetina: true,
tileSize: 512,
zoomReverse: true,
minZoom: -(mipmaps.length-1),
maxZoom: 0,
attribution: 'Generated by <a href="https://github.com/neocturne/MinedMap">MinedMap</a>',
});
this.options.maxNativeZoom = this.options.maxZoom;
this.options.maxZoom = undefined;
this.mipmaps = mipmaps;
this.layer = layer;
},
createTile: function (coords, done) {
const tile = L.TileLayer.prototype.createTile.call(this, coords, done);
if (coords.z - this.options.zoomOffset >= 0)
L.DomUtil.addClass(tile, 'overzoomed');
return tile;
},
getTileUrl: function (coords) {
let z = -coords.z + this.options.zoomOffset;
if (z < 0)
z = 0;
const mipmap = this.mipmaps[z];
if (coords.x < mipmap.bounds.minX || coords.x > mipmap.bounds.maxX ||
coords.y < mipmap.bounds.minZ || coords.y > mipmap.bounds.maxZ ||
!contains(mipmap.regions[coords.y] || [], coords.x))
return L.Util.emptyImageUrl;
return 'data/'+this.layer+'/'+z+'/r.'+coords.x+'.'+coords.y+'.png';
},
});
const CoordControl = L.Control.extend({
initialize: function () {
this.options.position = 'bottomleft';
},
onAdd: function (map) {
this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
return this._container;
},
update: function (x, z) {
if (!this._map) { return; }
this._container.innerHTML = 'X: ' + x + '&nbsp;&nbsp;&nbsp;Z: ' + z;
}
});
const parseHash = function () {
const args = {};
if (window.location.hash) {
const parts = window.location.hash.substring(1).split('&');
for (const part of parts) {
const key_value = part.split('=');
const key = key_value[0], value = key_value.slice(1).join('=');
args[key] = value;
}
}
return args;
}
window.createMap = function () {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
const res = JSON.parse(this.responseText),
mipmaps = res.mipmaps,
spawn = res.spawn;
let x, z, zoom, light;
const updateParams = function () {
const args = parseHash();
zoom = parseInt(args['zoom']);
x = parseFloat(args['x']);
z = parseFloat(args['z']);
light = parseInt(args['light']);
if (isNaN(zoom))
zoom = 0;
if (isNaN(x))
x = spawn.x;
if (isNaN(z))
z = spawn.z;
};
updateParams();
const map = L.map('map', {
center: [-z, x],
zoom: zoom,
minZoom: -(mipmaps.length-1),
maxZoom: 3,
crs: L.CRS.Simple,
maxBounds: [
[-512*(mipmaps[0].bounds.maxZ+1), 512*mipmaps[0].bounds.minX],
[-512*mipmaps[0].bounds.minZ, 512*(mipmaps[0].bounds.maxX+1)],
],
});
const mapLayer = new MinedMapLayer(mipmaps, 'map');
const lightLayer = new MinedMapLayer(mipmaps, 'light');
mapLayer.addTo(map);
if (light)
map.addLayer(lightLayer);
const overlayMaps = {
"Illumination": lightLayer,
};
L.control.layers({}, overlayMaps).addTo(map);
const coordControl = new CoordControl();
coordControl.addTo(map);
map.on('mousemove', function(e) {
coordControl.update(Math.round(e.latlng.lng), Math.round(-e.latlng.lat));
});
const makeHash = function () {
let ret = '#x='+x+'&z='+z;
if (zoom != 0)
ret += '&zoom='+zoom;
if (map.hasLayer(lightLayer))
ret += '&light=1';
return ret;
};
const updateHash = function () {
window.location.hash = makeHash();
};
const refreshHash = function () {
zoom = map.getZoom();
center = map.getCenter();
x = Math.round(center.lng);
z = Math.round(-center.lat);
updateHash();
}
updateHash();
map.on('moveend', refreshHash);
map.on('zoomend', refreshHash);
map.on('layeradd', refreshHash);
map.on('layerremove', refreshHash);
window.onhashchange = function () {
if (window.location.hash === makeHash())
return;
updateParams();
map.setView([-z, x], zoom);
if (light)
map.addLayer(lightLayer);
else
map.removeLayer(lightLayer);
updateHash();
};
};
xhr.open('GET', 'data/info.json', true);
xhr.send();
}