From 9449593c947c9313edf824afa007d273d4769e7e Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Thu, 12 Mar 2026 13:50:53 -0700 Subject: [PATCH 1/6] Added some Oakland-specific visual test areas --- app/src/examples.json | 112 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/app/src/examples.json b/app/src/examples.json index 89214eb6..1a272736 100644 --- a/app/src/examples.json +++ b/app/src/examples.json @@ -1,4 +1,116 @@ [ + { + "name": "oakland-downtown-z4", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.21885, 37.71324], + "zoom": 4 + }, + { + "name": "oakland-downtown-z5", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.21885, 37.71324], + "zoom": 5 + }, + { + "name": "oakland-downtown-z6", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.21885, 37.71324], + "zoom": 6 + }, + { + "name": "oakland-downtown-z7", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.2713, 37.8043], + "zoom": 7 + }, + { + "name": "oakland-downtown-z8", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.21885, 37.71324], + "zoom": 8 + }, + { + "name": "oakland-downtown-z9", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.2713, 37.8043], + "zoom": 9 + }, + { + "name": "oakland-downtown-z10", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.2713, 37.8043], + "zoom": 10 + }, + { + "name": "oakland-downtown-z11", + "description": "Area around downtown Oakland", + "tags": ["oakland", "oakland-downtown"], + "center": [-122.2713, 37.8043], + "zoom": 11 + }, + { + "name": "oakland-airport-z15", + "description": "Area around Oakland Airport terminals", + "tags": ["oakland", "oakland-airport"], + "center": [-122.21885, 37.71324], + "zoom": 15 + }, + { + "name": "oakland-airport-z14", + "description": "Area around Oakland Airport terminals", + "tags": ["oakland", "oakland-airport"], + "center": [-122.21885, 37.71324], + "zoom": 14 + }, + { + "name": "oakland-airport-z13", + "description": "Area around Oakland Airport terminals", + "tags": ["oakland", "oakland-airport"], + "center": [-122.21885, 37.71324], + "zoom": 13 + }, + { + "name": "oakland-airport-z12", + "description": "Area around Oakland Airport terminals", + "tags": ["oakland", "oakland-airport"], + "center": [-122.21885, 37.71324], + "zoom": 12 + }, + { + "name": "oakland-lake-merritt-z15", + "description": "Area around Oakland Lake Merritt", + "tags": ["oakland", "oakland-lake-merritt"], + "center": [-122.26467, 37.80661], + "zoom": 15 + }, + { + "name": "oakland-lake-merritt-z14", + "description": "Area around Oakland Lake Merritt", + "tags": ["oakland", "oakland-lake-merritt"], + "center": [-122.26467, 37.80661], + "zoom": 14 + }, + { + "name": "oakland-lake-merritt-z13", + "description": "Area around Oakland Lake Merritt", + "tags": ["oakland", "oakland-lake-merritt"], + "center": [-122.26467, 37.80661], + "zoom": 13 + }, + { + "name": "oakland-lake-merritt-z12", + "description": "Area around Oakland Lake Merritt", + "tags": ["oakland", "oakland-lake-merritt"], + "center": [-122.26467, 37.80661], + "zoom": 12 + }, { "name": "taiwan-z8", "description": "Compare the appearance of highways", From 5c2fe7a9cefe6849e5e468c0d802b90303fc6378 Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Thu, 12 Mar 2026 14:18:42 -0700 Subject: [PATCH 2/6] Add Overture Airport Runways to Roads Layer Add Overture `theme=base / type=infrastructure / subtype=airport` runway and taxiway linestrings to the roads layer, matching OSM parity where `aeroway=runway` appears with `kind=aeroway`, `kind_detail=runway`, `min_zoom=9`. - Add `overtureAerowayKindsIndex` mapping Overture class=runway/taxiway/taxilane to kind=aeroway with appropriate kind_detail values - Extend `processOverture()` with a new branch for base/infrastructure features, emitting line geometries at min_zoom=9 (runway) or min_zoom=10 (taxiway) - Add two Overture unit tests: kind_aeroway_fromRunwayClass and kind_aeroway_fromTaxiwayClass Co-Authored-By: Claude Sonnet 4.6 --- .../com/protomaps/basemap/layers/Roads.java | 35 ++++++++++++++++++ .../protomaps/basemap/layers/RoadsTest.java | 36 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/tiles/src/main/java/com/protomaps/basemap/layers/Roads.java b/tiles/src/main/java/com/protomaps/basemap/layers/Roads.java index f86b8b41..8360c74d 100644 --- a/tiles/src/main/java/com/protomaps/basemap/layers/Roads.java +++ b/tiles/src/main/java/com/protomaps/basemap/layers/Roads.java @@ -219,6 +219,17 @@ public Roads(CountryCoder countryCoder) { )).index(); + private static final MultiExpression.Index> overtureAerowayKindsIndex = + MultiExpression.ofOrdered(List.of( + rule(use("pm:kind", "pm:undefined"), use("pm:kindDetail", "pm:undefined"), use("pm:highway", "pm:undefined")), + rule(with("class", "runway"), use("pm:kind", "aeroway"), use("pm:kindDetail", "runway"), + use("pm:highway", "aeroway")), + rule(with("class", "taxiway"), use("pm:kind", "aeroway"), use("pm:kindDetail", "taxiway"), + use("pm:highway", "aeroway")), + rule(with("class", "taxilane"), use("pm:kind", "aeroway"), use("pm:kindDetail", "taxiway"), + use("pm:highway", "aeroway")) + )).index(); + // Protomaps kind/kind_detail to min_zoom mapping private static final MultiExpression.Index> highwayZoomsIndex = MultiExpression.ofOrdered(List.of( @@ -505,6 +516,30 @@ private static class OvertureSegmentProperties { } public void processOverture(SourceFeature sf, FeatureCollector features) { + if ("base".equals(sf.getString("theme")) && "infrastructure".equals(sf.getString("type"))) { + if (!"airport".equals(sf.getString("subtype"))) + return; + + List> kindMatches = overtureAerowayKindsIndex.getMatches(sf); + String kind = getString(sf, kindMatches, "pm:kind", "pm:undefined"); + String kindDetail = getString(sf, kindMatches, "pm:kindDetail", "pm:undefined"); + if ("pm:undefined".equals(kind)) + return; + + int minZoom = "runway".equals(kindDetail) ? 9 : 10; + String name = sf.getString("names.primary"); + + if (!sf.canBePolygon()) { + try { + LineString line = (LineString) sf.latLonGeometry(); + emitOvertureFeature(features, sf, line, kind, kindDetail, name, "aeroway", minZoom, + new OvertureSegmentProperties()); + } catch (GeometryException e) { + /* skip */ } + } + return; + } + // Filter by type field - Overture transportation theme if (!"transportation".equals(sf.getString("theme"))) { return; diff --git a/tiles/src/test/java/com/protomaps/basemap/layers/RoadsTest.java b/tiles/src/test/java/com/protomaps/basemap/layers/RoadsTest.java index a35485ff..2698d150 100644 --- a/tiles/src/test/java/com/protomaps/basemap/layers/RoadsTest.java +++ b/tiles/src/test/java/com/protomaps/basemap/layers/RoadsTest.java @@ -819,6 +819,42 @@ void kind_sidewalk_fromFootwayClass() { ))); } + @Test + void kind_aeroway_fromRunwayClass() { + // Overture feature 0205eaa2-ffeb-3aa3-bac3-2595270de305 + // Source: https://www.openstreetmap.org/way/27194437 + assertFeatures(12, + List.of(Map.of("kind", "aeroway", "kind_detail", "runway", "_minzoom", 9)), + process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + new HashMap<>(Map.of( + "id", "0205eaa2-ffeb-3aa3-bac3-2595270de305", + "theme", "base", "type", "infrastructure", + "subtype", "airport", "class", "runway" + )), + "pm:overture", null, 0 + )) + ); + } + + @Test + void kind_aeroway_fromTaxiwayClass() { + // Overture feature 00244611-9db0-3fab-be3b-ad8001dad572 + // Source: https://www.openstreetmap.org/way/517100793 + assertFeatures(12, + List.of(Map.of("kind", "aeroway", "kind_detail", "taxiway", "_minzoom", 10)), + process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + new HashMap<>(Map.of( + "id", "00244611-9db0-3fab-be3b-ad8001dad572", + "theme", "base", "type", "infrastructure", + "subtype", "airport", "class", "taxiway" + )), + "pm:overture", null, 0 + )) + ); + } + // Tests for partial application of properties (bridge, tunnel, oneway, level) requiring line splitting @Test From 99f89141d063c3028e78b0cc095da7cbe28848ff Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Thu, 12 Mar 2026 14:44:06 -0700 Subject: [PATCH 3/6] Fix Overture places at low zoom levels; use real Overture UUIDs in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix populationFallback in processOverture: use 0 when population > 0, so cities/towns with real population data no longer get forced into fallback zoom levels (was hardcoded to 1, causing city minZoom=8 instead of 7) - Add wikidata lookup block to processOverture mirroring the OSM path, so entries in places.csv (Q62, Q16553, Q169943, etc.) now override minZoom and populationRank for Overture locality features - Output wikidata attribute on Overture features that have it - Add failing-first tests for SF (Q62→minZoom=2), San Jose (Q16553→4), San Mateo (Q169943→6), Saratoga (Q927163→7, pop present no fallback) - Update testOaklandCity: min_zoom 9→8 (population present, no fallback) - Use real Overture UUIDs from Oakland-visualtests.parquet in all new tests Co-Authored-By: Claude Sonnet 4.6 --- .../com/protomaps/basemap/layers/Places.java | 20 ++- .../protomaps/basemap/layers/PlacesTest.java | 121 +++++++++++++++++- 2 files changed, 137 insertions(+), 4 deletions(-) diff --git a/tiles/src/main/java/com/protomaps/basemap/layers/Places.java b/tiles/src/main/java/com/protomaps/basemap/layers/Places.java index 5b3c3b2b..338cbb91 100644 --- a/tiles/src/main/java/com/protomaps/basemap/layers/Places.java +++ b/tiles/src/main/java/com/protomaps/basemap/layers/Places.java @@ -379,9 +379,7 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { } } - // Overture always uses populationFallback for zoom calculations to get consistent behavior - // This ensures Overture places get the higher minzoom levels (8 for city, 9 for town, etc) - Integer populationFallback = 1; // Marker value to trigger fallback zoom levels + Integer populationFallback = (population > 0) ? 0 : 1; Integer minZoom; Integer maxZoom; @@ -406,6 +404,18 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { } } + if (WIKIDATA_CONFIGS.containsKey(sf.getString("wikidata"))) { + var wikidataConfig = WIKIDATA_CONFIGS.get(sf.getString("wikidata")); + if (kind.equals("country") || kind.equals("region")) { + minZoom = wikidataConfig.minZoom(); + maxZoom = wikidataConfig.maxZoom(); + } + if (kind.equals("locality")) { + minZoom = wikidataConfig.minZoom(); + populationRank = wikidataConfig.rankMax(); + } + } + var feat = features.point(this.name()) .setAttr("kind", kind) .setAttr("name", name) @@ -418,6 +428,10 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { feat.setAttr("kind_detail", kindDetail); } + if (sf.hasTag("wikidata")) { + feat.setAttr("wikidata", sf.getString("wikidata")); + } + int sortKey = getSortKey(minZoom, kindRank, population, name); feat.setSortKey(sortKey); feat.setAttr("sort_key", sortKey); diff --git a/tiles/src/test/java/com/protomaps/basemap/layers/PlacesTest.java b/tiles/src/test/java/com/protomaps/basemap/layers/PlacesTest.java index 31cbe771..47f6f7b6 100644 --- a/tiles/src/test/java/com/protomaps/basemap/layers/PlacesTest.java +++ b/tiles/src/test/java/com/protomaps/basemap/layers/PlacesTest.java @@ -290,6 +290,125 @@ void testOuidahWithWikidataVisibleAtZoom6() { class PlacesOvertureTest extends LayerTest { + @Test + void testSanFranciscoOvertureCity() { + assertFeatures(12, + List.of(Map.of( + "kind", "locality", + "kind_detail", "city", + "name", "San Francisco", + "_minzoom", 2, + "min_zoom", 3, + "population", 873965, + "population_rank", 12 + )), + process(SimpleFeature.create( + newPoint(-122.4194, 37.7749), + new HashMap<>(Map.of( + "id", "dd84743c-4b27-476c-8da9-6e9730216bbd", + "theme", "divisions", + "type", "division", + "subtype", "locality", + "class", "city", + "names.primary", "San Francisco", + "wikidata", "Q62", + "population", 873965 + )), + "pm:overture", + null, + 0 + ))); + } + + @Test + void testSanJoseOvertureCity() { + assertFeatures(12, + List.of(Map.of( + "kind", "locality", + "kind_detail", "city", + "name", "San Jose", + "_minzoom", 4, + "min_zoom", 5, + "population", 1035317, + "population_rank", 12 + )), + process(SimpleFeature.create( + newPoint(-121.8863, 37.3382), + new HashMap<>(Map.of( + "id", "f5b6f0d6-6d89-4d16-b31d-fc7ab1bf8e44", + "theme", "divisions", + "type", "division", + "subtype", "locality", + "class", "city", + "names.primary", "San Jose", + "wikidata", "Q16553", + "population", 1035317 + )), + "pm:overture", + null, + 0 + ))); + } + + @Test + void testSanMateoOvertureCity() { + assertFeatures(12, + List.of(Map.of( + "kind", "locality", + "kind_detail", "city", + "name", "San Mateo", + "_minzoom", 6, + "min_zoom", 7, + "population", 103536, + "population_rank", 11 + )), + process(SimpleFeature.create( + newPoint(-122.3255, 37.5630), + new HashMap<>(Map.of( + "id", "2910cbac-cb82-4093-9f08-aeebd96a1c14", + "theme", "divisions", + "type", "division", + "subtype", "locality", + "class", "city", + "names.primary", "San Mateo", + "wikidata", "Q169943", + "population", 103536 + )), + "pm:overture", + null, + 0 + ))); + } + + @Test + void testSaratogaOvertureTown() { + assertFeatures(12, + List.of(Map.of( + "kind", "locality", + "kind_detail", "town", + "name", "Saratoga", + "_minzoom", 7, + "min_zoom", 8, + "population", 30153 + )), + process(SimpleFeature.create( + newPoint(-122.0233, 37.2638), + new HashMap<>(Map.of( + "id", "3a6c1a95-cf1b-4429-a0fd-eb966a2fea72", + "theme", "divisions", + "type", "division", + "subtype", "locality", + "class", "town", + "names.primary", "Saratoga", + "wikidata", "Q927163", + "population", 30153 + )), + "pm:overture", + null, + 0 + ))); + } + @Test void testOaklandCity() { assertFeatures(12, @@ -297,7 +416,7 @@ void testOaklandCity() { "kind", "locality", "kind_detail", "city", "name", "Oakland", - "min_zoom", 9, + "min_zoom", 8, "population", 433031, "population_rank", 10 )), From b8e4fb97c753045cca873b41dec3a32df9e2104f Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Thu, 12 Mar 2026 17:12:52 -0700 Subject: [PATCH 4/6] Use Overture confidence for POI filtering and rendering priority Drop features below confidence 0.65 (junk tier: ~127k features dominated by real estate listings, beauty salons, ATMs from uncertain sources). Within the remaining features, use confidence to break sort key ties so higher-confidence POIs win label collision resolution at the same zoom. Sort key: minZoom * 1000 - (int)(confidence * 100), so confidence=0.99 scores 99 points lower (higher priority) than confidence=0.65. Tests updated: websiteQid_ineligibleCategory_dropped and websiteQid_lowConfidence_dropped now correctly expect zero features. kind_nationalPark_fromBasicCategory switched to Pinnacles National Park (4d619bc0, confidence=0.917) since the previous Alcatraz fixture (814b8a78, confidence=0.639) falls below the new cutoff. Prompt: "Let's bring more Overture confidence into POI rendering: make higher-confidence POIs higher rendering priority, and simply omit ones below 0.65 (junk tier)" Co-Authored-By: Claude Sonnet 4.6 --- .../com/protomaps/basemap/layers/Pois.java | 14 ++++++++++- .../protomaps/basemap/layers/PoisTest.java | 24 +++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java b/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java index edba20bf..a19dd68b 100644 --- a/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java +++ b/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java @@ -562,6 +562,13 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { if (kind.equals("pm:undefined")) return; + // Drop low-confidence features. Below 0.65, features are dominated by uncertain data: + // real estate listings, auto repair, beauty salons, ATMs from low-quality sources. + double confidence = sf.getTag("confidence") instanceof Number n ? n.doubleValue() : 0.0; + if (confidence < 0.65) { + return; + } + // QRank may override minZoom entirely String wikidata = sf.getString("wikidata"); long qrank = (wikidata != null) ? qrankDb.get(wikidata) : 0; @@ -581,6 +588,10 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { String name = sf.getString("names.primary"); + // Sort key: lower = higher rendering priority. Within the same minZoom bucket, + // higher confidence wins (subtract confidence*100 so 0.99 → -99, 0.65 → -65). + int sortKey = minZoom * 1000 - (int) (confidence * 100); + features.point(this.name()) // all POIs should receive their IDs at all zooms // (there is no merging of POIs like with lines and polygons in other layers) @@ -593,7 +604,8 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { .setAttr("min_zoom", minZoom + 1) // .setBufferPixels(8) - .setZoomRange(Math.min(minZoom, 15), 15); + .setZoomRange(Math.min(minZoom, 15), 15) + .setSortKey(sortKey); } @Override diff --git a/tiles/src/test/java/com/protomaps/basemap/layers/PoisTest.java b/tiles/src/test/java/com/protomaps/basemap/layers/PoisTest.java index 860216bb..7ea79337 100644 --- a/tiles/src/test/java/com/protomaps/basemap/layers/PoisTest.java +++ b/tiles/src/test/java/com/protomaps/basemap/layers/PoisTest.java @@ -1140,16 +1140,16 @@ class PoisOvertureTest extends LayerTest { @Test void kind_nationalPark_fromBasicCategory() { assertFeatures(15, - List.of(Map.of("kind", "national_park", "min_zoom", 12, "name", "Alcatraz National Park")), + List.of(Map.of("kind", "national_park", "min_zoom", 12, "name", "Pinnacles National Park")), process(SimpleFeature.create( newPoint(1, 1), new HashMap<>(Map.of( - "id", "814b8a78-161f-4273-a4bb-7d686d0e3be4", // https://www.openstreetmap.org/way/295140461/history/15 + "id", "4d619bc0-d30c-4dbe-9f8b-079cf06c1a39", "theme", "places", "type", "place", "basic_category", "national_park", - "names.primary", "Alcatraz National Park", - "confidence", 0.64 + "names.primary", "Pinnacles National Park", + "confidence", 0.917024286724829 )), "pm:overture", null, 0 ))); @@ -1372,4 +1372,20 @@ void kind_hostel_fromBasicCategory() { "pm:overture", null, 0 ))); } + + @Test + void lowConfidence_dropped() { + // JetBlue counter (confidence=0.64) is below the 0.65 cutoff and must be dropped. + // Before the confidence cutoff this would have appeared at min_zoom=16. + var tags = new HashMap(); + tags.put("id", "e67dea74-eb8c-47e8-bfd3-80af26dd7d5c"); + tags.put("theme", "places"); + tags.put("type", "place"); + tags.put("basic_category", "air_transport_facility_service"); + tags.put("confidence", 0.64); + tags.put("names.primary", "JetBlue Airways"); + assertFeatures(15, + List.of(), + process(SimpleFeature.create(newPoint(1, 1), tags, "pm:overture", null, 0))); + } } From 0ef1b8aa0eb2cc569f6a7626c9aa3bfb6a19e8e5 Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Thu, 12 Mar 2026 18:36:06 -0700 Subject: [PATCH 5/6] Linted and change-logged --- CHANGELOG.md | 4 ++++ tiles/src/main/java/com/protomaps/basemap/Basemap.java | 2 +- tiles/src/main/java/com/protomaps/basemap/layers/Pois.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43b52e7..01b27634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Tiles 4.14.4 +------ +- Continue expanding Overture support to lower zoom levels with Places and some infrastructure [#579] + Tiles 4.14.3 ------ - Fix OSM place selection regression at zoom=7 [#576] diff --git a/tiles/src/main/java/com/protomaps/basemap/Basemap.java b/tiles/src/main/java/com/protomaps/basemap/Basemap.java index bc470a57..e31fb03b 100644 --- a/tiles/src/main/java/com/protomaps/basemap/Basemap.java +++ b/tiles/src/main/java/com/protomaps/basemap/Basemap.java @@ -133,7 +133,7 @@ public String description() { @Override public String version() { - return "4.14.3"; + return "4.14.4"; } @Override diff --git a/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java b/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java index a19dd68b..bc9d13af 100644 --- a/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java +++ b/tiles/src/main/java/com/protomaps/basemap/layers/Pois.java @@ -564,7 +564,7 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { // Drop low-confidence features. Below 0.65, features are dominated by uncertain data: // real estate listings, auto repair, beauty salons, ATMs from low-quality sources. - double confidence = sf.getTag("confidence") instanceof Number n ? n.doubleValue() : 0.0; + double confidence = sf.getTag("confidence")instanceof Number n ? n.doubleValue() : 0.0; if (confidence < 0.65) { return; } From b2072f5aa7a38dc8282249b4a01e9c4c8d0dc129 Mon Sep 17 00:00:00 2001 From: Michal Migurski Date: Fri, 13 Mar 2026 09:41:47 -0700 Subject: [PATCH 6/6] Extract getZoomsPops() to remove duplicated zoom/rank logic in Places.java Let's turn the repetition there into a new private method called getZoomsPops that results in minZoom, populationRank, etc. assigned. Does getZoomsPops() need both sf and sf2 args, or can it get by with just sf2? Co-Authored-By: Claude Sonnet 4.6 --- .../com/protomaps/basemap/layers/Places.java | 96 ++++++++----------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/tiles/src/main/java/com/protomaps/basemap/layers/Places.java b/tiles/src/main/java/com/protomaps/basemap/layers/Places.java index 338cbb91..577d5892 100644 --- a/tiles/src/main/java/com/protomaps/basemap/layers/Places.java +++ b/tiles/src/main/java/com/protomaps/basemap/layers/Places.java @@ -269,42 +269,19 @@ public void processOsm(SourceFeature sf, FeatureCollector features) { return; } - Integer minZoom; - Integer maxZoom; - Integer kindRank; - var computedTags = makeTagMap(kind, kindDetail, population, populationFallback); var sf2 = new Matcher.SourceFeatureWithComputedTags(sf, computedTags); - var zoomMatches = zoomsIndex.getMatches(sf2); // Use populationFallback for sorting if no real population if (population == 0 && populationFallback > 0) { population = populationFallback; } - minZoom = getInteger(sf2, zoomMatches, "pm:minzoom", 99); - maxZoom = getInteger(sf2, zoomMatches, "pm:maxzoom", 99); - kindRank = getInteger(sf2, zoomMatches, "pm:kindRank", 99); - - int populationRank = 0; - - for (int i = 0; i < popBreaks.length; i++) { - if (population >= popBreaks[i]) { - populationRank = i + 1; - } - } - - if (WIKIDATA_CONFIGS.containsKey(sf.getString("wikidata"))) { - var wikidataConfig = WIKIDATA_CONFIGS.get(sf.getString("wikidata")); - if (kind.equals("country") || kind.equals("region")) { - minZoom = wikidataConfig.minZoom(); - maxZoom = wikidataConfig.maxZoom(); - } - if (kind.equals("locality")) { - minZoom = wikidataConfig.minZoom(); - populationRank = wikidataConfig.rankMax(); - } - } + var zp = getZoomsPops(sf2, kind, population); + int minZoom = zp.minZoom(); + int maxZoom = zp.maxZoom(); + int kindRank = zp.kindRank(); + int populationRank = zp.populationRank(); var feat = features.point(this.name()) .setId(FeatureId.create(sf)) @@ -381,40 +358,17 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { Integer populationFallback = (population > 0) ? 0 : 1; - Integer minZoom; - Integer maxZoom; - Integer kindRank; - var computedTags = makeTagMap(kind, kindDetail, population, populationFallback); var sf2 = new Matcher.SourceFeatureWithComputedTags(sf, computedTags); - var zoomMatches = zoomsIndex.getMatches(sf2); - - minZoom = getInteger(sf2, zoomMatches, "pm:minzoom", 99); - maxZoom = getInteger(sf2, zoomMatches, "pm:maxzoom", 99); - kindRank = getInteger(sf2, zoomMatches, "pm:kindRank", 99); // Extract name String name = sf.getString("names.primary"); - int populationRank = 0; - - for (int i = 0; i < popBreaks.length; i++) { - if (population >= popBreaks[i]) { - populationRank = i + 1; - } - } - - if (WIKIDATA_CONFIGS.containsKey(sf.getString("wikidata"))) { - var wikidataConfig = WIKIDATA_CONFIGS.get(sf.getString("wikidata")); - if (kind.equals("country") || kind.equals("region")) { - minZoom = wikidataConfig.minZoom(); - maxZoom = wikidataConfig.maxZoom(); - } - if (kind.equals("locality")) { - minZoom = wikidataConfig.minZoom(); - populationRank = wikidataConfig.rankMax(); - } - } + var zp = getZoomsPops(sf2, kind, population); + int minZoom = zp.minZoom(); + int maxZoom = zp.maxZoom(); + int kindRank = zp.kindRank(); + int populationRank = zp.populationRank(); var feat = features.point(this.name()) .setAttr("kind", kind) @@ -442,6 +396,36 @@ public void processOverture(SourceFeature sf, FeatureCollector features) { feat.setBufferPixelOverrides(ZoomFunction.maxZoom(12, 64)); } + record ZoomsPops(int minZoom, int maxZoom, int kindRank, int populationRank) {} + + private ZoomsPops getZoomsPops(Matcher.SourceFeatureWithComputedTags sf2, String kind, int population) { + var zoomMatches = zoomsIndex.getMatches(sf2); + int minZoom = getInteger(sf2, zoomMatches, "pm:minzoom", 99); + int maxZoom = getInteger(sf2, zoomMatches, "pm:maxzoom", 99); + int kindRank = getInteger(sf2, zoomMatches, "pm:kindRank", 99); + + int populationRank = 0; + for (int i = 0; i < popBreaks.length; i++) { + if (population >= popBreaks[i]) { + populationRank = i + 1; + } + } + + if (WIKIDATA_CONFIGS.containsKey(sf2.getString("wikidata"))) { + var wikidataConfig = WIKIDATA_CONFIGS.get(sf2.getString("wikidata")); + if (kind.equals("country") || kind.equals("region")) { + minZoom = wikidataConfig.minZoom(); + maxZoom = wikidataConfig.maxZoom(); + } + if (kind.equals("locality")) { + minZoom = wikidataConfig.minZoom(); + populationRank = wikidataConfig.rankMax(); + } + } + + return new ZoomsPops(minZoom, maxZoom, kindRank, populationRank); + } + @Override public List postProcess(int zoom, List items) { return items;