Skip to content

Use city bounding box from API and accumulate metadata regions#10

Closed
swhitt wants to merge 4 commits into
JustDr00py:mainfrom
swhitt:city-bbox-metadata
Closed

Use city bounding box from API and accumulate metadata regions#10
swhitt wants to merge 4 commits into
JustDr00py:mainfrom
swhitt:city-bbox-metadata

Conversation

@swhitt
Copy link
Copy Markdown

@swhitt swhitt commented Dec 28, 2025

User description

⚠️ Depends on #9 - merge that first.

Inspired by #4. Two changes:

  • Use actual city bounding box from Nominatim API (not center ± buffer)
  • Accumulate regions in metadata.json instead of overwriting

PR Type

Enhancement, Tests


Description

  • Use actual city bounding box from Nominatim API instead of center point with buffer

  • Add tqdm progress bar for tile download tracking with detailed statistics

  • Implement DownloadStatus enum to track downloaded, cached, and failed tiles

  • Accumulate metadata regions instead of overwriting, supporting multiple cities

  • Improve progress reporting with elapsed time and per-tile status breakdown


Diagram Walkthrough

flowchart LR
  A["Nominatim API"] -->|"boundingbox data"| B["CityLookup.get_coordinates"]
  B -->|"north/south/east/west"| C["get_bounding_box_for_cities"]
  C -->|"expanded bounds"| D["generate_tiles"]
  D -->|"DownloadStatus enum"| E["download_tile"]
  E -->|"new/cached/failed"| F["tqdm progress bar"]
  F -->|"tile stats"| G["generate_metadata"]
  G -->|"accumulate regions"| H["metadata.json"]
Loading

File Walkthrough

Relevant files
Enhancement
meshtastic_tiles.py
Add bounding box API support and metadata accumulation     

meshtastic_tiles.py

  • Added DownloadStatus enum to track tile download states (DOWNLOADED,
    CACHED, FAILED)
  • Modified get_coordinates() to extract and return bounding box
    coordinates (north, south, east, west) from Nominatim API
  • Updated get_bounding_box_for_cities() to use actual city boundaries
    instead of center point with buffer
  • Integrated tqdm progress bar in generate_tiles() with real-time
    statistics for new, cached, and failed tiles
  • Refactored generate_metadata() to accumulate regions in metadata.json
    with support for updating existing regions and expanding bounds/zoom
    ranges
  • Added region_name parameter to track multiple cities in metadata
  • Improved console output with elapsed time and detailed tile statistics
+127/-48
Documentation
README.md
Update dependencies and remove completed task                       

README.md

  • Added tqdm to pip install dependencies
  • Removed "Progress bars" from common improvements list as it's now
    implemented
+1/-2     

@qodo-code-review
Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing edge checks: New code assumes external API/JSON fields exist (e.g., boundingbox, valid JSON structure)
and uses broad exception handling that can silently degrade behavior (e.g., counting
failures without logging context), reducing diagnosability and resilience.

Referred Code
        bbox = result['boundingbox']
        return {
            'name': result.get('display_name', 'Unknown'),
            'lat': float(result['lat']),
            'lon': float(result['lon']),
            'north': float(bbox[1]),
            'south': float(bbox[0]),
            'east': float(bbox[3]),
            'west': float(bbox[2]),
            'type': result.get('type', 'unknown')
        }

    except Exception as e:
        print(f"Error looking up coordinates for {query}: {e}")
        return None

def get_bounding_box_for_cities(self, cities, buffer_km=10):
    """Get bounding box for multiple cities with buffer in kilometers"""
    all_coords = []

    print(f"Looking up coordinates for {len(cities)} cities...")


 ... (clipped 268 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add defaults for old metadata migration

Add default values to the old metadata migration logic to prevent None values
from being processed, which could cause runtime errors.

meshtastic_tiles.py [295-304]

 # Migrate old format if needed
 if "regions" not in metadata:
     old_region = {
         "name": metadata.get("name", "Unknown"),
-        "bounds": metadata.get("bounds"),
-        "zoom": [metadata.get("minzoom"), metadata.get("maxzoom")],
+        "bounds": metadata.get("bounds", []),
+        "zoom": [metadata.get("minzoom", 0), metadata.get("maxzoom", 0)],
         "source": metadata.get("source", source),
-        "generated": metadata.get("generated")
+        "generated": metadata.get("generated", "")
     }
     metadata = {"regions": [old_region], "format": "png"}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies a potential TypeError if the old metadata.json is missing keys, and the proposed fix with default values makes the migration logic more robust.

Medium
Correctly calculate global zoom range

Refactor the global zoom range calculation to be more explicit by separately
finding the minimum of all minimum zooms and the maximum of all maximum zooms.

meshtastic_tiles.py [346-348]

 # Update global zoom range
-all_zooms = [z for r in metadata["regions"] for z in r["zoom"]]
-metadata["minzoom"] = min(all_zooms)
-metadata["maxzoom"] = max(all_zooms)
+all_min_zooms = [r["zoom"][0] for r in metadata["regions"]]
+all_max_zooms = [r["zoom"][1] for r in metadata["regions"]]
+metadata["minzoom"] = min(all_min_zooms)
+metadata["maxzoom"] = max(all_max_zooms)

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 2

__

Why: The existing code is correct and works as intended. The suggested change is slightly more explicit but offers only a marginal improvement in readability and robustness against a hypothetical edge case that is unlikely to occur with the current implementation.

Low
General
Handle corrupted metadata gracefully

Add a try-except block to handle potential json.JSONDecodeError or IOError when
loading metadata.json, falling back to a new metadata structure if the file is
invalid.

meshtastic_tiles.py [292-294]

 if metadata_path.exists():
-    with open(metadata_path) as f:
-        metadata = json.load(f)
+    try:
+        with open(metadata_path) as f:
+            metadata = json.load(f)
+    except (json.JSONDecodeError, IOError):
+        print("Warning: metadata.json is invalid or unreadable, creating new metadata")
+        metadata = {"regions": [], "format": "png"}
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This suggestion improves the script's robustness by handling corrupted or unreadable metadata.json files gracefully, preventing crashes and allowing the program to continue.

Low
Display failed tile count in progress

Update the tqdm progress bar to display the count of failed tile downloads in
real-time, in addition to new and cached tiles.

meshtastic_tiles.py [262-275]

 with tqdm(total=total_tiles, desc="Processing", unit="tile") as pbar:
     for future in as_completed(futures):
         try:
             tile_path, status = future.result()
             if status == DownloadStatus.DOWNLOADED:
                 new_tiles += 1
             elif status == DownloadStatus.CACHED:
                 cached_tiles += 1
             elif status == DownloadStatus.FAILED:
                 failed_tiles += 1
         except Exception:
             failed_tiles += 1
         pbar.update(1)
-        pbar.set_postfix_str(f"new: {new_tiles}, cached: {cached_tiles}")
+        pbar.set_postfix_str(f"new: {new_tiles}, cached: {cached_tiles}, failed: {failed_tiles}")
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: This is a good suggestion for improving user experience by providing more detailed real-time feedback in the progress bar. It's a useful enhancement but not a critical bug fix.

Low
  • More

@swhitt swhitt closed this Dec 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant