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

Pre-collide places layer point features and reduce density / overlaps #300

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
57 changes: 38 additions & 19 deletions tiles/src/main/java/com/protomaps/basemap/layers/Places.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ static int getSortKey(double minZoom, int kindRank, int populationRank, long pop
// (nvkelso 20230803) floats with significant single decimal precision
// but results in "Too many possible values"
// Order ASCENDING (smaller manually curated Natural Earth min_zoom win over larger values, across kinds)
.orderByInt((int) minZoom, 0, 15)
// minZoom is a float with 1 significant digit for manually curated places
.orderByInt((int) (minZoom * 10), 0, 150)
// Order ASCENDING (smaller values win, countries then locality then neighbourhood, breaks ties for same minZoom)
.thenByInt(kindRank, 0, 6)
.thenByInt(kindRank, 0, 12)
// Order DESCENDING (larger values win, San Francisco rank 11 wins over Oakland rank 10)
.thenByInt(populationRank, 15, 0)
// Disabled to allow population log to have larger range
//.thenByInt(populationRank, 15, 0)
// Order DESCENDING (larger values win, Millbrea 40k wins over San Bruno 20k, both rank 7)
.thenByLog(population, 1000000000, 1, 100)
.thenByLog(population, 40000000, 1, 100)
// Order ASCENDING (shorter strings are better than longer strings for map display and adds predictability)
.thenByInt(name == null ? 0 : name.length(), 0, 31)
.get();
Expand All @@ -55,15 +57,30 @@ static int getSortKey(double minZoom, int kindRank, int populationRank, long pop

private static final ZoomFunction<Number> LOCALITY_GRID_SIZE_ZOOM_FUNCTION =
ZoomFunction.fromMaxZoomThresholds(Map.of(
6, 32,
7, 64
3, 24,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This GRID_SIZE sequence could be simplified, but needs to match the zoom steps in the GRID_LIMIT sequence below.

4, 24,
5, 24,
7, 24,
8, 32,
9, 32,
10, 32,
11, 24,
14, 24,
15, 16
), 0);

private static final ZoomFunction<Number> LOCALITY_GRID_LIMIT_ZOOM_FUNCTION =
ZoomFunction.fromMaxZoomThresholds(Map.of(
6, 8,
7, 6,
9, 4
3, 1,
4, 1,
5, 1,
6, 1,
8, 1,
9, 1,
10, 1,
11, 1,
14, 2,
15, 3
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You could argue the zoom 15 step could be a larger int, or not limited at all

), 0);

public void processOsm(SourceFeature sf, FeatureCollector features) {
Expand Down Expand Up @@ -148,7 +165,7 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
// This minZoom can be changed to smaller value in the NE data join step below
minZoom = 11.0f;
maxZoom = 15.0f;
kindRank = 3;
kindRank = 4;
if (population == 0) {
minZoom = 12.0f;
population = 1000;
Expand All @@ -159,7 +176,7 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
// This minZoom can be changed to smaller value in the NE data join step below
minZoom = 11.0f;
maxZoom = 15.0f;
kindRank = 3;
kindRank = 5;
if (population == 0) {
minZoom = 12.0f;
population = 200;
Expand All @@ -170,7 +187,7 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
// This minZoom can be changed to smaller value in the NE data join step below
minZoom = 13.0f;
maxZoom = 15.0f;
kindRank = 3;
kindRank = 6;
if (population == 0) {
minZoom = 14.0f;
population = 100;
Expand All @@ -181,7 +198,7 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
// This minZoom can be changed to smaller value in the NE data join step below
minZoom = 13.0f;
maxZoom = 15.0f;
kindRank = 3;
kindRank = 7;
if (population == 0) {
minZoom = 14.0f;
population = 50;
Expand All @@ -193,7 +210,7 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
// This minZoom can be changed to smaller value in the NE data join step below
minZoom = 13.0f;
maxZoom = 15.0f;
kindRank = 3;
kindRank = 8;
if (population == 0) {
minZoom = 14.0f;
population = 1000;
Expand All @@ -204,19 +221,19 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
kind = "neighbourhood";
minZoom = 11.0f;
maxZoom = 15.0f;
kindRank = 4;
kindRank = 9;
break;
case "quarter":
kind = "macrohood";
minZoom = 10.0f;
maxZoom = 15.0f;
kindRank = 5;
kindRank = 10;
break;
case "neighbourhood":
kind = "neighbourhood";
minZoom = 12.0f;
maxZoom = 15.0f;
kindRank = 6;
kindRank = 11;
break;
}

Expand Down Expand Up @@ -287,12 +304,14 @@ public void processOsm(SourceFeature sf, FeatureCollector features) {
//feat.setSortKey(minZoom * 1000 + 400 - populationRank * 200 + placeNumber.incrementAndGet());
feat.setSortKey(getSortKey(minZoom, kindRank, populationRank, population, sf.getString("name")));

// This is only necessary when prepping for raster renderers
feat.setBufferPixels(16);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If Protomaps is only targeting vector renderers than this could be set to 0 to reduce file size

Copy link
Member

Choose a reason for hiding this comment

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

I think we still want a buffer for drawing townspots correctly very close to tile edges?

Copy link
Contributor

@msbarry msbarry Sep 18, 2024

Choose a reason for hiding this comment

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

If 256 isn't evenly dvisible by your label grid size then you need a bit extra in order for label grid squares to be aware of points in the square from neighboring tiles otherwise the density might get off a bit around tile boundaries. Looks like 24 is the only problematic one. I think a buffer of 16 should be the minimum that works with label grid of 24.

If you want to go smaller you could either switch label grid to a power of 2 or use FeatureMerge.removePointsOutsideBuffer in postProcess

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the min buffer pixel limit should be label grid size - gcd(256, label grid size) - I should probably update planetiler to use that limit and post-filter to whatever you set here so users don't need to now or care about that implementation detail...


// We set the sort keys so the label grid can be sorted predictably (bonus: tile features also sorted)
// NOTE: The buffer needs to be consistent with the innteral grid pixel sizes
//feat.setPointLabelGridSizeAndLimit(13, 64, 4); // each cell in the 4x4 grid can have 4 items
feat.setPointLabelGridPixelSize(LOCALITY_GRID_SIZE_ZOOM_FUNCTION)
.setPointLabelGridLimit(LOCALITY_GRID_LIMIT_ZOOM_FUNCTION)
.setBufferPixels(64);
.setPointLabelGridLimit(LOCALITY_GRID_LIMIT_ZOOM_FUNCTION);

// and also whenever you set a label grid size limit, make sure you increase the buffer size so no
// label grid squares will be the consistent between adjacent tiles
Expand Down