From 399ea02fdfc3aab8bbb0119de8a32d9e6f091805 Mon Sep 17 00:00:00 2001 From: ivanPyrohivskyi Date: Thu, 25 Feb 2021 17:32:25 +0200 Subject: [PATCH 1/4] Gradient colorization for route --- .../java/net/osmand/router/RouteColorize.java | 380 ++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java new file mode 100644 index 0000000000..9074403176 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -0,0 +1,380 @@ +package net.osmand.router; + +import net.osmand.GPXUtilities; +import net.osmand.osm.edit.Node; +import net.osmand.osm.edit.OsmMapUtils; +import net.osmand.util.MapUtils; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +public class RouteColorize { + + public int zoom; + public double[] latitudes; + public double[] longitudes; + public double[] values; + public double minValue; + public double maxValue; + public double[][] palette; + + private List dataList; + private static final String ANSI_RESET = "\u001B[0m"; + private static final String ANSI_RED = "\u001B[31m"; + private static final String ANSI_GREEN = "\u001B[32m"; + public static final String ANSI_YELLOW = "\u001B[33m"; + + public enum ValueType { + ELEVATION, + SPEED, + SLOPE, + NONE + } + + private ValueType valueType; + + public static int SLOPE_RANGE = 150; + + + /** + * @param minValue can be NaN + * @param maxValue can be NaN + * @param palette array {{[color][value]},...}, color in sRGB format + */ + public RouteColorize(int zoom, double[] latitudes, double[] longitudes, double[] values, double minValue, double maxValue, double[][] palette) { + this.zoom = zoom; + this.latitudes = latitudes; + this.longitudes = longitudes; + this.values = values; + this.minValue = minValue; + this.maxValue = maxValue; + this.palette = palette; + + if (Double.isNaN(minValue) || Double.isNaN(maxValue)) { + calculateMinMaxValue(); + } + checkPalette(); + sortPalette(); + } + + /** + * @param palette array {{[color][value]},...}, color in sRGB format + * @param type ELEVATION, SPEED, SLOPE + */ + public RouteColorize(int zoom, double[][] palette, List wptPtList, ValueType type) { + this.zoom = zoom; + this.palette = palette; + + latitudes = new double[wptPtList.size()]; + longitudes = new double[wptPtList.size()]; + values = new double[wptPtList.size()]; + double[] elevations = new double[wptPtList.size()]; + for (int i = 0; i < wptPtList.size(); i++) { + latitudes[i] = wptPtList.get(i).lat; + longitudes[i] = wptPtList.get(i).lon; + if (type == ValueType.ELEVATION) { + values[i] = wptPtList.get(i).ele; + } else if (type == ValueType.SPEED) { + values[i] = wptPtList.get(i).speed; + } else if (type == ValueType.SLOPE) { + elevations[i] = wptPtList.get(i).ele; + } + } + + if (type == ValueType.SLOPE) { + values = calculateSlopesByElevations(latitudes, longitudes, elevations, SLOPE_RANGE); + } + + calculateMinMaxValue(); + valueType = type; + checkPalette(); + sortPalette(); + } + + /** + * Calculate slopes from elevations needs for right colorizing + * + * @param slopeRange - in what range calculate the derivative, usually we used 150 meters + * @return slopes array, in the begin and the end present NaN values! + */ + public static double[] calculateSlopesByElevations(double[] latitudes, double[] longitudes, double[] elevations, double slopeRange) { + + double[] newElevations = elevations; + for (int i = 2; i < elevations.length - 2; i++) { + newElevations[i] = elevations[i - 2] + + elevations[i - 1] + + elevations[i] + + elevations[i + 1] + + elevations[i + 2]; + newElevations[i] /= 5; + } + elevations = newElevations; + + double[] slopes = new double[elevations.length]; + if (latitudes.length != longitudes.length || latitudes.length != elevations.length) { + System.out.println(ANSI_RED + "Sizes of arrays latitudes, longitudes and values are not match" + ANSI_RESET); + return slopes; + } + + double[] distances = new double[elevations.length]; + double totalDistance = 0.0d; + distances[0] = totalDistance; + for (int i = 0; i < elevations.length - 1; i++) { + totalDistance += MapUtils.getDistance(latitudes[i], longitudes[i], latitudes[i + 1], longitudes[i + 1]); + distances[i + 1] = totalDistance; + } + + for (int i = 0; i < elevations.length; i++) { + if (distances[i] < slopeRange / 2 || distances[i] > totalDistance - slopeRange / 2) { + slopes[i] = Double.NaN; + } else { + double[] arg = findDerivativeArguments(distances, elevations, i, slopeRange); + slopes[i] = (arg[1] - arg[0]) / (arg[3] - arg[2]); + } + } + return slopes; + } + + public List getResult(boolean simplify) { + List result = new ArrayList<>(); + if (simplify) { + result = simplify(); + } else { + for (int i = 0; i < latitudes.length; i++) { + result.add(new Data(i, latitudes[i], longitudes[i], values[i])); + } + } + for (Data data : result) { + data.color = getColorByValue(data.val); + } + return result; + } + + public Color getColorByValue(double value) { + if (Double.isNaN(value)) { + return getDefaultColor(); + } + for (int i = 0; i < palette.length - 1; i++) { + if (value == palette[i][1]) + return new Color((int) palette[i][0]); + if (value > palette[i][1] && value < palette[i + 1][1]) { + Color minPaletteColor = new Color((int) palette[i][0]); + Color maxPaletteColor = new Color((int) palette[i + 1][0]); + double minPaletteValue = palette[i][1]; + double maxPaletteValue = palette[i + 1][1]; + double percent = (value - minPaletteValue) / (maxPaletteValue - minPaletteValue); + double resultRed = minPaletteColor.getRed() + percent * (maxPaletteColor.getRed() - minPaletteColor.getRed()); + double resultGreen = minPaletteColor.getGreen() + percent * (maxPaletteColor.getGreen() - minPaletteColor.getGreen()); + double resultBlue = minPaletteColor.getBlue() + percent * (maxPaletteColor.getBlue() - minPaletteColor.getBlue()); + double resultAlpha = minPaletteColor.getAlpha() + percent * (maxPaletteColor.getAlpha() - minPaletteColor.getAlpha()); + return new Color((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); + } + } + return getDefaultColor(); + } + + private Color getDefaultColor() { + if (valueType != null && valueType == ValueType.SLOPE) { + return new Color(255, 222, 2, 227); + } + return new Color(0, 0, 0, 0); + } + + private List simplify() { + if (dataList == null) { + dataList = new ArrayList<>(); + for (int i = 0; i < latitudes.length; i++) { + dataList.add(new Data(i, latitudes[i], longitudes[i], values[i])); + } + } + List nodes = new ArrayList<>(); + List result = new ArrayList<>(); + for (Data data : dataList) { + nodes.add(new net.osmand.osm.edit.Node(data.lat, data.lon, data.id)); + } + OsmMapUtils.simplifyDouglasPeucker(nodes, zoom + 5, 1, result, true); + + List simplified = new ArrayList<>(); + + for (int i = 1; i < result.size() - 1; i++) { + int prevId = (int) result.get(i - 1).getId(); + int currentId = (int) result.get(i).getId(); + List sublist = dataList.subList(prevId, currentId); + simplified.addAll(getExtremums(sublist)); + } + return simplified; + } + + private List getExtremums(List subDataList) { + if (subDataList.size() <= 2) + return subDataList; + + List result = new ArrayList<>(); + double min; + double max; + min = max = subDataList.get(0).val; + for (Data pt : subDataList) { + if (min > pt.val) + min = pt.val; + if (max < pt.val) + max = pt.val; + } + + double diff = max - min; + + result.add(subDataList.get(0)); + for (int i = 1; i < subDataList.size() - 1; i++) { + double prev = subDataList.get(i - 1).val; + double current = subDataList.get(i).val; + double next = subDataList.get(i + 1).val; + Data currentData = subDataList.get(i); + + if ((current > prev && current > next) || (current < prev && current < next) + || (current < prev && current == next) || (current == prev && current < next) + || (current > prev && current == next) || (current == prev && current > next)) { + Data prevInResult; + if (result.size() > 0) { + prevInResult = result.get(0); + if (prevInResult.val / diff > 0.05d) {// check differences in 5% + result.add(currentData); + } + } else + result.add(currentData); + } + } + result.add(subDataList.get(subDataList.size() - 1)); + return result; + } + + private void checkPalette() { + if (palette.length < 2 || palette[0].length < 2 || palette[1].length < 2) { + System.out.println(ANSI_YELLOW + "Fill palette in {{[color][value]},...} format. Will use default palette" + ANSI_RESET); + palette = new double[3][2]; + Color red = new Color(255, 1, 1, 255); + Color yellow = new Color(255, 222, 2, 227); + Color green = new Color(46, 185, 0, 191); + + double[][] defaultPalette = { + { green.getRGB(), minValue}, + { yellow.getRGB(), valueType == ValueType.SLOPE ? 0 : (minValue + maxValue) / 2}, + { red.getRGB(), maxValue} + }; + palette = defaultPalette; + } + double min; + double max = min = palette[0][1]; + int minIndex = 0; + int maxIndex = 0; + for (int i = 0; i < palette.length; i++) { + double[] p = palette[i]; + if (p[1] > max) { + max = p[1]; + maxIndex = i; + } + if (p[1] < min) { + min = p[1]; + minIndex = i; + } + } + if (minValue < min) { + palette[minIndex][1] = minValue; + } + if (maxValue > max) { + palette[maxIndex][1] = maxValue; + } + } + + private void sortPalette() { + java.util.Arrays.sort(palette, new java.util.Comparator() { + public int compare(double[] a, double[] b) { + return Double.compare(a[1], b[1]); + } + }); + } + + /** + * @return double[minElevation, maxElevation, minDist, maxDist] + */ + private static double[] findDerivativeArguments(double[] distances, double[] elevations, int index, double slopeRange) { + double[] result = new double[4]; + double minDist = distances[index] - slopeRange / 2; + double maxDist = distances[index] + slopeRange / 2; + result[0] = Double.NaN; + result[1] = Double.NaN; + result[2] = minDist; + result[3] = maxDist; + int closestMaxIndex = -1; + int closestMinIndex = -1; + for (int i = index; i < distances.length; i++) { + if (distances[i] == maxDist) { + result[1] = elevations[i]; + break; + } + if (distances[i] > maxDist) { + closestMaxIndex = i; + break; + } + } + for (int i = index; i >= 0; i--) { + if (distances[i] == minDist) { + result[0] = elevations[i]; + break; + } + if (distances[i] < minDist) { + closestMinIndex = i; + break; + } + } + if (closestMaxIndex > 0) { + double diff = distances[closestMaxIndex] - distances[closestMaxIndex - 1]; + double coef = (maxDist - distances[closestMaxIndex - 1]) / diff; + if (coef > 1 || coef < 0) { + System.out.println(ANSI_RED + "Coefficient fo max must be 0..1 , coef=" + coef + ANSI_RESET); + } + result[1] = (1 - coef) * elevations[closestMaxIndex - 1] + coef * elevations[closestMaxIndex]; + } + if (closestMinIndex >= 0) { + double diff = distances[closestMinIndex + 1] - distances[closestMinIndex]; + double coef = (minDist - distances[closestMinIndex]) / diff; + if (coef > 1 || coef < 0) { + System.out.println(ANSI_RED + "Coefficient for min must be 0..1 , coef=" + coef + ANSI_RESET); + } + result[0] = (1 - coef) * elevations[closestMinIndex] + coef * elevations[closestMinIndex + 1]; + } + if (Double.isNaN(result[0]) || Double.isNaN(result[1])) { + System.out.println(ANSI_RED + "Elevations wasn't calculated" + ANSI_RESET); + } + return result; + } + + private void calculateMinMaxValue() { + if (values.length == 0) + return; + minValue = maxValue = Double.NaN; + for (double value : values) { + if ((Double.isNaN(maxValue) || Double.isNaN(minValue)) && !Double.isNaN(value)) + maxValue = minValue = value; + if (minValue > value) + minValue = value; + if (maxValue < value) + maxValue = value; + } + } + + public class Data { + int id; + public double lat; + public double lon; + public double val; + public Color color; + + Data(int id, double lat, double lon, double val) { + this.id = id; + this.lat = lat; + this.lon = lon; + this.val = val; + } + } + +} From 92c6ed4501c5dab25a1b6d90babb2eb5efa3bf42 Mon Sep 17 00:00:00 2001 From: ivanPyrohivskyi Date: Fri, 26 Feb 2021 18:29:07 +0200 Subject: [PATCH 2/4] Fixes --- .../java/net/osmand/router/RouteColorize.java | 150 +++++++++++------- 1 file changed, 93 insertions(+), 57 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java index 9074403176..383d57b7cd 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -20,10 +20,12 @@ public class RouteColorize { public double[][] palette; private List dataList; - private static final String ANSI_RESET = "\u001B[0m"; - private static final String ANSI_RED = "\u001B[31m"; - private static final String ANSI_GREEN = "\u001B[32m"; - public static final String ANSI_YELLOW = "\u001B[33m"; + + public static final int DARK_GREY = -10724260; + public static final int LIGHT_GREY = -3618616; + public static final int RED = -65279; + public static final int GREEN = -1087457024; + public static final int YELLOW = -469770750; public enum ValueType { ELEVATION, @@ -32,6 +34,13 @@ public class RouteColorize { NONE } + private final int VALUE_INDEX = 0; + private final int DECIMAL_COLOR_INDEX = 1;//sRGB decimal format + private final int RED_COLOR_INDEX = 1;//RGB + private final int GREEN_COLOR_INDEX = 2;//RGB + private final int BLUE_COLOR_INDEX = 3;//RGB + private final int ALPHA_COLOR_INDEX = 4;//RGBA + private ValueType valueType; public static int SLOPE_RANGE = 150; @@ -40,7 +49,7 @@ public class RouteColorize { /** * @param minValue can be NaN * @param maxValue can be NaN - * @param palette array {{[color][value]},...}, color in sRGB format + * @param palette array {{value,color},...} - color in sRGB (decimal) format OR {{value,RED,GREEN,BLUE,ALPHA},...} - color in RGBA format */ public RouteColorize(int zoom, double[] latitudes, double[] longitudes, double[] values, double minValue, double maxValue, double[][] palette) { this.zoom = zoom; @@ -59,31 +68,38 @@ public class RouteColorize { } /** - * @param palette array {{[color][value]},...}, color in sRGB format - * @param type ELEVATION, SPEED, SLOPE + * @param type ELEVATION, SPEED, SLOPE */ - public RouteColorize(int zoom, double[][] palette, List wptPtList, ValueType type) { - this.zoom = zoom; - this.palette = palette; + public RouteColorize(int zoom, GPXUtilities.GPXFile gpxFile, ValueType type) { - latitudes = new double[wptPtList.size()]; - longitudes = new double[wptPtList.size()]; - values = new double[wptPtList.size()]; - double[] elevations = new double[wptPtList.size()]; - for (int i = 0; i < wptPtList.size(); i++) { - latitudes[i] = wptPtList.get(i).lat; - longitudes[i] = wptPtList.get(i).lon; - if (type == ValueType.ELEVATION) { - values[i] = wptPtList.get(i).ele; - } else if (type == ValueType.SPEED) { - values[i] = wptPtList.get(i).speed; - } else if (type == ValueType.SLOPE) { - elevations[i] = wptPtList.get(i).ele; + if (!gpxFile.hasTrkPt()) + return; + + List latList = new ArrayList<>(); + List lonList = new ArrayList<>(); + List valList = new ArrayList<>(); + for (GPXUtilities.Track t : gpxFile.tracks) { + for (GPXUtilities.TrkSegment ts : t.segments) { + for (GPXUtilities.WptPt p : ts.points) { + latList.add(p.lat); + lonList.add(p.lon); + if (type == ValueType.SPEED) { + valList.add(p.speed); + } else { + valList.add(p.ele); + } + } } } + this.zoom = zoom; + latitudes = listToArray(latList); + longitudes = listToArray(lonList); + if (type == ValueType.SLOPE) { - values = calculateSlopesByElevations(latitudes, longitudes, elevations, SLOPE_RANGE); + values = calculateSlopesByElevations(latitudes, longitudes, listToArray(valList), SLOPE_RANGE); + } else { + values = listToArray(valList); } calculateMinMaxValue(); @@ -98,7 +114,7 @@ public class RouteColorize { * @param slopeRange - in what range calculate the derivative, usually we used 150 meters * @return slopes array, in the begin and the end present NaN values! */ - public static double[] calculateSlopesByElevations(double[] latitudes, double[] longitudes, double[] elevations, double slopeRange) { + public double[] calculateSlopesByElevations(double[] latitudes, double[] longitudes, double[] elevations, double slopeRange) { double[] newElevations = elevations; for (int i = 2; i < elevations.length - 2; i++) { @@ -113,7 +129,7 @@ public class RouteColorize { double[] slopes = new double[elevations.length]; if (latitudes.length != longitudes.length || latitudes.length != elevations.length) { - System.out.println(ANSI_RED + "Sizes of arrays latitudes, longitudes and values are not match" + ANSI_RESET); + //System.out.println("Sizes of arrays latitudes, longitudes and values are not match"); return slopes; } @@ -153,16 +169,16 @@ public class RouteColorize { public Color getColorByValue(double value) { if (Double.isNaN(value)) { - return getDefaultColor(); + value = (minValue + maxValue) / 2; } for (int i = 0; i < palette.length - 1; i++) { - if (value == palette[i][1]) - return new Color((int) palette[i][0]); - if (value > palette[i][1] && value < palette[i + 1][1]) { - Color minPaletteColor = new Color((int) palette[i][0]); - Color maxPaletteColor = new Color((int) palette[i + 1][0]); - double minPaletteValue = palette[i][1]; - double maxPaletteValue = palette[i + 1][1]; + if (value == palette[i][VALUE_INDEX]) + return new Color((int) palette[i][DECIMAL_COLOR_INDEX]); + if (value >= palette[i][VALUE_INDEX] && value <= palette[i + 1][VALUE_INDEX]) { + Color minPaletteColor = new Color((int) palette[i][DECIMAL_COLOR_INDEX]); + Color maxPaletteColor = new Color((int) palette[i + 1][DECIMAL_COLOR_INDEX]); + double minPaletteValue = palette[i][VALUE_INDEX]; + double maxPaletteValue = palette[i + 1][VALUE_INDEX]; double percent = (value - minPaletteValue) / (maxPaletteValue - minPaletteValue); double resultRed = minPaletteColor.getRed() + percent * (maxPaletteColor.getRed() - minPaletteColor.getRed()); double resultGreen = minPaletteColor.getGreen() + percent * (maxPaletteColor.getGreen() - minPaletteColor.getGreen()); @@ -174,10 +190,13 @@ public class RouteColorize { return getDefaultColor(); } + public void setPalette(double[][] palette) { + this.palette = palette; + checkPalette(); + sortPalette(); + } + private Color getDefaultColor() { - if (valueType != null && valueType == ValueType.SLOPE) { - return new Color(255, 222, 2, 227); - } return new Color(0, 0, 0, 0); } @@ -185,6 +204,7 @@ public class RouteColorize { if (dataList == null) { dataList = new ArrayList<>(); for (int i = 0; i < latitudes.length; i++) { + //System.out.println(latitudes[i] + " " + longitudes[i] + " " + values[i]); dataList.add(new Data(i, latitudes[i], longitudes[i], values[i])); } } @@ -248,47 +268,55 @@ public class RouteColorize { } private void checkPalette() { - if (palette.length < 2 || palette[0].length < 2 || palette[1].length < 2) { - System.out.println(ANSI_YELLOW + "Fill palette in {{[color][value]},...} format. Will use default palette" + ANSI_RESET); + if (palette == null || palette.length < 2 || palette[0].length < 2 || palette[1].length < 2) { + //System.out.println("Fill palette in {{[color][value]},...} format. Will use default palette"); palette = new double[3][2]; - Color red = new Color(255, 1, 1, 255); - Color yellow = new Color(255, 222, 2, 227); - Color green = new Color(46, 185, 0, 191); double[][] defaultPalette = { - { green.getRGB(), minValue}, - { yellow.getRGB(), valueType == ValueType.SLOPE ? 0 : (minValue + maxValue) / 2}, - { red.getRGB(), maxValue} + {minValue, GREEN}, + {valueType == ValueType.SLOPE ? 0 : (minValue + maxValue) / 2, YELLOW}, + {maxValue, RED} }; palette = defaultPalette; } double min; - double max = min = palette[0][1]; + double max = min = palette[0][VALUE_INDEX]; int minIndex = 0; int maxIndex = 0; + double[][] sRGBPalette = new double[palette.length][2]; for (int i = 0; i < palette.length; i++) { double[] p = palette[i]; - if (p[1] > max) { - max = p[1]; + if (p.length == 2) { + sRGBPalette[i] = p; + } else if (p.length == 4) { + Color color = new Color((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX]); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color.getRGB()}; + } else if (p.length >= 5) { + Color color = new Color((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], (int) p[ALPHA_COLOR_INDEX]); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color.getRGB()}; + } + if (p[VALUE_INDEX] > max) { + max = p[VALUE_INDEX]; maxIndex = i; } - if (p[1] < min) { - min = p[1]; + if (p[VALUE_INDEX] < min) { + min = p[VALUE_INDEX]; minIndex = i; } } + palette = sRGBPalette; if (minValue < min) { - palette[minIndex][1] = minValue; + palette[minIndex][VALUE_INDEX] = minValue; } if (maxValue > max) { - palette[maxIndex][1] = maxValue; + palette[maxIndex][VALUE_INDEX] = maxValue; } } private void sortPalette() { java.util.Arrays.sort(palette, new java.util.Comparator() { public int compare(double[] a, double[] b) { - return Double.compare(a[1], b[1]); + return Double.compare(a[VALUE_INDEX], b[VALUE_INDEX]); } }); } @@ -296,7 +324,7 @@ public class RouteColorize { /** * @return double[minElevation, maxElevation, minDist, maxDist] */ - private static double[] findDerivativeArguments(double[] distances, double[] elevations, int index, double slopeRange) { + private double[] findDerivativeArguments(double[] distances, double[] elevations, int index, double slopeRange) { double[] result = new double[4]; double minDist = distances[index] - slopeRange / 2; double maxDist = distances[index] + slopeRange / 2; @@ -330,7 +358,7 @@ public class RouteColorize { double diff = distances[closestMaxIndex] - distances[closestMaxIndex - 1]; double coef = (maxDist - distances[closestMaxIndex - 1]) / diff; if (coef > 1 || coef < 0) { - System.out.println(ANSI_RED + "Coefficient fo max must be 0..1 , coef=" + coef + ANSI_RESET); + //System.out.println("Coefficient fo max must be 0..1 , coef=" + coef); } result[1] = (1 - coef) * elevations[closestMaxIndex - 1] + coef * elevations[closestMaxIndex]; } @@ -338,12 +366,12 @@ public class RouteColorize { double diff = distances[closestMinIndex + 1] - distances[closestMinIndex]; double coef = (minDist - distances[closestMinIndex]) / diff; if (coef > 1 || coef < 0) { - System.out.println(ANSI_RED + "Coefficient for min must be 0..1 , coef=" + coef + ANSI_RESET); + //System.out.println("Coefficient for min must be 0..1 , coef=" + coef); } result[0] = (1 - coef) * elevations[closestMinIndex] + coef * elevations[closestMinIndex + 1]; } if (Double.isNaN(result[0]) || Double.isNaN(result[1])) { - System.out.println(ANSI_RED + "Elevations wasn't calculated" + ANSI_RESET); + //System.out.println("Elevations wasn't calculated"); } return result; } @@ -362,6 +390,14 @@ public class RouteColorize { } } + private double[] listToArray(List doubleList) { + double[] result = new double[doubleList.size()]; + for (int i = 0; i < doubleList.size(); i++) { + result[i] = doubleList.get(i); + } + return result; + } + public class Data { int id; public double lat; From 27aab34bf989d6dee21d16fb169ab086ecc94c97 Mon Sep 17 00:00:00 2001 From: ivanPyrohivskyi Date: Fri, 26 Feb 2021 19:09:54 +0200 Subject: [PATCH 3/4] Added rgbaToDecimal converter --- .../java/net/osmand/router/RouteColorize.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java index 383d57b7cd..18b5f72f2f 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -1,9 +1,12 @@ package net.osmand.router; import net.osmand.GPXUtilities; +import net.osmand.PlatformUtil; +import net.osmand.binary.BinaryMapAddressReaderAdapter; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.OsmMapUtils; import net.osmand.util.MapUtils; +import org.apache.commons.logging.Log; import java.awt.Color; import java.util.ArrayList; @@ -21,11 +24,13 @@ public class RouteColorize { private List dataList; - public static final int DARK_GREY = -10724260; - public static final int LIGHT_GREY = -3618616; - public static final int RED = -65279; - public static final int GREEN = -1087457024; - public static final int YELLOW = -469770750; + + + public static final int DARK_GREY = rgbaToDecimal(92, 92, 92, 255); + public static final int LIGHT_GREY = rgbaToDecimal(200, 200, 200, 255); + public static final int RED = rgbaToDecimal(255,1,1,255); + public static final int GREEN = rgbaToDecimal(46,185,0,191); + public static final int YELLOW = rgbaToDecimal(255,222,2,227); public enum ValueType { ELEVATION, @@ -45,6 +50,7 @@ public class RouteColorize { public static int SLOPE_RANGE = 150; + private static final Log LOG = PlatformUtil.getLog(RouteColorize.class); /** * @param minValue can be NaN @@ -72,8 +78,10 @@ public class RouteColorize { */ public RouteColorize(int zoom, GPXUtilities.GPXFile gpxFile, ValueType type) { - if (!gpxFile.hasTrkPt()) + if (!gpxFile.hasTrkPt()) { + LOG.warn("GPX file is not consist of track points"); return; + } List latList = new ArrayList<>(); List lonList = new ArrayList<>(); @@ -129,7 +137,7 @@ public class RouteColorize { double[] slopes = new double[elevations.length]; if (latitudes.length != longitudes.length || latitudes.length != elevations.length) { - //System.out.println("Sizes of arrays latitudes, longitudes and values are not match"); + LOG.warn("Sizes of arrays latitudes, longitudes and values are not match"); return slopes; } @@ -269,7 +277,7 @@ public class RouteColorize { private void checkPalette() { if (palette == null || palette.length < 2 || palette[0].length < 2 || palette[1].length < 2) { - //System.out.println("Fill palette in {{[color][value]},...} format. Will use default palette"); + LOG.info("Will use default palette"); palette = new double[3][2]; double[][] defaultPalette = { @@ -358,7 +366,7 @@ public class RouteColorize { double diff = distances[closestMaxIndex] - distances[closestMaxIndex - 1]; double coef = (maxDist - distances[closestMaxIndex - 1]) / diff; if (coef > 1 || coef < 0) { - //System.out.println("Coefficient fo max must be 0..1 , coef=" + coef); + LOG.warn("Coefficient fo max must be 0..1 , coef=" + coef); } result[1] = (1 - coef) * elevations[closestMaxIndex - 1] + coef * elevations[closestMaxIndex]; } @@ -366,12 +374,12 @@ public class RouteColorize { double diff = distances[closestMinIndex + 1] - distances[closestMinIndex]; double coef = (minDist - distances[closestMinIndex]) / diff; if (coef > 1 || coef < 0) { - //System.out.println("Coefficient for min must be 0..1 , coef=" + coef); + LOG.warn("Coefficient for min must be 0..1 , coef=" + coef); } result[0] = (1 - coef) * elevations[closestMinIndex] + coef * elevations[closestMinIndex + 1]; } if (Double.isNaN(result[0]) || Double.isNaN(result[1])) { - //System.out.println("Elevations wasn't calculated"); + LOG.warn("Elevations wasn't calculated"); } return result; } @@ -398,6 +406,14 @@ public class RouteColorize { return result; } + private static int rgbaToDecimal(int r, int g, int b, int a) { + int value = ((a & 0xFF) << 24) | + ((r & 0xFF) << 16) | + ((g & 0xFF) << 8) | + ((b & 0xFF) << 0); + return value; + } + public class Data { int id; public double lat; From 11f1ba5bc2901fbbce29209957ce38b9263d4a7b Mon Sep 17 00:00:00 2001 From: ivanPyrohivskyi Date: Fri, 26 Feb 2021 19:51:36 +0200 Subject: [PATCH 4/4] Removed java.awt.* --- .../java/net/osmand/router/RouteColorize.java | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java index 18b5f72f2f..6730965d1f 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -2,13 +2,11 @@ package net.osmand.router; import net.osmand.GPXUtilities; import net.osmand.PlatformUtil; -import net.osmand.binary.BinaryMapAddressReaderAdapter; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.OsmMapUtils; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; -import java.awt.Color; import java.util.ArrayList; import java.util.List; @@ -22,9 +20,7 @@ public class RouteColorize { public double maxValue; public double[][] palette; - private List dataList; - - + private List dataList; public static final int DARK_GREY = rgbaToDecimal(92, 92, 92, 255); public static final int LIGHT_GREY = rgbaToDecimal(200, 200, 200, 255); @@ -48,7 +44,8 @@ public class RouteColorize { private ValueType valueType; - public static int SLOPE_RANGE = 150; + public static int SLOPE_RANGE = 150;//150 meters + private static final double MIN_DIFFERENCE_SLOPE = 0.05d;//5% private static final Log LOG = PlatformUtil.getLog(RouteColorize.class); @@ -160,39 +157,39 @@ public class RouteColorize { return slopes; } - public List getResult(boolean simplify) { - List result = new ArrayList<>(); + public List getResult(boolean simplify) { + List result = new ArrayList<>(); if (simplify) { result = simplify(); } else { for (int i = 0; i < latitudes.length; i++) { - result.add(new Data(i, latitudes[i], longitudes[i], values[i])); + result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i])); } } - for (Data data : result) { + for (RouteColorizationPoint data : result) { data.color = getColorByValue(data.val); } return result; } - public Color getColorByValue(double value) { + public int getColorByValue(double value) { if (Double.isNaN(value)) { value = (minValue + maxValue) / 2; } for (int i = 0; i < palette.length - 1; i++) { if (value == palette[i][VALUE_INDEX]) - return new Color((int) palette[i][DECIMAL_COLOR_INDEX]); + return (int) palette[i][DECIMAL_COLOR_INDEX]; if (value >= palette[i][VALUE_INDEX] && value <= palette[i + 1][VALUE_INDEX]) { - Color minPaletteColor = new Color((int) palette[i][DECIMAL_COLOR_INDEX]); - Color maxPaletteColor = new Color((int) palette[i + 1][DECIMAL_COLOR_INDEX]); + int minPaletteColor = (int) palette[i][DECIMAL_COLOR_INDEX]; + int maxPaletteColor = (int) palette[i + 1][DECIMAL_COLOR_INDEX]; double minPaletteValue = palette[i][VALUE_INDEX]; double maxPaletteValue = palette[i + 1][VALUE_INDEX]; double percent = (value - minPaletteValue) / (maxPaletteValue - minPaletteValue); - double resultRed = minPaletteColor.getRed() + percent * (maxPaletteColor.getRed() - minPaletteColor.getRed()); - double resultGreen = minPaletteColor.getGreen() + percent * (maxPaletteColor.getGreen() - minPaletteColor.getGreen()); - double resultBlue = minPaletteColor.getBlue() + percent * (maxPaletteColor.getBlue() - minPaletteColor.getBlue()); - double resultAlpha = minPaletteColor.getAlpha() + percent * (maxPaletteColor.getAlpha() - minPaletteColor.getAlpha()); - return new Color((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); + double resultRed = getRed(minPaletteColor) + percent * (getRed(maxPaletteColor) - getRed(minPaletteColor)); + double resultGreen = getGreen(minPaletteColor) + percent * (getGreen(maxPaletteColor) - getGreen(minPaletteColor)); + double resultBlue = getBlue(minPaletteColor) + percent * (getBlue(maxPaletteColor) - getBlue(minPaletteColor)); + double resultAlpha = getAlpha(minPaletteColor) + percent * (getAlpha(maxPaletteColor) - getAlpha(minPaletteColor)); + return rgbaToDecimal((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); } } return getDefaultColor(); @@ -204,49 +201,52 @@ public class RouteColorize { sortPalette(); } - private Color getDefaultColor() { - return new Color(0, 0, 0, 0); + private int getDefaultColor() { + return rgbaToDecimal(0, 0, 0, 0); } - private List simplify() { + private List simplify() { if (dataList == null) { dataList = new ArrayList<>(); for (int i = 0; i < latitudes.length; i++) { //System.out.println(latitudes[i] + " " + longitudes[i] + " " + values[i]); - dataList.add(new Data(i, latitudes[i], longitudes[i], values[i])); + dataList.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i])); } } List nodes = new ArrayList<>(); List result = new ArrayList<>(); - for (Data data : dataList) { + for (RouteColorizationPoint data : dataList) { nodes.add(new net.osmand.osm.edit.Node(data.lat, data.lon, data.id)); } OsmMapUtils.simplifyDouglasPeucker(nodes, zoom + 5, 1, result, true); - List simplified = new ArrayList<>(); + List simplified = new ArrayList<>(); for (int i = 1; i < result.size() - 1; i++) { int prevId = (int) result.get(i - 1).getId(); int currentId = (int) result.get(i).getId(); - List sublist = dataList.subList(prevId, currentId); + List sublist = dataList.subList(prevId, currentId); simplified.addAll(getExtremums(sublist)); } return simplified; } - private List getExtremums(List subDataList) { - if (subDataList.size() <= 2) + private List getExtremums(List subDataList) { + if (subDataList.size() <= 2) { return subDataList; + } - List result = new ArrayList<>(); + List result = new ArrayList<>(); double min; double max; min = max = subDataList.get(0).val; - for (Data pt : subDataList) { - if (min > pt.val) + for (RouteColorizationPoint pt : subDataList) { + if (min > pt.val) { min = pt.val; - if (max < pt.val) + } + if (max < pt.val) { max = pt.val; + } } double diff = max - min; @@ -256,15 +256,15 @@ public class RouteColorize { double prev = subDataList.get(i - 1).val; double current = subDataList.get(i).val; double next = subDataList.get(i + 1).val; - Data currentData = subDataList.get(i); + RouteColorizationPoint currentData = subDataList.get(i); if ((current > prev && current > next) || (current < prev && current < next) || (current < prev && current == next) || (current == prev && current < next) || (current > prev && current == next) || (current == prev && current > next)) { - Data prevInResult; + RouteColorizationPoint prevInResult; if (result.size() > 0) { prevInResult = result.get(0); - if (prevInResult.val / diff > 0.05d) {// check differences in 5% + if (prevInResult.val / diff > MIN_DIFFERENCE_SLOPE) { result.add(currentData); } } else @@ -297,11 +297,11 @@ public class RouteColorize { if (p.length == 2) { sRGBPalette[i] = p; } else if (p.length == 4) { - Color color = new Color((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX]); - sRGBPalette[i] = new double[]{p[VALUE_INDEX], color.getRGB()}; + int color = rgbaToDecimal((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], 255); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color}; } else if (p.length >= 5) { - Color color = new Color((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], (int) p[ALPHA_COLOR_INDEX]); - sRGBPalette[i] = new double[]{p[VALUE_INDEX], color.getRGB()}; + int color = rgbaToDecimal((int) p[RED_COLOR_INDEX], (int) p[GREEN_COLOR_INDEX], (int) p[BLUE_COLOR_INDEX], (int) p[ALPHA_COLOR_INDEX]); + sRGBPalette[i] = new double[]{p[VALUE_INDEX], color}; } if (p[VALUE_INDEX] > max) { max = p[VALUE_INDEX]; @@ -414,14 +414,30 @@ public class RouteColorize { return value; } - public class Data { + private int getRed(int value) { + return (value >> 16) & 0xFF; + } + + private int getGreen(int value) { + return (value >> 8) & 0xFF; + } + + private int getBlue(int value) { + return (value >> 0) & 0xFF; + } + + private int getAlpha(int value) { + return (value >> 24) & 0xff; + } + + public static class RouteColorizationPoint { int id; public double lat; public double lon; public double val; - public Color color; + public int color; - Data(int id, double lat, double lon, double val) { + RouteColorizationPoint(int id, double lat, double lon, double val) { this.id = id; this.lat = lat; this.lon = lon;