UI polish, bug fixes, and README

Changes since last commit:

NATS airspace
- Remove D and D_OTHER types from parser — UK small-arms ranges covered the
  entire country and made the layer unusable
- Replace title-attribute tooltips on type filters with clickable ⓘ icons
  (mobile-friendly); info text appears in a shared box below the grid
- Add Select All / Deselect All controls (moved to bottom of all layers, now
  applies to every layer checkbox not just NATS types)
- Fix per-type filter using expression syntax ['match', ...] — legacy filter
  syntax was unreliable in MapLibre GL JS 4.x

Map style switching
- Fix overlay layers being lost when switching Terrain / Satellite / Streets
  by waiting for the 'idle' event instead of 'style.load'; MapTiler styles
  fire style.load before the map is ready to accept addSource/addLayer calls
- Defensive cleanup at top of addAllLayers() removes all custom layers and
  sources before re-adding, preventing "source already exists" crashes

Location tracking
- Move locate button from floating bottom-right into the left panel
- Auto-request location on page load
- Dynamic button label: "Getting location…" → "Stop Tracking" → "Track location"
- Suppress alert on permission-denied (error code 1); show inline message for
  other errors

Panel UX
- Move planning disclaimer out of panel body into a ⚠ icon in the header;
  click to expand, includes hyperlinked NOTAM link
- Add NOTAM link to the data-links section at the bottom
- Danger / Restricted and MoD / Military rows now have ⓘ icons explaining
  that these layers have no built-in data and require a GeoJSON upload;
  removes the old "Override: load GeoJSON below" note and "(override)" labels
  from the dropdown
- Load Data section: stacked full-width layout (dropdown above button)
- Lighten grey text (#555#888, #666#999) across section labels,
  layer notes, info icons, hints, data links, and popup close button

Map bounds and attribution
- Restrict panning to UK + Channel Islands with maxBounds
- Replace default attribution control with custom one appending
  "© Bournemouth Technology" with link to bournemouthtechnology.co.uk

README
- Add full project description, feature list, tech stack table, setup
  instructions, and data source reference table
This commit is contained in:
Jon
2026-05-25 18:28:25 +01:00
parent db9d58043d
commit f741a17202
4 changed files with 297 additions and 86 deletions

108
style.css
View File

@@ -44,12 +44,28 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
padding: 10px 14px; border-bottom: 1px solid #2a2d38;
}
.panel-title { font-weight: 700; font-size: .95rem; letter-spacing: .5px; }
.header-btns { display: flex; align-items: center; gap: 2px; }
#warn-btn {
background: none; border: none; color: #f59e0b; cursor: pointer;
padding: 2px 5px; border-radius: 4px; line-height: 1;
display: flex; align-items: center;
}
#warn-btn:hover { background: #ffffff15; }
#warn-btn svg { width: 15px; height: 15px; }
#panel-toggle {
background: none; border: none; color: #aaa; font-size: 1.1rem;
cursor: pointer; line-height: 1; padding: 2px 4px; border-radius: 4px;
}
#panel-toggle:hover { background: #ffffff15; color: #fff; }
#warn-popup {
padding: 8px 14px; border-bottom: 1px solid #5a4200;
background: #2a2206; font-size: .7rem; color: #c9a84c; line-height: 1.5;
}
#warn-popup.hidden { display: none; }
#panel-body {
padding: 12px 14px 14px;
max-height: calc(100dvh - 90px);
@@ -59,7 +75,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
.section-label {
font-size: .62rem; font-weight: 700; letter-spacing: 1.3px;
color: #555; margin: 8px 0 5px; text-transform: uppercase;
color: #888; margin: 8px 0 5px; text-transform: uppercase;
border-top: 1px solid #2a2d38; padding-top: 8px;
}
.section-label:first-child { border-top: none; padding-top: 0; margin-top: 0; }
@@ -83,8 +99,13 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
.dot-sssi { background: #06b6d4; }
.dot-forestry { background: #4d7c0f; }
.layer-row-outer {
display: flex; align-items: center; gap: 4px;
}
.layer-row-outer .layer-row { flex: 1; }
.layer-note {
font-size: .7rem; color: #555; margin: 1px 0 4px 23px; line-height: 1.4;
font-size: .7rem; color: #888; margin: 1px 0 4px 23px; line-height: 1.4;
}
.layer-note a { color: #5aacff; text-decoration: none; }
.layer-note a:hover { text-decoration: underline; }
@@ -104,7 +125,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
display: flex; align-items: center; justify-content: space-between;
margin-top: 7px; padding: 2px 1px; font-size: .82rem; color: #ccc;
}
.hint { font-size: .68rem; color: #555; }
.hint { font-size: .68rem; color: #888; }
.switch { position: relative; display: inline-block; width: 36px; height: 20px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
@@ -122,21 +143,21 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
.switch input:checked ~ .switch-track .switch-thumb { transform: translateX(16px); background: #fff; }
/* ── Load data ── */
.load-row { display: flex; gap: 6px; margin-top: 4px; align-items: center; }
.layer-select {
flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid #333;
background: #2a2d38; color: #ccc; font-size: .75rem; cursor: pointer;
appearance: none; outline: none;
width: 100%; margin-top: 5px; padding: 7px 8px; border-radius: 6px;
border: 1px solid #333; background: #2a2d38; color: #ccc;
font-size: .75rem; cursor: pointer; appearance: none; outline: none;
}
.layer-select:focus { border-color: #555; }
.load-btn {
flex-shrink: 0; padding: 6px 10px; border-radius: 6px;
padding: 7px 10px; border-radius: 6px;
border: 1px solid #333; background: #2a2d38;
color: #ccc; font-size: .75rem; cursor: pointer; white-space: nowrap;
transition: background .15s;
}
.load-btn:hover { background: #34384a; color: #fff; }
.load-btn-full { display: block; width: 100%; margin-top: 5px; text-align: center; }
/* ── Progress bar ── */
.progress-bar {
@@ -153,45 +174,67 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
/* ── NATS type filter grid ── */
.nats-type-grid {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 2px 4px; margin: 3px 0 5px 22px;
gap: 2px 4px; margin: 3px 0 3px 22px;
}
.type-cell { display: flex; align-items: center; gap: 2px; }
.type-check {
display: flex; align-items: center; gap: 3px;
display: flex; align-items: center; gap: 3px; flex: 1;
font-size: .7rem; cursor: pointer; user-select: none; color: #999;
}
.type-check input[type=checkbox] { width: 11px; height: 11px; cursor: pointer; flex-shrink: 0; }
.tc-r { color: #ef4444; font-weight: 600; }
.tc-d { color: #f97316; font-weight: 600; }
.tc-ctr { color: #facc15; font-weight: 600; }
.tc-cta { color: #60a5fa; font-weight: 600; }
.tc-ras { color: #818cf8; font-weight: 600; }
.tc-tmz { color: #c084fc; font-weight: 600; }
.info-btn {
width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;
border: none; background: none;
color: #888; cursor: pointer; padding: 0;
display: flex; align-items: center; justify-content: center;
transition: color .15s;
}
.info-btn:hover { color: #5aacff; }
.info-btn.active { color: #5aacff; }
.info-btn svg { width: 13px; height: 13px; }
.type-all-row {
display: flex; gap: 6px; margin: 4px 0 2px 22px;
}
.type-all-btn {
flex: 1; padding: 4px 6px; border-radius: 5px;
border: 1px solid #333; background: #2a2d38;
color: #bbb; font-size: .68rem; cursor: pointer;
transition: background .15s, color .15s;
}
.type-all-btn:hover { background: #34384a; color: #fff; }
.type-info-box {
margin: 4px 0 4px 22px; padding: 6px 8px; border-radius: 5px;
background: #1a1d24; border: 1px solid #2a2d38;
font-size: .7rem; color: #aaa; line-height: 1.45;
}
.type-info-box.hidden { display: none; }
.data-links {
margin-top: 7px; font-size: .68rem; color: #555; line-height: 1.9;
margin-top: 7px; font-size: .68rem; color: #888; line-height: 1.9;
}
.data-links a { color: #5aacff; text-decoration: none; }
.data-links a:hover { text-decoration: underline; }
.disclaimer {
margin-top: 12px; padding: 9px 10px; border-radius: 6px;
background: #2a2206; border: 1px solid #5a4200;
font-size: .7rem; color: #c9a84c; line-height: 1.5;
}
/* ── Locate button ── */
/* ── Locate button (inside panel) ── */
#locate-btn {
position: absolute; bottom: 30px; right: 12px; z-index: 100;
width: 44px; height: 44px; border-radius: 50%;
border: 1px solid #333; background: #1e2027cc; backdrop-filter: blur(10px);
color: #ccc; cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: all .15s;
display: flex; align-items: center; gap: 8px;
width: 100%; margin-top: 4px; padding: 8px 8px; border-radius: 6px;
border: 1px solid #333; background: #2a2d38;
color: #ccc; font-size: .83rem; cursor: pointer; text-align: left;
transition: background .15s, color .15s, border-color .15s;
}
#locate-btn:hover { background: #2a2d38cc; color: #fff; }
#locate-btn.locating { color: #2563eb; animation: btn-pulse 1s infinite; }
#locate-btn.tracking { color: #2563eb; border-color: #2563eb44; background: #1a2f5acc; }
#locate-btn.tracking:hover { background: #1d3560cc; }
#locate-btn svg { width: 20px; height: 20px; }
#locate-btn:hover { background: #34384a; color: #fff; }
#locate-btn.locating { color: #2563eb; border-color: #2563eb44; animation: btn-pulse 1s infinite; }
#locate-btn.tracking { color: #2563eb; border-color: #2563eb55; background: #1a2f5a88; }
#locate-btn svg { width: 16px; height: 16px; flex-shrink: 0; }
@keyframes btn-pulse { 0%,100% { opacity: 1; } 50% { opacity: .4; } }
@@ -215,7 +258,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
/* ── Popup ── */
#popup {
position: absolute; bottom: 84px; right: 12px; z-index: 100;
position: absolute; bottom: 30px; right: 12px; z-index: 100;
width: min(280px, calc(100vw - 24px));
background: #1e2027ee; backdrop-filter: blur(10px);
border: 1px solid #333; border-radius: 10px; padding: 14px 16px;
@@ -223,7 +266,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
#popup.hidden { display: none; }
#popup-close {
position: absolute; top: 10px; right: 12px;
background: none; border: none; color: #666; font-size: .85rem;
background: none; border: none; color: #999; font-size: .85rem;
cursor: pointer; line-height: 1;
}
#popup-close:hover { color: #fff; }
@@ -255,6 +298,5 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
/* ── Mobile ── */
@media (max-width: 480px) {
#panel { width: calc(100vw - 24px); top: 8px; left: 8px; right: 8px; }
#locate-btn { bottom: 20px; right: 8px; }
#popup { right: 8px; bottom: 74px; width: calc(100vw - 16px); }
#popup { right: 8px; bottom: 20px; width: calc(100vw - 16px); }
}