From 304857b6e00f4d875412b0c29d0bccd7de476093 Mon Sep 17 00:00:00 2001 From: Jon Howell Date: Sun, 5 Oct 2025 23:55:47 -0700 Subject: [PATCH 1/2] Fix tile filtering to use area overlap instead of only testing the corner point of each tile. Fixes missing tiles at zoom levels 8 and above. The bug: At zoom 7 and below, all tiles are included in region zip files regardless of geography (line 161). At zoom 8+, tiles are filtered by checking if they fall within region boundaries. However, the original is_in() function only checked if a single point (the tile's upper-left corner) was inside the region boundary. This incorrectly excluded tiles that overlapped the region boundary but whose upper-left corner was outside. That caused a column of missing tiles -- less frustrating as you zoom in, but at zoom level 8, 1.4 degrees of longitude are missing. For example, you can't find KONP on the sectional chart at zoom 8; it's in column 39, which is absent from the zip file. To fix this, I replaced is_in with Area.overlaps. I replaced the chain of singleton arguments with an Area datatype to make the code and the overlaps condition more readable, and use that type to represent both the constant region definitions and the tile bounds. --- common.py | 60 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/common.py b/common.py index 325e29c..3e875cf 100644 --- a/common.py +++ b/common.py @@ -12,11 +12,38 @@ import projection -def is_in(area, lon, lat): - x_u, y_u, x_l, y_l = area - if lat <= y_u and lat >= y_l and lon >= x_u and lon <= x_l: - return True - return False +class Area: + """Represents a geographic rectangular area with longitude and latitude bounds.""" + + def __init__(self, lon_min, lat_max, lon_max, lat_min): + """ + Initialize an Area. + + Args: + lon_min: Western longitude bound (most negative) + lat_max: Northern latitude bound (most positive) + lon_max: Eastern longitude bound (most positive) + lat_min: Southern latitude bound (most negative) + """ + self.lon_min = lon_min + self.lon_max = lon_max + self.lat_min = lat_min + self.lat_max = lat_max + + def overlaps(self, other): + """ + Check if this area overlaps with another area. + + Args: + other: Another Area object + + Returns: + True if the areas overlap, False otherwise + """ + # Two rectangles overlap if they overlap in both dimensions + lon_overlap = self.lon_max >= other.lon_min and self.lon_min <= other.lon_max + lat_overlap = self.lat_max >= other.lat_min and self.lat_min <= other.lat_max + return lon_overlap and lat_overlap def list_crawl(url, match): @@ -94,15 +121,15 @@ def zip_charts(list_of_all_tiles, chart): # US geo regions regions = ["AK", "PAC", "NW", "SW", "NC", "EC", "SC", "NE", "SE"] region_coordinates = [ - (-180, 71, -126, 51), # AK - (-162, 24, -152, 18), # PAC - (-125, 50, -103, 40), # NW - (-125, 40, -103, 15), # SW - (-105, 50, -90, 37), # NC - (-95, 50, -80, 37), # EC - (-110, 37, -90, 15), # SC - (-80, 50, -60, 37), # NE - (-90, 37, -60, 15), # SE + Area(-180, 71, -126, 51), # AK + Area(-162, 24, -152, 18), # PAC + Area(-125, 50, -103, 40), # NW + Area(-125, 40, -103, 15), # SW + Area(-105, 50, -90, 37), # NC + Area(-95, 50, -80, 37), # EC + Area(-110, 37, -90, 15), # SC + Area(-80, 50, -60, 37), # NE + Area(-90, 37, -60, 15), # SE ] zip_files = [] manifest_files = [] @@ -126,11 +153,12 @@ def zip_charts(list_of_all_tiles, chart): y_tile = int(tokens[len(tokens) - 1].split(".")[0]) x_tile = int(tokens[len(tokens) - 2]) z_tile = int(tokens[len(tokens) - 3]) - lon_tile, lat_tile, lon1_tile, lat1_tile = projection.findBounds(x_tile, y_tile, z_tile) + lon_min, lat_max, lon_max, lat_min = projection.findBounds(x_tile, y_tile, z_tile) + tile_area = Area(lon_min, lat_max, lon_max, lat_min) # include zoom 7 and below in every chart for count in range(len(regions)): - if is_in(region_coordinates[count], lon_tile, lat_tile) or z_tile <= 7: + if region_coordinates[count].overlaps(tile_area) or z_tile <= 7: zip_files[count].write(tile) manifest_files[count].write(tile + "\n") From 00512817aceae9ff6bb5961cc68288fcc84e4992 Mon Sep 17 00:00:00 2001 From: Jon Howell Date: Wed, 8 Oct 2025 08:33:28 -0700 Subject: [PATCH 2/2] Fix tile filtering to use area overlap instead of only checking northwest corner. At zoom 8+, tiles were filtered by checking if the northwest corner was inside the region boundary. This incorrectly excluded tiles that overlapped the region but whose northwest corner was outside. --- common.py | 59 +++++++++++++------------------------------------------ 1 file changed, 14 insertions(+), 45 deletions(-) diff --git a/common.py b/common.py index 3e875cf..d285412 100644 --- a/common.py +++ b/common.py @@ -12,40 +12,6 @@ import projection -class Area: - """Represents a geographic rectangular area with longitude and latitude bounds.""" - - def __init__(self, lon_min, lat_max, lon_max, lat_min): - """ - Initialize an Area. - - Args: - lon_min: Western longitude bound (most negative) - lat_max: Northern latitude bound (most positive) - lon_max: Eastern longitude bound (most positive) - lat_min: Southern latitude bound (most negative) - """ - self.lon_min = lon_min - self.lon_max = lon_max - self.lat_min = lat_min - self.lat_max = lat_max - - def overlaps(self, other): - """ - Check if this area overlaps with another area. - - Args: - other: Another Area object - - Returns: - True if the areas overlap, False otherwise - """ - # Two rectangles overlap if they overlap in both dimensions - lon_overlap = self.lon_max >= other.lon_min and self.lon_min <= other.lon_max - lat_overlap = self.lat_max >= other.lat_min and self.lat_min <= other.lat_max - return lon_overlap and lat_overlap - - def list_crawl(url, match): charts = [] html_page = urllib.request.urlopen(url) @@ -121,15 +87,15 @@ def zip_charts(list_of_all_tiles, chart): # US geo regions regions = ["AK", "PAC", "NW", "SW", "NC", "EC", "SC", "NE", "SE"] region_coordinates = [ - Area(-180, 71, -126, 51), # AK - Area(-162, 24, -152, 18), # PAC - Area(-125, 50, -103, 40), # NW - Area(-125, 40, -103, 15), # SW - Area(-105, 50, -90, 37), # NC - Area(-95, 50, -80, 37), # EC - Area(-110, 37, -90, 15), # SC - Area(-80, 50, -60, 37), # NE - Area(-90, 37, -60, 15), # SE + (-180, 71, -126, 51), # AK + (-162, 24, -152, 18), # PAC + (-125, 50, -103, 40), # NW + (-125, 40, -103, 15), # SW + (-105, 50, -90, 37), # NC + (-95, 50, -80, 37), # EC + (-110, 37, -90, 15), # SC + (-80, 50, -60, 37), # NE + (-90, 37, -60, 15), # SE ] zip_files = [] manifest_files = [] @@ -154,11 +120,14 @@ def zip_charts(list_of_all_tiles, chart): x_tile = int(tokens[len(tokens) - 2]) z_tile = int(tokens[len(tokens) - 3]) lon_min, lat_max, lon_max, lat_min = projection.findBounds(x_tile, y_tile, z_tile) - tile_area = Area(lon_min, lat_max, lon_max, lat_min) # include zoom 7 and below in every chart for count in range(len(regions)): - if region_coordinates[count].overlaps(tile_area) or z_tile <= 7: + region_lon_min, region_lat_max, region_lon_max, region_lat_min = region_coordinates[count] + # Check if tile overlaps region anywhere + lon_overlap = lon_max >= region_lon_min and lon_min <= region_lon_max + lat_overlap = lat_max >= region_lat_min and lat_min <= region_lat_max + if (lon_overlap and lat_overlap) or z_tile <= 7: zip_files[count].write(tile) manifest_files[count].write(tile + "\n")