I created a code that uses a CSV file that is imported into the project folder so that it can display the Markers for each data point, while making Marker Clusters and Layers.
My code works great, but my boss would like me to add a function so that he could add a CSV file himself on the interface.
I figured out another code so that he can add a CSV file and display the Markers, but I don't know how to apply it to the code I already have so that the Layers and Marker SubGroups still work.
Here's my original code that works great and I want to keep, just with the added CSV file uploaded feature:
var engineerLayers = {}; // Create an object to hold the engineer marker cluster groups
//this retrieves the file
$.get('\practice.csv', function (csvString) {
var data = Papa.parse(csvString, {
header: true,
dynamicTyping: true
}).data;
var parentGroup = L.markerClusterGroup();
for (var i in data) {
var row = data[i];
var engineer = row.Engineer;
if (!(engineer in engineerLayers)) {
// Each overlay that should actually be a part of MCG
// should be made as a Subgroup, with the MCG as their parent
engineerLayers[engineer] = L.featureGroup.subGroup(parentGroup, []);
controlLayers.addOverlay(engineerLayers[engineer], engineer);
}
var marker = L.circleMarker([row.Latitude, row.Longitude], {
radius: 10,
stroke: true,
color: getColor(engineer),
opacity: 1,
weight: 1,
fill: true,
fillColor: getColor(engineer),
fillOpacity: 0.5
}).bindPopup('Plan File: ' + row.PFN + '</br>' + 'Engineer: ' + row.Engineer + '</br>' + ' Date Received: ' + row.Date_Received + '</br>' + 'Status: ' + row.Status);
// Add the marker only to its overlay
marker.addTo(engineerLayers[engineer]);
}
map.addLayer(parentGroup);
controlLayers.addTo(map);
});
// Must change these if engineers ever change
function getColor(engineer) {
switch (engineer) {
case 'Mike':
return 'green';
case 'Salar':
return 'blue';
case 'Diego':
return 'purple';
case 'Saul':
return 'orange';
case 'Chan':
return 'red';
default:
return 'black';
}
}
Here's the code I have written to allow the user to upload the CSV file and then to print the data:
// Listen for file upload changes
$('#csvFileUpload').change(function (e) {
var file = e.target.files[0];
var reader = new FileReader();
// Read file contents
reader.onload = function (e) {
var csv = e.target.result;
var markers = parseCSV(csv);
// Add markers to the map
markers.forEach(function (marker) {
L.marker([marker.Latitude, marker.Longitude]).addTo(map)
.bindPopup(marker.title);
});
};
reader.readAsText(file);
});
// Parse CSV data into an array of objects
function parseCSV(csv) {
var lines = csv.split("\n");
var result = [];
var headers = lines[0].split(",");
// Iterate through each line (excluding header) and create marker objects
for (var i = 1; i < lines.length; i++) {
var obj = {};
var currentLine = lines[i].split(",");
// Populate marker object with latitude, longitude, and title
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentLine[j];
}
result.push(obj);
}
return result;
}
Here's some lines of my practice.csv:
PFN,Latitude,Longitude,Date_Received,District,Engineer,Status
0001-23-765-3000W,34.04395483,-118.1481822,1/23/2023,LA,Mike,Active
0002-23-3000W,34.04447045,-118.1509931,5/28/2003,Pomona,Salar,Not Active
0003-23NC,34.04271911,-118.1468947,9/30/1983,LA,Saul,Active
004-23-404-406,34.04264799,-118.1487937,2/2/1964,Pomona,Diego,Not Active
406,34.04210569,-118.1501992,6/6/1944,LA,Mike,Active
1249-23NC,34.0901146,-118.1486999,10/9/1924,Pomona,Salar,Not Active
1250-23NC,34.08822204,-118.151189,2/11/1905,LA,Saul,Active
UPDATE: My code now works great thanks to Ghybs answer, but for whatever reason, my code keeps creating a layer called undefined. I noticed in my previous code that an undefined layer would be created if a file with extra line spaces were included. Is there anyway to get rid of this so that the code is only reading the file with words on it rather then the extra space at the end? Here's what it looks like:
I just added these lines to the function and it stops reading the last line:
// Read file contents
reader.onload = function (e) {
var csv = e.target.result;
var lines = csv.split("\n");
var csvWithoutLastLine = lines.slice(0,-1).join("\n");
parseCsv(csvWithoutLastLine);
// Parse, build Markers and populate Layer Groups
//parseCsv(csv);
};
reader.readAsText(file);
});
You can re-use most of your initial callback of $.get
for handling user input CSV file as well (in particular re-using PapaParse instead of manually reading CSV). Mostly make sure to put Layer placeholder variables in outer shared scope, to re-use them as well:
var engineerLayers = {}; // Create an object to hold the engineer subgroups
var parentGroup = L.markerClusterGroup();
map.addLayer(parentGroup);
controlLayers.addTo(map);
// Parse CSV string, build Markers and
// populate Layer Groups
function parseCsv(csvString) {
var data = Papa.parse(csvString, {
header: true,
dynamicTyping: true
}).data;
for (var i in data) {
var row = data[i];
var engineer = row.Engineer;
if (!(engineer in engineerLayers)) {
// Each overlay that should actually be a part of MCG
// should be made as a Subgroup, with the MCG as their parent
engineerLayers[engineer] = L.featureGroup.subGroup(parentGroup, []);
controlLayers.addOverlay(engineerLayers[engineer], engineer);
}
var marker = L.circleMarker([row.Latitude, row.Longitude], {
radius: 10,
stroke: true,
color: getColor(engineer),
opacity: 1,
weight: 1,
fill: true,
fillColor: getColor(engineer),
fillOpacity: 0.5
}).bindPopup('Plan File: ' + row.PFN + '</br>' + 'Engineer: ' + row.Engineer + '</br>' + ' Date Received: ' + row.Date_Received + '</br>' + 'Status: ' + row.Status);
// Add the marker only to its overlay
marker.addTo(engineerLayers[engineer]);
}
});
Then your initial file load becomes:
$.get('\practice.csv', parseCsv);
And your user CSV input handling:
// Listen for file upload changes
$('#csvFileUpload').change(function (e) {
var file = e.target.files[0];
var reader = new FileReader();
// Read file contents
reader.onload = function (e) {
var csv = e.target.result;
// Parse, build Markers and populate Layer Groups
parseCsv(csv);
};
reader.readAsText(file);
});