Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.DS_Store
|
||||||
100
convert.html
Normal file
100
convert.html
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!-- This should only be needed once in a rare while to convert data scraped from the Government of Canada website -->
|
||||||
|
<script src="raw-scraped-data.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button id="download-button">Download</button>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
let converted = [];
|
||||||
|
|
||||||
|
let min_freq = 9999999;
|
||||||
|
let max_freq = 0;
|
||||||
|
|
||||||
|
scrape_table(t1, 1);
|
||||||
|
scrape_table(t2, 1000);
|
||||||
|
scrape_table(t3, 1000000);
|
||||||
|
|
||||||
|
console.log(max_freq + "kHz");
|
||||||
|
console.log(max_freq / 1000 + "MHz");
|
||||||
|
console.log(max_freq / 1000000 + "GHz");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const downloadButton = document.getElementById('download-button');
|
||||||
|
downloadButton.addEventListener('click', function () {
|
||||||
|
// Example data: create a Blob from a string
|
||||||
|
const textContent = JSON.stringify(converted);
|
||||||
|
const blob = new Blob([textContent], { type: 'application/json' });
|
||||||
|
|
||||||
|
// 3. Create a temporary anchor (<a>) element
|
||||||
|
const link = document.createElement('a');
|
||||||
|
|
||||||
|
// 4. Generate a temporary URL for the blob
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// 5. Set the anchor's attributes for download
|
||||||
|
link.href = url;
|
||||||
|
link.download = 'data.json'; // Specify the default filename
|
||||||
|
link.style.display = 'none'; // Hide the element
|
||||||
|
|
||||||
|
// 6. Append the link to the document body, click it, and remove it
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click(); // Programmatically trigger the download
|
||||||
|
|
||||||
|
// 7. Clean up by revoking the object URL and removing the link
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url); // Free up memory
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function scrape_table(tbl, freq_mult) {
|
||||||
|
for (let entry of tbl) {
|
||||||
|
let conv_entry = {};
|
||||||
|
|
||||||
|
let freqs = entry.f.split(" - ");
|
||||||
|
let low_freq = parseFloat(freqs[0].replaceAll(' ', '')) * freq_mult;
|
||||||
|
let hi_freq = parseFloat(freqs[1].replaceAll(' ', '')) * freq_mult;
|
||||||
|
|
||||||
|
min_freq = Math.min(min_freq, low_freq);
|
||||||
|
max_freq = Math.max(max_freq, hi_freq);
|
||||||
|
|
||||||
|
conv_entry.minf = low_freq;
|
||||||
|
conv_entry.maxf = hi_freq;
|
||||||
|
|
||||||
|
let regexp = /primary service\s(?<service>.+?)\send primary service/gm;
|
||||||
|
|
||||||
|
// TODO: secondary services are listed after the primary in non-caps,
|
||||||
|
// are not surrounded by delimiters like primary service
|
||||||
|
// seems a bit more annoying to match
|
||||||
|
let result;
|
||||||
|
|
||||||
|
let service_cats = {};
|
||||||
|
let services = [];
|
||||||
|
|
||||||
|
// execute the regexp multiple times until we exhaust it,
|
||||||
|
// creating a new service find each time
|
||||||
|
while ((result = regexp.exec(entry.d)) != null) {
|
||||||
|
let service_name = result.groups["service"].replaceAll(' ', '_');
|
||||||
|
|
||||||
|
if (service_cats[service_name] == null) {
|
||||||
|
service_cats[service_name] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_cats[service_name]++;
|
||||||
|
services.push(result.groups["service"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
conv_entry.primary = services;
|
||||||
|
|
||||||
|
converted.push(conv_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
57
default.css
Normal file
57
default.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #2c2423;
|
||||||
|
font: 18px 'Jost';
|
||||||
|
font-weight: 300;
|
||||||
|
color: #ffeecf;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: 'Bodoni Moda';
|
||||||
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.freq-entry {
|
||||||
|
margin: 0 100px;
|
||||||
|
border-bottom: 1px solid #ffeecf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.freq-entry span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat1,
|
||||||
|
.cat2,
|
||||||
|
.cat3 {
|
||||||
|
display: none;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: .1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* div.freq-entry:hover .cat1,
|
||||||
|
div.freq-entry:hover .cat2,
|
||||||
|
div.freq-entry:hover .cat3 {
|
||||||
|
display: block;
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.cat1 {
|
||||||
|
background-color: #e73636;
|
||||||
|
color: #2c2423;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat2 {
|
||||||
|
background-color: #a09cb0;
|
||||||
|
color: #e73636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat3 {
|
||||||
|
background-color: #28666e;
|
||||||
|
color: #e73636;
|
||||||
|
}
|
||||||
112
index.html
Normal file
112
index.html
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Radio Freq Visualizer</title>
|
||||||
|
<script src="js/data.js"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Bodoni+Moda:ital,opsz,wght@0,6..96,400..900;1,6..96,400..900&family=Jost:ital,wght@0,100..900;1,100..900&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="default.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Interactive Canadian Radio Frequency Allocation</h1>
|
||||||
|
|
||||||
|
<label for="min_freq">Frequency</label>
|
||||||
|
<input type="range" id="min_freq" min="0" max="25670" />
|
||||||
|
<input type="range" id="max_freq" min="1" max="25670" />
|
||||||
|
<div id="content"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
let content = document.getElementById("content");
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
let service_cats = {};
|
||||||
|
|
||||||
|
let min_freq = 9999999;
|
||||||
|
let max_freq = 0;
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
let str = "";
|
||||||
|
for (let entry of data) {
|
||||||
|
|
||||||
|
let services = [];
|
||||||
|
|
||||||
|
for (service_name in entry.primary) {
|
||||||
|
let service_slug = service_name.replaceAll(' ', '_');
|
||||||
|
if (service_cats[service_slug] == null) {
|
||||||
|
service_cats[service_slug] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_cats[service_slug]++;
|
||||||
|
|
||||||
|
let service_span = document.createElement("span");
|
||||||
|
service_span.className = service_slug;
|
||||||
|
service_span.innerHTML = service_name;
|
||||||
|
services.push(service_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hi_freq = entry.maxf;
|
||||||
|
let low_freq = entry.minf;
|
||||||
|
|
||||||
|
min_freq = Math.min(min_freq, low_freq);
|
||||||
|
max_freq = Math.max(max_freq, hi_freq);
|
||||||
|
|
||||||
|
|
||||||
|
let range = hi_freq - low_freq;
|
||||||
|
let color = `rgb(${i}, 0, 0)`;
|
||||||
|
let container = document.createElement('div');
|
||||||
|
container.style.height = range + 'px';
|
||||||
|
container.style.backgroundColor = color;
|
||||||
|
container.className = "freq-entry";
|
||||||
|
|
||||||
|
let freq_range_span = document.createElement('span');
|
||||||
|
freq_range_span.innerHTML = `${low_freq} → ${hi_freq}`;
|
||||||
|
container.appendChild(freq_range_span);
|
||||||
|
for (let s of services) {
|
||||||
|
container.appendChild(s);
|
||||||
|
}
|
||||||
|
content.appendChild(container);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_min = document.createElement('div');
|
||||||
|
max_min.innerHTML = min_freq + "kHz to " + max_freq + "kHz";
|
||||||
|
content.prepend(max_min);
|
||||||
|
|
||||||
|
let categories = document.createElement('div');
|
||||||
|
for (let service in service_cats) {
|
||||||
|
let span = document.createElement('span');
|
||||||
|
span.className = service;
|
||||||
|
span.innerHTML = service.replaceAll('_', ' ');
|
||||||
|
categories.appendChild(span);
|
||||||
|
}
|
||||||
|
content.prepend(categories);
|
||||||
|
|
||||||
|
// find all elements tagged with service cats and replace them with cat{x}
|
||||||
|
// for styling
|
||||||
|
console.log(service_cats);
|
||||||
|
let current_cat = 1;
|
||||||
|
for (let service in service_cats) {
|
||||||
|
console.log(`Searching for elements with ${service}`);
|
||||||
|
let elems = [...document.getElementsByClassName(service)];
|
||||||
|
console.log(elems);
|
||||||
|
for (let elem of elems) {
|
||||||
|
console.log(`Removing ${service} from ${elem}`)
|
||||||
|
console.log(elem);
|
||||||
|
elem.classList.remove(service);
|
||||||
|
elem.classList.add("cat" + current_cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_cat = (current_cat + 1) % 3 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1
js/data.js
Normal file
1
js/data.js
Normal file
File diff suppressed because one or more lines are too long
5
raw-scraped-data.js
Normal file
5
raw-scraped-data.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user