Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstract Google Spreadsheet Method #105

Merged
merged 7 commits into from
Dec 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = function(grunt) {

browserify: {
dist: {
src: ['node_modules/jquery/dist/jquery.min.js', 'node_modules/leaflet/dist/leaflet.js', 'src/leafletEnvironmentalLayers.js'],
src: ['node_modules/jquery/dist/jquery.min.js', 'node_modules/leaflet/dist/leaflet.js', 'src/leafletEnvironmentalLayers.js', 'src/util/*.js'],
dest: 'dist/LeafletEnvironmentalLayers.js'
}
},
Expand Down
168 changes: 167 additions & 1 deletion dist/LeafletEnvironmentalLayers.js
Original file line number Diff line number Diff line change
Expand Up @@ -28960,4 +28960,170 @@ L.layerGroup.toxicReleaseLayer = function (options) {
return new L.LayerGroup.ToxicReleaseLayer(options);
};

},{}]},{},[3,7,13]);
},{}],21:[function(require,module,exports){
L.SpreadsheetLayer = L.LayerGroup.extend({
//options: {
//Must be supplied:
//url: String url of data sheet
//columns: Array of column names to be used
//lat, lon column names
//generatePopup: function used to create content of popups

//Optional:
//imageOptions: defaults to blank
//sheet index: defaults to 0 (first sheet)
//},

initialize: function(options) {
options = options || {};
L.Util.setOptions(this, options);
this._layers = {};
this._columns = this.options.columns || [];
this.options.imageOptions = this.options.imageOptions || {};
this.options.sheetNum = this.options.sheetNum || 0;
this._parsedToOrig = {};
this._lat = this._cleanColumnName(this.options.lat);
this._lon = this._cleanColumnName(this.options.lon);
this._columns = this._cleanColumns(this._columns);
},

_cleanColumns: function(columns) {
for(var i = 0; i < columns.length; i++) { //the names of the columns are processed before given in JSON, so we must parse these column names too
var parsedColumnName = this._cleanColumnName(columns[i]);
this._parsedToOrig[parsedColumnName] = columns[i]; //Here we create an object with the parsed names as keys and original names as values;
columns[i] = parsedColumnName;
}
if(L.Util.indexOf(columns, this._lat) <= -1) { //parse lat and lon names the same way, then add them to columns if not there
columns.push(this._lat);
this._parsedToOrig[this._lat] = this.options.lat;
}
if(L.Util.indexOf(columns, this._lon) <= -1) {
columns.push(this._lon);
this._parsedToOrig[this._lon] = this.options.lon;
}
return columns;
},

_cleanColumnName: function(columnName) { //Tries to emulate google's conversion of column titles
return columnName.replace(/^[^a-zA-Z]+/g, '') //remove any non letters from the front till first letter
.replace(/\s+|[!@#$%^&*()]+/g, '') //remove most symbols
.toLowerCase();
},

onAdd: function(map) {
this._map = map;
var self = this;
this._getURL().then(function() { //Wait for getURL to finish before requesting data. This way we can do it just once
self.requestData();
});
},

onRemove: function(map) {
this.clearLayers();
map.spin(false);
this._layers = {};
},

_getURL: function() {
var spreadsheetID = this._getSpreadsheetID(); //To find the URL we need, we first need to find the spreadsheetID
var self = this;
//Then we have to make another request in order to find the worksheet ID, which is changed by the sheet within the spreadsheet we want
var spreadsheetFeedURL = 'https://spreadsheets.google.com/feeds/worksheets/' + spreadsheetID + '/public/basic?alt=json';
//Here we return the getjson request so that the previous code may know when it has completed
return this._getWorksheetID(spreadsheetID, spreadsheetFeedURL);
},

_getSpreadsheetID: function() {
var sections = this.options.url.split('/'); //The spreadsheet ID generally comes after a section with only 1 character, usually a D.
var spreadsheetID;
var len = sections.length;
for (var i = 1; i < len; i++) {
if (sections[i - 1].length === 1) { //Here we check to see if the previous one was 1 character
spreadsheetID = sections[i];
break;
}
}
return spreadsheetID;
},

_getWorksheetID: function(spreadsheetID, spreadsheetFeedURL) {
var self = this;
return $.getJSON(spreadsheetFeedURL, function(data) {
//The worksheetID we want is dependent on which sheet we are looking for
var tmpLink = data.feed.entry[self.options.sheetNum].id.$t;
var sections = tmpLink.split('/');
//It is always the last section of the URL
var sheetID = sections[sections.length - 1];
//Set the URL to the final one.
self.options.url = 'https://spreadsheets.google.com/feeds/list/' + spreadsheetID + '/' + sheetID + '/public/values?alt=json';
});
},

requestData: function() {
var self = this;
(function() {
var script = document.createElement("SCRIPT");
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
script.type = 'text/javascript';
script.onload = function() {
var $ = window.jQuery;
var ssURL = self.options.url || '';
self._map.spin(true);
//start fetching data from the URL
$.getJSON(ssURL, function(data) {
self.parseData(data.feed.entry);
self._map.spin(false);
});
};
document.getElementsByTagName("head")[0].appendChild(script);
})();

},

parseData: function(data) {
for (var i = 0; i < data.length; i++) {
this.addMarker(data[i]);
}
},

addMarker: function(data) {
var urlSections = data.id.$t.split('/');
var key = urlSections[urlSections.length - 1];
if(!this._layers[key]) {
var marker = this.getMarker(data);
this._layers[key] = marker;
this.addLayer(marker);
}
},

getMarker: function(data) {
var info = {};
for (var i = 0; i < this._columns.length; i++) {
info[this._columns[i]] = data['gsx$' + this._columns[i]].$t || ''; //The JSON has gsx$ appended to the front of each columnname
}
//Get coordinates the coordinates; remember that _lat and _lon are the column names, not the actual values
var latlon = [parseInt(info[this._lat]), parseInt(info[this._lon])];
var generatePopup = this.options.generatePopup || function() {return;};
//Generate an object using the original column names as keys
var origInfo = this._createOrigInfo(info);
return L.marker(latlon, this.options.imageOptions).bindPopup(generatePopup(origInfo));
},

_createOrigInfo: function(info) {
//The user will most likely give their generatePopup in terms of the column names typed in,
//not the parsed names. So this creates a new object that uses the original typed column
//names as the keys
var origInfo = {};
for(var key in info) {
var origKey = this._parsedToOrig[key];
origInfo[origKey] = info[key];
}
return origInfo;
}

});

L.spreadsheetLayer = function(options) {
return new L.SpreadsheetLayer(options);
};
},{}]},{},[3,7,13,21]);
2 changes: 2 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<script src="https://unpkg.com/esri-leaflet@2.2.3/dist/esri-leaflet.js"></script>
<script src="https://unpkg.com/esri-leaflet-renderers@2.0.6"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>

<body>

<div id="map"></div>
Expand Down
165 changes: 165 additions & 0 deletions src/util/googleSpreadsheetLayer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
L.SpreadsheetLayer = L.LayerGroup.extend({
//options: {
//Must be supplied:
//url: String url of data sheet
//columns: Array of column names to be used
//lat, lon column names
//generatePopup: function used to create content of popups

//Optional:
//imageOptions: defaults to blank
//sheet index: defaults to 0 (first sheet)
//},

initialize: function(options) {
options = options || {};
L.Util.setOptions(this, options);
this._layers = {};
this._columns = this.options.columns || [];
this.options.imageOptions = this.options.imageOptions || {};
this.options.sheetNum = this.options.sheetNum || 0;
this._parsedToOrig = {};
this._lat = this._cleanColumnName(this.options.lat);
this._lon = this._cleanColumnName(this.options.lon);
this._columns = this._cleanColumns(this._columns);
},

_cleanColumns: function(columns) {
for(var i = 0; i < columns.length; i++) { //the names of the columns are processed before given in JSON, so we must parse these column names too
var parsedColumnName = this._cleanColumnName(columns[i]);
this._parsedToOrig[parsedColumnName] = columns[i]; //Here we create an object with the parsed names as keys and original names as values;
columns[i] = parsedColumnName;
}
if(L.Util.indexOf(columns, this._lat) <= -1) { //parse lat and lon names the same way, then add them to columns if not there
columns.push(this._lat);
this._parsedToOrig[this._lat] = this.options.lat;
}
if(L.Util.indexOf(columns, this._lon) <= -1) {
columns.push(this._lon);
this._parsedToOrig[this._lon] = this.options.lon;
}
return columns;
},

_cleanColumnName: function(columnName) { //Tries to emulate google's conversion of column titles
return columnName.replace(/^[^a-zA-Z]+/g, '') //remove any non letters from the front till first letter
.replace(/\s+|[!@#$%^&*()]+/g, '') //remove most symbols
.toLowerCase();
},

onAdd: function(map) {
this._map = map;
var self = this;
this._getURL().then(function() { //Wait for getURL to finish before requesting data. This way we can do it just once
self.requestData();
});
},

onRemove: function(map) {
this.clearLayers();
map.spin(false);
this._layers = {};
},

_getURL: function() {
var spreadsheetID = this._getSpreadsheetID(); //To find the URL we need, we first need to find the spreadsheetID
var self = this;
//Then we have to make another request in order to find the worksheet ID, which is changed by the sheet within the spreadsheet we want
var spreadsheetFeedURL = 'https://spreadsheets.google.com/feeds/worksheets/' + spreadsheetID + '/public/basic?alt=json';
//Here we return the getjson request so that the previous code may know when it has completed
return this._getWorksheetID(spreadsheetID, spreadsheetFeedURL);
},

_getSpreadsheetID: function() {
var sections = this.options.url.split('/'); //The spreadsheet ID generally comes after a section with only 1 character, usually a D.
var spreadsheetID;
var len = sections.length;
for (var i = 1; i < len; i++) {
if (sections[i - 1].length === 1) { //Here we check to see if the previous one was 1 character
spreadsheetID = sections[i];
break;
}
}
return spreadsheetID;
},

_getWorksheetID: function(spreadsheetID, spreadsheetFeedURL) {
var self = this;
return $.getJSON(spreadsheetFeedURL, function(data) {
//The worksheetID we want is dependent on which sheet we are looking for
var tmpLink = data.feed.entry[self.options.sheetNum].id.$t;
var sections = tmpLink.split('/');
//It is always the last section of the URL
var sheetID = sections[sections.length - 1];
//Set the URL to the final one.
self.options.url = 'https://spreadsheets.google.com/feeds/list/' + spreadsheetID + '/' + sheetID + '/public/values?alt=json';
});
},

requestData: function() {
var self = this;
(function() {
var script = document.createElement("SCRIPT");
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
script.type = 'text/javascript';
script.onload = function() {
var $ = window.jQuery;
var ssURL = self.options.url || '';
self._map.spin(true);
//start fetching data from the URL
$.getJSON(ssURL, function(data) {
self.parseData(data.feed.entry);
self._map.spin(false);
});
};
document.getElementsByTagName("head")[0].appendChild(script);
})();

},

parseData: function(data) {
for (var i = 0; i < data.length; i++) {
this.addMarker(data[i]);
}
},

addMarker: function(data) {
var urlSections = data.id.$t.split('/');
var key = urlSections[urlSections.length - 1];
if(!this._layers[key]) {
var marker = this.getMarker(data);
this._layers[key] = marker;
this.addLayer(marker);
}
},

getMarker: function(data) {
var info = {};
for (var i = 0; i < this._columns.length; i++) {
info[this._columns[i]] = data['gsx$' + this._columns[i]].$t || ''; //The JSON has gsx$ appended to the front of each columnname
}
//Get coordinates the coordinates; remember that _lat and _lon are the column names, not the actual values
var latlon = [parseInt(info[this._lat]), parseInt(info[this._lon])];
var generatePopup = this.options.generatePopup || function() {return;};
//Generate an object using the original column names as keys
var origInfo = this._createOrigInfo(info);
return L.marker(latlon, this.options.imageOptions).bindPopup(generatePopup(origInfo));
},

_createOrigInfo: function(info) {
//The user will most likely give their generatePopup in terms of the column names typed in,
//not the parsed names. So this creates a new object that uses the original typed column
//names as the keys
var origInfo = {};
for(var key in info) {
var origKey = this._parsedToOrig[key];
origInfo[origKey] = info[key];
}
return origInfo;
}

});

L.spreadsheetLayer = function(options) {
return new L.SpreadsheetLayer(options);
};