mirror of
https://github.com/cfpwastaken/planetiler-openmaptiles.git
synced 2026-02-04 04:21:08 +00:00
Optimize tile sizes (#112)
This commit is contained in:
@@ -178,7 +178,10 @@ public class Building implements
|
|||||||
@Override
|
@Override
|
||||||
public List<VectorTile.Feature> postProcess(int zoom,
|
public List<VectorTile.Feature> postProcess(int zoom,
|
||||||
List<VectorTile.Feature> items) throws GeometryException {
|
List<VectorTile.Feature> items) throws GeometryException {
|
||||||
return (mergeZ13Buildings && zoom == 13) ? FeatureMerge.mergeNearbyPolygons(items, 4, 4, 0.5, 0.5) : items;
|
return (mergeZ13Buildings && zoom == 13) ?
|
||||||
|
FeatureMerge.mergeNearbyPolygons(items, 4, 4, 0.5, 0.5) :
|
||||||
|
// reduces the size of some heavy z14 tiles with many small buildings by 60% or more
|
||||||
|
FeatureMerge.mergeMultiPolygon(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record BuildingRelationInfo(long id) implements OsmRelationInfo {
|
private record BuildingRelationInfo(long id) implements OsmRelationInfo {
|
||||||
|
|||||||
@@ -36,9 +36,14 @@ See https://github.com/openmaptiles/openmaptiles/blob/master/LICENSE.md for deta
|
|||||||
package org.openmaptiles.layers;
|
package org.openmaptiles.layers;
|
||||||
|
|
||||||
import com.onthegomap.planetiler.FeatureCollector;
|
import com.onthegomap.planetiler.FeatureCollector;
|
||||||
|
import com.onthegomap.planetiler.FeatureMerge;
|
||||||
|
import com.onthegomap.planetiler.ForwardingProfile;
|
||||||
|
import com.onthegomap.planetiler.VectorTile;
|
||||||
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
import com.onthegomap.planetiler.config.PlanetilerConfig;
|
||||||
|
import com.onthegomap.planetiler.geo.GeometryException;
|
||||||
import com.onthegomap.planetiler.stats.Stats;
|
import com.onthegomap.planetiler.stats.Stats;
|
||||||
import com.onthegomap.planetiler.util.Translations;
|
import com.onthegomap.planetiler.util.Translations;
|
||||||
|
import java.util.List;
|
||||||
import org.openmaptiles.generated.OpenMapTilesSchema;
|
import org.openmaptiles.generated.OpenMapTilesSchema;
|
||||||
import org.openmaptiles.generated.Tables;
|
import org.openmaptiles.generated.Tables;
|
||||||
|
|
||||||
@@ -51,7 +56,8 @@ import org.openmaptiles.generated.Tables;
|
|||||||
*/
|
*/
|
||||||
public class Housenumber implements
|
public class Housenumber implements
|
||||||
OpenMapTilesSchema.Housenumber,
|
OpenMapTilesSchema.Housenumber,
|
||||||
Tables.OsmHousenumberPoint.Handler {
|
Tables.OsmHousenumberPoint.Handler,
|
||||||
|
ForwardingProfile.FeaturePostProcessor {
|
||||||
|
|
||||||
public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) {}
|
public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) {}
|
||||||
|
|
||||||
@@ -62,4 +68,10 @@ public class Housenumber implements
|
|||||||
.setAttr(Fields.HOUSENUMBER, element.housenumber())
|
.setAttr(Fields.HOUSENUMBER, element.housenumber())
|
||||||
.setMinZoom(14);
|
.setMinZoom(14);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> list) throws GeometryException {
|
||||||
|
// reduces the size of some heavy z14 tiles with many repeated housenumber values by 60% or more
|
||||||
|
return FeatureMerge.mergeMultiPoint(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ public class Landcover implements
|
|||||||
if (clazz != null) {
|
if (clazz != null) {
|
||||||
features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
|
||||||
.setMinPixelSizeOverrides(MIN_PIXEL_SIZE_THRESHOLDS)
|
.setMinPixelSizeOverrides(MIN_PIXEL_SIZE_THRESHOLDS)
|
||||||
|
// default is 0.1, this helps reduce size of some heavy z7-10 tiles
|
||||||
|
.setPixelToleranceBelowZoom(10, 0.25)
|
||||||
.setAttr(Fields.CLASS, clazz)
|
.setAttr(Fields.CLASS, clazz)
|
||||||
.setAttr(Fields.SUBCLASS, subclass)
|
.setAttr(Fields.SUBCLASS, subclass)
|
||||||
.setNumPointsAttr(TEMP_NUM_POINTS_ATTR)
|
.setNumPointsAttr(TEMP_NUM_POINTS_ATTR)
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ import com.onthegomap.planetiler.stats.Stats;
|
|||||||
import com.onthegomap.planetiler.util.Parse;
|
import com.onthegomap.planetiler.util.Parse;
|
||||||
import com.onthegomap.planetiler.util.Translations;
|
import com.onthegomap.planetiler.util.Translations;
|
||||||
import com.onthegomap.planetiler.util.ZoomFunction;
|
import com.onthegomap.planetiler.util.ZoomFunction;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.openmaptiles.OpenMapTilesProfile;
|
import org.openmaptiles.OpenMapTilesProfile;
|
||||||
import org.openmaptiles.generated.OpenMapTilesSchema;
|
import org.openmaptiles.generated.OpenMapTilesSchema;
|
||||||
import org.openmaptiles.generated.Tables;
|
import org.openmaptiles.generated.Tables;
|
||||||
@@ -126,19 +126,20 @@ public class Landuse implements
|
|||||||
@Override
|
@Override
|
||||||
public List<VectorTile.Feature> postProcess(int zoom,
|
public List<VectorTile.Feature> postProcess(int zoom,
|
||||||
List<VectorTile.Feature> items) throws GeometryException {
|
List<VectorTile.Feature> items) throws GeometryException {
|
||||||
if (zoom < 6 || zoom > 12) {
|
List<VectorTile.Feature> toMerge = new ArrayList<>();
|
||||||
return items;
|
List<VectorTile.Feature> result = new ArrayList<>();
|
||||||
|
for (var item : items) {
|
||||||
|
if (FieldValues.CLASS_RESIDENTIAL.equals(item.attrs().get(Fields.CLASS))) {
|
||||||
|
toMerge.add(item);
|
||||||
} else {
|
} else {
|
||||||
// merging only merges polygons with class "residential" for z6-z12
|
result.add(item);
|
||||||
Map<Boolean, List<VectorTile.Feature>> splitLists =
|
}
|
||||||
items.stream().collect(Collectors.partitioningBy(
|
}
|
||||||
i -> FieldValues.CLASS_RESIDENTIAL.equals(i.attrs().get(Fields.CLASS)))
|
var merged = zoom <= 12 ?
|
||||||
);
|
FeatureMerge.mergeNearbyPolygons(toMerge, 1, 1, 0.1, 0.1) :
|
||||||
List<VectorTile.Feature> result = splitLists.get(Boolean.FALSE);
|
// reduces size of some heavy z13-14 tiles with lots of small polygons
|
||||||
List<VectorTile.Feature> toMerge = splitLists.get(Boolean.TRUE);
|
FeatureMerge.mergeMultiPolygon(toMerge);
|
||||||
var merged = FeatureMerge.mergeNearbyPolygons(toMerge, 1, 1, 0.1, 0.1);
|
|
||||||
result.addAll(merged);
|
result.addAll(merged);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -267,6 +267,8 @@ public class Place implements
|
|||||||
.putAttrs(names)
|
.putAttrs(names)
|
||||||
.setAttr(Fields.CLASS, element.place())
|
.setAttr(Fields.CLASS, element.place())
|
||||||
.setAttr(Fields.RANK, rank)
|
.setAttr(Fields.RANK, rank)
|
||||||
|
// TODO: This starts including every "state" point at z2, even before many countries show up.
|
||||||
|
// Instead we might want to set state min zooms based on rank from natural earth?
|
||||||
.setMinZoom(2)
|
.setMinZoom(2)
|
||||||
.setSortKey(rank);
|
.setSortKey(rank);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,8 +361,10 @@ public class Transportation implements
|
|||||||
// main attributes at all zoom levels (used for grouping <= z8)
|
// main attributes at all zoom levels (used for grouping <= z8)
|
||||||
.setAttr(Fields.CLASS, highwayClass)
|
.setAttr(Fields.CLASS, highwayClass)
|
||||||
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), highway))
|
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), highway))
|
||||||
.setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()))
|
|
||||||
.setAttr(Fields.NETWORK, networkType != null ? networkType.name : null)
|
.setAttr(Fields.NETWORK, networkType != null ? networkType.name : null)
|
||||||
|
// TODO: including brunnel at low zooms leads to some large 300-400+kb z4-7 tiles, instead
|
||||||
|
// we should only set brunnel if the line is above a certain length
|
||||||
|
.setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()))
|
||||||
// z8+
|
// z8+
|
||||||
.setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8)
|
.setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8)
|
||||||
// z9+
|
// z9+
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
|||||||
|
|
||||||
import com.onthegomap.planetiler.TestUtils;
|
import com.onthegomap.planetiler.TestUtils;
|
||||||
import com.onthegomap.planetiler.VectorTile;
|
import com.onthegomap.planetiler.VectorTile;
|
||||||
|
import com.onthegomap.planetiler.archive.Tile;
|
||||||
import com.onthegomap.planetiler.config.Arguments;
|
import com.onthegomap.planetiler.config.Arguments;
|
||||||
import com.onthegomap.planetiler.mbtiles.Mbtiles;
|
import com.onthegomap.planetiler.mbtiles.Mbtiles;
|
||||||
import com.onthegomap.planetiler.util.FileUtils;
|
import com.onthegomap.planetiler.util.FileUtils;
|
||||||
@@ -91,7 +92,7 @@ class OpenMapTilesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void ensureValidGeometries() throws Exception {
|
void ensureValidGeometries() throws Exception {
|
||||||
Set<Mbtiles.TileEntry> parsedTiles = TestUtils.getAllTiles(mbtiles);
|
Set<Tile> parsedTiles = TestUtils.getTiles(mbtiles);
|
||||||
for (var tileEntry : parsedTiles) {
|
for (var tileEntry : parsedTiles) {
|
||||||
var decoded = VectorTile.decode(gunzip(tileEntry.bytes()));
|
var decoded = VectorTile.decode(gunzip(tileEntry.bytes()));
|
||||||
for (VectorTile.Feature feature : decoded) {
|
for (VectorTile.Feature feature : decoded) {
|
||||||
|
|||||||
@@ -143,13 +143,24 @@ class BuildingTest extends AbstractLayerTest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Assertions.assertEquals(
|
Assertions.assertEquals(
|
||||||
2,
|
1, // merged into 1 multipolygon
|
||||||
profile.postProcessLayerFeatures(Building.LAYER_NAME, 14, List.of(poly1, poly2)).size()
|
profile.postProcessLayerFeatures(Building.LAYER_NAME, 14, List.of(poly1, poly2)).size()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
2, // merged into 1 multipolygon
|
||||||
|
profile.postProcessLayerFeatures(Building.LAYER_NAME, 14, List.of(poly1, poly2)).get(0).geometry().decode()
|
||||||
|
.getNumGeometries()
|
||||||
|
);
|
||||||
Assertions.assertEquals(
|
Assertions.assertEquals(
|
||||||
1,
|
1,
|
||||||
profile.postProcessLayerFeatures(Building.LAYER_NAME, 13, List.of(poly1, poly2)).size()
|
profile.postProcessLayerFeatures(Building.LAYER_NAME, 13, List.of(poly1, poly2)).size()
|
||||||
);
|
);
|
||||||
|
Assertions.assertEquals(
|
||||||
|
1,
|
||||||
|
profile.postProcessLayerFeatures(Building.LAYER_NAME, 13, List.of(poly1, poly2)).get(0).geometry().decode()
|
||||||
|
.getNumGeometries()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -120,12 +120,26 @@ class LanduseTest extends AbstractLayerTest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Assertions.assertEquals(
|
Assertions.assertEquals(
|
||||||
3,
|
List.of(1, 2),
|
||||||
profile.postProcessLayerFeatures(Landuse.LAYER_NAME, 13, List.of(poly1, poly2, poly3)).size()
|
profile.postProcessLayerFeatures(Landuse.LAYER_NAME, 13, List.of(poly1, poly2, poly3)).stream()
|
||||||
|
.map(d -> {
|
||||||
|
try {
|
||||||
|
return d.geometry().decode().getNumGeometries();
|
||||||
|
} catch (GeometryException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}).toList()
|
||||||
);
|
);
|
||||||
Assertions.assertEquals(
|
Assertions.assertEquals(
|
||||||
2,
|
List.of(1, 1),
|
||||||
profile.postProcessLayerFeatures(Landuse.LAYER_NAME, 12, List.of(poly1, poly2, poly3)).size()
|
profile.postProcessLayerFeatures(Landuse.LAYER_NAME, 12, List.of(poly1, poly2, poly3)).stream()
|
||||||
|
.map(d -> {
|
||||||
|
try {
|
||||||
|
return d.geometry().decode().getNumGeometries();
|
||||||
|
} catch (GeometryException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}).toList()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class VerifyMonacoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testStilInvalidWithOneTile() throws IOException {
|
void testStillInvalidWithOneTile() throws IOException {
|
||||||
mbtiles.createTablesWithIndexes();
|
mbtiles.createTablesWithIndexes();
|
||||||
mbtiles.metadataTable().setMetadata("name", "name");
|
mbtiles.metadataTable().setMetadata("name", "name");
|
||||||
try (var writer = mbtiles.newBatchedTileWriter()) {
|
try (var writer = mbtiles.newBatchedTileWriter()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user