Small fixes

This commit is contained in:
LilyRose2798 2024-04-09 18:05:36 +10:00
parent 5c59bf3bc8
commit b3a4ce6fdc
2 changed files with 156 additions and 84 deletions

View File

@ -180,7 +180,7 @@ def generate_tiles(parquet_path: Path, tiles_dir: Path, *, tile_size = default_t
while True:
for colormap in colormaps:
if generate_density:
generate_images(colormap, "density", "count", possible_overlaps)
generate_images(colormap, "density", "count", 256 if possible_overlaps == 1 else possible_overlaps)
if generate_rtt:
generate_images(colormap, "rtt", "rtt_us", df.get_column("rtt_us").std() / tiles_per_side.bit_length())
if tiles_per_side == 1:

View File

@ -50,32 +50,57 @@
font-size: 1.3rem;
color: black;
}
.maplibregl-ctrl-top-right {
display: flex;
}
.maplibregl-ctrl-top-right .custom-control {
align-self: flex-start;
}
.maplibregl-ctrl button.maplibregl-ctrl-subnets .maplibregl-ctrl-icon {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' stroke='%23333' stroke-width='1.5' fill='none' viewBox='0 0 29 29'%3E%3Cpath d='m 6 6 v 17 h 17 v -17 h -17 z m 0 8.5 h 17 m -8.5 -8.5 v 17'/%3E%3C/svg%3E");
}
.custom-control {
display: inline;
float: unset !important;
vertical-align: top;
user-select: none;
}
.custom-control summary {
details.custom-control > summary {
margin: 0;
padding: 0.2rem 0.5rem;
padding: 0 0.5rem;
display: flex;
align-items: center;
height: 29px;
cursor: pointer;
}
.custom-control[open] summary {
details.custom-control[open] > summary {
border-bottom: 2px solid black;
}
.custom-control h3 {
details.custom-control > summary::marker {
content: "";
}
details.custom-control > summary {
list-style: none;
}
details.custom-control > summary::-webkit-details-marker {
display:none;
}
details.custom-control > summary h3 {
margin: 0;
padding: 0;
vertical-align: middle;
display: inline;
font-size: 0.9rem;
}
.custom-control button {
details.custom-control > .button-container {
max-height: 12rem;
overflow-y: scroll;
}
details.custom-control > .button-container button {
width: 100%;
padding: 0 0.5rem;
color: black;
}
.custom-control button.active {
.maplibregl-ctrl button {
color: black;
font-size: 0.8rem;
}
.maplibregl-ctrl button.active {
background-color: rgb(0 0 0/15%);
}
.maplibregl-ctrl button:not(:disabled):hover {
@ -84,6 +109,13 @@
.maplibregl-ctrl button:not(:disabled):active {
background-color: rgb(0 0 0/15%);
}
.maplibregl-ctrl p {
margin: 0.25rem 0.5rem;
font-size: 0.8rem;
}
.maplibregl-ctrl p:empty {
display: none;
}
</style>
</head>
<body>
@ -142,8 +174,10 @@
return coord
}
const ipToString = ip => `${ip >> 24 & 0xFF}.${ip >> 16 & 0xFF}.${ip >> 8 & 0xFF}.${ip >> 0 & 0xFF}`
const ipToRange = (ip, subnet) => `${ipToString((ip >> (32 - subnet)) << (32 - subnet))}/${subnet}`
const ipToString = (ip, subnet = 32) => {
const x = (ip >> (32 - subnet)) << (32 - subnet)
return `${x >> 24 & 0xFF}.${x >> 16 & 0xFF}.${x >> 8 & 0xFF}.${x >> 0 & 0xFF}`
}
const rangeToCoords = range => {
const [a, b, c, d, subnet] = range.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/).slice(1).map(Number)
@ -191,34 +225,13 @@
}
}
const map = new maplibregl.Map({
container: "map",
attributionControl: false,
renderWorldCopies: false,
doubleClickZoom: false,
dragRotate: false,
pitchWithRotate: false,
touchPitch: false,
style: {
version: 8,
sources: {},
layers: []
},
center: [0, 0],
minZoom: -2,
maxZoom: 12,
zoom: -2
})
map.painter.context.extTextureFilterAnisotropic = undefined
map.touchZoomRotate.disableRotation()
const defaultVariant = "density"
const defaultColormap = "jet"
const tileSize = 256
const tilesDir = "assets/tiles"
const tilesId = "ipmap-tiles"
const privateRangesId = "private-ranges"
const subnetsId = "subnets"
const dataP = fetch(`${tilesDir}/tiles.json`, { cache: "no-store" }).then(res => res.json())
const privateRangePatternP = map.loadImage("assets/private-range-pattern.png")
const canvas = document.createElement("canvas")
const canvasScale = window.devicePixelRatio
@ -230,13 +243,12 @@
ctx.lineWidth = 3
ctx.font = "bold 20px sans-serif"
maplibregl.addProtocol(subnetsId, async (params, abortController) => {
console.log(params.url)
const [z, y, x] = params.url.split("://")[1].split("/")
const width = 2 ** z
const subnet = 2 * z
const hy = Math.floor(0x10000 * (y / width))
const hx = Math.floor(0x10000 * (x / width))
const text = ipToRange(coordsToHilbert({ x: hx, y: hy }), subnet)
const text = `${ipToString(coordsToHilbert({ x: hx, y: hy }), subnet)}/${subnet}`
const metrics = ctx.measureText(text)
const cy = tileSize / 2 + metrics.actualBoundingBoxAscent / 2
const cx = tileSize / 2 - metrics.actualBoundingBoxRight / 2
@ -254,23 +266,51 @@
return { data: await new Promise((res, rej) => canvas.toBlob(b => b.arrayBuffer().then(res))) }
})
const map = new maplibregl.Map({
container: "map",
attributionControl: false,
renderWorldCopies: false,
doubleClickZoom: false,
dragRotate: false,
pitchWithRotate: false,
touchPitch: false,
style: {
version: 8,
sources: {},
layers: []
},
center: [0, 0],
minZoom: -2,
maxZoom: 11.499,
zoom: -2
})
map.painter.context.extTextureFilterAnisotropic = undefined
map.touchZoomRotate.disableRotation()
const dataP = fetch(`${tilesDir}/tiles.json`, { cache: "no-store" }).then(res => res.json())
const privateRangePatternP = map.loadImage("assets/private-range-pattern.png")
map.once("style.load", async () => {
const data = await dataP
const flatData = Object.entries(data)
.sort(([a], [b]) => a.localeCompare(b))
.flatMap(([date, variantData]) =>
Object.entries(variantData)
.sort(([a], [b]) => a.localeCompare(b))
.flatMap(([variant, colormaps]) => colormaps
.sort((a, b) => a.localeCompare(b))
.flatMap(colormap => ({ date, variant, colormap }))))
if (flatData.length === 0) {
console.log("no data found")
let curDate, curVariant, curColormap
const dates = Object.keys(data).sort()
curDate = dates.find(date => {
const variantEntries = Object.entries(data[date])
if (variantEntries.length === 0) return false
const variantEntry = variantEntries.find(([variant, colormaps]) => variant === defaultVariant && colormaps.length > 0) ??
variantEntries.sort(([a], [b]) => a.localeCompare(b)).find(([_, colormaps]) => colormaps.length > 0)
if (!variantEntry) return false
const [variant, colormaps] = variantEntry
curVariant = variant
curColormap = colormaps.find(colormap => colormap === defaultColormap) ?? colormaps.sort()[0]
return true
})
if (!curDate) {
console.error("no data found")
return
}
let { date: curDate, variant: curVariant, colormap: curColormap } = flatData[flatData.length - 1]
map.addSource(tilesId, {
type: "raster",
tiles: [`${tilesDir}/${curDate}/${curVariant}/${curColormap}/{z}/{y}/{x}.png`],
@ -334,10 +374,13 @@
}
})
let subnetsVisible = true
map.addSource(subnetsId, {
type: "raster",
tiles: [`${subnetsId}://{z}/{y}/{x}`],
tileSize,
minzoom: 0,
maxzoom: 12,
})
map.addLayer({
id: subnetsId,
@ -348,34 +391,41 @@
const setStyle = (date, variant, colormap) => {
if (date === curDate && variant === curVariant && colormap === curColormap || !data[date]?.[variant]?.includes(colormap))
return false
map.getSource(tilesId)?.setTiles([`${tilesDir}/${date}/${variant}/${colormap}/{z}/{y}/{x}.png`])
const source = map.getSource(tilesId)
if (!source) return false
source.setTiles([`${tilesDir}/${date}/${variant}/${colormap}/{z}/{y}/{x}.png`])
curDate = date
curVariant = variant
curColormap = colormap
return true
}
const customControl = (name) => {
const addControl = (elem, side) => map.addControl({
onAdd: () => elem,
onRemove: () => elem.parentNode.removeChild(elem)
}, side)
const detailsControl = (name) => {
const container = document.createElement("details")
container.className = "maplibregl-ctrl maplibregl-ctrl-group custom-control"
const header = document.createElement("h3")
header.textContent = name
const summary = document.createElement("summary")
summary.replaceChildren(header)
container.replaceChildren(summary)
const setButtons = (buttons) => container.replaceChildren(summary, ...buttons.map(({ button }) => button))
const addControl = () => map.addControl({
onAdd: () => container,
onRemove: () => container.parentNode.removeChild(container)
}, "top-right")
return { container, setButtons, addControl }
const buttonContainer = document.createElement("div")
buttonContainer.className = "button-container"
container.replaceChildren(summary, buttonContainer)
return {
container,
setButtons: buttons => buttonContainer.replaceChildren(...buttons.map(({ button }) => button)),
addControl: (side = "top-right") => addControl(container, side)
}
}
const dateControl = customControl("Date")
const variantControl = customControl("Variant")
const colormapControl = customControl("Colormap")
const dateControl = detailsControl("Date")
const variantControl = detailsControl("Variant")
const colormapControl = detailsControl("Colormap")
const dates = Object.keys(data).sort()
const dateButtons = dates.map(date => {
const button = document.createElement("button")
button.textContent = date
@ -422,33 +472,55 @@
renderControls()
const subnetsControl = document.createElement("div")
subnetsControl.className = "maplibregl-ctrl maplibregl-ctrl-group custom-control"
const subnetsButton = document.createElement("button")
subnetsButton.className = "maplibregl-ctrl-subnets active"
const subnetsIcon = document.createElement("span")
subnetsIcon.className = "maplibregl-ctrl-icon"
subnetsButton.replaceChildren(subnetsIcon)
subnetsButton.addEventListener("click", () => {
subnetsVisible = !subnetsVisible
subnetsButton.classList.toggle("active")
map.setLayoutProperty(subnetsId, "visibility", subnetsVisible ? "visible" : "none")
})
subnetsControl.replaceChildren(subnetsButton)
addControl(subnetsControl, "top-right")
dateControl.addControl()
variantControl.addControl()
colormapControl.addControl()
})
const getIPAtMouse = (e, features, link) => {
const { x, y } = maplibregl.MercatorCoordinate.fromLngLat(e.lngLat, 0)
const ip = coordsToHilbert({ x: Math.floor(0x10000 * x), y: Math.floor(0x10000 * y) })
const subnet = Math.min(32, Math.round(map.getZoom()) * 2 + 18)
const ipStr = ipToString(ip, subnet)
const props = features && map.queryRenderedFeatures(e.point).find(f => f.layer.id === privateRangesId)?.properties
const propsText = props ? `<br>Part of private range ${props.range}<br>Used for ${props.description}` : ""
const name = subnet < 32 ? "Range" : "IP"
const ipText = subnet < 32 ? `${ipStr}/${subnet}` : ipStr
const ipLink = link ? `<a href="https://bgp.tools/prefix/${ipStr}" target="_blank">${ipText}</a>` : ipText
return `${name}: ${ipLink}${propsText}`
}
const hoverTextControl = document.createElement("div")
hoverTextControl.className = "maplibregl-ctrl maplibregl-ctrl-group"
const hoverTextP = document.createElement("p")
hoverTextControl.replaceChildren(hoverTextP)
map.on("mousemove", e => hoverTextP.textContent = getIPAtMouse(e, false, false))
addControl(hoverTextControl, "bottom-left")
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-left")
let curPopup
map.on("click", e => {
const { x, y } = maplibregl.MercatorCoordinate.fromLngLat(e.lngLat, 0)
const ip = coordsToHilbert({ x: Math.floor(0x10000 * x), y: Math.floor(0x10000 * y) })
const subnet = Math.min(32, Math.round(map.getZoom()) * 2 + 18)
const props = map.queryRenderedFeatures(e.point).find(f => f.layer.id === privateRangesId)?.properties
const propsText = props ? `<br>Part of private range ${props.range}<br>Used for ${props.description}` : ""
const text = subnet < 32 ?
`Range: ${ipToRange(ip, subnet)}${propsText}` :
`IP: ${ipToString(ip)}${propsText}`
curPopup = new maplibregl.Popup()
.setHTML(text)
.setLngLat(e.lngLat)
.addTo(map)
})
map.on("click", e => curPopup = new maplibregl.Popup().setHTML(getIPAtMouse(e, true, true)).setLngLat(e.lngLat).addTo(map))
const main = document.getElementsByTagName("main")[0]
main.addEventListener("click", e => {
if (e.target === main && curPopup) curPopup.remove()
})
})
</script>
</body>
</html>