Open CrazyBug-11 opened 1 month ago
For points the label grid size/limit does this. It divides each tile into a grid of a certain size then limits the number of features that can go in each cell. Maybe if we just add the capability for this to apply to small polygons too that would accomplish what you're trying to do?
My dataset consists of polygon (area) data, not point data. The current issue is that setting min_size=0 results in too many features at lower zoom levels, which increases memory requirements. However, setting min_size=256/4096 causes some areas to be blank, and in such cases, the tile size at lower zoom levels inflates to 20-30MB. Therefore, I hope to have a feature that can evenly sample features within a tile, limit the number of features, and also control the tile size.
If source code modification is necessary, I would appreciate any advice you can provide. Thank you very much.
On each feature you can call setPointLabelGridPixelSize
and setPointLabelGridLimit
(there are a few aliases) and FeatureRenderer.renderPoint
calls getPointLabelGridPixelSizeAtZoom
and getPointLabelGridLimitAtZoom
to get these values when generating vector tile features from the input feature. We'd have to add similar handling to renderLineOrPolygon
probably using either the first or center point of the polygon or line so that it passes the groupInfo
parameter into encodeAndEmitFeature
. The rest of the pipeline should handle size/limit automatically, but we should add an end to end test to PlanetilerTests
to be sure.
Once this is implemented it would be good to rename all of the setPointLabelGrid...
methods to setLabelGrid...
but we'd have to add the new methods, mark old as deprecated then remove later since it changes the external API.
Feel free to submit a PR, this would be useful for others as well. For example the openmaptiles profile should be limiting mountain_peak linestrings (cliffs and ridges) using label grid like this, but because it's not implemented we just end up with very crowded tiles.
delete
I have added the following code to calculate the centroid of each feature after executing sliceIntoTiles(). However, after testing, I found that the results did not change, and the label grid functionality is still not implemented as expected. Currently, the pipeline does not automatically handle size/limit constraints. The constraints are set using methods like setPointLabelGridPixelSize and setPointLabelGridLimit.
Could you please advise on which other parts need to be modified besides this?
private void writeTileFeatures_labelGrid(int zoom, long id, FeatureCollector.Feature feature, TiledGeometry sliced,
Map<String, Object> attrs) {
int emitted = 0;
RenderedFeature.Group groupInfo = null;
for (var entry : sliced.getTileData().entrySet()) {
TileCoord tile = entry.getKey();
try {
List<List<CoordinateSequence>> geoms = entry.getValue();
Coordinate centroid;
Geometry geom;
int scale = 0;
PrecisionModel precision = null;
if (feature.isPolygon()) {
geom = GeometryCoordinateSequences.reassemblePolygons(geoms);
precision = GeoUtils.getPrecision(zoom, config.maxzoom());
geom = GeoUtils.snapAndFixPolygon(geom, stats, "render", precision);
centroid = getGlobalCoord(tile, geom.getCentroid());
geom = geom.reverse();
} else {
geom = GeometryCoordinateSequences.reassembleLineStrings(geoms);
centroid = getGlobalCoord(tile, geom.getCentroid());
scale = Math.max(config.maxzoom(), 14) - zoom;
scale = Math.min(31 - 14, scale);
}
if (!geom.isEmpty()) {
// for "label grid" point density limiting, compute the grid square that this point sits in
// only valid if not a multipoint
boolean hasLabelGrid = feature.hasLabelGrid();
if (hasLabelGrid) {
int tilesAtZoom = 1 << zoom;
Coordinate coord = new Coordinate(GeoUtils.getWorldX(centroid.x) * tilesAtZoom,
GeoUtils.getWorldY(centroid.y) * tilesAtZoom);
double labelGridTileSize = feature.getPointLabelGridPixelSizeAtZoom(zoom) / 256d;
groupInfo = labelGridTileSize < 1d / 4096d ? null : new RenderedFeature.Group(
GeoUtils.labelGridId(tilesAtZoom, labelGridTileSize, coord),
feature.getPointLabelGridLimitAtZoom(zoom)
);
}
encodeAndEmitFeature(feature, id, attrs, tile, geom, groupInfo, scale);
emitted++;
}
} catch (GeometryException e) {
e.log(stats, "write_tile_features", "Error writing tile " + tile + " feature " + feature);
}
}
// polygons that span multiple tiles contain detail about the outer edges separate from the filled tiles, so emit
// filled tiles now
if (feature.isPolygon()) {
emitted += emitFilledTiles_labelGrid(id, feature, sliced, groupInfo);
}
stats.emittedFeatures(zoom, feature.getLayer(), emitted);
}
private Coordinate getGlobalCoord(TileCoord tileCoord, Point coordinate) {
double worldWidthAtZoom = Math.pow(2, tileCoord.z());
double minX = tileCoord.x() / worldWidthAtZoom;
double maxX = (tileCoord.x() + 1) / worldWidthAtZoom;
double minY = (tileCoord.y() + 1) / worldWidthAtZoom;
double maxY = tileCoord.y() / worldWidthAtZoom;
double relativeX = coordinate.getX() / 256.0;
double relativeY = coordinate.getY() / 256.0;
double worldX = minX + relativeX * (maxX - minX);
double worldY = maxY - relativeY * (maxY - minY);
double lon = normalizeLongitude(GeoUtils.getWorldLon(worldX));
double lat = GeoUtils.getWorldLat(worldY);
return new Coordinate(lon, lat);
}
private static double normalizeLongitude(double lon) {
while (lon > 180) {
lon -= 360;
}
while (lon < -180) {
lon += 360;
}
return lon;
}
I noticed that parameters related to grid limits are set in the encodeValue method. However, I could not find where the logic to delete features that exceed the limit is implemented. Could you please provide guidance on this?
if (group != null) {
packer.packLong(group.group());
packer.packInt(group.limit());
}
packe
Can you post what you have in a PR and I can test from there?
It would be helpful to provide a max_feature_count parameter that can remove elements exceeding the limit based on the density within the tile, ensuring a more uniform appearance across the entire tile.