Skip to content

Commit 17e5c36

Browse files
committed
Broke up code into moduals and added some GeoJSON support.
1 parent a761db1 commit 17e5c36

30 files changed

Lines changed: 743 additions & 147 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@
1515
/libpeerconnection.log
1616
npm-debug.log*
1717
testem.log
18+
.DS_Store
19+
.vscode/*

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ yarn installation
1616
ember g excel2geojson
1717
```
1818

19+
## Development
20+
21+
### Clone this repo and install dependencies
22+
~~~bash
23+
git clone https://github.com/Atlanta-Explorer/ember-cli-excel2geojson.git
24+
cd ember-cli-excel2geojson
25+
yarn install # or npm install
26+
bower install # this may not be needed
27+
~~~
28+
29+
### Run the dummy app
30+
~~~bash
31+
ember s
32+
~~~
33+
34+
See the dummy app at [http://localhost:4200](http://localhost:4200)
35+
36+
1937
### Usage
2038
When used as a block, a simple file input filed will be rendered. After an excel file is uploaded, it will appear in an [ember-light-table](http://offirgolan.github.io/ember-light-table/). Below the table will be a list of select fields for the control attributes.
2139

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { assert } from '@ember/debug';
2+
import Component from '@ember/component';
3+
import { get, set, setProperties } from '@ember/object';
4+
import { A } from '@ember/array';
5+
import layout from '../templates/components/geojson-parse-data';
6+
import $ from 'jquery';
7+
import GeojsonData from '../lib/geojson-data';
8+
import SpreadsheetData from '../lib/spreadsheet-data';
9+
10+
const fileTypes = A(['xlsx', 'json', 'geojson']);
11+
const reader = new FileReader();
12+
13+
export default Component.extend({
14+
layout,
15+
data: null,
16+
table: null,
17+
bounds: A([[33.7489954, -84.3879824]]),
18+
19+
getExtension(fileName) {
20+
// TODO: Should we check mime type? https://stackoverflow.com/a/29672957/1792144
21+
// It would be nice, but not a prioritory.
22+
return fileName.name.split('.').pop();
23+
},
24+
25+
// types: {label: 'Title'},
26+
types: [{
27+
label: 'Title',
28+
disabled: false,
29+
value: 'title'
30+
},
31+
{
32+
label: 'Lat',
33+
disabled: false,
34+
value: 'lat'
35+
},
36+
{
37+
label: 'Lng',
38+
disabled: false,
39+
value: 'lng'
40+
},
41+
{
42+
label: 'Description',
43+
disabled: false,
44+
value: 'description'
45+
},
46+
{
47+
label: 'Image(s)',
48+
disabled: false,
49+
value: 'images'
50+
},
51+
{
52+
label: 'Image Credit',
53+
disabled: false,
54+
value: 'image-credit'
55+
},
56+
{
57+
label: 'Video',
58+
disabled: false,
59+
value: 'video'
60+
},
61+
{
62+
label: 'Audio',
63+
disabled: false,
64+
value: 'audio'
65+
},
66+
{
67+
label: 'Filter',
68+
disabled: false,
69+
value: 'filters'
70+
}
71+
],
72+
73+
didInsertElement() {
74+
let dataInput = $('#datafile').first()[0];
75+
dataInput.onchange = (event) => {
76+
const file = event.target.files[0];
77+
const extension = this.getExtension(file);
78+
assert(`File must be ${fileTypes.join(', ')}`, fileTypes.includes(extension));
79+
if (extension === 'xlsx') {
80+
reader.onload = () => {
81+
set(this, 'data', SpreadsheetData.init());
82+
};
83+
reader.readAsBinaryString(file);
84+
85+
} else {
86+
reader.onload = () => {
87+
set(this, 'data', GeojsonData.init());
88+
set(this, 'table', get(this, 'data.table'));
89+
};
90+
reader.readAsText(file);
91+
}
92+
};
93+
},
94+
95+
actions: {
96+
layerAdded(feature) {
97+
if (feature.layer._latlng) {
98+
get(this, 'bounds').push([feature.layer._latlng.lat, feature.layer._latlng.lng]);
99+
}
100+
feature.layer._map.fitBounds(get(this, 'bounds'));
101+
},
102+
103+
updateLocation(feature, event) {
104+
let location = event.target.getLatLng();
105+
setProperties(feature, {
106+
lat: location.lat,
107+
lng: location.lng
108+
});
109+
},
110+
111+
updateFeature(attribute, feature, event) {
112+
set(feature, `${attribute}`, event);
113+
},
114+
115+
mapAttribute(type, column) {
116+
const data = get(this, 'sheetJson');
117+
if (type === 'description') {
118+
$(`<p>${data[0][column.target.value]}</p>`).appendTo('#preview-description');
119+
}
120+
get(this, 'attributeMap')[type] = column.target.value
121+
}
122+
}
123+
});

addon/components/parse-spreadsheet.js

Lines changed: 31 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,28 @@
1-
import Ember from 'ember';
1+
import { assert } from '@ember/debug';
2+
import { inject as service } from '@ember/service';
3+
import { oneWay } from '@ember/object/computed';
4+
import $ from 'jquery';
5+
import Component from '@ember/component';
6+
import { set, get } from '@ember/object';
7+
import { A } from '@ember/array';
28
import Table from 'ember-light-table'
39
import layout from '../templates/components/parse-spreadsheet';
4-
/* globals XLSX, L */
10+
import GeoJson from '../lib/geojson';
11+
import XLSX from 'xlsx';
512

6-
const {
7-
Component,
8-
computed,
9-
get,
10-
set,
11-
inject: {
12-
service
13-
}
14-
} = Ember;
15-
16-
const GeoJson = Ember.Object.extend({
17-
type: "Feature",
18-
geometry: {
19-
type: "Point",
20-
coordinates: [0, 1]
21-
},
22-
properties: {
23-
24-
}
25-
});
26-
27-
export default Component.extend({
13+
const GeojsonParseSpreadsheetComponent = Component.extend({
2814
store: service(),
2915
layout,
3016
model: null,
3117
type: 'file',
32-
attributeBindings: ['type', 'value'],
33-
34-
getExtension(fileName) {
35-
var dotIndex = fileName.lastIndexOf('.');
36-
if (dotIndex >= 0) {
37-
return fileName.substring(dotIndex + 1).toLowerCase();
38-
}
39-
return '';
40-
},
41-
42-
lat: 33.7489954,
43-
lng: -84.3879824,
44-
zoom: 10,
18+
attributeBindings: A(['type', 'value']),
4519

46-
isLoading: computed.oneWay('fetchRecords.isRunning'),
20+
isLoading: oneWay('fetchRecords.isRunning'),
4721

48-
table: null,
22+
init() {
23+
this._super(...arguments);
24+
},
4925

50-
tabelColumns: [],
5126
geoJsonFeatures: [],
5227

5328
features: [],
@@ -62,37 +37,6 @@ export default Component.extend({
6237
filters: null
6338
},
6439

65-
onEachFeature(feature, layer) {
66-
// TODO Add all attributes.
67-
// Content for map preview popups. Does not show all attributes.
68-
let popUpContent = `
69-
<table>
70-
<tr>
71-
<td>
72-
Title
73-
</td>
74-
<td>
75-
${feature.properties.title}
76-
</td>
77-
</tr>
78-
<tr>
79-
<td>
80-
Description
81-
</td>
82-
<td>
83-
${feature.properties.description}
84-
</td>
85-
</tr>
86-
</table>`;
87-
layer.bindPopup(popUpContent);
88-
},
89-
90-
pointToLayer(stankonia, latlng) {
91-
return L.marker(latlng, {
92-
draggable: true
93-
});
94-
},
95-
9640
// types: {label: 'Title'},
9741
types: [{
9842
label: 'Title',
@@ -190,43 +134,41 @@ export default Component.extend({
190134
});
191135
},
192136

193-
loadSpreadsheet(file) { // Receive data through loading a spreadsheet.
137+
loadSpreadsheet(file) {
194138
const extension = this.getExtension(file.name);
195139
const reader = new FileReader();
196-
alert(extension)
197140

198-
if (extension == 'xlsx') {
141+
// TODO: Should we accept `xls` files?
142+
if (extension === 'xlsx') {
199143
reader.onload = (event) => {
200144
this.xlsx2json(event.target.result)
201145
};
202146
reader.readAsBinaryString(file);
203147
} else if (extension == 'csv') {
204148
reader.onload = (/*event*/) => {
205-
//
149+
// TODO: Do we need to support CSV?
206150
};
207151
reader.readAsText(file);
208152
}
153+
154+
assert('File must end in \'.xlsx\'', extension === 'xlsx');
209155
},
210156

211157
didInsertElement() {
212-
this._super(...arguments);
213-
this.setupParseSpreadsheet();
214-
let fileInput = this.$('#sheet')[0];
158+
// this._super(...arguments);
159+
// this.setupParseSpreadsheet();
160+
// let fileInput = this.$('#sheet')[0];
215161

216-
fileInput.onchange = (event) => {
217-
this.loadSpreadsheet(event.target.files[0]);
218-
};
162+
// fileInput.onchange = (event) => {
163+
// this.loadSpreadsheet(event.target.files[0]);
164+
// };
219165
},
220166

221167
willDestroyElement() {
222168
this._super(...arguments);
223169
this.teardownParseSpreadsheet();
224170
},
225171

226-
setupParseSpreadsheet() {
227-
//
228-
},
229-
230172
teardownParseSpreadsheet() {
231173
this.destroy();
232174
},
@@ -239,9 +181,9 @@ export default Component.extend({
239181

240182
data.forEach((d) => {
241183
let feature = {
242-
type: "Feature",
184+
type: 'Feature',
243185
geometry: {
244-
type: "Point",
186+
type: 'Point',
245187
coordinates: [d[attributeMap['lng']], d[attributeMap['lat']]]
246188
},
247189
properties: {
@@ -271,3 +213,5 @@ export default Component.extend({
271213
}
272214
}
273215
});
216+
217+
export default GeojsonParseSpreadsheetComponent;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Component from '@ember/component';
2+
import { set } from '@ember/object';
3+
import layout from '../templates/components/table-cell-edit';
4+
5+
export default Component.extend({
6+
layout,
7+
tagName: ['input'],
8+
attributeBindings: ['value'],
9+
10+
change(event) {
11+
set(this, `row.${this.column.label}`, event.target.value);
12+
}
13+
});

addon/lib/geo-data.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { A } from '@ember/array';
2+
import { dasherize } from '@ember/string';
3+
import Table from 'ember-light-table';
4+
5+
const GeoData = ({
6+
setTableColumns(features) {
7+
let tableColumn = A([]);
8+
Object.keys(features[0]).forEach((col) => {
9+
tableColumn.push({
10+
label: col,
11+
resizable: true,
12+
valuePath: col,
13+
sortable: false,
14+
value: dasherize(col),
15+
cellComponent: 'table-cell-edit'
16+
})
17+
});
18+
return tableColumn;
19+
},
20+
21+
setTable(features) {
22+
return new Table(this.setTableColumns(features), features, {
23+
enableSync: true
24+
});
25+
}
26+
});
27+
28+
export default GeoData;

0 commit comments

Comments
 (0)