/*global saveAs, saveTextAs */
|
define([
|
'dojo/_base/declare',
|
'dojo/_base/lang',
|
'dojo/_base/array',
|
'dojo/json',
|
'dojo/Deferred',
|
'esri/tasks/query',
|
'esri/tasks/QueryTask',
|
'esri/tasks/FeatureSet',
|
'esri/graphic',
|
'esri/SpatialReference',
|
'esri/tasks/ProjectParameters',
|
'esri/config',
|
'esri/geometry/webMercatorUtils',
|
'jimu/LayerInfos/LayerInfos',
|
'./utils',
|
'./GeojsonConverters'],
|
function(declare, lang, array, JSON, Deferred, Query, QueryTask, FeatureSet, Graphic,
|
SpatialReference, ProjectParameters, esriConfig, webMercatorUtils, LayerInfos,
|
jimuUtils, GeojsonConverters) {
|
/* global dojo */
|
var mo = {};
|
|
/**
|
* options should contain the following attributes:
|
* 1. type: type of the data source, can be mo.TYPE_TABLE or mo.TYPE_FEATURESET
|
* 2. filename: output file name
|
* 3. url: url of the data source if it is fetched remotely
|
* 4. data: data source if it is local.
|
* You can choose to use url or data, but not both of them.
|
*/
|
mo.createDataSource = function(options){
|
if(options.type === mo.TYPE_TABLE){
|
return new TableDS(options);
|
}else if(options.type === mo.TYPE_FEATURESET){
|
return new FeatureSetDS(options);
|
}else{
|
return null;
|
}
|
};
|
|
mo.TYPE_TABLE = 'table';
|
mo.TYPE_FEATURESET = 'FeatureSet';
|
mo.FORMAT_CSV = 'CSV';
|
mo.FORMAT_FEATURESET = 'FeatureSet';
|
mo.FORMAT_GEOJSON = 'GeoJSON';
|
|
var DataSource = declare(null, {
|
filename: undefined,
|
suffix: '.txt',
|
format: undefined,
|
nls: undefined,
|
|
constructor: function(){
|
this.nls = window.jimuNls.exportTo;
|
},
|
|
/**
|
* Calculate the string content of the exported data.
|
* Must be implemented by sub class.
|
*/
|
getExportString: function(){
|
|
},
|
|
/**
|
* Return the supported format array. Each item contains two attributes:
|
* label: the nls string for this format
|
* value: used to invoke the setFormat() method
|
*/
|
getSupportExportFormats: function(){
|
|
},
|
|
setFormat: function(value){
|
this.format = value;
|
},
|
|
download: function(){
|
this.getExportString().then(lang.hitch(this, function(str){
|
download(this.filename + this.suffix, str);
|
}));
|
},
|
|
exportToPortal: function(format, itemName){
|
/*jshint unused: false*/
|
}
|
});
|
|
/**
|
* options should include:
|
* featureSet or url,
|
* filename
|
*/
|
var FeatureSetDS = declare(DataSource, {
|
featureSet: null,
|
|
constructor: function(options){
|
this.inherited(arguments);
|
|
this.featureSet = options.data;
|
this.url = options.url;
|
this.filename = options.filename;
|
},
|
|
getExportString: function(){
|
if(this.format === mo.FORMAT_CSV){
|
this.suffix = '.csv';
|
return this._getAsCSVString();
|
}else if(this.format === mo.FORMAT_FEATURESET){
|
this.suffix = '.json';
|
return this._getAsFeatureSetString();
|
}else if(this.format === mo.FORMAT_GEOJSON){
|
this.suffix = '.geojson';
|
return this._getAsGeoJsonString();
|
}else{
|
var ret = new Deferred();
|
ret.resolve('');
|
return ret;
|
}
|
},
|
|
getSupportExportFormats: function(){
|
return [{
|
value: mo.FORMAT_CSV,
|
label: this.nls.toCSV
|
}, {
|
value: mo.FORMAT_FEATURESET,
|
label: this.nls.toFeatureCollection
|
}, {
|
value: mo.FORMAT_GEOJSON,
|
label: this.nls.toGeoJSON
|
}];
|
},
|
|
_getFeatureSet: function(){
|
var ret = new Deferred();
|
|
if(this.featureSet){
|
ret.resolve(this.featureSet);
|
}else if(this.url){
|
var query = new Query();
|
query.returnGeometry = true;
|
query.outFields = ['*'];
|
|
this.queryTask = new QueryTask(this.url);
|
this.queryTask.execute(query, lang.hitch(this, function(fs){
|
ret.resolve(fs);
|
}), lang.hitch(this, function(){
|
ret.resolve(null);
|
}));
|
}else{
|
ret.resolve(null);
|
}
|
|
return ret;
|
},
|
|
_getSpatialReference: function(featureset) {
|
if (featureset.spatialReference) {
|
return featureset.spatialReference;
|
}
|
// Get spatial refrence from graphics
|
var sf;
|
array.some(featureset.features, function(feature) {
|
if (feature.geometry && feature.geometry.spatialReference){
|
sf = feature.geometry.spatialReference;
|
return true;
|
}
|
});
|
return sf;
|
},
|
|
_projectToWGS84: function(featureset) {
|
var ret = new Deferred();
|
var sf = this._getSpatialReference(featureset);
|
if (!sf) {
|
ret.resolve([]);
|
} else {
|
var wkid = parseInt(sf.wkid, 10);
|
|
if (wkid === 4326) {
|
ret.resolve(featureset);
|
} else if (sf.isWebMercator()) {
|
var outFeatureset = new FeatureSet();
|
var features = [];
|
array.forEach(featureset.features, function(feature) {
|
var g = new Graphic();
|
g.attributes = feature.attributes;
|
g.geometry = webMercatorUtils.webMercatorToGeographic(feature.geometry);
|
features.push(g);
|
});
|
outFeatureset.features = features;
|
ret.resolve(outFeatureset);
|
} else {
|
var params = new ProjectParameters();
|
params.geometries = array.map(featureset.features, function(feature) {
|
return feature.geometry;
|
});
|
params.outSR = new SpatialReference(4326);
|
|
var gs = esriConfig && esriConfig.defaults && esriConfig.defaults.geometryService;
|
var existGS = gs && gs.declaredClass === "esri.tasks.GeometryService";
|
if (!existGS) {
|
gs = jimuUtils.getArcGISDefaultGeometryService();
|
}
|
|
gs.project(params).then(function(geometries) {
|
var outFeatureset = new FeatureSet();
|
var features = [];
|
array.forEach(featureset.features, function(feature, i) {
|
var g = new Graphic();
|
g.attributes = feature.attributes;
|
g.geometry = geometries[i];
|
features.push(g);
|
});
|
outFeatureset.features = features;
|
ret.resolve(outFeatureset);
|
}, function(err) {
|
console.error(err);
|
ret.resolve([]);
|
});
|
}
|
}
|
return ret;
|
},
|
|
_getAsFeatureSetString: function(){
|
return this._getFeatureSet().then(lang.hitch(this, function(fs){
|
var str = '';
|
if(fs){
|
var jsonObj = fs.toJson();
|
if(jsonObj){
|
str = JSON.stringify(jsonObj);
|
}
|
}
|
return str;
|
}));
|
},
|
|
_getAsGeoJsonString: function(){
|
return this._getFeatureSet()
|
.then(lang.hitch(this, function(fs) {
|
return this._projectToWGS84(fs);
|
}))
|
.then(lang.hitch(this, function(fs){
|
var str = '';
|
if(fs && fs.features && fs.features.length > 0){
|
var jsonObj = {
|
type: 'FeatureCollection',
|
features: []
|
};
|
array.forEach(fs.features, function(feature) {
|
jsonObj.features.push(GeojsonConverters.arcgisToGeoJSON(feature));
|
});
|
str = JSON.stringify(jsonObj);
|
}
|
return str;
|
}));
|
},
|
|
_getAsCSVString: function(){
|
return this._getFeatureSet().then(lang.hitch(this, function(fs){
|
var str = '';
|
if(fs){
|
str = this._createCSVFromFeatureSet(fs);
|
}
|
return str;
|
}));
|
},
|
|
_createCSVFromFeatureSet: function(featureSet){
|
var fields = this._generateFields(featureSet);
|
|
var datas = array.map(featureSet.features, function(feature){
|
var attributes = lang.clone(feature.attributes);
|
if (featureSet.geometryType === 'esriGeometryPoint' ||
|
featureSet.geometryType === 'point') {
|
if (feature.geometry) {
|
attributes.x = feature.geometry.x;
|
attributes.y = feature.geometry.y;
|
if (feature.geometry.spatialReference &&
|
feature.geometry.spatialReference.wkid) {
|
attributes.wkid = feature.geometry.spatialReference.wkid;
|
}
|
}
|
}
|
return attributes;
|
});
|
|
return createCSVString(fields, datas);
|
},
|
|
_generateFields: function(featureSet) {
|
var feature = featureSet.features[0];
|
var fields, item, layerId;
|
|
if(feature._layer) {
|
fields = feature._layer.fields;
|
layerId = feature._layer.id;
|
}
|
|
fields = lang.clone(fields || featureSet.fields);
|
if(!fields || fields.length === 0){
|
fields = [];
|
var attributes = feature.attributes;
|
for(item in attributes){
|
if(attributes.hasOwnProperty(item)){
|
fields.push({
|
name: item
|
});
|
}
|
}
|
}
|
|
var layerInfos = LayerInfos.getInstanceSync();
|
var layerInfo = layerInfos.getLayerInfoById(layerId);
|
if (layerInfo) {
|
var popupInfo = layerInfo.getPopupInfo();
|
if (!popupInfo) {
|
// Try another way to get popupInfo
|
popupInfo = layerInfo.layerObject.infoTemplate && layerInfo.layerObject.infoTemplate.info;
|
}
|
array.forEach(fields, lang.hitch(this, function(field) {
|
field.fieldInfo = this._findFieldInfo(popupInfo, field.name);
|
}));
|
}
|
|
if(featureSet.fieldAliases){
|
//Set of name-value pairs for the attribute's field and alias names.
|
array.forEach(fields, function(field) {
|
if (featureSet.fieldAliases[field.name]) {
|
field.alias = featureSet.fieldAliases[field.name];
|
}
|
});
|
}
|
if (featureSet.geometryType === 'esriGeometryPoint' ||
|
featureSet.geometryType === 'point') {
|
fields.push({
|
name: 'x',
|
type: 'esriFieldTypeDouble',
|
alias: 'x'
|
});
|
fields.push({
|
name: 'y',
|
type: 'esriFieldTypeDouble',
|
alias: 'y'
|
});
|
fields.push({
|
name: 'wkid',
|
type: 'esriFieldTypeInteger',
|
alias: 'wkid'
|
});
|
}
|
return fields;
|
},
|
|
_findFieldInfo: function(popupInfo, fieldName) {
|
if (!popupInfo) {
|
return null;
|
}
|
var fieldInfo;
|
array.some(popupInfo.fieldInfos, function(info) {
|
if (info.fieldName === fieldName) {
|
fieldInfo = info;
|
return true;
|
}
|
});
|
return fieldInfo;
|
}
|
});
|
|
/**
|
* options should include:
|
* table or url,
|
* filename
|
*/
|
var TableDS = declare(DataSource, {
|
table: null,
|
|
constructor: function(options){
|
this.inherited(arguments);
|
this.table = options.data;
|
this.url = options.url;
|
this.filename = options.filename;
|
},
|
|
getExportString: function(){
|
if(this.format === mo.FORMAT_CSV){
|
this.suffix = '.csv';
|
return this._getAsCSVString();
|
}else{
|
var ret = new Deferred();
|
ret.resolve('');
|
return ret;
|
}
|
},
|
|
getSupportExportFormats: function(){
|
return [{
|
value: mo.FORMAT_CSV,
|
label: this.nls.toCSV
|
}];
|
},
|
|
_getTableData: function(){
|
var ret = new Deferred();
|
|
if(this.table){
|
ret.resolve(this.table);
|
}else if(this.url){
|
var query = new Query();
|
query.outFields = ['*'];
|
|
this.queryTask = new QueryTask(this.url);
|
this.queryTask.execute(query, lang.hitch(this, function(data){
|
var table = {};
|
table.fields = data.fields;
|
table.datas = array.map(data.features, function(feature){
|
return feature.attributes;
|
});
|
ret.resolve(table);//RecordSet
|
}), lang.hitch(this, function(){
|
ret.resolve(null);
|
}));
|
}else{
|
ret.resolve(null);
|
}
|
|
return ret;
|
},
|
|
_getAsCSVString: function(){
|
return this._getTableData().then(lang.hitch(this, function(tableData){
|
var str = '', BOM = '\uFEFF';
|
if(tableData){
|
str = BOM + createCSVString(tableData.fields, tableData.datas);
|
}
|
return str;
|
}));
|
}
|
});
|
|
/*************
|
datas: Object[], Object properties depend on fields' name
|
fields: String[] | Object[]
|
String[]: field name array,
|
Object[]: Object is the same with layer definition
|
{
|
name:
|
type:
|
alias:
|
}
|
**************/
|
function createCSVString(fields, datas){
|
var textField = '"';
|
var content = '';
|
var len = datas.length,
|
n = fields.length,
|
comma = '',
|
value = '',
|
feature;
|
try {
|
array.forEach(fields, function(_field) {
|
if(typeof _field === 'string'){
|
content = content + comma + _field;
|
}else{
|
content = content + comma + (_field.alias || _field.name);
|
}
|
|
comma = ',';
|
});
|
|
content = content + '\r\n';
|
|
for (var i = 0; i < len; i++) {
|
comma = '';
|
feature = datas[i];
|
for (var m = 0; m < n; m++) {
|
var _field = fields[m];
|
value = feature[typeof _field === 'string'? _field: _field.name];
|
if (!value && typeof value !== 'number') {
|
value = '';
|
}
|
if (value) {
|
if(_field.type === 'esriFieldTypeDate'){
|
value = jimuUtils.localizeDateByFieldInfo(value, _field.fieldInfo);
|
}else if(_field.fieldInfo &&
|
(_field.type === 'esriFieldTypeDouble' ||
|
_field.type === 'esriFieldTypeSingle' ||
|
_field.type === 'esriFieldTypeInteger' ||
|
_field.type === 'esriFieldTypeSmallInteger')) {
|
value = jimuUtils.localizeNumberByFieldInfo(value, _field.fieldInfo);
|
}
|
}
|
|
if (value && /[",\r\n]/g.test(value)) {
|
value = textField + value.replace(/(")/g, '""') + textField;
|
}
|
content = content + comma + value;
|
comma = ',';
|
}
|
content = content + '\r\n';
|
}
|
return content;
|
} catch (err) {
|
return '';
|
}
|
}
|
|
function download(filename, text) {
|
if (dojo.isIE < 10) {
|
saveTextAs(text, filename, 'utf-8');
|
}else{
|
var blob = new Blob([text], {type: 'text/plain;charset=utf-8'});
|
// Use saveAs(blob, name, true) to turn off the auto-BOM stuff
|
saveAs(blob, filename, true);
|
}
|
}
|
|
return mo;
|
});
|