264 lines
10 KiB
JavaScript
264 lines
10 KiB
JavaScript
class Unmined {
|
|
|
|
map(mapId, options, regions) {
|
|
|
|
const dpiScale = window.devicePixelRatio ?? 1.0;
|
|
|
|
const worldMinX = options.minRegionX * 512;
|
|
const worldMinY = options.minRegionZ * 512;
|
|
const worldWidth = (options.maxRegionX + 1 - options.minRegionX) * 512;
|
|
const worldHeight = (options.maxRegionZ + 1 - options.minRegionZ) * 512;
|
|
|
|
const worldTileSize = 256;
|
|
|
|
const worldMaxZoomFactor = Math.pow(2, options.maxZoom);
|
|
|
|
// left, bottom, right, top, Y is negated
|
|
var mapExtent = ol.extent.boundingExtent([
|
|
[worldMinX * worldMaxZoomFactor, -(worldMinY + worldHeight) * worldMaxZoomFactor],
|
|
[(worldMinX + worldWidth) * worldMaxZoomFactor, -worldMinY * worldMaxZoomFactor]]);
|
|
|
|
var viewProjection = new ol.proj.Projection({
|
|
code: 'VIEW',
|
|
units: 'pixels',
|
|
});
|
|
|
|
var dataProjection = new ol.proj.Projection({
|
|
code: 'DATA',
|
|
units: 'pixels',
|
|
});
|
|
|
|
// Coordinate transformation between view and data
|
|
// OpenLayers Y is positive up, world Y is positive down
|
|
ol.proj.addCoordinateTransforms(viewProjection, dataProjection,
|
|
function (coordinate) {
|
|
return [coordinate[0], -coordinate[1]];
|
|
},
|
|
function (coordinate) {
|
|
return [coordinate[0], -coordinate[1]];
|
|
});
|
|
|
|
const mapZoomLevels = options.maxZoom - options.minZoom;
|
|
// Resolution for each OpenLayers zoom level
|
|
var resolutions = new Array(mapZoomLevels + 1);
|
|
for (let z = 0; z < mapZoomLevels + 1; ++z) {
|
|
resolutions[mapZoomLevels - z] = Math.pow(2, z) * dpiScale / worldMaxZoomFactor;
|
|
}
|
|
|
|
var tileGrid = new ol.tilegrid.TileGrid({
|
|
extent: mapExtent,
|
|
origin: [0, 0],
|
|
resolutions: resolutions,
|
|
tileSize: worldTileSize / dpiScale
|
|
});
|
|
|
|
var unminedLayer =
|
|
new ol.layer.Tile({
|
|
source: new ol.source.XYZ({
|
|
projection: viewProjection,
|
|
tileGrid: tileGrid,
|
|
tilePixelRatio: dpiScale,
|
|
tileSize: worldTileSize / dpiScale,
|
|
|
|
tileUrlFunction: function (coordinate) {
|
|
const worldZoom = -(mapZoomLevels - coordinate[0]) + options.maxZoom;
|
|
const worldZoomFactor = Math.pow(2, worldZoom);
|
|
|
|
const minTileX = Math.floor(worldMinX * worldZoomFactor / worldTileSize);
|
|
const minTileY = Math.floor(worldMinY * worldZoomFactor / worldTileSize);
|
|
const maxTileX = Math.ceil((worldMinX + worldWidth) * worldZoomFactor / worldTileSize) - 1;
|
|
const maxTileY = Math.ceil((worldMinY + worldHeight) * worldZoomFactor / worldTileSize) - 1;
|
|
|
|
const tileX = coordinate[1];
|
|
const tileY = coordinate[2];
|
|
|
|
const tileBlockSize = worldTileSize / worldZoomFactor;
|
|
const tileBlockPoint = {
|
|
x: tileX * tileBlockSize,
|
|
z: tileY * tileBlockSize
|
|
};
|
|
|
|
const hasTile = function () {
|
|
const tileRegionPoint = {
|
|
x: Math.floor(tileBlockPoint.x / 512),
|
|
z: Math.floor(tileBlockPoint.z / 512)
|
|
};
|
|
const tileRegionSize = Math.ceil(tileBlockSize / 512);
|
|
|
|
for (let x = tileRegionPoint.x; x < tileRegionPoint.x + tileRegionSize; x++) {
|
|
for (let z = tileRegionPoint.z; z < tileRegionPoint.z + tileRegionSize; z++) {
|
|
const group = {
|
|
x: Math.floor(x / 32),
|
|
z: Math.floor(z / 32)
|
|
};
|
|
const regionMap = regions.find(e => e.x == group.x && e.z == group.z);
|
|
if (regionMap) {
|
|
const relX = x - group.x * 32;
|
|
const relZ = z - group.z * 32;
|
|
const inx = relZ * 32 + relX;
|
|
var b = regionMap.m[Math.floor(inx / 32)];
|
|
var bit = inx % 32;
|
|
var found = (b & (1 << bit)) != 0;
|
|
if (found) return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (tileX >= minTileX
|
|
&& tileY >= minTileY
|
|
&& tileX <= maxTileX
|
|
&& tileY <= maxTileY
|
|
&& hasTile()) {
|
|
const url = ('tiles/zoom.{z}/{xd}/{yd}/tile.{x}.{y}.' + options.imageFormat)
|
|
.replace('{z}', worldZoom)
|
|
.replace('{yd}', Math.floor(tileY / 10))
|
|
.replace('{xd}', Math.floor(tileX / 10))
|
|
.replace('{y}', tileY)
|
|
.replace('{x}', tileX);
|
|
return url;
|
|
}
|
|
else
|
|
return undefined;
|
|
}
|
|
})
|
|
});
|
|
|
|
var mousePositionControl = new ol.control.MousePosition({
|
|
coordinateFormat: ol.coordinate.createStringXY(0),
|
|
projection: dataProjection
|
|
});
|
|
|
|
var map = new ol.Map({
|
|
target: mapId,
|
|
controls: ol.control.defaults().extend([
|
|
mousePositionControl
|
|
]),
|
|
layers: [
|
|
unminedLayer,
|
|
/*
|
|
new ol.layer.Tile({
|
|
source: new ol.source.TileDebug({
|
|
tileGrid: unminedTileGrid,
|
|
projection: viewProjection
|
|
})
|
|
})
|
|
*/
|
|
|
|
],
|
|
view: new ol.View({
|
|
center: [0, 0],
|
|
extent: mapExtent,
|
|
projection: viewProjection,
|
|
resolutions: tileGrid.getResolutions(),
|
|
maxZoom: mapZoomLevels,
|
|
zoom: mapZoomLevels - options.maxZoom,
|
|
constrainResolution: true,
|
|
showFullExtent: true,
|
|
constrainOnlyCenter: true
|
|
})
|
|
});
|
|
|
|
if (options.markers) {
|
|
var markersLayer = this.createMarkersLayer(options.markers, dataProjection, viewProjection);
|
|
map.addLayer(markersLayer);
|
|
}
|
|
|
|
if (options.background){
|
|
document.getElementById(mapId).style.backgroundColor = options.background;
|
|
}
|
|
|
|
this.openlayersMap = map;
|
|
}
|
|
|
|
createMarkersLayer(markers, dataProjection, viewProjection) {
|
|
var features = [];
|
|
|
|
for (var i = 0; i < markers.length; i++) {
|
|
var item = markers[i];
|
|
var longitude = item.x;
|
|
var latitude = item.z;
|
|
|
|
var feature = new ol.Feature({
|
|
geometry: new ol.geom.Point(ol.proj.transform([longitude, latitude], dataProjection, viewProjection))
|
|
});
|
|
|
|
var style = new ol.style.Style();
|
|
if (item.image)
|
|
style.setImage(new ol.style.Icon({
|
|
src: item.image,
|
|
anchor: item.imageAnchor,
|
|
scale: item.imageScale
|
|
}));
|
|
|
|
if (item.text) {
|
|
style.setText(new ol.style.Text({
|
|
text: item.text,
|
|
font: item.font,
|
|
offsetX: item.offsetX,
|
|
offsetY: item.offsetY,
|
|
fill: item.textColor ? new ol.style.Fill({
|
|
color: item.textColor
|
|
}) : null,
|
|
padding: item.textPadding ?? [2, 4, 2, 4],
|
|
stroke: item.textStrokeColor ? new ol.style.Stroke({
|
|
color: item.textStrokeColor,
|
|
width: item.textStrokeWidth
|
|
}) : null,
|
|
backgroundFill: item.textBackgroundColor ? new ol.style.Fill({
|
|
color: item.textBackgroundColor
|
|
}) : null,
|
|
backgroundStroke: item.textBackgroundStrokeColor ? new ol.style.Stroke({
|
|
color: item.textBackgroundStrokeColor,
|
|
width: item.textBackgroundStrokeWidth
|
|
}) : null,
|
|
}));
|
|
}
|
|
|
|
feature.setStyle(style);
|
|
|
|
features.push(feature);
|
|
}
|
|
|
|
var vectorSource = new ol.source.Vector({
|
|
features: features
|
|
});
|
|
|
|
var vectorLayer = new ol.layer.Vector({
|
|
source: vectorSource
|
|
});
|
|
return vectorLayer;
|
|
}
|
|
|
|
defaultPlayerMarkerStyle = {
|
|
image: "playerimages/default.png",
|
|
imageAnchor: [0.5, 0.5],
|
|
imageScale: 0.25,
|
|
|
|
textColor: "white",
|
|
offsetX: 0,
|
|
offsetY: 20,
|
|
font: "14px Arial",
|
|
//textStrokeColor: "black",
|
|
//textStrokeWidth: 2,
|
|
textBackgroundColor: "#00000088",
|
|
//textBackgroundStrokeColor: "black",
|
|
//textBackgroundStrokeWidth: 1,
|
|
textPadding: [2, 4, 2, 4],
|
|
}
|
|
|
|
playerToMarker(player) {
|
|
var marker = Object.assign({}, this.defaultPlayerMarkerStyle);
|
|
marker.x = player.x;
|
|
marker.z = player.z;
|
|
marker.text = player.name;
|
|
return marker;
|
|
}
|
|
|
|
createPlayerMarkers(players) {
|
|
let markers = players.map(player => this.playerToMarker(player));
|
|
return markers;
|
|
}
|
|
|
|
} |