Fix violation of unmined license (distribution)

This commit is contained in:
Kai Vogelgesang 2022-09-12 00:48:54 +02:00
parent c63eeb83a0
commit 16f60f6075
Signed by: kai
GPG Key ID: 0A95D3B6E62C0879
7 changed files with 94 additions and 253 deletions

View File

@ -1,36 +1,41 @@
<script lang="ts"> <script lang="ts">
import Navbar from "../../Navbar.svelte"; import Navbar from "../../Navbar.svelte";
import { unmined } from "../../stores";
import { import {
Unmined, ol_proxy,
type UnminedOptions, type UnminedOptions,
type UnminedRegions, type UnminedRegions,
} from "./unmined"; } from "./unmined";
export let id = "map"; export let id = "map";
let unmined = new Unmined();
type Metadata = { type Metadata = {
properties: UnminedOptions; properties: UnminedOptions;
regions: UnminedRegions; regions: UnminedRegions;
}; };
async function get_metadata() { async function get_unmined() {
if ($unmined === null) {
let resp = await fetch("map/unmined.js");
let code = await resp.text();
$unmined = Function("ol", code).call({}, ol_proxy);
}
let resp = await fetch("map/metadata.js"); let resp = await fetch("map/metadata.js");
let code = await resp.text(); let code = await resp.text();
let meta = Function(code).call({}); // TODO possibly vulnerable to prototype pollution let meta: Metadata = Function(code).call({}); // TODO possibly vulnerable to prototype pollution
return meta; return meta;
} }
function setupMap(node: HTMLDivElement, metadata: Metadata) { function setupMap(node: HTMLDivElement, metadata: Metadata) {
unmined.map(node.id, metadata.properties, metadata.regions); $unmined.map(node.id, metadata.properties, metadata.regions);
return { return {
destroy() { destroy() {
if (unmined.openlayersMap) { if ($unmined.openlayersMap) {
unmined.openlayersMap.setTarget(null); $unmined.openlayersMap.setTarget(null);
unmined.openlayersMap = null; $unmined.openlayersMap = null;
} }
}, },
}; };
@ -39,7 +44,7 @@
<div class="map-outer"> <div class="map-outer">
<Navbar /> <Navbar />
{#await get_metadata() then metadata} {#await get_unmined() then metadata}
<div class="map-target" {id} use:setupMap={metadata} /> <div class="map-target" {id} use:setupMap={metadata} />
{:catch err} {:catch err}
<div class="container"> <div class="container">

View File

@ -1,18 +1,15 @@
import type OlMap from "ol/Map";
import type VectorLayer from "ol/layer/Vector";
import type VectorSource from "ol/source/Vector";
import { boundingExtent } from "ol/extent"; import { boundingExtent } from "ol/extent";
import { addCoordinateTransforms, Projection, transform } from "ol/proj"; import { addCoordinateTransforms, Projection } from "ol/proj";
import TileGrid from "ol/tilegrid/TileGrid"; import TileGrid from "ol/tilegrid/TileGrid";
import { Tile } from "ol/layer";
import XYZ from "ol/source/XYZ"; import XYZ from "ol/source/XYZ";
import MousePosition from "ol/control/MousePosition"; import Tile from "ol/layer/Tile";
import { createStringXY } from "ol/coordinate"; import { createStringXY } from "ol/coordinate";
import OlMap from "ol/Map"; import MousePosition from "ol/control/MousePosition";
import { defaults as controlDefaults } from "ol/control/defaults"; import { defaults } from "ol/control/defaults";
import { Feature, View } from "ol"; import { Map, 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 SourceVector from "ol/source/Vector";
import LayerVector from "ol/layer/Vector"; import LayerVector from "ol/layer/Vector";
@ -30,227 +27,45 @@ export type UnminedOptions = {
markers: Array<any>; markers: Array<any>;
} }
export type UnminedRegions = {}[]; export type UnminedRegions = {
x: number,
y: number,
m: Uint32Array,
}[];
export class Unmined { export type Unmined = {
openlayersMap: OlMap,
openlayersMap: OlMap; map: (mapId: string, options: UnminedOptions, regions: UnminedRegions) => void,
createMarkersLayer: (markers, dataProjection, viewProjection) => VectorLayer<VectorSource>,
}
map(mapId: string, options: UnminedOptions, regions: any[]) { /** (cursed) proxy variable to provide the fetched unmined script access to `ol.extent.boundingExtent` etc */
const dpiScale = window.devicePixelRatio ?? 1.0; export const ol_proxy = {
extent: {
const worldMinX = options.minRegionX * 512; boundingExtent: boundingExtent,
const worldMinY = options.minRegionZ * 512; },
const worldWidth = (options.maxRegionX + 1 - options.minRegionX) * 512; proj: {
const worldHeight = (options.maxRegionZ + 1 - options.minRegionZ) * 512; Projection: Projection,
addCoordinateTransforms: addCoordinateTransforms,
const worldTileSize = 256; },
tilegrid: {
const worldMaxZoomFactor = Math.pow(2, options.maxZoom); TileGrid: TileGrid,
},
// left, bottom, right, top, Y is negated layer: {
var mapExtent = boundingExtent([ Tile: Tile,
[worldMinX * worldMaxZoomFactor, -(worldMinY + worldHeight) * worldMaxZoomFactor], Vector: LayerVector,
[(worldMinX + worldWidth) * worldMaxZoomFactor, -worldMinY * worldMaxZoomFactor]]); },
source: {
var viewProjection = new Projection({ XYZ: XYZ,
code: 'VIEW', Vector: SourceVector,
units: 'pixels', },
}); control: {
MousePosition: MousePosition,
var dataProjection = new Projection({ defaults: defaults,
code: 'DATA', },
units: 'pixels', coordinate: {
}); createStringXY: createStringXY,
},
// Coordinate transformation between view and data View: View,
// OpenLayers Y is positive up, world Y is positive down Map: Map,
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;
}
}

View File

@ -1,4 +1,5 @@
import { writable, type Writable } from "svelte/store"; import { writable, type Writable } from "svelte/store";
import type { Unmined } from "./pages/mining/unmined";
type User = { type User = {
name: string, name: string,
@ -6,4 +7,5 @@ type User = {
picture: string, picture: string,
} }
export const user: Writable<User | null> = writable(null); export const user: Writable<User | null> = writable(null);
export const unmined: Writable<Unmined | null> = writable(null);

View File

@ -4,13 +4,14 @@ from fastapi.responses import HTMLResponse
from .settings import settings from .settings import settings
from .user import user_auth from .user import user_auth
from .map_tiles import map_tiles from .map_tiles import map_tiles, map_meta
from .templates import j2env from .templates import j2env
app = FastAPI() app = FastAPI()
app.mount("/user/", user_auth) app.mount("/user/", user_auth)
app.mount("/map/", map_tiles) app.mount("/map/", map_meta)
app.mount("/tiles/", map_tiles)
frontend = FastAPI() frontend = FastAPI()

View File

@ -4,17 +4,23 @@ from fastapi.staticfiles import StaticFiles
from .settings import settings from .settings import settings
from .templates import j2env from .templates import j2env
map_tiles = FastAPI() map_tiles = StaticFiles(directory=f"{settings.unmined_out_path}/tiles")
map_tiles.mount("/tiles/", StaticFiles(directory=f"{settings.unmined_out_path}/tiles")) map_meta = FastAPI()
class MetadataJsResponse(Response): class JsResponse(Response):
media_type = "text/javascript" media_type = "text/javascript"
MAP_PROPS_FILE = f"{settings.unmined_out_path}/unmined.map.properties.js" MAP_PROPS_FILE = f"{settings.unmined_out_path}/unmined.map.properties.js"
MAP_REGIONS_FILE = f"{settings.unmined_out_path}/unmined.map.regions.js" MAP_REGIONS_FILE = f"{settings.unmined_out_path}/unmined.map.regions.js"
UNMINED_FILE = f"{settings.unmined_out_path}/unmined.openlayers.js"
with open(UNMINED_FILE, "r") as f:
unmined_script = j2env.get_template("unmined.js").render(
unmined_file=UNMINED_FILE, unmined_script=f.read()
)
def parse_metadata(): def parse_metadata():
@ -31,7 +37,12 @@ def parse_metadata():
) )
@map_tiles.get("/metadata.js", response_class=MetadataJsResponse) @map_meta.get("/unmined.js", response_class=JsResponse)
async def get_unmined():
return unmined_script
@map_meta.get("/metadata.js", response_class=JsResponse)
async def get_metadata(): async def get_metadata():
# TODO cache # TODO cache
return parse_metadata() return parse_metadata()

View File

@ -1,8 +1,10 @@
/* {{props_file}} */ /* {{ props_file }} */
{{map_props}} {{ map_props }}
/* end of {{ props_file }} */
/* {{regions_file}} */ /* {{ regions_file }} */
{{map_regions}} {{map_regions}}
/* end of {{ regions_file }} */
return { return {
properties: UnminedMapProperties, properties: UnminedMapProperties,

View File

@ -0,0 +1,5 @@
/* {{ unmined_file }} */
{{ unmined_script }}
/* end of {{ unmined_file }} */
return new Unmined();