Last active
January 9, 2019 21:11
-
-
Save menion/807f4fe480f8abb6455a536d1f6762e1 to your computer and use it in GitHub Desktop.
Source code for class, that optimize single variable in Locus Map tracks, like elevation, speed, etc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************************************************** | |
* | |
* Created by menion | |
* Copyright (c) 2019. All rights reserved. | |
* | |
* This file is part of the Asamm team software. | |
* | |
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | |
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
* | |
***************************************************************************/ | |
package com.asamm.locus.utils.math; | |
import com.asamm.locus.utils.Const; | |
import java.util.Arrays; | |
/** | |
* Class that serve as container for one type of information. | |
* It allow to analyze this array and optimize it (filter) | |
* | |
* @author menion | |
*/ | |
public class DatasetOptimizer { | |
public enum OptimizeStrength { | |
LIGHT(5.0f), | |
MEDIUM(10.0f), | |
STRONG(50.0f); | |
// param that define how lon distance will be considered as equal. So for example value | |
// 10.0 means, that distance <= 10 m , will have weight 1 | |
private float mParam; | |
/** | |
* Basic constructor. | |
* | |
* @param param weight parameter | |
*/ | |
OptimizeStrength(float param) { | |
this.mParam = param; | |
} | |
/** | |
* Compute weight for current item based on distance. | |
* | |
* @param dist distance for weight | |
* @return computed weight | |
*/ | |
public float getWeight(float dist) { | |
// check distance | |
if (dist < 1.0f) { | |
dist = 1.0f; | |
} | |
// compute weight | |
float weight = mParam / dist; | |
if (weight > 1.0f) { | |
weight = 1.0f; | |
} | |
return weight; | |
} | |
} | |
// data container | |
private float[] data; | |
// maximum value in container | |
private float min; | |
// minimum value in container | |
private float max; | |
// maximum distance where value is filtering | |
private static final float MAX_TEST_DISTANCE = 250.0f; | |
// maximum amount of points, where value is filtering | |
private static final int MAX_TEST_POINTS = 10; | |
public DatasetOptimizer() { | |
clear(); | |
} | |
public float[] getData() { | |
return data; | |
} | |
/** | |
* Clear whole dataset to it's default state | |
*/ | |
public void clear() { | |
data = null; | |
min = Float.POSITIVE_INFINITY; | |
max = Float.NEGATIVE_INFINITY; | |
} | |
/** | |
* Prepare containers for data. | |
* | |
* @param size expected size of container | |
*/ | |
public void prepareContainers(int size) { | |
if (data == null) { | |
data = new float[size]; | |
} else { | |
data = Arrays.copyOfRange(data, 0, size); | |
} | |
// clear min/max values | |
if (size == 0) { | |
min = Float.POSITIVE_INFINITY; | |
max = Float.NEGATIVE_INFINITY; | |
} | |
} | |
/** | |
* Add single value into certain place in container. | |
* | |
* @param index index of content to add | |
* @param value value we wants to store | |
*/ | |
public void addValue(int index, float value) { | |
data[index] = value; | |
if (value != Const.NULL_VALUE) { | |
min = Math.min(min, value); | |
max = Math.max(max, value); | |
} | |
} | |
// OPTIMIZATION | |
// temporary summary of value | |
private float sumValue; | |
// temporary summary of weight | |
private float sumWeight; | |
/** | |
* Allow to optimize recorded values | |
* | |
* @param dataDist array of pre-computed step distances between points. This array is useful | |
* for faster optimization. Values should be | |
* in metres. | |
*/ | |
public void optimizeData(float[] dataDist) { | |
optimizeData(OptimizeStrength.LIGHT, dataDist); | |
} | |
public void optimizeData(OptimizeStrength strength, float[] dataDist) { | |
// check available data | |
if (data == null || data.length < 5) { | |
return; | |
} | |
// prepare new container | |
float[] dataOld = data; | |
clear(); | |
data = new float[dataOld.length]; | |
// iterate and optimize all data | |
for (int i = 0; i < dataOld.length; i++) { | |
addValue(i, optimizeDataPoint(strength, dataOld, i, dataDist)); | |
} | |
} | |
/** | |
* Perform optimization of single item in dataset. | |
* | |
* @param strength filter strength | |
* @param dataOld container with "old" original data | |
* @param i index of item to filter | |
* @param dataDist container with pre-computed distances between measurements | |
* @return computed optimized value for index [i] | |
*/ | |
private float optimizeDataPoint(OptimizeStrength strength, | |
float[] dataOld, int i, float[] dataDist) { | |
// do not optimize null values | |
if (dataOld[i] == Const.NULL_VALUE) { | |
return dataOld[i]; | |
} | |
// add original value to sum | |
sumValue = 0.0f; | |
sumWeight = 0.0f; | |
addOptimizedValues(strength, dataOld[i], 0.0f); | |
float partSumValue = sumValue; | |
float partSumWeight = sumWeight; | |
// add values "before" current index | |
sumValue = 0.0f; | |
sumWeight = 0.0f; | |
float sumDist = 0.0f; | |
int count = 0; | |
int index = i - 1; | |
// iterate to "history" | |
while (sumDist < MAX_TEST_DISTANCE && count < MAX_TEST_POINTS) { | |
if (index < 0) { | |
break; | |
} else { | |
sumDist += dataDist[index + 1] - dataDist[index]; | |
} | |
addOptimizedValues(strength, dataOld[index], sumDist); | |
index -= 1; | |
count += 1; | |
} | |
// add "before" values to sum | |
partSumValue += sumValue; | |
partSumWeight += sumWeight; | |
// add values "after" current index | |
sumValue = 0.0f; | |
sumWeight = 0.0f; | |
sumDist = 0.0f; | |
count = 0; | |
index = i + 1; | |
// iterate to "future" | |
while (sumDist < MAX_TEST_DISTANCE && count < MAX_TEST_POINTS) { | |
if (index >= dataOld.length) { | |
break; | |
} else if (index > 0) { | |
sumDist += dataDist[index] - dataDist[index - 1]; | |
} | |
addOptimizedValues(strength, dataOld[index], sumDist); | |
index += 1; | |
count += 1; | |
} | |
// add "after" values to sum | |
partSumValue += sumValue; | |
partSumWeight += sumWeight; | |
// compute averaged value | |
return partSumValue / partSumWeight; | |
} | |
/** | |
* Add optimized value to summary. | |
* | |
* @param strength current filter | |
* @param value value to filter | |
* @param dist distance from center point | |
*/ | |
private void addOptimizedValues(OptimizeStrength strength, float value, float dist) { | |
// ski invalid values | |
if (value == Const.NULL_VALUE) { | |
return; | |
} | |
// compute weight | |
float weight = strength.getWeight(dist); | |
// add values to "sum" | |
sumValue += (value * weight); | |
sumWeight += weight; | |
} | |
// GET METHODS | |
/** | |
* Get value at certain index. | |
*/ | |
public float getValue(int index) { | |
if (index < 0) { | |
return 0.0f; | |
} else if (index >= data.length) { | |
return 0.0f; | |
} | |
return data[index]; | |
} | |
/** | |
* Get number of items in dataset. | |
*/ | |
public int getCount() { | |
if (data == null) { | |
return 0; | |
} else { | |
return data.length; | |
} | |
} | |
/** | |
* Get minimal value in dataset. | |
*/ | |
public float getMin() { | |
return min; | |
} | |
/** | |
* Get maximal value in dataset. | |
*/ | |
public float getMax() { | |
return max; | |
} | |
public int getLastLowerValue(float value) { | |
if (!isReady()) { | |
return 0; | |
} | |
int length = data.length; | |
int index = 0; | |
for (int i = 1; i < length; i++) { | |
if (data[i] > value) { | |
index = i - 1; | |
break; | |
} | |
} | |
return index; | |
} | |
public int getFirstHigherValue(float value) { | |
if (!isReady()) { | |
return 0; | |
} | |
int length = data.length; | |
int index = 0; | |
for (int i = 0; i < length; i++) { | |
if (data[i] >= value) { | |
index = i; | |
break; | |
} | |
} | |
return index; | |
} | |
/** | |
* Check if current data series is ready. It mainly means if it contains any data | |
* and if min and max values are different. | |
* | |
* @return <code>true</code> if data are valid, otherwise returns <code>false</code>. | |
*/ | |
public boolean isReady() { | |
return data != null && data.length > 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment