elevation-profile-widget
Version:
This is the widget that will chart elevation profiles for the lines that the user draws.
1,170 lines (1,045 loc) • 50.8 kB
JavaScript
///////////////////////////////////////////////////////////////////////////
// Robert Scheitlin WAB Elevation Profile Widget
///////////////////////////////////////////////////////////////////////////
/*global define, console*/
define([
'dojo/_base/declare',
'jimu/BaseWidget',
'dojo/Evented',
'dijit/_OnDijitClickMixin',
'dijit/_WidgetsInTemplateMixin',
'dojo/on',
'dojo/aspect',
'dojo/_base/lang',
'dojo/_base/Deferred',
'dojo/_base/array',
'dojo/_base/json',
'dojo/number',
'dijit/registry',
'dijit/form/Button',
'dijit/form/ToggleButton',
'put-selector/put',
'dojo/dom-geometry',
'dojo/dom',
'dojo/dom-style',
'dojo/dom-class',
'dojo/_base/Color',
'dojo/colors',
'dojo/fx/easing',
'dojox/charting/Chart',
'dojox/charting/axis2d/Default',
'dojox/charting/plot2d/Grid',
'dojox/charting/plot2d/Areas',
'dojox/charting/action2d/MouseIndicator',
'dojox/charting/action2d/TouchIndicator',
'dojox/charting/themes/ThreeD',
'esri/sniff',
'esri/request',
'esri/tasks/Geoprocessor',
'esri/geometry/Polyline',
'esri/symbols/SimpleLineSymbol',
'esri/symbols/SimpleMarkerSymbol',
'esri/graphic',
'esri/tasks/FeatureSet',
'esri/tasks/LinearUnit',
'esri/geometry/geodesicUtils',
'esri/geometry/webMercatorUtils',
'esri/units',
'jimu/utils',
'esri/dijit/Measurement',
'dojo/_base/html',
'dijit/ProgressBar',
'jimu/dijit/TabContainer',
'jimu/dijit/Message',
'dojo/dom-construct',
'dojo/dom-attr',
'dojo/_base/window',
"esri/tasks/ProjectParameters",
"esri/tasks/GeometryService",
'jimu/WidgetManager',
'dojo/i18n!esri/nls/jsapi',
'esri/SpatialReference',
'dojo/window',
'dojo/_base/fx',
'jimu/dijit/LoadingShelter'
],
function (declare, BaseWidget, Evented, _OnDijitClickMixin, _WidgetsInTemplateMixin,
on, aspect, lang, Deferred, array, dojoJson, number, registry, Button, ToggleButton,
put, domGeometry,dom, domStyle, domClass, Color, colors, easing,
Chart, Default, Grid, Areas, MouseIndicator, TouchIndicator, ThreeD, esriSniff,
esriRequest, Geoprocessor, Polyline, SimpleLineSymbol, SimpleMarkerSymbol,
Graphic, FeatureSet, LinearUnit, geodesicUtils, webMercatorUtils, Units, utils,
Measurement, html, ProgressBar, TabContainer, Message, domConstruct, domAttr, baseWin,
ProjectParameters, GeometryService,WidgetManager, esriBundle, SpatialReference, win, fx) {
return declare([BaseWidget, _OnDijitClickMixin, _WidgetsInTemplateMixin, Evented], {
baseClass: 'widget-elevation-profile',
declaredClass: 'ElevationsProfile',
samplingPointCount: 199,
profileService: null,
loaded: false,
domNode: put('div#profileChartNode'),
profileTaskUrl: null,
scalebarUnits: null,
elevLineSymbol: null,
measureTool: null,
lastMeasure: null,
widgetMaximized:true,
_sourceStr: null,
_gainLossStr: null,
/**
* POSTCREATE - CONNECT UI ELEMENT EVENTS
*/
postCreate: function () {
this.inherited(arguments);
var panel = dom.byId('widgets_ElevationProfile_Widget_17_panel');
domClass.add(panel, 'jimu-widget-panel-elevation');
esriBundle.widgets.measurement.NLS_length_kilometers = "Kilometres";
esriBundle.widgets.measurement.NLS_length_meters = "Metres";
var target = false,
tooltip = false,
tip = false;
if (panel.childNodes.length > 0) {
on(panel.childNodes[0], 'click', lang.hitch(this, function () {
if (this.widgetMaximized===true) {
this.widgetMaximized = false;
this.onMinimized();
}
else {
this.widgetMaximized = true;
this.onMaximized();
}
}));
}
on(this.btnInfo, 'mouseenter', lang.hitch(this, function (evt) {
target = evt.target;
tip = domAttr.get(target, 'title');
tooltip = domConstruct.create('div', { id: 'tooltip', innerHTML: tip, style: { opacity: 0 } }, this.domNode);
if (!tip || tip === '') {
return false;
}
domAttr.remove(target, 'title');
var init_tooltip = lang.hitch(this, function () {
//registry.getEnclosingWidget(this.domNode)
if (win.getBox().w < tooltip.clientWidth * 1.5) {
domStyle.set(tooltip, 'max-width', win.getBox().w / 2);
} else {
domStyle.set(tooltip, 'max-width', 340);
}
var pos_left = target.offsetLeft + (target.clientWidth / 2) - (tooltip.clientWidth / 2),
pos_top = target.offsetTop - tooltip.clientHeight - 20;
if (pos_left < 0) {
pos_left = target.offsetLeft + target.clientWidth / 2 - 20;
domClass.add(tooltip, 'left');
} else {
domClass.remove(tooltip, 'left');
}
if (pos_left + tooltip.clientWidth > win.getBox().w) {
pos_left = target.offsetLeft - tooltip.clientWidth + target.clientWidth / 2 + 20;
domClass.add(tooltip, 'right');
} else {
domClass.remove(tooltip, 'right');
}
if (pos_top < 0) {
pos_top = target.offsetTop + target.clientHeight;
domClass.add(tooltip, 'top');
} else {
domClass.remove(tooltip, 'top');
}
//domStyle.set(tooltip, 'left', pos_left);
domStyle.set(tooltip, 'top', pos_top + 81 + 'px');
fx.animateProperty({
node: tooltip,
duration: 500,
properties: {
opacity: { end: 1, start: 0 },
top: '+=10'
}
}).play();
});
init_tooltip();
on(registry.getEnclosingWidget(this.domNode), 'resize', init_tooltip);
var remove_tooltip = function () {
fx.animateProperty({
node: tooltip,
duration: 50,
properties: {
opacity: { end: 0, start: 0.9 },
top: '-=10'
}
}).play();
domConstruct.destroy(tooltip);
domAttr.set(target, 'title', tip);
};
on(target, 'mouseleave', remove_tooltip);
on(tooltip, 'click', remove_tooltip);
}));
this.scalebarUnits = this.config.scalebarUnits;
this.chartRenderingOptions = lang.mixin({}, this.config.chartRenderingOptions);
this.profileServiceUrl = lang.replace('{0}/Profile', [this.config.profileTaskUrl]);
this.own(
aspect.after(registry.getEnclosingWidget(this.domNode), 'resize', lang.hitch(this, this._resizeChart), true)
);
this._initProfileService = lang.hitch(this, this._initProfileService);
this.displayProfileChart = lang.hitch(this, this.displayProfileChart);
this.clearProfileChart = lang.hitch(this, this.clearProfileChart);
this._updateProfileChart = lang.hitch(this, this._updateProfileChart);
this._createProfileChart = lang.hitch(this, this._createProfileChart);
this._getDisplayValue = lang.hitch(this, this._getDisplayValue);
this._initMeasureTool = lang.hitch(this, this._initMeasureTool);
this._initTabContainer();
this._initProgressBar();
if (this.config.symbols && this.config.symbols.simplelinesymbol) {
this.elevLineSymbol = new SimpleLineSymbol(this.config.symbols.simplelinesymbol);
} else {
this.elevLineSymbol = new SimpleLineSymbol();
}
this._bindEvents();
},
/**
* STARTUP THE DIJIT
*/
startup: function () {
this.inherited(arguments);
this._initUI();
},
_initTabContainer: function () {
var tabs = [];
tabs.push({
title: this.nls.measurelabel,
content: this.tabNode1
});
tabs.push({
title: this.nls.resultslabel,
content: this.tabNode2
});
this.selTab = this.nls.measurelabel;
this.tabContainer = new TabContainer({
tabs: tabs,
selected: this.selTab
}, this.tabMain);
this.tabContainer.startup();
this.own(on(this.tabContainer, 'tabChanged', lang.hitch(this, function (title) {
if (title !== this.nls.resultslabel) {
this.selTab = title;
}
this._resizeChart();
})));
utils.setVerticalCenter(this.tabContainer.domNode);
},
_initProgressBar: function () {
this.progressBar = new ProgressBar({
indeterminate: true
}, this.progressbar);
html.setStyle(this.progressBar.domNode, 'display', 'none');
},
onClose: function () {
if (this.measureTool) {
this.measureTool.setTool("distance", false);
this.measureTool.clearResult();
this._displayChartLocation(-1);
this.measureTool.destroy();
var node = dom.byId("measurementToolDiv");
domConstruct.create('div', { "data-dojo-attach-point": "_measureNode", "id": "_measureNode" }, node);
}
//var node = dom.byId("searchDiv");
},
onOpen: function () {
var wm = WidgetManager.getInstance();
this.map = wm.map;
this._initMeasureTool();
var widgetTitlebar = this.domNode.parentNode.parentNode.parentNode.childNodes[0];
if (typeof widgetTitlebar.onmousedown !== "function") {
this.own(on(widgetTitlebar, 'mousedown', lang.hitch(this, function (event) {
event.stopPropagation();
if (event.altKey) {
var msgStr = this.nls.widgetverstr + ': ' + this.manifest.version;
msgStr += '\n' + this.nls.wabversionmsg + ': ' + this.manifest.wabVersion;
msgStr += '\n' + this.manifest.description;
new Message({
titleLabel: this.nls.widgetversion,
message: msgStr
});
}
})));
}
if (this.lastMeasure && this.measureTool) {
this.measureTool.measure(this.lastMeasure);
}
},
onMinimized: function () {
var panel = dom.byId('widgets_ElevationProfile_Widget_17_panel');
domClass.replace(panel, 'jimu-widget-panel-elevation-minimize', 'jimu-widget-panel-elevation')
},
onMaximized: function () {
var panel = dom.byId('widgets_ElevationProfile_Widget_17_panel');
domClass.replace(panel, 'jimu-widget-panel-elevation', 'jimu-widget-panel-elevation-minimize');
},
/**
* INITIALIZE ESRI MEASUREMENT DIJIT
*
* @private
*/
_initMeasureTool: function () {
// MEASUREMENT TOOL //
this._measureNode = dom.byId("_measureNode");
this.measureTool = new Measurement({
map: this.map,
lineSymbol: this.elevLineSymbol,
defaultAreaUnit: (this.scalebarUnits === 'metric') ? Units.SQUARE_KILOMETERS : Units.SQUARE_MILES,
defaultLengthUnit: (this.scalebarUnits === 'metric') ? Units.KILOMETERS : Units.MILES
}, this._measureNode);
aspect.after(this.measureTool, 'setTool', lang.hitch(this, function () {
if (this.measureTool.activeTool) {
this.map.setInfoWindowOnClick(false);
this.disableWebMapPopup();
} else {
this.map.setInfoWindowOnClick(true);
this.enableWebMapPopup();
}
}));
this.measureTool.startup();
// HIDE AREA AND LOCATION TOOLS //
this.measureTool.hideTool('area');
this.measureTool.hideTool('location');
//Activate then deactivate the distance tool to enable the measure units
on.once(this.measureTool, "tool-change", lang.hitch(this, function () {
this.measureTool.setTool("distance", false);
this.measureTool.clearResult();
}));
this.measureTool.setTool("distance", true);
// CREATE PROFILE ON DISTANCE MEASURE-END EVENT //
this.measureTool.on('measure-end', lang.hitch(this, this._onMeasureEnd));
// Clear existing profiles when distance tool is clicked.
this.measureTool._distanceButton.on("click", lang.hitch(this, this._onMeasureClick));
// Update the chart when units change
on(this.measureTool, "unit-change", lang.hitch(this, this._unitsChanged), true);
},
disableWebMapPopup: function () {
if (this.map && this.map.webMapResponse) {
var handler = this.map.webMapResponse.clickEventHandle;
if (handler) {
handler.remove();
this.map.webMapResponse.clickEventHandle = null;
}
}
},
enableWebMapPopup: function () {
if (this.map && this.map.webMapResponse) {
var handler = this.map.webMapResponse.clickEventHandle;
var listener = this.map.webMapResponse.clickEventListener;
if (listener && !handler) {
this.map.webMapResponse.clickEventHandle = on(
this.map,
'click',
lang.hitch(this.map, listener)
);
}
}
},
/**
* MEASUREMENT DISTACE TOOL CLICK
*
* @private
*/
_onMeasureClick: function () {
this.clearProfileChart();
this.map.infoWindow.clearFeatures();
this.map.infoWindow.hide();
this.emit("measure-distance-checked", {
checked: this.measureTool._distanceButton.checked
});
},
/**
* ON MEASURE-END EVENT
*
* @param evt
* @private
*/
/*_onMeasureEnd: function (evt) {
if (evt.toolName === 'distance') {
this.tabContainer.selectTab(this.nls.resultslabel);
this.lastMeasure = evt.geometry;
this.displayProfileChart(evt.geometry);
// UPDATE THE CHART WHEN USER CHANGES UNITS //
aspect.after(this.measureTool.unit.dropDown, 'onItemClick', lang.hitch(this, this._updateProfileChart), true);
}
},*/
_onMeasureEnd: function (evt) {
if (evt.toolName === "distance") {
//Todo we should really list distance for both select and measure
console.log(number.format(evt.values) + " " + evt.unitName);
this.tabContainer.selectTab(this.nls.resultslabel);
this.lastMeasure = evt.geometry;
this.displayProfileChart(evt.geometry);
}
},
_bindEvents: function () {
this.own(on(this.btnClear, 'click', lang.hitch(this, this._clear)));
html.setStyle(this.btnClear, 'display', 'none');
html.setStyle(this.btnInfo, 'display', 'none');
},
_clear: function () {
html.setStyle(this.btnClear, 'display', 'none');
html.setStyle(this.btnInfo, 'display', 'none');
this.clearProfileChart();
this.tabContainer.selectTab(this.nls.measurelabel);
this.measureTool.clearResult();
return false;
},
/**
* INITIALIZE THE UI
*
* @private
*/
_initUI: function () {
if (this.chartRenderingOptions.constrain) {
domClass.add(this._chartNode, "PanelMax");
}
// MAKE SURE WE HAVE ACCESS TO THE PROFILE SERVICE //
this._initProfileService().then(lang.hitch(this, function () {
this._updateProfileChart();
// DIJIT SUCCESSFULLY LOADED //
this.loaded = true;
this.emit('load', {});
}), lang.hitch(this, function () {
this.emit('error', new Error(this.nls.errors.InvalidConfiguration));
this.destroy();
}));
},
/**
* INITIALIZE THE PROFILE SERVICE
*
* @returns {*}
* @private
*/
_initProfileService: function () {
var deferred = new Deferred();
if (this.profileServiceUrl) {
// MAKE SURE PROFILE SERVICE IS AVAILABLE //
esriRequest({
url: this.profileServiceUrl,
content: {
f: 'json'
},
callbackParamName: 'callback'
}).then(lang.hitch(this, function (taskInfo) {
//console.log('GP Service Details: ', taskInfo);
// TASK DETAILS //
this.taskInfo = taskInfo;
// CREATE GP PROFILE SERVICE //
this.profileService = new Geoprocessor(this.profileServiceUrl);
this.profileService.setOutSpatialReference(this.map.spatialReference);
// SAMPLING DISTANCE //
this.samplingDistance = new LinearUnit();
this.samplingDistance.units = Units.METERS;
deferred.resolve();
}), lang.hitch(this, function (error) {
deferred.reject(error);
}));
} else {
deferred.reject(new Error(this.nls.errors.InvalidConfiguration));
}
return deferred.promise;
},
projectCoordinatesNZTMToWGS84: function (polyline) {
var projectParameters = new ProjectParameters();
var outSR = new SpatialReference(4326);
projectParameters.outSR = outSR;
projectParameters.geometries = polyline;
var gs = new GeometryService(this.appConfig.geometryService);
//gs.project(projectParameters, projectCoordinatesNZMGToNZTM, function (data) {
// console.log("Failed to project data to NZMG", data);
//});
gs.project(projectParameters,
lang.hitch(this, this.onProjectComplete),
lang.hitch(this, this.onError));
},
onProjectComplete: function (geometries) {
var geometry = geometries[0];
this._getProfile(geometry).then(lang.hitch(this, function (elevationInfo) {
this.elevationInfo = elevationInfo;
this._updateProfileChart();
this.emit('display-profile', elevationInfo);
html.setStyle(this.btnClear, 'display', 'block');
html.setStyle(this.btnInfo, 'display', 'block');
html.setStyle(this.progressBar.domNode, 'display', 'none');
}), lang.hitch(this, function (error) {
html.setStyle(this.progressBar.domNode, 'display', 'none');
alert(lang.replace('{message}\n\n{details.0}', error));
this.emit('error', error);
}));
},
/**
* GET PROFILE OVER POLYLINE FROM PROFILE SERVICE
*
* @param polyline
* @returns {*}
* @private
*/
_getProfile: function (polyline) {
var deferred = new Deferred();
// CONVERT WEBMERCATOR POLYLINE TO GEOGRAPHIC //
// - IF NOT WEBMERCATOR ASSUME ALREADY IN GEOGRAPHIC //
var geoPolyline = (polyline.spatialReference.isWebMercator()) ? webMercatorUtils.webMercatorToGeographic(polyline) : polyline;
// GET LENGTH IN METERS //
var profileLengthMeters = geodesicUtils.geodesicLengths([geoPolyline], Units.METERS)[0];
// GET SAMPLING DISTANCE //
var samplingDistance = (profileLengthMeters / this.samplingPointCount);
// CREATE GP TASK INPUT FEATURE SET //
var inputProfileGraphic = new Graphic(polyline, null, {
OID: 1
});
var inputLineFeatures = new FeatureSet();
inputLineFeatures.features = [inputProfileGraphic];
// MAKE SURE OID FIELD IS AVAILABLE TO GP SERVICE //
inputLineFeatures.fields = [
{
'name': 'OID',
'type': 'esriFieldTypeObjectID',
'alias': 'OID'
}
];
// MAKE GP REQUEST //
this.profileService.execute({
'InputLineFeatures': inputLineFeatures,
'ProfileIDField': 'OID',
'DEMResolution': 'FINEST',
'MaximumSampleDistance': samplingDistance,
'MaximumSampleDistanceUnits': 'Meters',
'returnZ': true,
'returnM': true
}).then(lang.hitch(this, function (results) {
// GET RESULT //
if (results.length > 0) {
var profileOutput = results[0].value;
// GET PROFILE FEATURE //
if (profileOutput.features.length > 0) {
var profileFeature = profileOutput.features[0];
// SET DEM RESOLUTION DETAILS //
this._sourceStr = lang.replace('{0}: {1}', [this.nls.chart.demResolution, profileFeature.attributes.DEMResolution]);
// GET PROFILE GEOMETRY //
var profileGeometry = profileFeature.geometry;
var allElevations = [];
var allDistances = [];
if (profileGeometry.paths.length > 0) {
// POLYLINE PATHS //
array.forEach(profileGeometry.paths, lang.hitch(this, function (profilePoints, pathIndex) {
// ELEVATION INFOS //
array.forEach(profilePoints, lang.hitch(this, function (coords, pointIndex) {
var elevationInfo = {
x: ((coords.length > 3) ? coords[3] : (pointIndex * samplingDistance)),
y: ((coords.length > 2) ? coords[2] : 0.0),
pathIdx: pathIndex,
pointIdx: pointIndex
};
allElevations.push(elevationInfo);
allDistances.push(elevationInfo.x);
}));
}));
// RESOLVE TASK //
deferred.resolve({
geometry: profileGeometry,
elevations: allElevations,
distances: allDistances,
samplingDistance: samplingDistance
});
} else {
deferred.reject(new Error(this.nls.errors.UnableToProcessResults));
}
} else {
deferred.reject(new Error(this.nls.errors.UnableToProcessResults));
}
} else {
deferred.reject(new Error(this.nls.errors.UnableToProcessResults));
}
}), deferred.reject);
return deferred.promise;
},
/**
* DISPLAY PROFILE CHART
*
* @param geometry
* @returns {*}
*/
displayProfileChart: function (geometry) {
html.setStyle(this.progressBar.domNode, 'display', 'block');
this.projectCoordinatesNZTMToWGS84([geometry]);
},
/**
* CLEAR PROFILE CHART
*
* @private
*/
clearProfileChart: function () {
this.elevationInfo = null;
this._updateProfileChart();
this.emit('clear-profile', {});
},
/**
* UPDATE PROFILE CHART
*
* @private
*/
_updateProfileChart: function () {
html.setStyle(this.progressBar.domNode, 'display', 'block');
this._createProfileChart(this.elevationInfo).then(lang.hitch(this, function () {
this.profileChart.resize();
html.setStyle(this.progressBar.domNode, 'display', 'none');
}), lang.hitch(this, function (error) {
html.setStyle(this.progressBar.domNode, 'display', 'none');
this.emit('error', error);
}));
},
_unitsChanged: function () {
//Check to see if the measure tool is active. If so call update profile chart
if (this.measureTool._distanceButton.checked) {
//measure tool
this._updateProfileChart();
}
},
/**
* CREATE PROFILE CHART
*
* @param elevationInfo
* @returns {*}
* @private
*/
_createProfileChart: function (elevationInfo) {
var deferred = new Deferred();
// CHART SERIES NAMES //
var waterDataSeriesName = 'Water';
var elevationDataSeriesName = 'ElevationData';
// MIN/MAX/STEP //
var yMin = -10.0;
var yMax = 100.0;
// DID WE GET NEW ELEVATION INFORMATION //
if (!elevationInfo) {
// CLEAR GRAPHIC FROM MAP //
this._displayChartLocation(-1);
// SAMPLING DISTANCE //
this.samplingDistance.distance = (this.map.extent.getWidth() / this.samplingPointCount);
// GEOMETRY AND ELEVATIONS //
this.profilePolyline = null;
var samplingDisplayDistance = this._convertDistancesArray([this.samplingDistance.distance])[0];
this.elevationData = this._getFilledArray(this.samplingPointCount, samplingDisplayDistance, true);
// CLEAR GAIN/LOSS AND SOURCE DETAILS //
this._gainLossStr = '';
this._sourceStr = '';
// REMOVE ELEVATION INDICATORS //
if (this.elevationIndicator) {
this.elevationIndicator.destroy();
this.elevationIndicator = null;
}
if (this.elevationIndicator2) {
this.elevationIndicator2.destroy();
this.elevationIndicator2 = null;
}
} else {
// GEOMETRY, ELEVATIONS, DISTANCES AND SAMPLING DISTANCE //
this.profilePolyline = elevationInfo.geometry;
this.elevationData = this._convertElevationsInfoArray(elevationInfo.elevations);
this.distances = this._convertDistancesArray(elevationInfo.distances);
this.samplingDistance.distance = this._convertDistancesArray([elevationInfo.samplingDistance.distance])[0];
// CALC MIN/MAX/STEP //
var yMinSource = this._getArrayMin(this.elevationData);
var yMaxSource = this._getArrayMax(this.elevationData);
var yRange = (yMaxSource - yMinSource);
yMin = yMinSource - (yRange * 0.05);
yMax = yMaxSource + (yRange * 0.05);
// GAIN/LOSS DETAILS //
var detailsNumberFormat = {
places: 0
};
var elevFirst = this.elevationData[0].y;
var elevLast = this.elevationData[this.elevationData.length - 1].y;
var gainLossDetails = {
min: number.format(yMinSource, detailsNumberFormat),
max: number.format(yMaxSource, detailsNumberFormat),
start: number.format(elevFirst, detailsNumberFormat),
end: number.format(elevLast, detailsNumberFormat),
gainloss: number.format((elevLast - elevFirst), detailsNumberFormat)
};
this._gainLossStr = lang.replace(this.nls.chart.gainLossTemplate, gainLossDetails);
//this.btnInfo.title = this._gainLossStr + "\n" + this._sourceStr;
this.btnInfo.title = this._gainLossStr + "<br>" + this._sourceStr;
// REMOVE ELEVATION INDICATORS //
if (this.elevationIndicator) {
this.elevationIndicator.destroy();
this.elevationIndicator = null;
}
if (this.elevationIndicator2) {
this.elevationIndicator2.destroy();
this.elevationIndicator2 = null;
}
// MOUSE/TOUCH ELEVATION INDICATOR //
var indicatorProperties = {
series: elevationDataSeriesName,
mouseOver: true,
font: 'normal normal bold 9pt Tahoma',
fontColor: this.chartRenderingOptions.indicatorFontColor,
fill: this.chartRenderingOptions.indicatorFillColor,
markerFill: 'none',
markerStroke: {
color: 'red',
width: 3.0
},
markerSymbol: 'm -6 -6, l 12 12, m 0 -12, l -12 12', // RED X //
offset: {
y: -2,
x: -25
},
labelFunc: lang.hitch(this, function (obj) {
this._displayChartLocation(obj.x);
var elevUnitsLabel = this._getDisplayUnits(true);
var elevChangeLabel = number.format(obj.y, detailsNumberFormat);
return lang.replace('{0} {1}', [elevChangeLabel, elevUnitsLabel]);
})
};
// MOUSE/TOUCH ELEVATION CHANGE INDICATOR //
var indicatorProperties2 = {
series: waterDataSeriesName,
mouseOver: true,
font: 'normal normal bold 8pt Tahoma',
fontColor: this.chartRenderingOptions.indicatorFontColor,
fill: this.chartRenderingOptions.indicatorFillColor,
fillFunc: lang.hitch(this, function (obj) {
var elevIndex = this.distances.indexOf(obj.x);
var elev = this.elevationData[elevIndex].y;
return (elev >= elevFirst) ? 'green' : 'red';
}),
offset: {
y: 25,
x: -30
},
labelFunc: lang.hitch(this, function (obj) {
var elevIndex = this.distances.indexOf(obj.x);
var elev = this.elevationData[elevIndex].y;
var elevChangeLabel = number.format(elev - elevFirst, detailsNumberFormat);
var plusMinus = ((elev - elevFirst) > 0) ? '+' : '';
return lang.replace('{0}{1}', [plusMinus, elevChangeLabel]);
})
};
if (esriSniff('has-touch')) {
this.elevationIndicator2 = new TouchIndicator(this.profileChart, 'default', indicatorProperties2);
this.elevationIndicator = new TouchIndicator(this.profileChart, 'default', indicatorProperties);
} else {
this.elevationIndicator2 = new MouseIndicator(this.profileChart, 'default', indicatorProperties2);
this.elevationIndicator = new MouseIndicator(this.profileChart, 'default', indicatorProperties);
}
this.profileChart.fullRender();
}
// FILLED ZERO ARRAY //
var waterData = this._resetArray(this.elevationData, 0.0);
// ARE WE UPDATING OR CREATING THE CHART //
if (this.profileChart != null) {
// UPDATE CHART //
this.profileChart.getAxis('y').opt.min = yMin;
this.profileChart.getAxis('y').opt.max = yMax;
this.profileChart.getAxis('y').opt.title = lang.replace(this.nls.chart.elevationTitleTemplate, [this._getDisplayUnits(true)]);
this.profileChart.getAxis('x').opt.title = lang.replace(this.nls.chart.distanceTitleTemplate, [this._getDisplayUnits(false)]);
this.profileChart.dirty = true;
this.profileChart.updateSeries(waterDataSeriesName, waterData);
this.profileChart.updateSeries(elevationDataSeriesName, this.elevationData);
// RENDER CHART //
this.profileChart.render();
deferred.resolve();
} else {
// CREATE CHART //
this.profileChart = new Chart(this._chartNode, {
title: this.nls.chart.title,
titlePos: 'top',
titleGap: 13,
titleFont: lang.replace('normal normal bold {chartTitleFontSize}pt verdana', this.chartRenderingOptions),
titleFontColor: this.chartRenderingOptions.titleFontColor
});
// SET THEME //
this.profileChart.setTheme(ThreeD);
// OVERRIDE DEFAULTS //
this.profileChart.fill = 'transparent';
this.profileChart.theme.axis.stroke.width = 2;
this.profileChart.theme.axis.majorTick.color = Color.named.white.concat(0.5);
this.profileChart.theme.axis.majorTick.width = 1.0;
this.profileChart.theme.plotarea.fill = {
type: 'linear',
space: 'plot',
x1: 50,
y1: 100,
x2: 50,
y2: 0,
colors: [
{
offset: 0.0,
color: this.chartRenderingOptions.skyTopColor
},
{
offset: 1.0,
color: this.chartRenderingOptions.skyBottomColor
}
]
};
// Y AXIS //
this.profileChart.addAxis('y', {
min: yMin,
max: yMax,
fontColor: this.chartRenderingOptions.axisFontColor,
font: lang.replace('normal normal bold {axisLabelFontSize}pt verdana', this.chartRenderingOptions),
vertical: true,
natural: true,
fixed: true,
includeZero: false,
majorLabels: true,
minorLabels: true,
majorTicks: true,
minorTicks: true,
majorTick: {
color: this.chartRenderingOptions.axisMajorTickColor,
length: 6
},
title: lang.replace(this.nls.chart.elevationTitleTemplate, [this._getDisplayUnits(true)]),
titleGap: 30,
titleFont: lang.replace('normal normal bold {axisTitleFontSize}pt verdana', this.chartRenderingOptions),
titleFontColor: this.chartRenderingOptions.titleFontColor,
titleOrientation: 'axis'
});
// X AXIS //
this.profileChart.addAxis('x', {
fontColor: this.chartRenderingOptions.axisFontColor,
font: lang.replace('normal normal bold {axisLabelFontSize}pt verdana', this.chartRenderingOptions),
natural: true,
fixed: true,
includeZero: false,
majorLabels: true,
minorLabels: true,
majorTicks: true,
minorTicks: true,
majorTick: {
color: this.chartRenderingOptions.axisMajorTickColor,
length: 6
},
title: lang.replace(this.nls.chart.distanceTitleTemplate, [this._getDisplayUnits(false)]),
titleGap: 5,
titleFont: lang.replace('normal normal bold {axisTitleFontSize}pt verdana', this.chartRenderingOptions),
titleFontColor: this.chartRenderingOptions.titleFontColor,
titleOrientation: 'away'
});
// GRID //
this.profileChart.addPlot('grid', {
type: Grid,
hMajorLines: true,
hMinorLines: false,
vMajorLines: false,
vMinorLines: false
});
// PROFIlE PLOT //
this.profileChart.addPlot('default', {
type: Areas,
tension: 'X'
});
// WATER PLOT //
this.profileChart.addPlot('water', {
type: Areas
});
// WATER DATA //
this.profileChart.addSeries(waterDataSeriesName, waterData, {
plot: 'water',
stroke: {
width: 2.0,
color: this.chartRenderingOptions.waterLineColor
},
fill: {
type: 'linear',
space: 'plot',
x1: 50,
y1: 0,
x2: 50,
y2: 100,
colors: [
{
offset: 0.0,
color: this.chartRenderingOptions.waterTopColor
},
{
offset: 1.0,
color: this.chartRenderingOptions.waterBottomColor
}
]
}
});
// PROFILE DATA //
this.profileChart.addSeries(elevationDataSeriesName, this.elevationData, {
plot: 'default',
stroke: {
width: 1.5,
color: this.chartRenderingOptions.elevationLineColor
},
fill: {
type: 'linear',
space: 'plot',
x1: 50,
y1: 0,
x2: 50,
y2: 100,
colors: [
{
offset: 0.0,
color: this.chartRenderingOptions.elevationTopColor
},
{
offset: 1.0,
color: this.chartRenderingOptions.elevationBottomColor
}
]
}
});
// RENDER CHART //
this.profileChart.render();
deferred.resolve();
}
return deferred.promise;
},
/**
* RESIZE PROFILE CHART
*
* @private
*/
_resizeChart: function () {
if (this.profileChart) {
this.profileChart.resize();
}
},
/**
* DISPLAY CHART LOCATION AS RED X GRAPHIC ON MAP
*
* @param {Number} chartObjectX
*/
_displayChartLocation: function (chartObjectX) {
if (this.map && this.elevationData && this.profilePolyline) {
if (!this.chartLocationGraphic) {
// CREATE LOCATION GRAPHIC //
var red = new Color(Color.named.red);
var outline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, red, 3);
var chartLocationSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_X, 13, outline, red);
this.chartLocationGraphic = new Graphic(null, chartLocationSymbol); // RED X //
this.map.graphics.add(this.chartLocationGraphic);
}
// SET GEOMETRY OF LOCATION GRAPHIC //
var distanceIndex = (this.distances) ? array.indexOf(this.distances, chartObjectX) : -1;
if (distanceIndex >= 0) {
var elevData = this.elevationData[distanceIndex];
this.chartLocationGraphic.setGeometry(this.profilePolyline.getPoint(elevData.pathIdx, elevData.pointIdx));
} else {
this.chartLocationGraphic.setGeometry(null);
}
}
},
/**
* GET DISPLAY VALUE GIVEN A VALUE IN METERS AND THE DISPLAY UNITS
* CONVERT FROM METERS TO MILES THEN FROM MILES TO DISPLAY UNITS
*
* @param {Number} valueMeters
* @param {String} displayUnits
*/
_getDisplayValue: function (valueMeters, displayUnits) {
if (displayUnits === this.measureTool._unitStrings.esriMeters) {
return valueMeters;
} else {
var distanceMiles = (valueMeters / this.measureTool._unitDictionary[this.measureTool._unitStrings.esriMeters]);
return (distanceMiles * this.measureTool._unitDictionary[displayUnits]);
}
},
/**
* GET DISPLAY UNITS
*
* @param {Boolean} isElevation
*/
_getDisplayUnits: function (isElevation) {
var displayUnits = this.measureTool._unitDropDown.label;
if (isElevation) {
switch (displayUnits) {
case this.measureTool._unitStrings.esriMiles:
displayUnits = this.measureTool._unitStrings.esriFeet;
break;
case this.measureTool.esriYards:
displayUnits = this.measureTool.esriFeet;
break;
case this.measureTool._unitStrings.esriKilometers:
displayUnits = this.measureTool._unitStrings.esriMeters;
break;
}
}
return displayUnits;
},
/**
* CONVERT ELEVATION INFO (X=DISTANCE,Y=ELEVATION) FROM METERS TO DISPLAY UNITS
*
* @param elevationArray
* @returns {Array}
* @private
*/
_convertElevationsInfoArray: function (elevationArray) {
var displayUnitsX = this._getDisplayUnits(false);
var displayUnitsY = this._getDisplayUnits(true);
return array.map(elevationArray, lang.hitch(this, function (item) {
return lang.mixin(item, {
x: this._getDisplayValue(item.x, displayUnitsX),
y: this._getDisplayValue(item.y, displayUnitsY)
});
}));
},
/**
* CONVERT DISTANCES FROM METERS TO DISPLAY UNITS
*
* @param distancesArray
* @returns {Array}
* @private
*/
_convertDistancesArray: function (distancesArray) {
var displayUnitsX = this._getDisplayUnits(false);
return array.map(distancesArray, lang.hitch(this, function (distance) {
return this._getDisplayValue(distance, displayUnitsX);
}));
},
/**
* CREATE ARRAY WITH INPUT VALUE AND ALLOW MULTIPLIER
*
* @param size
* @param value
* @param asMultiplier
* @returns {Array}
* @private
*/
_getFilledArray: function (size, value, asMultiplier) {
var dataArray = new Array(size);
for (var dataIdx = 0; dataIdx < size; ++dataIdx) {
dataArray[dataIdx] = {
x: asMultiplier ? (dataIdx * value) : dataIdx,
y: asMultiplier ? 0.0 : (value || 0.0)
};
}
return dataArray;
},
/**
* RESET Y VALUES IN ARRAY
*
* @param dataArray
* @param value
* @returns {*}
* @private
*/
_resetArray: function (dataArray, value) {
return array.map(dataArray, function (item) {
return {
x: item.x,
y: value
};
});
},
/**
* GET MAXIMUM Y VALUE IN ARRAY
*
* @param {[]} dataArray
* @return {number}
* @private
*/
_getArrayMax: function (dataArray) {
var values = array.map(dataArray, function (item) {