150 lines
4.2 KiB
HTML
150 lines
4.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="color-scheme" content="dark light">
|
|
<title>IP Map</title>
|
|
<script src="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.js"></script>
|
|
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.css" />
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
html, body {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
#map {
|
|
width: 100vh;
|
|
height: 100%;
|
|
margin: 0 auto;
|
|
}
|
|
.maplibregl-canvas {
|
|
cursor: pointer;
|
|
}
|
|
.maplibregl-popup-content {
|
|
background-color: #222;
|
|
font-size: 1rem;
|
|
padding: 0.8rem 1.2rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="map"></div>
|
|
<script>
|
|
function hilbert_c2i({ x, y }) {
|
|
var b, d;
|
|
var rotation = 0;
|
|
let reflection = 0;
|
|
let index = 0;
|
|
for (b = 16; b--;) {
|
|
let bits = reflection;
|
|
|
|
reflection = (y >>> b) & 1;
|
|
reflection |= ((x >>> b) & 1) << 1;
|
|
|
|
bits = bits ^ reflection;
|
|
bits = ((bits >>> rotation) | (bits << (2 - rotation))) & 3;
|
|
|
|
index |= bits << (b << 1);
|
|
reflection ^= (1 << rotation);
|
|
|
|
bits = bits & (-bits) & 1;
|
|
|
|
while (bits) { ++rotation; bits >>>= 1; }
|
|
if (++rotation >= 2) rotation -= 2;
|
|
}
|
|
|
|
index ^= 0x2aaaaaaa;
|
|
|
|
for (d = 1; d < 32; d *= 2) {
|
|
let t = index >>> d;
|
|
if (!t) break;
|
|
index ^= t;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
function hilbert_i2c(index) {
|
|
var b, d, t;
|
|
var rotation = 0;
|
|
let reflection = 0;
|
|
let coord = { x: 0, y: 0 };
|
|
|
|
index ^= (index >>> 1) ^ 0x2aaaaaaa;
|
|
for (b = 16; b--; ) {
|
|
var bits = index >>> (2 * b) & 3;
|
|
reflection ^= ((bits >>> (2 - rotation)) | (bits << rotation)) & 3;
|
|
|
|
coord.x |= (reflection & 1) << b;
|
|
coord.y |= ((reflection >>> 1) & 1) << b;
|
|
|
|
reflection ^= (1 << rotation);
|
|
|
|
bits &= (-bits) & 1;
|
|
while (bits) {
|
|
bits >>>= 1;
|
|
++rotation;
|
|
}
|
|
if (++rotation >= 2)
|
|
rotation -= 2;
|
|
}
|
|
return coord;
|
|
}
|
|
|
|
const map = new maplibregl.Map({
|
|
container: "map",
|
|
attributionControl: false,
|
|
renderWorldCopies: false,
|
|
doubleClickZoom: false,
|
|
dragRotate: false,
|
|
style: {
|
|
"version": 8,
|
|
"sources": {
|
|
"raster-tiles": {
|
|
"type": "raster",
|
|
"tiles": [
|
|
"tiles/{z}/{y}/{x}.png"
|
|
],
|
|
"minzoom": 0,
|
|
"maxzoom": 8,
|
|
"tileSize": 256
|
|
}
|
|
},
|
|
"layers": [
|
|
{
|
|
"id": "simple-tiles",
|
|
"type": "raster",
|
|
"source": "raster-tiles",
|
|
"paint": {
|
|
"raster-resampling": "nearest"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
center: [0, 0],
|
|
minZoom: -1,
|
|
maxZoom: 12,
|
|
zoom: 0
|
|
})
|
|
map.painter.context.extTextureFilterAnisotropic = undefined
|
|
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-left")
|
|
const toIp = v => `${v >> 24 & 0xFF}.${v >> 16 & 0xFF}.${v >> 8 & 0xFF}.${v >> 0 & 0xFF}`
|
|
map.on("click", (e) => {
|
|
const { x, y } = maplibregl.MercatorCoordinate.fromLngLat(e.lngLat, 0)
|
|
const rawIp = hilbert_c2i({ x: Math.floor(0x10000 * x), y: Math.floor(0x10000 * y) })
|
|
const subnet = Math.min(32, Math.round(map.getZoom()) * 2 + 18)
|
|
const text = subnet < 32 ?
|
|
`Range: ${toIp((rawIp >> (32 - subnet)) << (32 - subnet))}/${subnet}` :
|
|
`IP: ${toIp(rawIp)}`
|
|
new maplibregl.Popup()
|
|
.setHTML(text)
|
|
.setLngLat(e.lngLat)
|
|
.addTo(map)
|
|
})
|
|
</script>
|
|
</body>
|
|
</html>
|