Skip to content

Commit 94bba57

Browse files
authored
Merge pull request #1 from jameson-dev/fix/data-optimisation
Compress data and optimise page loading
2 parents a6a85b0 + b1bd61e commit 94bba57

10 files changed

Lines changed: 119 additions & 675714 deletions

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,14 @@ cython_debug/
184184
start_server.bat
185185
/scripts
186186
/data/raw
187+
188+
189+
# Node modules
190+
node_modules/
191+
192+
# Data compression - only commit compressed .gz files
193+
data/*.csv
194+
data/*.json
195+
!data/*.json.gz
196+
package-lock.json
197+
package.json

app.js

Lines changed: 96 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -296,91 +296,108 @@ function convertCoordinates(x, y) {
296296
}
297297
}
298298

299-
// Load and parse CSV data
300-
function loadData() {
301-
showLoading('Loading crash data (190,000+ records)...');
302-
303-
// Load crash data first
304-
Papa.parse('data/2012-2024_DATA_SA_Crash.csv', {
305-
download: true,
306-
header: true,
307-
skipEmptyLines: true,
308-
complete: function(results) {
309-
console.log('Crash CSV loaded. Total rows:', results.data.length);
310-
311-
crashData = results.data.filter(row => {
312-
return row.ACCLOC_X && row.ACCLOC_Y &&
313-
row.ACCLOC_X.trim() !== '' &&
314-
row.ACCLOC_Y.trim() !== '';
315-
});
299+
// Load and decompress JSON data
300+
async function loadData() {
301+
showLoading('Loading crash data...');
316302

317-
console.log('Filtered crash data:', crashData.length, 'records with coordinates');
303+
try {
304+
// Fetch gzipped data
305+
const response = await fetch('data/2012-2024_DATA_SA_Crash.json.gz');
318306

319-
// Load casualty data
320-
loadCasualtyData();
321-
},
322-
error: function(error) {
323-
console.error('Error loading crash data:', error);
324-
hideLoading();
325-
alert('Error loading crash data. Please ensure the CSV file is in the correct location.');
307+
if (!response.ok) {
308+
throw new Error(`HTTP error! status: ${response.status}`);
326309
}
327-
});
310+
311+
// Get compressed bytes
312+
const compressedData = await response.arrayBuffer();
313+
314+
// Decompress with pako.js
315+
const decompressed = pako.inflate(new Uint8Array(compressedData), { to: 'string' });
316+
317+
// Parse JSON
318+
const allCrashData = JSON.parse(decompressed);
319+
console.log('Crash data loaded:', allCrashData.length, 'total records');
320+
321+
// Filter crashes with valid coordinates
322+
crashData = allCrashData.filter(row => {
323+
return row.ACCLOC_X && row.ACCLOC_Y &&
324+
row.ACCLOC_X !== '' && row.ACCLOC_Y !== '';
325+
});
326+
327+
console.log('Filtered crash data:', crashData.length, 'records with coordinates');
328+
329+
// Load casualty data
330+
await loadCasualtyData();
331+
332+
} catch (error) {
333+
console.error('Error loading crash data:', error);
334+
hideLoading();
335+
alert('Error loading crash data. Please check your connection and try again.');
336+
}
328337
}
329338

330339
// Load casualty data
331-
function loadCasualtyData() {
332-
showLoading('Loading casualty data (77,000+ records)...');
333-
334-
Papa.parse('data/2012-2024_DATA_SA_Casualty.csv', {
335-
download: true,
336-
header: true,
337-
skipEmptyLines: true,
338-
complete: function(results) {
339-
casualtyData = results.data;
340-
console.log('Casualty data loaded:', casualtyData.length, 'records');
341-
342-
// Load units data
343-
loadUnitsData();
344-
},
345-
error: function(error) {
346-
console.error('Error loading casualty data:', error);
347-
hideLoading();
348-
alert('Error loading casualty data. Please ensure the CSV file is in the correct location.');
340+
async function loadCasualtyData() {
341+
showLoading('Loading casualty data...');
342+
343+
try {
344+
const response = await fetch('data/2012-2024_DATA_SA_Casualty.json.gz');
345+
346+
if (!response.ok) {
347+
throw new Error(`HTTP error! status: ${response.status}`);
349348
}
350-
});
349+
350+
const compressedData = await response.arrayBuffer();
351+
const decompressed = pako.inflate(new Uint8Array(compressedData), { to: 'string' });
352+
casualtyData = JSON.parse(decompressed);
353+
354+
console.log('Casualty data loaded:', casualtyData.length, 'records');
355+
356+
// Load units data
357+
await loadUnitsData();
358+
359+
} catch (error) {
360+
console.error('Error loading casualty data:', error);
361+
hideLoading();
362+
alert('Error loading casualty data. Please check your connection and try again.');
363+
}
351364
}
352365

353366
// Load units data
354-
function loadUnitsData() {
355-
showLoading('Loading units data (407,000+ records)...');
367+
async function loadUnitsData() {
368+
showLoading('Loading units data...');
356369

357-
Papa.parse('data/2012-2024_DATA_SA_Units.csv', {
358-
download: true,
359-
header: true,
360-
skipEmptyLines: true,
361-
complete: function(results) {
362-
unitsData = results.data;
363-
console.log('Units data loaded:', unitsData.length, 'records');
370+
try {
371+
const response = await fetch('data/2012-2024_DATA_SA_Units.json.gz');
364372

365-
// Link data together
366-
linkCrashData();
373+
if (!response.ok) {
374+
throw new Error(`HTTP error! status: ${response.status}`);
375+
}
367376

368-
// Populate filter dropdowns
369-
populateFilterOptions();
377+
const compressedData = await response.arrayBuffer();
378+
const decompressed = pako.inflate(new Uint8Array(compressedData), { to: 'string' });
379+
unitsData = JSON.parse(decompressed);
370380

371-
// Initialise marker colour legend with the default (severity) mode
372-
updateMarkerColorLegend();
381+
console.log('Units data loaded:', unitsData.length, 'records');
373382

374-
// Load LGA boundaries first, then apply filters
375-
// This ensures LGA assignments are complete before map is displayed
376-
loadLGABoundaries();
377-
},
378-
error: function(error) {
379-
console.error('Error loading units data:', error);
380-
hideLoading();
381-
alert('Error loading units data. Please ensure the CSV file is in the correct location.');
382-
}
383-
});
383+
// Link data together
384+
linkCrashData();
385+
386+
// Populate filter dropdowns
387+
populateFilterOptions();
388+
389+
// Initialise marker colour legend with the default (severity) mode
390+
updateMarkerColorLegend();
391+
392+
// Load LGA boundaries first, then apply filters
393+
// This ensures LGA assignments are complete before map is displayed
394+
loadLGABoundaries();
395+
396+
} catch (error) {
397+
console.error('Error loading units data:', error);
398+
hideLoading();
399+
alert('Error loading units data. Please check your connection and try again.');
400+
}
384401
}
385402

386403
// Link casualty and units data to crashes by REPORT_ID
@@ -593,10 +610,8 @@ function loadSuburbBoundaries(filePath = 'data/sa_suburbs.geojson') {
593610
}
594611

595612
// Pre-compute LGA assignments for crashes with missing/N/A LGA names
596-
// Note: LGA assignments are now pre-computed in the CSV file (see scripts/add_lga_column.py)
597-
// This function now just applies filters immediately without client-side computation
598613
function precomputeLGAAssignments() {
599-
console.log('Using pre-computed LGA assignments from CSV');
614+
console.log('Using pre-computed LGA assignments');
600615

601616
// Count how many crashes have LGA assignments
602617
const withLGA = crashData.filter(row => row['LGA'] && row['LGA'].trim()).length;
@@ -787,7 +802,12 @@ function getFullLGAName(abbreviatedName) {
787802
// Returns unique, alphabetically-sorted, non-empty values for a column
788803
function uniqueValues(data, column) {
789804
return [...new Set(data.map(row => row[column]).filter(v => v))]
790-
.sort((a, b) => a.localeCompare(b));
805+
.sort((a, b) => {
806+
// Handle both strings and numbers
807+
const aStr = String(a);
808+
const bStr = String(b);
809+
return aStr.localeCompare(bStr);
810+
});
791811
}
792812

793813
// Appends <option> elements to a <select> element
@@ -1847,7 +1867,7 @@ function getMarkerIcon(row) {
18471867
function addMarkers(callback) {
18481868
markersLayer.clearLayers();
18491869

1850-
const chunkSize = 2000; // Process 2000 markers per chunk
1870+
const chunkSize = 20000;
18511871
const totalMarkers = filteredData.length;
18521872
let processedCount = 0;
18531873

@@ -2034,7 +2054,7 @@ function addChoropleth() {
20342054
const lgaCountsNormalized = {};
20352055

20362056
filteredData.forEach(row => {
2037-
// Use pre-computed LGA from CSV
2057+
// Use pre-computed LGA
20382058
const lga = row['LGA'];
20392059

20402060
if (lga && lga.trim()) {

0 commit comments

Comments
 (0)