Created
October 12, 2026 21:13
-
-
Save Rub21/14112ddb97e26d74ebff8570dc84fdb9 to your computer and use it in GitHub Desktop.
ohm non-admin-lines
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>OHM non-admin boundary lines</title> | |
| <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css"> | |
| <script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script> | |
| <style> | |
| html, body, #map { margin: 0; height: 100%; } | |
| body { font: 13px/1.4 system-ui, sans-serif; } | |
| #panel { | |
| position: absolute; top: 10px; left: 10px; z-index: 1; | |
| background: #fff; padding: 10px 12px; border-radius: 6px; | |
| box-shadow: 0 1px 6px rgba(0,0,0,.3); max-height: calc(100% - 40px); overflow: auto; | |
| } | |
| #panel h1 { font-size: 14px; margin: 0 0 6px; } | |
| #panel label { display: flex; align-items: center; gap: 6px; cursor: pointer; } | |
| #types { margin-top: 8px; } | |
| .swatch { width: 18px; height: 4px; border-radius: 2px; flex: none; } | |
| #year { width: 190px; } | |
| .maplibregl-popup-content { font: 12px/1.4 system-ui, sans-serif; } | |
| .maplibregl-popup-content td:first-child { color: #666; padding-right: 8px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="map"></div> | |
| <div id="panel"> | |
| <h1>Non-admin boundary lines (production)</h1> | |
| <label><input type="checkbox" id="useYear" checked> Filter by year: <b id="yearLabel"></b></label> | |
| <input type="range" id="year" min="1000" max="2026" value="2000"> | |
| <div id="types"></div> | |
| </div> | |
| <script> | |
| const TILES = 'https://vtiles.openhistoricalmap.org/maps/ohm/{z}/{x}/{y}'; | |
| const TYPES = { | |
| timezone: '#1f78b4', | |
| disputed: '#e31a1c', | |
| religious_administration: '#6a3d9a', | |
| judicial: '#b15928', | |
| postal_code: '#ff7f00', | |
| census: '#33a02c', | |
| political: '#fb9a99', | |
| ceremonial: '#cab2d6', | |
| place: '#a6761d', | |
| statistical: '#66a61e', | |
| protected_area: '#1b9e77', | |
| national_park: '#006d2c', | |
| indigenous_administration: '#e7298a', | |
| aboriginal_lands: '#d95f02', | |
| maritime: '#17becf', | |
| historic: '#7570b3' | |
| }; | |
| const OTHER = '#555555'; | |
| const map = new maplibregl.Map({ | |
| container: 'map', | |
| hash: true, | |
| center: [-100, 45], | |
| zoom: 4, | |
| style: { | |
| version: 8, | |
| sources: { | |
| osm: { | |
| type: 'raster', | |
| tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], | |
| tileSize: 256, | |
| maxzoom: 19, | |
| attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' | |
| }, | |
| ohm: { | |
| type: 'vector', | |
| tiles: [TILES], | |
| maxzoom: 16, | |
| attribution: '<a href="https://www.openhistoricalmap.org/copyright">OpenHistoricalMap</a>' | |
| } | |
| }, | |
| layers: [ | |
| { id: 'osm', type: 'raster', source: 'osm', paint: { 'raster-opacity': 0.6 } }, | |
| { | |
| id: 'non-admin-lines', | |
| type: 'line', | |
| source: 'ohm', | |
| 'source-layer': 'land_ohm_lines', | |
| layout: { 'line-cap': 'round', 'line-join': 'round' }, | |
| paint: { | |
| 'line-width': ['interpolate', ['linear'], ['zoom'], 2, 1, 10, 2.5], | |
| 'line-color': ['match', ['get', 'type'], ...Object.entries(TYPES).flat(), OTHER] | |
| } | |
| } | |
| ] | |
| } | |
| }); | |
| map.addControl(new maplibregl.NavigationControl(), 'top-right'); | |
| const hidden = new Set(); | |
| const useYear = document.getElementById('useYear'); | |
| const year = document.getElementById('year'); | |
| const yearLabel = document.getElementById('yearLabel'); | |
| function applyFilter() { | |
| yearLabel.textContent = useYear.checked ? year.value : 'all'; | |
| year.disabled = !useYear.checked; | |
| if (!map.getLayer('non-admin-lines')) return; | |
| const filter = ['all', | |
| ['!=', ['get', 'type'], 'administrative'], | |
| ['!', ['in', ['get', 'type'], ['literal', [...hidden]]]] | |
| ]; | |
| if (useYear.checked) { | |
| const y = Number(year.value); | |
| // Features with no start or end date have no property in the tile. | |
| filter.push(['<', ['coalesce', ['get', 'start_decdate'], -99999], y + 1]); | |
| filter.push(['>=', ['coalesce', ['get', 'end_decdate'], 99999], y]); | |
| } | |
| map.setFilter('non-admin-lines', filter); | |
| } | |
| const typesEl = document.getElementById('types'); | |
| for (const [type, color] of [...Object.entries(TYPES), ['other', OTHER]]) { | |
| const label = document.createElement('label'); | |
| label.innerHTML = `<input type="checkbox" checked><span class="swatch" style="background:${color}"></span>${type}`; | |
| const input = label.querySelector('input'); | |
| if (type === 'other') { | |
| input.disabled = true; | |
| } else { | |
| input.addEventListener('change', () => { | |
| input.checked ? hidden.delete(type) : hidden.add(type); | |
| applyFilter(); | |
| }); | |
| } | |
| typesEl.appendChild(label); | |
| } | |
| useYear.addEventListener('change', applyFilter); | |
| year.addEventListener('input', applyFilter); | |
| map.on('load', applyFilter); | |
| applyFilter(); | |
| map.on('click', 'non-admin-lines', (e) => { | |
| const p = e.features[0].properties; | |
| const rows = ['type', 'admin_level', 'start_date', 'end_date', 'osm_id'] | |
| .filter((k) => p[k] !== undefined && p[k] !== '') | |
| .map((k) => `<tr><td>${k}</td><td>${p[k]}</td></tr>`).join(''); | |
| const link = `https://www.openhistoricalmap.org/way/${Math.abs(p.osm_id)}`; | |
| new maplibregl.Popup() | |
| .setLngLat(e.lngLat) | |
| .setHTML(`<table>${rows}</table><a href="${link}" target="_blank" rel="noopener">Open way in OHM</a>`) | |
| .addTo(map); | |
| }); | |
| map.on('mouseenter', 'non-admin-lines', () => { map.getCanvas().style.cursor = 'pointer'; }); | |
| map.on('mouseleave', 'non-admin-lines', () => { map.getCanvas().style.cursor = ''; }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment