import { boundingExtent } from "ol/extent"; import { addCoordinateTransforms, Projection, transform } from "ol/proj"; import TileGrid from "ol/tilegrid/TileGrid"; import { Tile } from "ol/layer"; import XYZ from "ol/source/XYZ"; import MousePosition from "ol/control/MousePosition"; import { createStringXY } from "ol/coordinate"; import OlMap from "ol/Map"; import { defaults as controlDefaults } from "ol/control/defaults"; import { Feature, View } from "ol"; import { Point } from "ol/geom"; import Style from "ol/style/Style"; import Icon from "ol/style/Icon"; import Text from "ol/style/Text"; import Fill from "ol/style/Fill"; import SourceVector from "ol/source/Vector"; import LayerVector from "ol/layer/Vector"; export type UnminedOptions = { minZoom: number; maxZoom: number; defaultZoom: number; imageFormat: string; minRegionX: number; minRegionZ: number; maxRegionX: number; maxRegionZ: number; worldName: string; background: string; markers: Array; } export type UnminedRegions = {}[]; export class Unmined { openlayersMap: OlMap; map(mapId: string, options: UnminedOptions, regions: any[]) { 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 = boundingExtent([ [worldMinX * worldMaxZoomFactor, -(worldMinY + worldHeight) * worldMaxZoomFactor], [(worldMinX + worldWidth) * worldMaxZoomFactor, -worldMinY * worldMaxZoomFactor]]); var viewProjection = new Projection({ code: 'VIEW', units: 'pixels', }); var dataProjection = new Projection({ code: 'DATA', units: 'pixels', }); // Coordinate transformation between view and data // OpenLayers Y is positive up, world Y is positive down 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 TileGrid({ extent: mapExtent, origin: [0, 0], resolutions: resolutions, tileSize: worldTileSize / dpiScale }); var unminedLayer = new Tile({ source: new 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 z = worldZoom, yd = Math.floor(tileY / 10), xd = Math.floor(tileX / 10), y = tileY, x = tileX const url = `map/tiles/zoom.${z}/${xd}/${yd}/tile.${x}.${y}.${options.imageFormat}` return url; } else return undefined; } }) }); var mousePositionControl = new MousePosition({ coordinateFormat: createStringXY(0), projection: dataProjection }); var map = new OlMap({ target: mapId, controls: controlDefaults().extend([ mousePositionControl ]), layers: [ unminedLayer, /* new ol.layer.Tile({ source: new ol.source.TileDebug({ tileGrid: unminedTileGrid, projection: viewProjection }) }) */ ], view: new 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 Feature({ geometry: new Point(transform([longitude, latitude], dataProjection, viewProjection)) }); var style = new Style(); if (item.image) style.setImage(new Icon({ src: item.image, anchor: item.imageAnchor, scale: item.imageScale })); if (item.text) style.setText(new Text({ text: item.text, font: item.font, offsetX: item.offsetX, offsetY: item.offsetY, fill: new Fill({ color: item.textColor }) })); feature.setStyle(style); features.push(feature); } var vectorSource = new SourceVector({ features: features }); var vectorLayer = new LayerVector({ source: vectorSource }); return vectorLayer; } }