Unverified Commit 5cd90d73 authored by User expired's avatar User expired
Browse files

fixed hill shading and meanCalculator (skewness part)

parent 4ec5f56c
Upcoming version:
-----------------
- fixed hill shading and meanCalculator (skewness part) (Nikolaus Krismer)
- fixed time invariant isochrone computation (Nikolaus Krismer)
- fixing findbugs warnings (Nikolaus Krismer)
- updating webpack to latest version (Nikolaus Krismer)
......
......@@ -6,6 +6,11 @@ div.ui-widget-overlay {
background-size: 100% 100%;
}
.leaflet-overlay-pane {
/* allows correct opacity values for L.geoJson (in meanCalculator) */
mix-blend-mode: normal !important;
}
.leaflet-touch .leaflet-control-locate.leaflet-bar a,
.leaflet-touch .leaflet-control-zoom.leaflet-bar a {
font-size: 28px;
......
......@@ -180,7 +180,7 @@ define(['jquery', 'util/loggingUtils', 'isochrone/configuration', 'isochrone/iso
isoMap.getControlSettings().initDialog();
meanCalculator = new MeanCalculator(ws, isoMap);
isoMap.getControlMeanCalculator().initDialog(searchExtender, meanCalculator.startCalculations);
isoMap.getControlMeanCalculator().initDialog(searchExtender, meanCalculator.startCalculations, meanCalculator.resetDialog);
}
}
......
......@@ -715,7 +715,7 @@ define(['jquery', 'leaflet', 'util/loggingUtils', 'isochrone/configuration', 'ma
var hillshadeUrl = 'http://{s}.tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png';
logger.debug('Adding hillshade overlay');
self.addLayer(L.tileLayer(hillshadeUrl, cacheConfig), LanguageUtils.get('leaflet.overlay.hillshade'));
self.addLayer(L.tileLayer(hillshadeUrl), LanguageUtils.get('leaflet.overlay.hillshade'));
}
/**
......
......@@ -19,7 +19,7 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
isoMap = iMap;
isoNameMap = LanguageUtils.get('layer', { returnObjectTrees: true });
if (!isoNameMap) {
console.warn('Could not initialize layer names in mean calculator!');
logger.warn('Could not initialize layer names in mean calculator!');
} else {
$.each(isoNameMap, function(layerKey, layerName) {
isoNames.push(layerName);
......@@ -50,15 +50,35 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
});
};
/**
* @public
* @method MeanCalculator#resetDialog
*/
this.resetDialog = function(dlg) {
var buttonArr = dlg.next('.ui-dialog-buttonpane').find('button'),
intermediateResultSelect = dlg.find('#mean-calculator-results');
logger.debug('Closing mean calculator dialog');
configArr = null;
intermediateResults = null;
intermediateResultSelect.empty();
buttonArr.filter(function() {
return $(this).text() === LanguageUtils.get('btn.next');
}).text(LanguageUtils.get('btn.ok'));
};
/**
* @public
* @method MeanCalculator#startCalculations
*/
this.startCalculations = function(points, dlg) {
if (intermediateResults === null || intermediateResults.length <= 0) {
// perform calculation
calculateIntermediateResults(points, dlg);
} else {
showResult(dlg);
// show results
showLayers(dlg);
dlg.dialog('close');
}
};
......@@ -118,21 +138,24 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
* @method MeanCalculator#calculateIntersectedIsochrones
*/
function calculateIntersectedIsochrones(calcMean, calcSkewness) {
var clipper = null,
var aSkewness = 0,
clipper = null,
count = 0,
firstDate = StringUtils.dateTimeFromIsoString(configArr[0].dateTime),
isochronesWithFrequency = null,
isochronesWithSkewness = null,
lastDate = StringUtils.dateTimeFromIsoString(configArr[configArr.length - 1].dateTime),
maxSkewness = 0,
rawIsochrones = [],
tmp = null,
stats = null,
succeeded = false;
if (!calcMean && !calcSkewness) {
return;
}
logger.log('Calculating mean isochrone from intermediate results');
logger.log('Converting intermediate isochrones to library format');
// converting from geoJson to clipper format
$.each(intermediateResults, function(interIndex, interResult) {
......@@ -162,6 +185,7 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
// performing intersection (after all conversions) is performed
if (calcMean) {
logger.log('Calculating mean isochrone');
logger.debug(' - calculating frequencies');
isochronesWithFrequency = calculateFrequency(rawIsochrones);
logger.debug(' - converting frequency results to geoJson');
......@@ -172,22 +196,30 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
});
}
if (calcSkewness) {
logger.log('Calculating time invariant isochrone');
logger.debug(' - calculating time marks (for skewness)');
isochronesWithSkewness = calculateTimeMarks(rawIsochrones);
logger.debug(' - converting time mark results to geoJson and calculating skewness');
$.each(isochronesWithSkewness, function(index, isochrone) {
stats = calculateSkenewssStats(isochrone.times, count, firstDate, lastDate);
tmp = toGeojson(isochrone);
tmp.properties = $.extend({
id: isochrone.id,
frequency: isochrone.times.length
}, calculateSkenewssStats(isochrone.times, firstDate, lastDate));
}, stats);
isochronesWithSkewness[index] = tmp;
aSkewness = Math.abs(stats.skewnewss);
if (aSkewness > maxSkewness) {
maxSkewness = aSkewness;
}
});
}
return {
count: count,
frequency: isochronesWithFrequency,
maxSkewness: maxSkewness,
skewness: isochronesWithSkewness
};
}
......@@ -202,41 +234,39 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
j = 0,
results = [],
succeeded = false,
tmp0 = null,
tmp1 = null;
tmp = null;
results.push($.extend(true, {frequency: 1}, rawIsochrones[0]));
results.push($.extend(true, {frequency: 1, path: rawIsochrones[0].path}));
for (i = 1; i < rawIsochrones.length; ++i) {
tmp0 = $.extend(true, {}, rawIsochrones[i]);
for (j = results.length - 1; j >= 0; --j) {
tmp1 = {path: new ClipperLib.Paths()};
tmp = {path: new ClipperLib.Paths()};
// intersection (of tmp0 and intersectedIsochrones[j])
// intersection (of rawIsochrones[i] and rawIsochrones[j])
clipper.Clear();
clipper.AddPaths(tmp0.path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(rawIsochrones[i].path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(results[j].path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctIntersection, tmp1.path);
succeeded = clipper.Execute(ClipperLib.ClipType.ctIntersection, tmp.path);
if (!succeeded) {
logger.warn('Could not build intersection for isochrone #', i + 1);
}
if (j + 1 < results.length) {
// Union (of intersectedIsochrones[j + 1] and tmp1)
// Union (of rawIsochrones[j + 1] and tmp)
clipper.Clear();
clipper.AddPaths(results[j + 1].path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(tmp1.path, ClipperLib.PolyType.ptClip, true);
clipper.AddPaths(tmp.path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctUnion, results[j + 1].path);
if (!succeeded) {
logger.warn('Could not update out-dated intersection after calculating isochrone #', i + 1);
}
} else {
results.push({frequency: results.length + 1, path: tmp1.path});
results.push({frequency: results.length + 1, path: tmp.path});
}
}
// Union (of tmp0 and intersectedIsochrones[0])
// Union (of rawIsochrones[i] and results[0])
clipper.Clear();
clipper.AddPaths(tmp0.path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(rawIsochrones[i].path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(results[0].path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctUnion, results[0].path);
if (!succeeded) {
......@@ -260,59 +290,57 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
k = 0,
results = [],
succeeded = false,
tmp0 = null,
tmp1 = null;
tmp = null;
$.each(rawIsochrones, function(index, isochrone) {
allIsochrones[index] = $.extend(true, {times: [isochrone.dateTime]}, isochrone);
});
results.push($.extend(true, {}, allIsochrones[0]));
for (i = 1; i < rawIsochrones.length; ++i) {
tmp0 = $.extend(true, {}, allIsochrones[i]);
results.push(allIsochrones[0]);
for (i = 1; i < allIsochrones.length; ++i) {
for (j = results.length - 1; j >=0; --j) {
tmp1 = {times: $.extend(true, [], results[j].times), path: new ClipperLib.Paths()};
tmp = {times: $.extend(true, [], results[j].times), path: new ClipperLib.Paths()};
// intersection
clipper.Clear();
clipper.AddPaths(results[j].path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(tmp0.path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctIntersection, tmp1.path);
clipper.AddPaths(allIsochrones[i].path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctIntersection, tmp.path);
if (!succeeded) {
logger.warn('Could not build intersection for isochrone #', i + 1);
} else if (tmp1.path.length > 0){
tmp1.times = tmp1.times.concat(tmp0.times);
results.push($.extend({ id: counter++ }, tmp1));
} else if (tmp.path.length > 0){
tmp.times = tmp.times.concat(allIsochrones[i].times);
results.push($.extend({ id: counter++ }, tmp));
}
tmp1 = {times: $.extend(true, [], results[j].times), path: new ClipperLib.Paths()};
tmp = {times: $.extend(true, [], results[j].times), path: new ClipperLib.Paths()};
// Difference
clipper.Clear();
clipper.AddPaths(results[j].path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(tmp0.path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctDifference, tmp1.path);
clipper.AddPaths(allIsochrones[i].path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctDifference, tmp.path);
if (!succeeded) {
logger.warn('Could not build difference for isochrone #', i + 1);
} else if (tmp1.path.length > 0){
results.push($.extend({ id: counter++ }, tmp1));
} else if (tmp.path.length > 0){
results.push($.extend({ id: counter++ }, tmp));
}
tmp1 = $.extend(true, [], {times: results[j].times, path: tmp0.path});
tmp = $.extend(true, {}, {times: allIsochrones[i].times, path: allIsochrones[i].path});
for (k = 0; k < results.length; ++k) {
// Difference
clipper.Clear();
clipper.AddPaths(tmp1.path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(tmp.path, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(results[k].path, ClipperLib.PolyType.ptClip, true);
succeeded = clipper.Execute(ClipperLib.ClipType.ctDifference, tmp1.path);
succeeded = clipper.Execute(ClipperLib.ClipType.ctDifference, tmp.path);
if (!succeeded) {
logger.warn('Could not build difference with previous results for isochrone #', i + 1);
}
}
if (tmp1.path.length > 0){
results.push($.extend({ id: counter++ }, tmp1));
if (tmp.path.length > 0){
results.push($.extend({ id: counter++ }, tmp));
}
// remove "old" element in results
......@@ -324,43 +352,42 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
}
/**
* Calculates the skewness coefficient by using the time marks given in the timeArr parameter.
* Calculates the skewness coefficient by using the time marks given in the dateArr parameter.
*
* @private
* @method MeanCalculator#calculateSkenewssStats
*/
function calculateSkenewssStats(dateArr, firstDate, lastDate) {
var deltas = [],
function calculateSkenewssStats(dateArr, nrIsochrones, firstDate, lastDate) {
var aSkewness = 0,
delta = 0,
deltas = [],
// same as delta but shifted by a step size and extended by (arbitrary) values at the beginning and end (in order to always be able to compute a standard deviation when only one value is available)
deltas2 = [],
maxSkewness = 0,
result = {},
stepSize;
tFirst = firstDate.getTime(),
tLast = lastDate.getTime(),
timeArr = [];
tLast = lastDate.getTime();
stepSize = (tLast - tFirst) / (nrIsochrones - 1);
deltas2.push(0);
$.each(dateArr, function(index, d) {
timeArr.push(d.getTime());
delta = d.getTime() - tFirst;
deltas.push(delta);
deltas2.push(delta + stepSize);
});
if (timeArr[0] !== 0) {
deltas.push(0);
}
$.each(timeArr, function(index, t) {
deltas.push(t - tFirst);
});
if (timeArr[timeArr.length - 1] !== tLast) {
deltas.push(tLast - tFirst);
}
deltas2.push((tLast - tFirst) + 2 * stepSize);
result = {
mean: MathUtils.getMean(deltas),
deltas: deltas,
mean: MathUtils.getMean(deltas),
median: MathUtils.getMedian(deltas),
stddev: MathUtils.getStandardDerivation(deltas)
// note that this skewness is an adapted one and not a one-to-one fitting of the mathematical definition of skewness
// the reason for this is that we want to handle cases were the a place is reachable within the first two times and not the last two (if using 4 time steps)
// the distribution is therefore extended at the beginning and in the end (difference between delta and delta2 arrays)
skewness: 3 * ((MathUtils.getMean(deltas2) - MathUtils.getMedian(deltas2)) / MathUtils.getStandardDerivation(deltas2))
};
result = $.extend(result, {
skewness: (result.mean === result.median) ? 0.0 : (3 * (result.mean - result.median) / result.stddev)
});
return result;
}
......@@ -373,7 +400,7 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
dsName = dlgFields.find('#dataset').val(),
dtEnd = dlgFields.find('#dateTime-end').datepicker('getDate'),
dtStart = dlgFields.find('#dateTime-start').datepicker('getDate'),
dtBetween = (dtEnd.getTime() - dtStart.getTime()) / dlg.find('#count').val(),
dtBetween = (dtEnd.getTime() - dtStart.getTime()) / (dlg.find('#count').val() - 1),
settings = $('#settings-fields'),
msg = {},
directionEl = settings.children('#direction'),
......@@ -445,8 +472,6 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
step = 1 / intersectedIsochrones.count,
tmp = 0;
logger.log('Adding mean isochrone (all intersected isochrone as featureCollection with frequencies assigned)');
// remove layers before adding new ones (this will prevent doubled isochrone layers)
removeIsochroneLayers();
self.removeLayers();
......@@ -462,13 +487,18 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
geoJsonSkewness = null;
layers = {};
if (intersectedIsochrones.skewness) {
logger.log('Showing time invariant isochrone');
geoJsonSkewness = toGeoJsonCollection(intersectedIsochrones.skewness);
layers.skewness = {
layer: L.geoJson(geoJsonSkewness),
name: LanguageUtils.get('layer.skewnessIsochrones')
};
layers.skewness.layer.eachLayer(function (layer) {
tmp = Math.max(0, 1 - Math.abs(layer.feature.properties.skewness));
tmp = Math.abs(layer.feature.properties.skewness);
if (intersectedIsochrones.maxSkewness !== 0) {
tmp = tmp / intersectedIsochrones.maxSkewness;
}
tmp = Math.max(0, 1 - tmp);
tmp = (tmp * (1 - minOpacity)) + minOpacity;
layer.setStyle(GeoJsonStyle.getSkewnessStyle({opacity: tmp}));
});
......@@ -476,6 +506,7 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
bounds = layers.skewness.layer.getBounds();
}
if (intersectedIsochrones.frequency) {
logger.log('Showing mean isochrone');
geoJsonBorderline = intersectedIsochrones.frequency[Math.floor(intersectedIsochrones.frequency.length/2)];
geoJsonFrequency = toGeoJsonCollection(intersectedIsochrones.frequency);
layers.frequency = {
......@@ -502,34 +533,19 @@ define(['jquery', 'leaflet', 'evispa-timo-jsclipper', 'util/loggingUtils', 'isoc
}
}
console.debug('Added skewness isochrone:', geoJsonSkewness);
console.debug('Added frequency isochrone:', geoJsonFrequency);
console.debug('Added mean isochrone (borderline):', geoJsonBorderline);
if (geoJsonSkewness) {
logger.debug('Added skewness isochrone:', geoJsonSkewness);
}
if (geoJsonFrequency) {
logger.debug('Added frequency isochrone:', geoJsonFrequency);
}
if (geoJsonBorderline) {
logger.debug('Added mean isochrone (borderline):', geoJsonBorderline);
}
isoMap.getMap().fitBounds(bounds);
}
/**
* @private
* @method MeanCalculator#showResult
*/
function showResult(dlg) {
var buttonArr = dlg.next('.ui-dialog-buttonpane').find('button'),
intermediateResultSelect = dlg.find('#mean-calculator-results');
showLayers(dlg);
// reset dialog
configArr = null;
intermediateResults = null;
intermediateResultSelect.empty();
buttonArr.filter(function() {
return $(this).text() === LanguageUtils.get('btn.next');
}).text(LanguageUtils.get('btn.ok'));
dlg.dialog('close');
}
/**
* Converts all the features of the given geojson to clipper paths (one array entry for each feature)
*
......
......@@ -207,7 +207,7 @@ define(['jquery', 'util/loggingUtils', '@turf/inside', 'isochrone/analyseHelper'
success: function(data) {
logger.debug('Checking point for isochrone dataset boundary: ', pointGeoJson);
isPointInDataset = tInside(pointGeoJson, { 'type': 'Feature', 'geometry': data });
logger.debug(' - is isochrone calculation possible? ' + isPointInDataset);
logger.debug(' - is isochrone calculation possible? ' + isPointInDataset);
},
error: function(xhr, ajaxOptions, errorThrown) {
if (xhr.status == 404 || errorThrown == 'Not Found') {
......
......@@ -8,7 +8,7 @@ define(['jquery', 'leaflet', 'util/loggingUtils', 'isochrone/configuration', 'is
return this._points;
},
initDialog: function(searchExtender, callbackFn) {
initDialog: function(searchExtender, startFn, resetFn) {
logger.debug('Initializing mean calculator dialog');
if (!searchExtender) {
logger.warn('Can not call mean calculator without initializing searchExtender first');
......@@ -31,11 +31,7 @@ define(['jquery', 'leaflet', 'util/loggingUtils', 'isochrone/configuration', 'is
buttons: [{
text: LanguageUtils.get('btn.ok'),
click: function() {
if (callbackFn) {
callbackFn(self._points, $(this));
} else {
$(this).dialog('close');
}
startFn(self._points, $(this));
}
}, {
text: LanguageUtils.get('btn.cancel'),
......@@ -43,6 +39,9 @@ define(['jquery', 'leaflet', 'util/loggingUtils', 'isochrone/configuration', 'is
$(this).dialog('close');
}
}],
close: function() {
resetFn($(this));
},
open: function() {
logger.log('Initializing meanCalculator:');
// focus second button (cancel - to prevent tooltip showing without hovering)
......@@ -73,15 +72,32 @@ define(['jquery', 'leaflet', 'util/loggingUtils', 'isochrone/configuration', 'is
LanguageUtils.translateObject(dlgElement);
$('#mean-calculator-dialog #dataset').change(function() {
var dataset = Configuration.getInstance().getDatasetConfig($(this).val()),
dtStart = new Date(dataset.dateTime.getTime()),
dtEnd = new Date(dataset.dateTime.getTime());
var dsName = $(this).val(),
dataset = Configuration.getInstance().getDatasetConfig(dsName),
e = null,
eEnd = $('#mean-calculator-dialog #dateTime-end'),
eStart = $('#mean-calculator-dialog #dateTime-start'),
valueEnd = eEnd.datepicker('getDate');
valueStart = eStart.datepicker('getDate');
if (valueStart == null) {
e = $('#' + dsName + '-datetimepicker');
if (e.length !== 0) {
valueStart = e.datepicker('getDate');
}
if (valueStart === null) {
valueStart = new Date(dataset.dateTime.getTime());
}
// default end dateTime is one hour after start dateTime
dtEnd.setUTCHours(dtEnd.getUTCHours() + 1);
eStart.datetimepicker('setDate', valueStart);
}
if (valueEnd == null) {
valueEnd = new Date(valueStart.getTime());
valueEnd.setUTCHours(valueEnd.getUTCHours() + 1); // default endDateTime: one hour after start
$('#mean-calculator-dialog #dateTime-start').datetimepicker('setDate', dtStart);
$('#mean-calculator-dialog #dateTime-end').datetimepicker('setDate', dtEnd);
eEnd.datetimepicker('setDate', valueEnd);
}
});
$('#mean-calculator-dialog #count').spinner({
......
......@@ -31,6 +31,8 @@ define([], function() {
* @return {double} the calculated median
*/
MathUtils.getMedian = function(list) {
list.sort();
var tmpIndex = Math.floor(list.length / 2);
if (list.length % 2) {
return list[tmpIndex];
......@@ -89,6 +91,6 @@ define([], function() {
return delta / list.length;
};
return MathUtils;
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment