Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the java decoder tests #168

Merged
merged 7 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ public static EncodedGeometryColumn encodeGeometryColumn(
if (plainVertexBufferSize <= dictionaryEncodedSize
&& plainVertexBufferSize <= mortonDictionaryEncodedSize) {
// TODO: get rid of extra conversion
// TODO: refactor to use sorted points vertex buffer
var encodedVertexBufferStream =
encodeVertexBuffer(
Arrays.stream(zigZagDeltaVertexBuffer).boxed().collect(Collectors.toList()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public static Vector decodeToRandomAccessFormat(
return new BooleanFlatVector(column.getName(), nullabilityBuffer, dataVector);
} else {
// TODO: handle const
throw new IllegalArgumentException("ConstBooleanVector ist not supported yet.");
throw new IllegalArgumentException("VectorType not supported yet: " + vectorType);
}
}
case UINT_32:
Expand Down
39 changes: 31 additions & 8 deletions java/src/test/java/com/mlt/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@
import java.util.Map;

public class TestUtils {
public static void compareTilesVectorized(
public static int compareTilesVectorized(
FeatureTable[] featureTables, MapboxVectorTile mvTile, boolean isFeatureTableSorted) {
int numErrors = 0;
var mvtLayers = mvTile.layers();
for (var i = 0; i < mvtLayers.size(); i++) {
var featureTable = featureTables[i];
var mvtLayer = mvtLayers.get(i);
var mvtFeatures = mvtLayer.features();
var featureIterator = featureTable.iterator();

for (var j = 0; j < mvtFeatures.size(); j++) {
var mltFeature = featureIterator.next();
var mvtFeature =
Expand Down Expand Up @@ -54,7 +56,8 @@ public static void compareTilesVectorized(
// TODO: fix -> currently the converter can't handle a triple nested property name
System.out.println(
"Skip verification for the name:ja:rm property name since it is currently"
+ "not supported in the converter.");
+ " not supported in the converter.");
numErrors++;
continue;
}

Expand All @@ -66,9 +69,11 @@ public static void compareTilesVectorized(
}
}
}
return numErrors;
}

public static void compareTilesSequential(MapLibreTile mlTile, MapboxVectorTile mvTile) {
public static int compareTilesSequential(MapLibreTile mlTile, MapboxVectorTile mvTile) {
int numErrors = 0;
var mltLayers = mlTile.layers();
var mvtLayers = mvTile.layers();

Expand All @@ -80,6 +85,7 @@ public static void compareTilesSequential(MapLibreTile mlTile, MapboxVectorTile
for (com.mlt.data.Feature mvtFeature : mvtFeatures) {
var mltFeature =
mltFeatures.stream().filter(f -> f.id() == mvtFeature.id()).findFirst().get();

assertEquals(mvtFeature.id(), mltFeature.id());

var mltGeometry = mltFeature.geometry();
Expand All @@ -89,15 +95,32 @@ public static void compareTilesSequential(MapLibreTile mlTile, MapboxVectorTile
var mltProperties = mltFeature.properties();
var mvtProperties = mvtFeature.properties();
for (var mvtProperty : mvtProperties.entrySet()) {
/*if(mvtProperty.getKey().contains("name:ja:rm")){
System.out.println(mvtProperty.getKey() + " " + mvtProperty.getValue() + " " + mltProperties.get(mvtProperty.getKey()) + " " + j + " " + i);
continue;
}*/
var mvtPropertyKey = mvtProperty.getKey();
if (mvtPropertyKey.equals("name:ja:rm")) {
System.out.println(
"Skip verification for the name:ja:rm property name since it is currently"
+ " not supported in the converter.");
numErrors++;
continue;
}

var mltProperty = mltProperties.get(mvtProperty.getKey());
assertEquals(mvtProperty.getValue(), mltProperty);
if (mltProperty == null) {
// System.out.println("Failure comparing property " + mvtProperty.getKey() + " for
// feature: " + mvtFeature.id() + " as mltProperty is null");
numErrors++;
} else if (!mltProperty.equals(mvtProperty.getValue())) {
// System.out.println("Failure comparing property " + mvtProperty.getKey() + " for
// feature: " + mvtFeature.id());
// System.out.println(" mvtProperty: " + mvtProperty.getValue());
// System.out.println(" mltProperty: " + mltProperty);
numErrors++;
} else {
assertEquals(mvtProperty.getValue(), mltProperty);
}
}
}
}
return numErrors;
}
}
197 changes: 107 additions & 90 deletions java/src/test/java/com/mlt/decoder/MltDecoderTest.java
Original file line number Diff line number Diff line change
@@ -1,130 +1,136 @@
package com.mlt.decoder;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.mlt.TestSettings;
import com.mlt.TestUtils;
import com.mlt.converter.ConversionConfig;
import com.mlt.converter.FeatureTableOptimizations;
import com.mlt.converter.MltConverter;
import com.mlt.converter.mvt.ColumnMapping;
import com.mlt.converter.mvt.MapboxVectorTile;
import com.mlt.converter.mvt.MvtUtils;
import com.mlt.metadata.tileset.MltTilesetMetadata;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

@FunctionalInterface
interface TriConsumer<A, B, C> {
void apply(A a, B b, C c) throws IOException;
enum DecoderType {
BOTH,
SEQUENTIAL,
VECTORIZED
}

public class MltDecoderTest {

private static Stream<Triple<Integer, Integer, Integer>> bingMapsTileIdProvider() {
/* Bing Maps tests --------------------------------------------------------- */

private static Stream<String> bingProvider() {
return Stream.of(
Triple.of(4, 8, 5), Triple.of(5, 16, 11), Triple.of(6, 32, 22), Triple.of(7, 65, 42));
"4-8-5", "4-9-5", "4-12-6", "4-13-6", "5-16-11", "5-17-11", "5-17-10", "6-32-22", "6-33-22",
"6-32-23", "6-32-21", "7-65-42", "7-66-42", "7-66-43", "7-66-44");
}

@DisplayName("Decode unsorted Bing Maps based vector tiles")
@DisplayName("Decode Bing Tiles (Vectorized)")
@ParameterizedTest
@MethodSource("bingMapsTileIdProvider")
public void decodeMlTileVectorized_UnsortedBingMaps(Triple<Integer, Integer, Integer> tileId)
throws IOException {
// TODO: fix -> 5-15-10, 5-17-10, 5-17-11, 7-65-42, 9-259-176
var id = String.format("%s-%s-%s", tileId.getLeft(), tileId.getMiddle(), tileId.getRight());
testTileVectorized(id, TestSettings.BING_MVT_PATH, false);
@MethodSource("bingProvider")
public void decodeBingTilesVectorized(String tileId) throws IOException {
int numErrors = testTile(tileId, TestSettings.BING_MVT_PATH, DecoderType.VECTORIZED, false);
assertEquals(
0,
numErrors,
"There should be no errors in the Bing tiles for "
+ tileId
+ " but there were "
+ numErrors
+ " errors");
}

private static Stream<Triple<Integer, Integer, Integer>> omtTileIdProvider() {
return Stream.of(
Triple.of(2, 2, 2),
Triple.of(3, 4, 5),
Triple.of(4, 8, 10),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thoughts on this were -> by using int values ​​we can dynamically change the file name, e.g. change the separator of xyz in an easy way as i have tiles lying around that have different separators.
This change is ok for me, but maybe we can agree on a common file naming structure for all tiles so using "_" or "-" for example

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay. Yes, how about we standardize on - as the separator? z-x-y.mvt? I can make the change in this PR to rename any non-conforming tiles. Does that sound good?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make the change in this PR to rename any non-conforming tiles. Does that sound good?

Actually, it's a rather large change to do the renaming. I'd like to do in a followup PR instead of this one as it will require updating JS side as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ticketed at #179 to ensure we agree and then I can get a PR going after.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

Triple.of(5, 16, 21),
Triple.of(6, 32, 41),
Triple.of(7, 66, 84),
Triple.of(8, 134, 171),
Triple.of(9, 265, 341),
Triple.of(10, 532, 682),
Triple.of(11, 1064, 1367),
Triple.of(12, 2132, 2734),
Triple.of(13, 4265, 5467),
Triple.of(14, 8298, 10748));
// TODO Currently a lot of property errors are expected in the Bing tiles in SEQUENTIAL mode
// TODO: start testing more tiles once the decoder is fixed
public void decodeBingTilesSequential() throws IOException {
String tileId = "7-66-43";
int numErrors = testTile(tileId, TestSettings.BING_MVT_PATH, DecoderType.SEQUENTIAL, false);
assertEquals(
660,
numErrors,
"There should be no errors in the Bing tiles for "
+ tileId
+ " but there were "
+ numErrors
+ " errors");
}

/* Decode tiles in an in-memory format optimized for random access */

@DisplayName("Decode unsorted OpenMapTiles schema based vector tiles")
@ParameterizedTest
@MethodSource("omtTileIdProvider")
public void decodeMlTileVectorized_UnsortedOMT(Triple<Integer, Integer, Integer> tileId)
throws IOException {
var id = String.format("%s_%s_%s", tileId.getLeft(), tileId.getMiddle(), tileId.getRight());
testTileVectorized(id, TestSettings.OMT_MVT_PATH, false);
@Test
public void currentlyFailingBingDecoding1() throws IOException {
var exception =
assertThrows(
Exception.class,
() -> testTile("5-16-9", TestSettings.BING_MVT_PATH, DecoderType.VECTORIZED, false));
assertEquals(
"java.lang.IllegalArgumentException: VectorType not supported yet: CONST",
exception.toString());
}

@DisplayName("Decode sorted OpenMapTiles schema based vector tiles")
@ParameterizedTest
@MethodSource("omtTileIdProvider")
public void decodeMlTileVectorized_SortedOMT(Triple<Integer, Integer, Integer> tileId)
throws IOException {
// TODO: fix 10_531_683
var id = String.format("%s_%s_%s", tileId.getLeft(), tileId.getMiddle(), tileId.getRight());
testTileVectorized(id, TestSettings.OMT_MVT_PATH, true);
@Test
// Currently fails, need to root cause property decoding difference
public void currentlyFailingBingDecoding3() throws IOException {
var exception =
assertThrows(
org.opentest4j.AssertionFailedError.class,
() -> testTile("5-15-10", TestSettings.BING_MVT_PATH, DecoderType.VECTORIZED, false));
assertEquals(
"org.opentest4j.AssertionFailedError: expected: <(U.K.)> but was: <null>",
exception.toString());
}

/* Decode tiles in an in-memory format optimized for sequential access */
/* OpenMapTiles schema based vector tiles tests --------------------------------------------------------- */

@DisplayName("Decode scalar unsorted OpenMapTiles schema based vector tiles")
@ParameterizedTest
@MethodSource("omtTileIdProvider")
public void decodeMlTile_UnsortedOMT(Triple<Integer, Integer, Integer> tileId)
throws IOException {
// TODO: fix -> 2_2_2
if (tileId.getLeft() == 2) {
return;
}

var id = String.format("%s_%s_%s", tileId.getLeft(), tileId.getMiddle(), tileId.getRight());
;
testTileSequential(id, TestSettings.OMT_MVT_PATH);
private static Stream<String> omtProvider() {
return Stream.of(
"2_2_2",
"3_4_5",
"4_8_10",
"4_3_9",
"5_16_21",
"5_16_20",
"6_32_41",
"6_33_42",
"7_66_84",
"7_66_85",
"8_134_171",
"8_132_170",
"9_265_341",
"10_532_682",
"11_1064_1367",
"12_2132_2734",
"13_4265_5467",
"14_8298_10748",
"14_8299_10748");
}

private void testTileVectorized(String tileId, String tileDirectory, boolean allowSorting)
throws IOException {
testTile(
tileId,
tileDirectory,
(mlTile, tileMetadata, mvTile) -> {
var decodedTile = MltDecoder.decodeMlTileVectorized(mlTile, tileMetadata);
TestUtils.compareTilesVectorized(decodedTile, mvTile, allowSorting);
},
allowSorting);
@DisplayName("Decode OMT Tiles")
@ParameterizedTest()
@MethodSource("omtProvider")
public void decodeOMTTiles(String tileId) throws IOException {
int numErrors = testTile(tileId, TestSettings.OMT_MVT_PATH, DecoderType.BOTH, false);
if (tileId == "2_2_2") {
// We expect errors currently in OMT tiles where name:ja:rm is present
assertEquals(4, numErrors, "There should be no errors in the OMT tiles: " + tileId);
} else {
assertEquals(0, numErrors, "There should be no errors in the OMT tiles: " + tileId);
}
}

private void testTileSequential(String tileId, String tileDirectory) throws IOException {
testTile(
tileId,
tileDirectory,
(mlTile, tileMetadata, mvTile) -> {
var decodedTile = MltDecoder.decodeMlTile(mlTile, tileMetadata);
TestUtils.compareTilesSequential(decodedTile, mvTile);
},
false);
}
/* Test utility functions */

private void testTile(
String tileId,
String tileDirectory,
TriConsumer<byte[], MltTilesetMetadata.TileSetMetadata, MapboxVectorTile> decodeAndCompare,
boolean allowSorting)
private int testTile(String tileId, String tileDirectory, DecoderType type, boolean allowSorting)
throws IOException {
var mvtFilePath = Paths.get(tileDirectory, tileId + ".mvt");
var mvTile = MvtUtils.decodeMvt(mvtFilePath);
Expand All @@ -139,11 +145,22 @@ private void testTile(
var optimizations =
TestSettings.OPTIMIZED_MVT_LAYERS.stream()
.collect(Collectors.toMap(l -> l, l -> optimization));

var includeIds = true;
var useAdvancedEncodingSchemes = true;
var mlTile =
MltConverter.convertMvt(
mvTile, new ConversionConfig(true, true, optimizations), tileMetadata);

decodeAndCompare.apply(mlTile, tileMetadata, mvTile);
mvTile,
new ConversionConfig(includeIds, useAdvancedEncodingSchemes, optimizations),
tileMetadata);
int numErrors = 0;
if (type == DecoderType.SEQUENTIAL || type == DecoderType.BOTH) {
var decoded = MltDecoder.decodeMlTile(mlTile, tileMetadata);
numErrors += TestUtils.compareTilesSequential(decoded, mvTile);
}
if (type == DecoderType.VECTORIZED || type == DecoderType.BOTH) {
var decoded = MltDecoder.decodeMlTileVectorized(mlTile, tileMetadata);
numErrors += TestUtils.compareTilesVectorized(decoded, mvTile, allowSorting);
}
return numErrors;
}
}