/* global define, console, window, $, i18n, RAMP, t, TimelineLite */
/**
* @module RAMP
* @submodule FilterManager
* @main FilterManager
*/
/**
* Creates a choice tree for adding datasets.
*
* ####Imports RAMP Modules:
* {{#crossLink "PopupManager"}}{{/crossLink}}
* {{#crossLink "DataLoader"}}{{/crossLink}}
* {{#crossLink "Theme"}}{{/crossLink}}
* {{#crossLink "Map"}}{{/crossLink}}
* {{#crossLink "LayerLoader"}}{{/crossLink}}
* {{#crossLink "GlobalStorage"}}{{/crossLink}}
* {{#crossLink "StepItem"}}{{/crossLink}}
* {{#crossLink "Util"}}{{/crossLink}}
* {{#crossLink "TmplHelper"}}{{/crossLink}}
* {{#crossLink "TmplUtil"}}{{/crossLink}}
* {{#crossLink "Array"}}{{/crossLink}}
* {{#crossLink "Dictionary"}}{{/crossLink}}
* {{#crossLink "Bricks"}}{{/crossLink}}
*
* ####Uses RAMP Templates:
* {{#crossLink "templates/filter_manager_template.json"}}{{/crossLink}}
*
* @class DataLoaderGui
* @static
* @uses dojo/lang
*/
define([
/* Dojo */
'dojo/_base/lang',
/* Text */
'dojo/text!./templates/filter_manager_template.json',
/* Ramp */
'utils/popupManager', 'ramp/dataLoader', 'ramp/theme', 'ramp/map', 'ramp/layerLoader', 'ramp/globalStorage', 'ramp/stepItem',
/* Util */
'utils/util', 'utils/tmplHelper', 'utils/tmplUtil', 'utils/array', 'utils/dictionary', 'utils/bricks'
],
function (
lang,
filter_manager_template,
PopupManager, DataLoader, Theme, RampMap, LayerLoader, GlobalStorage, StepItem,
UtilMisc, TmplHelper, TmplUtil, UtilArray, UtilDict, Bricks
) {
'use strict';
var rootNode,
addDatasetToggle,
addDatasetContainer,
layerList,
layerToggles,
filterToggles,
symbologyPreset = {},
choiceTree,
choiceTreeCallbacks,
choiceTreeErrors,
stepLookup = {},
addDatasetPopup,
transitionDuration = 0.5,
templates = JSON.parse(TmplHelper.stringifyTemplate(filter_manager_template));
/**
* A collection of callbacks used by the choice tree.
*
* @property choiceTreeCallbacks
* @private
* @type {Object}
*/
choiceTreeCallbacks = {
/**
* A callback that advances the choice tree to the specified child step of the supplied step item.
*
* @method choiceTreeCallbacks.simpleAdvance
* @private
* @param {StepItem} step step item to advance from
* @param {Object} data data from the choice brick in step item
* @param {Object} targetChildData data to be passed to the step being advanced to
*/
simpleAdvance: function (step, data, targetChildData) {
step.advance(data.selectedChoice, targetChildData);
},
/**
* A callback that retreats part of the choice tree to the step item specified.
*
* @method choiceTreeCallbacks.simpleCancel
* @private
* @param {StepItem} step step item to retreat to
* @param {Object} data data from the callback function
*/
simpleCancel: function (step, data) {
console.log('Step cancel click:', this, step, data);
if (step.isCompleted()) {
step.retreat();
} else {
step.clearStep();
}
},
/**
* A callback to guess the service type from the service url provided in step data.
*
* @method choiceTreeCallbacks.serviceTypeStepGuess
* @private
* @param {StepItem} step step item to guess service type on
* @param {[type]} data data from the callback function
*/
serviceTypeStepGuess: function (step, data) {
var value = data.inputValue,
serviceTypeBrick = step.contentBricks.serviceType,
guess = '';
// make a guess if it's a feature or wms server only if the user hasn't already selected the type
if (!serviceTypeBrick.isUserSelected()) {
if (value.match(/ArcGIS\/rest\/services/ig)) {
guess = 'featureServiceAttrStep';
} else if (value.match(/wms/ig)) {
guess = 'wmsServiceAttrStep';
}
serviceTypeBrick.setChoice(guess);
}
},
/**
* A callback to guess the file type from the file url or file object provided in step data.
*
* @method choiceTreeCallbacks.fileTypeStepGuess
* @private
* @param {StepItem} step step item to guess file type on
* @param {[type]} data data from the callback function
*/
fileTypeStepGuess: function (step, data) {
var fileName = data.inputValue,
serviceFileBrick = step.contentBricks.fileType,
guess = '';
if (!serviceFileBrick.isUserSelected() && !serviceFileBrick.isUserEntered) {
if (fileName.endsWith('.csv')) {
guess = 'csvFileAttrStep';
} else if (fileName.endsWith('.json')) {
guess = 'geojsonFileAttrStep';
} else if (fileName.endsWith('.zip')) {
guess = 'shapefileFileAttrStep';
}
serviceFileBrick.setChoice(guess);
}
}
};
/**
* Create choice tree structure. This function is executed as part of the module initialization so that i18n strings can be properly loaded
*
* @method prepareChoiceTreeStructure
* @private
*/
function prepareChoiceTreeStructure() {
/**
* A collection of precanned error messages that are used by the choice tree.
*
* @property choiceTreeErrors
* @private
* @type {Object}
*/
choiceTreeErrors = {
base: {
type: 'error',
header: 'Cannot load',
message: 'You have IE9?'
}
};
choiceTreeErrors.featureError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerFeature')
});
choiceTreeErrors.wmsError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerWMS')
});
choiceTreeErrors.fileError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerFile')
});
choiceTreeErrors.geojsonError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerGeojson')
});
choiceTreeErrors.csvError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerCSV')
});
choiceTreeErrors.shapefileError = lang.mixin({}, choiceTreeErrors.base, {
header: i18n.t('addDataset.error.headerShapefile')
});
/**
* A choice tree config object
*
* Config has a simple tree structure, with content being an array of Brick object to be placed inside a StepItem.
*
* @property choiceTree
* @private
* @type {Object}
*/
choiceTree = {
// step for choosing between adding a service or a file
id: 'sourceTypeStep',
content: [
{
id: 'sourceType',
type: Bricks.ChoiceBrick,
config: {
header: i18n.t('addDataset.dataSource'),
instructions: i18n.t('addDataset.help.dataSource'),
choices: [
{
key: 'serviceTypeStep',
value: i18n.t('addDataset.dataSourceService')
},
{
key: 'fileTypeStep',
value: i18n.t('addDataset.dataSourceFile')
}
]
},
on: [
{
eventName: Bricks.ChoiceBrick.event.CHANGE,
//expose: { as: 'advance' },
callback: choiceTreeCallbacks.simpleAdvance
}
]
}
],
children: [
{
// step for choosing between feature and wms service and providing a service url
id: 'serviceTypeStep',
content: [
{
id: 'serviceURL',
type: Bricks.SimpleInputBrick,
config: {
header: i18n.t('addDataset.serviceLayerURL'),
instructions: i18n.t('addDataset.help.serviceURL'),
placeholder: i18n.t('addDataset.serviceLayerURLPlaceholder'),
freezeStates: [Bricks.Brick.state.SUCCESS]
},
on: [
{
eventName: Bricks.SimpleInputBrick.event.CHANGE,
callback: choiceTreeCallbacks.serviceTypeStepGuess
}
]
},
{
id: 'serviceType',
type: Bricks.ChoiceBrick,
config: {
//template: 'template_name', //optional, has a default
header: i18n.t('addDataset.serviceType'),
instructions: i18n.t('addDataset.help.serviceType'),
choices: [
{
key: 'featureServiceAttrStep',
value: i18n.t('addDataset.serviceTypeFeature')
},
{
key: 'wmsServiceAttrStep',
value: i18n.t('addDataset.serviceTypeWMS')
}
],
freezeStates: [Bricks.Brick.state.SUCCESS]
}
},
{
id: 'serviceTypeOkCancel',
type: Bricks.OkCancelButtonBrick,
config: {
okLabel: i18n.t('addDataset.connect'),
okFreezeStates: [
Bricks.Brick.state.SUCCESS,
Bricks.Brick.state.ERROR
],
cancelLabel: i18n.t('addDataset.cancel'),
//cancelFreezeStates: false,
reverseOrder: true,
required: [
{
id: Bricks.OkCancelButtonBrick.okButtonId,
type: 'all',
check: ['serviceType', 'serviceURL']
},
{
id: Bricks.OkCancelButtonBrick.cancelButtonId,
type: 'any',
check: ['serviceType', 'serviceURL']
}
]
},
on: [
/*{
eventName: Bricks.OkCancelButtonBrick.event.CLICK,
callback: function (step, data) {
console.log('Just Click:', this, step, data);
}
},*/
{
eventName: Bricks.OkCancelButtonBrick.event.OK_CLICK,
// connect to feature service
callback: function (step/*, data*/) {
var promise,
handle = delayLoadingState(step, 100),
bricksData = step.getData().bricksData,
serviceTypeValue = bricksData.serviceType.selectedChoice,
serviceUrlValue = bricksData.serviceURL.inputValue.trim(); // trimming spaces from service url string
switch (serviceTypeValue) {
case 'featureServiceAttrStep':
// get data from feature layer endpoint
promise = DataLoader.getFeatureLayer(serviceUrlValue);
promise.then(function (data) {
// get data from feature layer's legend endpoint
var layerInRampLODRange = RampMap.layerInLODRange(data.maxScale, data.minScale);
if (!layerInRampLODRange) {
handleFailure(step, handle, {
serviceType:
lang.mixin(choiceTreeErrors.featureError, {
message: i18n.t('addDataset.error.messageFeatureOutsideZoomRange')
})
});
} else {
var legendPromise = DataLoader.getFeatureLayerLegend(serviceUrlValue);
legendPromise.then(function (legendLookup) {
var fieldOptions;
window.clearTimeout(handle);
data.legendLookup = legendLookup;
// TODO: when field name aliases are available, change how the dropdown values are generated
fieldOptions = data.fields.map(function (field) { return { value: field, text: field }; });
// no fields available; likely this is not a Feature service
if (!fieldOptions || fieldOptions.length === 0) {
handleFailure(step, handle, {
serviceType:
lang.mixin(choiceTreeErrors.featureError, {
message: i18n.t('addDataset.error.messageFeatureInvalid')
})
});
} else {
choiceTreeCallbacks.simpleAdvance(step, bricksData.serviceType, {
stepData: data,
bricksData: {
primaryAttribute: {
options: fieldOptions
}
}
});
}
}, function (event) {
console.error(event);
handleFailure(step, handle, {
serviceType:
lang.mixin(choiceTreeErrors.featureError, {
message: i18n.t('addDataset.error.messageFeatureLegend')
})
});
});
}
}, function (event) {
// error connection to service
console.error(event);
handleFailure(step, handle, {
serviceURL:
lang.mixin(choiceTreeErrors.featureError, {
message: i18n.t('addDataset.error.messageFeatureConnect')
})
});
});
break;
case 'wmsServiceAttrStep':
// get data from wms endpoint
promise = DataLoader.getWmsLayerList(serviceUrlValue);
promise.then(function (data) {
var layerOptions;
window.clearTimeout(handle);
// TODO: when field name aliases are available, change how the dropdown values are generated
layerOptions = data.layers.map(function (layer) { return { value: layer.name, text: layer.desc }; });
// no layer names available; likely this is not a WMS service
if (!layerOptions || layerOptions.length === 0) {
handleFailure(step, handle, {
serviceType:
lang.mixin(choiceTreeErrors.wmsError, {
message: i18n.t('addDataset.error.messageWMSInvalid')
})
});
} else {
choiceTreeCallbacks.simpleAdvance(step, bricksData.serviceType, {
stepData: {
wmsData: data,
wmsUrl: serviceUrlValue
},
bricksData: {
layerName: {
options: layerOptions
}
}
});
}
}, function (event) {
console.error(event);
handleFailure(step, handle, {
serviceURL:
lang.mixin(choiceTreeErrors.wmsError, {
message: i18n.t('addDataset.error.messageWMSConnect')
})
});
});
break;
}
}
//expose: { as: 'advance' }
},
{
eventName: Bricks.OkCancelButtonBrick.event.CANCEL_CLICK,
//expose: { as: 'retreat' },
callback: choiceTreeCallbacks.simpleCancel
}
]
}
],
children: [
{
id: 'featureServiceAttrStep',
content: [
{
id: 'primaryAttribute',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.featurePrimaryAttribute'),
header: i18n.t('addDataset.primaryAttribute')
}
},
{
id: 'addDataset',
type: Bricks.ButtonBrick,
config: {
label: i18n.t('addDataset.addDatasetButton'),
containerClass: 'button-brick-container-main',
buttonClass: 'btn-primary'
},
on: [
{
eventName: Bricks.ButtonBrick.event.CLICK,
// add feature service layer to the map
callback: function (step /*,data*/) {
var data = step.getData(),
bricksData = data.bricksData,
layerData = data.stepData,
newConfig = { //make feature layer config.
id: LayerLoader.nextId(),
displayName: layerData.layerName,
nameField: bricksData.primaryAttribute.dropDownValue,
datagrid: DataLoader.createDatagridConfig(layerData.fields, layerData.aliasMap),
symbology: DataLoader.createSymbologyConfig(layerData.renderer, layerData.legendLookup),
url: layerData.layerUrl,
aliasMap: layerData.aliasMap
},
featureLayer;
//TODO: set symbology and colour on feature layer (obj.data)
newConfig = GlobalStorage.applyFeatureDefaults(newConfig);
//make layer
featureLayer = RampMap.makeFeatureLayer(newConfig, true);
RAMP.config.layers.feature.push(newConfig);
LayerLoader.loadLayer(featureLayer);
addDatasetPopup.close();
//mainPopup.close();
}
//expose: { as: 'ADD_DATASET' }
}
]
}
]
},
{
id: 'wmsServiceAttrStep',
content: [
{
id: 'layerName',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.wmsLayerName'),
header: i18n.t('addDataset.layerName')
}
},
{
id: 'addDataset',
type: Bricks.ButtonBrick,
config: {
label: i18n.t('addDataset.addDatasetButton'),
containerClass: 'button-brick-container-main',
buttonClass: 'btn-primary'
},
on: [
{
eventName: Bricks.ButtonBrick.event.CLICK,
// add wms service layer to the map
callback: function (step /*,data*/) {
var data = step.getData(),
bricksData = data.bricksData,
stepData = data.stepData,
wmsLayerName = bricksData.layerName.dropDownValue,
wmsConfig,
layer,
wmsLayer;
layer = UtilArray.find(stepData.wmsData.layers,
function (l) {
return l.name === wmsLayerName;
}
);
wmsConfig = {
id: LayerLoader.nextId(),
displayName: layer.desc,
format: 'png',
layerName: wmsLayerName,
imageUrl: 'assets/images/wms.png',
url: stepData.wmsUrl,
legendMimeType: 'image/jpeg'
};
if (layer.queryable) {
wmsConfig.featureInfo = {
parser: 'stringParse',
mimeType: 'text/plain'
};
}
wmsConfig = GlobalStorage.applyWMSDefaults(wmsConfig);
wmsLayer = RampMap.makeWmsLayer(wmsConfig, true);
RAMP.config.layers.wms.push(wmsConfig);
LayerLoader.loadLayer(wmsLayer);
addDatasetPopup.close();
}
//expose: { as: 'ADD_DATASET' }
}
]
}
]
}
]
},
{
id: 'fileTypeStep',
content: [
{
id: 'fileOrFileURL',
type: Bricks.FileInputBrick,
config: {
//template: 'template_name', //optional, has a default
header: i18n.t('addDataset.fileOrURL'),
instructions: i18n.t('addDataset.help.fileOrURL'),
//label: 'Service URL', // optional, equals to header by default
placeholder: i18n.t('addDataset.fileOrURLPlaceholder'),
freezeStates: [Bricks.Brick.state.SUCCESS]
},
on: [
{
eventName: Bricks.FileInputBrick.event.CHANGE,
callback: choiceTreeCallbacks.fileTypeStepGuess
}
]
},
{
id: 'fileType',
type: Bricks.ChoiceBrick,
config: {
//template: 'template_name', //optional, has a default
instructions: i18n.t('addDataset.help.fileOrURLType'),
header: i18n.t('addDataset.fileType'),
choices: [
{
key: 'geojsonFileAttrStep',
value: i18n.t('addDataset.geojson')
},
{
key: 'csvFileAttrStep',
value: i18n.t('addDataset.csv')
},
{
key: 'shapefileFileAttrStep',
value: i18n.t('addDataset.shapefile')
}
],
freezeStates: [Bricks.Brick.state.SUCCESS]
}
},
{
id: 'fileTypeOkCancel',
type: Bricks.OkCancelButtonBrick,
config: {
okLabel: i18n.t('addDataset.load'),
okFreezeStates: [
Bricks.Brick.state.SUCCESS,
Bricks.Brick.state.ERROR
],
cancelLabel: i18n.t('addDataset.cancel'),
reverseOrder: true,
required: [
{
id: Bricks.OkCancelButtonBrick.okButtonId,
type: 'all',
check: ['fileType', 'fileOrFileURL']
},
{
id: Bricks.OkCancelButtonBrick.cancelButtonId,
type: 'any',
check: ['fileType', 'fileOrFileURL']
}
]
},
on: [
/*{
eventName: Bricks.OkCancelButtonBrick.event.CLICK,
callback: function (step, data) {
console.log('Just Click:', this, step, data);
}
},*/
{
eventName: Bricks.OkCancelButtonBrick.event.OK_CLICK,
// load and process files
callback: function (step/*, data*/) {
var promise,
handle = delayLoadingState(step, 100),
bricksData = step.getData().bricksData,
fileTypeValue = bricksData.fileType.selectedChoice,
fileValue = bricksData.fileOrFileURL.fileValue,
fileUrlValue = bricksData.fileOrFileURL.inputValue.trim(), // trimming spaces from file url string
fileName = bricksData.fileOrFileURL.fileName;
promise = DataLoader.loadDataSet({
url: fileValue ? null : fileUrlValue,
file: fileValue,
type: fileTypeValue === 'shapefileFileAttrStep' ? 'binary' : 'text'
});
promise.then(function (data) {
switch (fileTypeValue) {
case 'geojsonFileAttrStep':
var geojsonPromise = DataLoader.buildGeoJson(data);
geojsonPromise.then(function (featureLayer) {
var fieldOptions;
window.clearTimeout(handle);
// TODO: when field name aliases are available, change how the dropdown values are generated
fieldOptions = featureLayer.fields.map(function (field) { return { value: field.name, text: field.name }; });
// no layer names available; likely this is not a geojson file
if (!fieldOptions || fieldOptions.length === 0) {
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.geojsonError, {
message: i18n.t('addDataset.error.messageGeojsonInvalid')
})
});
} else {
choiceTreeCallbacks.simpleAdvance(step, bricksData.fileType, {
stepData: featureLayer,
bricksData: {
datasetName: {
inputValue: fileName
},
primaryAttribute: {
options: fieldOptions
}
}
});
}
}, function (event) {
//error building geojson
console.error(event);
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.geojsonError, {
message: i18n.t('addDataset.error.messageGeojsonBroken')
})
});
});
break;
case 'csvFileAttrStep':
var rows,
delimiter = UtilMisc.detectDelimiter(data),
guess,
primaryAttribute,
headers;
window.clearTimeout(handle);
rows = DataLoader.csvPeek(data, delimiter);
headers = rows[0].map(function (header) { return { value: header, text: header }; });
// no properties names available; likely this is not a csv file
if (!headers || headers.length === 0) {
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.csvError, {
message: i18n.t('addDataset.error.messageCSVInvalid')
})
});
} else if (!rows || rows.length < 2) {
// no rows, no layer
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.csvError, {
message: i18n.t('addDataset.error.messageCSVShort')
})
});
} else if (headers.length < 2) {
// only one column? are you kidding me?
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.csvError, {
message: i18n.t('addDataset.error.messageCSVThin')
})
});
} else {
guess = guessLatLong(rows);
// preselect primary attribute so it's not one of the lat or long guesses;
// if csv has only two fields (lat, long), select the first as primary
primaryAttribute = rows[0].filter(function (header) {
return header !== guess.lat && header !== guess.long;
})[0] || rows[0][0];
// TODO: if you can't detect lat or long make the user choose them, don't just select the first header from the list, maybe.
choiceTreeCallbacks.simpleAdvance(step, bricksData.fileType, {
stepData: {
csvData: data,
csvHeaders: rows[0],
csvDelimeter: delimiter
},
bricksData: {
datasetName: {
inputValue: fileName
},
primaryAttribute: {
options: headers,
selectedOption: primaryAttribute
},
latitude: {
options: headers,
selectedOption: guess.lat
},
longitude: {
options: headers,
selectedOption: guess.long
}
}
});
}
break;
case 'shapefileFileAttrStep':
var shapefilePromise = DataLoader.buildShapefile(data);
shapefilePromise.then(function (featureLayer) {
var fieldOptions;
window.clearTimeout(handle);
// TODO: when field name aliases are available, change how the dropdown values are generated
fieldOptions = featureLayer.fields.map(function (field) { return { value: field.name, text: field.name }; });
// no layer names available; likely this is not a geojson file
if (!fieldOptions || fieldOptions.length === 0) {
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.shapefileError, {
message: i18n.t('addDataset.error.messageShapefileInvalid')
})
});
} else {
choiceTreeCallbacks.simpleAdvance(step, bricksData.fileType, {
stepData: featureLayer,
bricksData: {
datasetName: {
inputValue: fileName
},
primaryAttribute: {
options: fieldOptions
}
}
});
}
}, function (event) {
// error to build shapefiles
console.error(event);
handleFailure(step, handle, {
fileType:
lang.mixin(choiceTreeErrors.shapefileError, {
message: i18n.t('addDataset.error.messageShapefileBroken')
})
});
});
break;
}
}, function (event) {
//error loading file
console.error(event);
handleFailure(step, handle, {
fileOrFileURL:
lang.mixin(choiceTreeErrors.fileError, {
message: i18n.t('addDataset.error.messageFileConnect')
})
});
});
}
//expose: { as: 'advance' },
},
{
eventName: Bricks.OkCancelButtonBrick.event.CANCEL_CLICK,
expose: { as: 'retreat' },
callback: choiceTreeCallbacks.simpleCancel
}
]
}
],
children: [
{
id: 'geojsonFileAttrStep',
content: [
{
id: 'datasetName',
type: Bricks.SimpleInputBrick,
config: {
instructions: i18n.t('addDataset.help.geojsonDatasetName'),
header: i18n.t('addDataset.datasetName')
}
},
{
id: 'primaryAttribute',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.geojsonPrimaryAttribute'),
header: i18n.t('addDataset.primaryAttribute')
}
},
{
id: 'color',
type: Bricks.ColorPickerBrick,
config: {
instructions: i18n.t('addDataset.help.geojsonColour'),
header: i18n.t('addDataset.colour')
}
},
{
id: 'addDataset',
type: Bricks.ButtonBrick,
config: {
label: i18n.t('addDataset.addDatasetButton'),
containerClass: 'button-brick-container-main',
buttonClass: 'btn-primary'
},
on: [
{
eventName: Bricks.ButtonBrick.event.CLICK,
// add geojson layer to the map
callback: function (step /*,data*/) {
var data = step.getData(),
bricksData = data.bricksData,
featureLayer = data.stepData,
iconTemplate = makeIconTemplate('a_d_icon_' + featureLayer.renderer._RAMP_rendererType, bricksData.color.hex);
DataLoader.enhanceFileFeatureLayer(featureLayer, {
//renderer: obj.style,
colour: [
bricksData.color.rgb_[0],
bricksData.color.rgb_[1],
bricksData.color.rgb_[2],
255
],
nameField: bricksData.primaryAttribute.dropDownValue,
icon: iconTemplate,
datasetName: bricksData.datasetName.inputValue,
fields: featureLayer.fields.map(function (field) { return field.name; })
});
LayerLoader.loadLayer(featureLayer);
addDatasetPopup.close();
}
}
]
}
]
},
{
id: 'csvFileAttrStep',
content: [
{
id: 'datasetName',
type: Bricks.SimpleInputBrick,
config: {
instructions: i18n.t('addDataset.help.csvDatasetName'),
header: i18n.t('addDataset.datasetName')
}
},
{
id: 'primaryAttribute',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.csvPrimaryAttribute'),
header: i18n.t('addDataset.primaryAttribute')
}
},
{
id: 'latLongAttribute',
type: Bricks.MultiBrick,
config: {
//header: 'Service URL', //optional, omitted if not specified
content: [
{
id: 'latitude',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.csvLatitude'),
header: i18n.t('addDataset.latitude')
}
},
{
id: 'longitude',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.csvLongitude'),
header: i18n.t('addDataset.longitude')
}
}
]
}
},
{
id: 'color',
type: Bricks.ColorPickerBrick,
config: {
instructions: i18n.t('addDataset.help.csvColour'),
header: i18n.t('addDataset.colour')
}
},
{
id: 'addDataset',
type: Bricks.ButtonBrick,
config: {
label: i18n.t('addDataset.addDatasetButton'),
containerClass: 'button-brick-container-main',
buttonClass: 'btn-primary'
},
on: [
{
eventName: Bricks.ButtonBrick.event.CLICK,
// add wms service layer to the map
callback: function (step /*,data*/) {
var data = step.getData(),
bricksData = data.bricksData,
stepData = data.stepData,
csvData = stepData.csvData,
csvHeaders = stepData.csvHeaders,
csvDelimeter = stepData.csvDelimeter,
featureLayer,
iconTemplate = makeIconTemplate('a_d_icon_circlePoint', bricksData.color.hex),
promise;
promise = DataLoader.buildCsv(csvData, {
latfield: bricksData.latitude.dropDownValue,
lonfield: bricksData.longitude.dropDownValue,
delimiter: csvDelimeter,
fields: csvHeaders
});
promise.then(function (event) {
featureLayer = event;
DataLoader.enhanceFileFeatureLayer(featureLayer, {
renderer: 'circlePoint',
colour: [
bricksData.color.rgb_[0],
bricksData.color.rgb_[1],
bricksData.color.rgb_[2],
255
],
nameField: bricksData.primaryAttribute.dropDownValue,
icon: iconTemplate,
datasetName: bricksData.datasetName.inputValue,
fields: csvHeaders
});
//TODO: set symbology and colour on feature layer (obj.data)
LayerLoader.loadLayer(featureLayer);
addDatasetPopup.close();
}, function () {
// can't construct csv
handleFailure(step, null, {
datasetName:
lang.mixin(choiceTreeErrors.csvError, {
message: i18n.t('addDataset.error.messageCSVBroken')
})
});
});
}
}
]
}
]
},
{
id: 'shapefileFileAttrStep',
content: [
{
id: 'datasetName',
type: Bricks.SimpleInputBrick,
config: {
instructions: i18n.t('addDataset.help.shapefileDatasetName'),
header: i18n.t('addDataset.datasetName')
}
},
{
id: 'primaryAttribute',
type: Bricks.DropDownBrick,
config: {
instructions: i18n.t('addDataset.help.shapefilePrimaryAttribute'),
header: i18n.t('addDataset.primaryAttribute')
}
},
{
id: 'color',
type: Bricks.ColorPickerBrick,
config: {
instructions: i18n.t('addDataset.help.shapefileColour'),
header: i18n.t('addDataset.colour')
}
},
{
id: 'addDataset',
type: Bricks.ButtonBrick,
config: {
label: i18n.t('addDataset.addDatasetButton'),
containerClass: 'button-brick-container-main',
buttonClass: 'btn-primary'
},
on: [
{
eventName: Bricks.ButtonBrick.event.CLICK,
// add wms service layer to the map
callback: function (step /*,data*/) {
var data = step.getData(),
bricksData = data.bricksData,
featureLayer = data.stepData,
iconTemplate = makeIconTemplate('a_d_icon_' + featureLayer.renderer._RAMP_rendererType, bricksData.color.hex);
DataLoader.enhanceFileFeatureLayer(featureLayer, {
//renderer: obj.style,
colour: [
bricksData.color.rgb_[0],
bricksData.color.rgb_[1],
bricksData.color.rgb_[2],
255
],
nameField: bricksData.primaryAttribute.dropDownValue,
icon: iconTemplate,
datasetName: bricksData.datasetName.inputValue,
fields: featureLayer.fields.map(function (field) { return field.name; })
});
LayerLoader.loadLayer(featureLayer);
addDatasetPopup.close();
}
}
]
}
]
}
]
}
]
};
}
/**
* Creates a new choice tree html representation and appends it to the page.
*
* @method createChoiceTree
* @private
*/
function createChoiceTree() {
// clear steps
t.dfs(choiceTree, function (node) {
node.stepItem = null;
});
// create the choice tree
t.dfs(choiceTree, function (node, par/*, ctrl*/) {
var stepItem,
level = par ? par.level + 1 : 1;
node.level = level;
stepItem = new StepItem(node);
stepItem.on(StepItem.event.CURRENT_STEP_CHANGE, setCurrentStep);
stepItem.on(StepItem.event.STATE_CHANGE, setStepState);
node.stepItem = stepItem;
stepLookup[node.id] = stepItem;
if (par) {
par.stepItem.addChild(stepItem);
}
console.log(node);
});
// append tree to the page
rootNode
.find('.add-dataset-content')
.empty()
.append(stepLookup.sourceTypeStep.node)
;
// set the first step as active
stepLookup.sourceTypeStep.currentStep(1);
// It's IE9. Run Away!
if (!window.FileReader) {
var fileOrFileURL = stepLookup.fileTypeStep.contentBricks.fileOrFileURL;
console.warn('You have IE9');
fileOrFileURL.fileNode.remove();
fileOrFileURL.filePseudoNode.attr('disabled', true);
fileOrFileURL.browseFilesContainer
.attr({
title: i18n.t('addDataset.error.ie9FileAPI')
})
.addClass('_tooltip')
;
}
Theme.tooltipster(addDatasetContainer);
}
/**
* From provided CSV data, guesses which columns are long and lat.
*
* @method guessLatLong
* @private
* @param {Array} rows csv data
* @return {Object} an object with lat and long string properties indicating corresponding field names
*/
function guessLatLong(rows) {
// try guessing lat and long columns
var latRegex = new RegExp(/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/i),
longRegex = new RegExp(/^[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/i),
guessesLat,
guessesLong,
guessedLatHeader,
guessedLongHeader;
// first filter out all columns that are not lat fro sure
guessesLat = rows[0].filter(function (header, i) {
return rows.every(function (row, rowi) {
return rowi === 0 || latRegex.test(row[i]);
});
});
// filter out all columns that are not long for sure
guessesLong = rows[0].filter(function (header, i) {
return rows.every(function (row, rowi) {
return rowi === 0 || longRegex.test(row[i]);
});
});
// console.log(guessesLat);
// console.log(guessesLong);
// if there more than one lat guesses
if (guessesLat.length > 1) {
// filter out ones that don't have 'la' or 'y' in header name
guessesLat = guessesLat.filter(function (header) {
var h = header.toLowerCase();
return h.indexOf('la') !== -1 || h.indexOf('y') !== -1;
});
}
// console.log(guessesLat);
// pick the first lat guess or null
guessedLatHeader = guessesLat[0] || null;
// if there more than one long guesses
if (guessesLong.length > 1) {
// first, remove lat guess from long options in case they overlap
UtilArray.remove(guessesLong, guessedLatHeader);
// filter out ones that don't have 'lo' or 'x' in header name
guessesLong = guessesLong.filter(function (header) {
var h = header.toLowerCase();
return h.indexOf('lo') !== -1 || h.indexOf('x') !== -1;
});
}
// console.log(guessesLong);
// pick the first long guess or null
guessedLongHeader = guessesLong[0] || null;
return {
lat: guessedLatHeader,
long: guessedLongHeader
};
}
/**
* Creates a icon base64 template to be displayed in the layer selector.
*
* @method makeIconTemplate
* @private
* @param {String} templateName a name of the template to use for an icon
* @param {String} hex color value in hex
* @return {String} a base64 encoded icon template
*/
function makeIconTemplate(templateName, hex) {
/*jshint validthis: true */
return 'data:image/svg+xml;base64,' +
UtilMisc.b64EncodeUnicode(
TmplHelper.template.call(this, templateName, {
colour: hex
}, templates)
);
}
/**
* Delay setting loading state to the step for a specified time in case it happens really fast and it will flicker.
*
* @method delayLoadingState
* @param {StepItem} step step to delay setting loading state on
* @param {Number} time a delay in ms
* @private
* @return {Number} setTimeout handle
*/
function delayLoadingState(step, time) {
return window.setTimeout(function () {
step._notifyStateChange(StepItem.state.LOADING);
}, time);
}
/**
* Handles any failure happening in the choice tree by setting the responsible step to error and displaying appropriate notices.
*
* @method handleFailure
* @private
* @param {StepItem} step a step item that should handle failure
* @param {Number} handle a timeout handle to be canceled
* @param {Object} brickNotices brick notices to be displayed
*/
function handleFailure(step, handle, brickNotices) {
if (handle) {
window.clearTimeout(handle);
}
step
._notifyStateChange(StepItem.state.ERROR)
.displayBrickNotices(brickNotices)
;
}
/**
* Notifies all step items in the tree which step is current at the moment.
*
* @method setCurrentStep
* @private
* @param {String} event a StepItem.CURRENT_STEP_CHANGE event
*/
function setCurrentStep(event) {
t.dfs(choiceTree, function (node) {
node.stepItem.currentStep(event.level, event.id);
});
}
/**
* Notifies all step items in the tree that a certain step has changed its state.
*
* @method setStepState
* @private
* @param {Object} event a StepItem.STATE_CHANGE event
* @param {StepItem} step a step item; this doesn't seem to be used
* @param {String} state a state; this doesn't seem to be used
*/
function setStepState(event, step, state) {
if (step && state) {
event = {
id: step.id,
level: step.level,
state: state
};
}
t.dfs(choiceTree, function (node) {
node.stepItem.setState(event.level, event.id, event.state);
});
}
/**
* Closes the add dataset choice tree.
*
* @method closeChoiceTree
* @private
*/
function closeChoiceTree() {
stepLookup.sourceTypeStep.retreat();
}
return {
/**
* Initialize add-dataset functionality and creates a add-dataset choice tree.
*
* @method init
* @static
*/
init: function () {
var tl = new TimelineLite({ paused: true });
rootNode = $('#searchMapSectionBody');
addDatasetToggle = rootNode.find('#addDatasetToggle');
addDatasetContainer = rootNode.find('#add-dataset-section-container');
layerList = rootNode.find('#layerList');
layerToggles = rootNode.find('.layer-checkboxes:first');
filterToggles = rootNode.find('#filterGlobalToggles');
prepareChoiceTreeStructure();
createChoiceTree();
tl
.set(addDatasetContainer, { display: 'block' })
.set(layerList, { className: '+=scroll' }, 0.01)
.set(filterToggles, { className: '+=scroll' }, 0.01)
.to(filterToggles, transitionDuration, { top: -60, ease: 'easeOutCirc' })
.to(layerList, transitionDuration, { top: layerList.height() / 3, ease: 'easeOutCirc' }, 0)
.to(layerList, transitionDuration / 2, { autoAlpha: 0, ease: 'easeOutCirc' }, transitionDuration / 2)
.set(layerToggles, { display: 'none' })
;
addDatasetPopup = PopupManager.registerPopup(addDatasetToggle, 'click',
function (d) {
createChoiceTree();
tl
.eventCallback('onComplete', function () {
addDatasetContainer.find(':focusable:first').focus();
})
.play()
;
d.resolve();
},
{
closeHandler: function (d) {
closeChoiceTree();
tl
.eventCallback('onReverseComplete', function () {
})
.reverse()
;
d.resolve();
},
target: addDatasetContainer,
activeClass: 'button-pressed',
resetFocusOnClose: true
}
);
UtilDict.forEachEntry(GlobalStorage.DefaultRenderers,
function (key) {
symbologyPreset[key] = i18n.t('presets.defaultRenderers.' + key);
//symbologyPreset[key] = value.title;
}
);
}
};
});