Skip to content

Instantly share code, notes, and snippets.

@menion
Last active January 9, 2019 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save menion/807f4fe480f8abb6455a536d1f6762e1 to your computer and use it in GitHub Desktop.
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.
/****************************************************************************
*
* 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