1212import com .protomaps .basemap .feature .FeatureId ;
1313import java .util .ArrayList ;
1414import java .util .List ;
15+ import java .util .Locale ;
1516import java .util .OptionalInt ;
1617
1718@ SuppressWarnings ("java:S1192" )
@@ -25,25 +26,40 @@ public String name() {
2526 return LAYER_NAME ;
2627 }
2728
29+ /**
30+ * Reads a Natural Earth attribute, falling back to the upper case spelling of the column.
31+ * <p>
32+ * Column name casing is not consistent across the Natural Earth distributions: the sqlite package is entirely lower
33+ * case, while the GeoPackage package preserves whatever the source shapefiles use. That is lower case for most
34+ * physical layers but upper case for the admin layers this handler reads, and some tables mix both.
35+ */
36+ private static String getNeString (SourceFeature sf , String key ) {
37+ Object value = sf .getTag (key );
38+ if (value == null ) {
39+ value = sf .getTag (key .toUpperCase (Locale .ROOT ));
40+ }
41+ return value == null ? null : value .toString ();
42+ }
43+
2844 public void processNe (SourceFeature sf , FeatureCollector features ) {
2945 String sourceLayer = sf .getSourceLayer ();
3046 String kind = "" ;
3147 int adminLevel = 2 ;
3248 boolean disputed = false ;
33- int themeMinZoom = 0 ;
3449 int themeMaxZoom = 0 ;
3550
3651 if (sourceLayer .equals ("ne_10m_admin_0_boundary_lines_land" ) ||
3752 sourceLayer .equals ("ne_10m_admin_0_boundary_lines_map_units" ) ||
3853 sourceLayer .equals ("ne_10m_admin_0_boundary_lines_disputed_areas" ) ||
3954 sourceLayer .equals ("ne_10m_admin_1_states_provinces_lines" )) {
40- themeMinZoom = 4 ;
4155 themeMaxZoom = 5 ;
4256 kind = "tz_boundary" ;
4357 }
4458
59+ String featureClass = getNeString (sf , "featurecla" );
60+
4561 if (!kind .isEmpty ()) {
46- switch (sf . getString ( "featurecla" , "" ) ) {
62+ switch (featureClass == null ? "" : featureClass ) {
4763 case "Disputed (please verify)" -> {
4864 kind = "country" ;
4965 disputed = true ;
@@ -130,19 +146,19 @@ public void processNe(SourceFeature sf, FeatureCollector features) {
130146 }
131147 }
132148
133- if (sf .canBeLine () && sf .getString ("min_zoom" ) != null && (!kind .isEmpty () && !kind .equals ("tz_boundary" ))) {
134- var minZoom = Double .parseDouble (sf .getString ("min_zoom" )) - 1.0 ;
149+ String minZoomString = getNeString (sf , "min_zoom" );
150+
151+ if (sf .canBeLine () && minZoomString != null && (!kind .isEmpty () && !kind .equals ("tz_boundary" ))) {
152+ var minZoom = Double .parseDouble (minZoomString ) - 1.0 ;
135153 int sortRank = 289 - (disputed ? 1 : 0 );
136154 features .line (this .name ())
137155 .setAttr ("kind" , kind )
138156 .setAttr ("kind_detail" , adminLevel )
139157 .setAttr ("sort_rank" , sortRank )
140158 .setSortKey (sortRank )
141159 .setAttr ("disputed" , disputed ? true : null )
142- .setAttr ("brk_a3" , sf .getString ("brk_a3" ))
143- .setZoomRange (
144- sf .getString ("min_zoom" ) == null ? themeMinZoom : (int ) minZoom ,
145- themeMaxZoom )
160+ .setAttr ("brk_a3" , getNeString (sf , "brk_a3" ))
161+ .setZoomRange ((int ) minZoom , themeMaxZoom )
146162 .setMinPixelSize (0 )
147163 .setBufferPixels (8 );
148164 }
0 commit comments