Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.