/* 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;
}
);
}
};
});