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; } }