- Created backend structure with Go, including main application logic and API endpoints. - Added Docker support for both development and production environments. - Introduced frontend using Nuxt 3 with Tailwind CSS for styling. - Included configuration files for Docker and environment variables. - Established basic documentation for contributing, development, and deployment processes. - Set up .gitignore and .dockerignore files to manage ignored files in the repository.
89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
import L, { Bounds, LatLng, Point } from 'leaflet'
|
|
|
|
export const TileSize = 100
|
|
export const HnHMaxZoom = 6
|
|
export const HnHMinZoom = 1
|
|
export const HnHDefaultZoom = 6
|
|
|
|
/** When scaleFactor exceeds this, render one label per tile instead of a full grid (avoids 100k+ DOM nodes at zoom 1). */
|
|
const GRID_COORD_SCALE_FACTOR_THRESHOLD = 8
|
|
|
|
export const GridCoordLayer = L.GridLayer.extend({
|
|
options: {
|
|
visible: true,
|
|
},
|
|
createTile(coords) {
|
|
if (!this.options.visible) {
|
|
const element = document.createElement('div')
|
|
element.style.width = TileSize + 'px'
|
|
element.style.height = TileSize + 'px'
|
|
element.classList.add('map-tile')
|
|
return element
|
|
}
|
|
const element = document.createElement('div')
|
|
element.style.width = TileSize + 'px'
|
|
element.style.height = TileSize + 'px'
|
|
element.style.position = 'relative'
|
|
element.classList.add('map-tile')
|
|
|
|
const scaleFactor = Math.pow(2, HnHMaxZoom - coords.z)
|
|
const topLeft = { x: coords.x * scaleFactor, y: coords.y * scaleFactor }
|
|
const bottomRight = { x: topLeft.x + scaleFactor - 1, y: topLeft.y + scaleFactor - 1 }
|
|
|
|
if (scaleFactor > GRID_COORD_SCALE_FACTOR_THRESHOLD) {
|
|
// Low zoom: one label per tile to avoid hundreds of thousands of DOM nodes (Reset view freeze fix)
|
|
const textElement = document.createElement('div')
|
|
textElement.classList.add('map-tile-text')
|
|
textElement.textContent = `(${topLeft.x}, ${topLeft.y})`
|
|
textElement.style.position = 'absolute'
|
|
textElement.style.left = '2px'
|
|
textElement.style.top = '2px'
|
|
textElement.style.fontSize = Math.max(8, 12 - Math.log2(scaleFactor) * 2) + 'px'
|
|
element.appendChild(textElement)
|
|
return element
|
|
}
|
|
|
|
for (let gx = topLeft.x; gx <= bottomRight.x; gx++) {
|
|
for (let gy = topLeft.y; gy <= bottomRight.y; gy++) {
|
|
const leftPx = ((gx - topLeft.x) / scaleFactor) * TileSize
|
|
const topPx = ((gy - topLeft.y) / scaleFactor) * TileSize
|
|
const textElement = document.createElement('div')
|
|
textElement.classList.add('map-tile-text')
|
|
textElement.textContent = `(${gx}, ${gy})`
|
|
textElement.style.position = 'absolute'
|
|
textElement.style.left = leftPx + 2 + 'px'
|
|
textElement.style.top = topPx + 2 + 'px'
|
|
if (scaleFactor > 1) {
|
|
textElement.style.fontSize = Math.max(8, 12 - Math.log2(scaleFactor) * 2) + 'px'
|
|
}
|
|
element.appendChild(textElement)
|
|
}
|
|
}
|
|
return element
|
|
},
|
|
})
|
|
|
|
export const ImageIcon = L.Icon.extend({
|
|
options: {
|
|
iconSize: [32, 32],
|
|
iconAnchor: [16, 16],
|
|
},
|
|
})
|
|
|
|
const latNormalization = (90.0 * TileSize) / 2500000.0
|
|
const lngNormalization = (180.0 * TileSize) / 2500000.0
|
|
|
|
const HnHProjection = {
|
|
project(latlng) {
|
|
return new Point(latlng.lat / latNormalization, latlng.lng / lngNormalization)
|
|
},
|
|
unproject(point) {
|
|
return new LatLng(point.x * latNormalization, point.y * lngNormalization)
|
|
},
|
|
bounds: (() => new Bounds([-latNormalization, -lngNormalization], [latNormalization, lngNormalization]))(),
|
|
}
|
|
|
|
export const HnHCRS = L.extend({}, L.CRS.Simple, {
|
|
projection: HnHProjection,
|
|
})
|