Handle elevations in units besides meters (#226)

pull/230/head
Michael Barry 2022-05-17 20:23:11 -04:00 zatwierdzone przez GitHub
rodzic 6b4eca3a78
commit dab37f572e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 149 dodań i 5 usunięć

Wyświetl plik

@ -113,14 +113,14 @@ public class MountainPeak implements
@Override
public void process(Tables.OsmPeakPoint element, FeatureCollector features) {
Integer meters = Parse.parseIntSubstring(element.ele());
Double meters = Parse.meters(element.ele());
if (meters != null && Math.abs(meters) < 10_000) {
var feature = features.point(LAYER_NAME)
.setAttr(Fields.CLASS, element.source().getTag("natural"))
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
.putAttrs(elevationTags(meters))
.setSortKeyDescending(
meters +
meters.intValue() +
(nullIfEmpty(element.wikipedia()) != null ? 10_000 : 0) +
(nullIfEmpty(element.name()) != null ? 10_000 : 0)
)

Wyświetl plik

@ -49,9 +49,9 @@ public class Utils {
}
/** Returns a map with {@code ele} (meters) and {ele_ft} attributes from an elevation in meters. */
public static Map<String, Object> elevationTags(int meters) {
public static Map<String, Object> elevationTags(double meters) {
return Map.of(
"ele", meters,
"ele", (int) Math.round(meters),
"ele_ft", (int) Math.round(meters * 3.2808399)
);
}
@ -61,7 +61,7 @@ public class Utils {
* meters} can be parsed as a valid number.
*/
public static Map<String, Object> elevationTags(String meters) {
Integer ele = Parse.parseIntSubstring(meters);
Double ele = Parse.meters(meters);
return ele == null ? Map.of() : elevationTags(ele);
}

Wyświetl plik

@ -37,6 +37,36 @@ class AerodromeLabelTest extends AbstractLayerTest {
))));
}
@Test
void testElevationFeet() {
assertFeatures(14, List.of(Map.of(
"ele", 100,
"ele_ft", 328
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"name", "osm name",
"ele", "328'",
"aerodrome", "international",
"iata", "123",
"icao", "1234"
))));
}
@Test
void testElevationFeetInches() {
assertFeatures(14, List.of(Map.of(
"ele", 100,
"ele_ft", 328
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"name", "osm name",
"ele", "328' 1\"",
"aerodrome", "international",
"iata", "123",
"icao", "1234"
))));
}
@Test
void testInternational() {
assertFeatures(14, List.of(Map.of(

Wyświetl plik

@ -75,6 +75,30 @@ class MountainPeakTest extends AbstractLayerTest {
))));
}
@Test
void testElevationFeet() {
assertFeatures(14, List.of(Map.of(
"class", "volcano",
"ele", 30,
"ele_ft", 100
)), process(pointFeature(Map.of(
"natural", "volcano",
"ele", "100'"
))));
}
@Test
void testElevationFeetInches() {
assertFeatures(14, List.of(Map.of(
"class", "volcano",
"ele", 31,
"ele_ft", 101
)), process(pointFeature(Map.of(
"natural", "volcano",
"ele", "100' 11\""
))));
}
@Test
void testSaddle() {
assertFeatures(14, List.of(Map.of(

Wyświetl plik

@ -12,6 +12,11 @@ public class Parse {
private static final Pattern INT_SUBSTRING_PATTERN = Pattern.compile("^(-?\\d+)(\\D|$)");
private static final Pattern TO_ROUND_INT_SUBSTRING_PATTERN = Pattern.compile("^(-?[\\d.]+)(\\D|$)");
// See https://wiki.openstreetmap.org/wiki/Map_features/Units
private static final Pattern DISTANCE =
Pattern.compile(
"(?<value>-?[\\d.]+)\\s*((?<mi>mi)|(?<m>m|$)|(?<km>km|kilom)|(?<ft>ft|')|(?<in>in|\")|(?<nmi>nmi|international nautical mile|nautical))",
Pattern.CASE_INSENSITIVE);
/** Returns {@code tag} as a long or null if invalid. */
public static Long parseLongOrNull(Object tag) {
@ -131,4 +136,49 @@ public class Parse {
throw new IllegalArgumentException("Unable to parse size: " + value);
}
}
/**
* Parses {@code tag} as a measure of distance with unit, converted to a round number of meters or {@code null} if
* invalid.
*
* See <a href="https://wiki.openstreetmap.org/wiki/Map_features/Units">Map features/Units</a> for the list of
* supported units.
*/
public static Double meters(Object tag) {
if (tag != null) {
if (tag instanceof Number num) {
return num.doubleValue();
}
var str = tag.toString();
var matcher = DISTANCE.matcher(str);
if (matcher.find()) {
try {
double value = Double.parseDouble(matcher.group("value"));
if (matcher.group("m") != null) {
// value *= 1;
} else if (matcher.group("km") != null) {
value *= 1000d;
} else if (matcher.group("mi") != null) {
value *= 1609.344;
} else if (matcher.group("nmi") != null) {
value *= 1852d;
} else if (matcher.group("ft") != null) {
value *= 12 * 0.0254;
// handle 15'3"
if (matcher.find() && matcher.group("in") != null) {
value += Double.parseDouble(matcher.group("value")) * 0.0254;
}
} else if (matcher.group("in") != null) {
value *= 0.0254;
} else {
return null;
}
return value;
} catch (NumberFormatException e) {
return null;
}
}
}
return null;
}
}

Wyświetl plik

@ -1,6 +1,7 @@
package com.onthegomap.planetiler.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
@ -52,6 +53,45 @@ class ParseTest {
assertEquals(out, Parse.direction(in));
}
@ParameterizedTest
@CsvSource(value = {
"1, 1",
"100, 100",
"-1.23 m, -1.23",
"100.2, 100.2",
"1m, 1",
"1meter, 1",
"100 meters, 100",
"1.5m, 1.5",
"1km, 1000",
"0.2km, 200",
"0.2 km, 200",
"1mi, 1609.344",
"1 mi, 1609.344",
"328', 99.974",
"328ft, 99.974",
"328'11\", 100.254",
"328ft 11in, 100.254",
"garbage, null",
"1nmi, 1852",
"1.5 nmi, 2778",
"1..5 nmi, null",
"36\", 0.9144",
"1'11\", 0.584",
"132.74', 40.4592",
"132'8.88\", 40.4592",
"1'11m, 0.305",
"1.5 smoots, null",
}, nullValues = "null")
void testLength(String in, Double out) {
Double result = Parse.meters(in);
if (out == null) {
assertNull(result);
} else {
assertEquals(result, out, 1e-3);
}
}
@ParameterizedTest
@CsvSource(value = {
"1, 1",