|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Debug script to test static map generation and diagnose firewall/network issues. |
| 4 | +
|
| 5 | +This script tests: |
| 6 | +1. Outbound HTTP connectivity to tile servers |
| 7 | +2. Static map generation with a test resource |
| 8 | +3. Network error handling |
| 9 | +""" |
| 10 | + |
| 11 | +import asyncio |
| 12 | +import logging |
| 13 | +import sys |
| 14 | +from pathlib import Path |
| 15 | + |
| 16 | +# Add project root to path |
| 17 | +project_root = Path(__file__).parent.parent |
| 18 | +sys.path.insert(0, str(project_root)) |
| 19 | + |
| 20 | +from app.services.static_map_service import StaticMapService |
| 21 | + |
| 22 | +logging.basicConfig( |
| 23 | + level=logging.DEBUG, |
| 24 | + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", |
| 25 | +) |
| 26 | +logger = logging.getLogger(__name__) |
| 27 | + |
| 28 | + |
| 29 | +def test_tile_server_connectivity(): |
| 30 | + """Test if we can reach the Carto tile server.""" |
| 31 | + import urllib.request |
| 32 | + import urllib.error |
| 33 | + |
| 34 | + test_url = "http://a.basemaps.cartocdn.com/rastertiles/light_all/1/0/0.png" |
| 35 | + logger.info(f"Testing connectivity to tile server: {test_url}") |
| 36 | + |
| 37 | + try: |
| 38 | + req = urllib.request.Request(test_url) |
| 39 | + req.add_header("User-Agent", "BTAA-Geospatial-API/1.0") |
| 40 | + with urllib.request.urlopen(req, timeout=10) as response: |
| 41 | + status = response.getcode() |
| 42 | + content_length = len(response.read()) |
| 43 | + logger.info( |
| 44 | + f"✓ Successfully connected to tile server: HTTP {status}, " |
| 45 | + f"received {content_length} bytes" |
| 46 | + ) |
| 47 | + return True |
| 48 | + except urllib.error.URLError as e: |
| 49 | + logger.error(f"✗ Failed to connect to tile server: {e}") |
| 50 | + logger.error(" This suggests outbound HTTP traffic may be blocked by firewall") |
| 51 | + return False |
| 52 | + except Exception as e: |
| 53 | + logger.error(f"✗ Unexpected error connecting to tile server: {e}") |
| 54 | + return False |
| 55 | + |
| 56 | + |
| 57 | +def test_static_map_generation(): |
| 58 | + """Test static map generation with a sample bounding box.""" |
| 59 | + logger.info("Testing static map generation...") |
| 60 | + |
| 61 | + # Test with a small bounding box (Minneapolis area) |
| 62 | + test_bbox = "ENVELOPE(-93.5, -93.0, 45.0, 44.9)" |
| 63 | + test_resource_id = "debug-test-map" |
| 64 | + |
| 65 | + try: |
| 66 | + service = StaticMapService() |
| 67 | + logger.info(f"Using maps directory: {service.maps_dir}") |
| 68 | + |
| 69 | + # Try to generate a map |
| 70 | + map_path = service.generate_map(test_resource_id, test_bbox) |
| 71 | + |
| 72 | + if map_path and map_path.exists(): |
| 73 | + logger.info(f"✓ Successfully generated static map: {map_path}") |
| 74 | + logger.info(f" File size: {map_path.stat().st_size} bytes") |
| 75 | + return True |
| 76 | + else: |
| 77 | + logger.error("✗ Map generation returned None or file doesn't exist") |
| 78 | + return False |
| 79 | + |
| 80 | + except Exception as e: |
| 81 | + logger.error(f"✗ Error generating static map: {e}", exc_info=True) |
| 82 | + return False |
| 83 | + |
| 84 | + |
| 85 | +def test_py_staticmaps_import(): |
| 86 | + """Test if py-staticmaps can be imported and basic functionality works.""" |
| 87 | + logger.info("Testing py-staticmaps import and basic functionality...") |
| 88 | + |
| 89 | + try: |
| 90 | + import staticmaps |
| 91 | + |
| 92 | + logger.info(f"✓ py-staticmaps imported successfully (version: {staticmaps.__version__ if hasattr(staticmaps, '__version__') else 'unknown'})") |
| 93 | + |
| 94 | + # Try creating a context |
| 95 | + context = staticmaps.Context() |
| 96 | + logger.info("✓ Created staticmaps.Context()") |
| 97 | + |
| 98 | + return True |
| 99 | + except ImportError as e: |
| 100 | + logger.error(f"✗ Failed to import py-staticmaps: {e}") |
| 101 | + logger.error(" Install with: pip install py-staticmaps") |
| 102 | + return False |
| 103 | + except Exception as e: |
| 104 | + logger.error(f"✗ Error testing py-staticmaps: {e}") |
| 105 | + return False |
| 106 | + |
| 107 | + |
| 108 | +def main(): |
| 109 | + """Run all diagnostic tests.""" |
| 110 | + logger.info("=" * 60) |
| 111 | + logger.info("Static Map Generation Diagnostic Tool") |
| 112 | + logger.info("=" * 60) |
| 113 | + logger.info("") |
| 114 | + |
| 115 | + results = {} |
| 116 | + |
| 117 | + # Test 1: Import check |
| 118 | + logger.info("Test 1: Checking py-staticmaps import...") |
| 119 | + results["import"] = test_py_staticmaps_import() |
| 120 | + logger.info("") |
| 121 | + |
| 122 | + # Test 2: Network connectivity |
| 123 | + logger.info("Test 2: Testing tile server connectivity...") |
| 124 | + results["connectivity"] = test_tile_server_connectivity() |
| 125 | + logger.info("") |
| 126 | + |
| 127 | + # Test 3: Map generation |
| 128 | + if results["import"]: |
| 129 | + logger.info("Test 3: Testing static map generation...") |
| 130 | + results["generation"] = test_static_map_generation() |
| 131 | + logger.info("") |
| 132 | + else: |
| 133 | + logger.warning("Skipping map generation test (import failed)") |
| 134 | + results["generation"] = False |
| 135 | + |
| 136 | + # Summary |
| 137 | + logger.info("=" * 60) |
| 138 | + logger.info("Summary") |
| 139 | + logger.info("=" * 60) |
| 140 | + logger.info(f"Import check: {'✓ PASS' if results['import'] else '✗ FAIL'}") |
| 141 | + logger.info(f"Network connectivity: {'✓ PASS' if results['connectivity'] else '✗ FAIL'}") |
| 142 | + logger.info(f"Map generation: {'✓ PASS' if results['generation'] else '✗ FAIL'}") |
| 143 | + logger.info("") |
| 144 | + |
| 145 | + if not results["connectivity"]: |
| 146 | + logger.warning("=" * 60) |
| 147 | + logger.warning("NETWORK CONNECTIVITY ISSUE DETECTED") |
| 148 | + logger.warning("=" * 60) |
| 149 | + logger.warning( |
| 150 | + "The tile server connectivity test failed. This suggests that:\n" |
| 151 | + "1. Outbound HTTP traffic may be blocked by a firewall\n" |
| 152 | + "2. The server may not have internet access\n" |
| 153 | + "3. DNS resolution may be failing\n" |
| 154 | + "\n" |
| 155 | + "Solutions:\n" |
| 156 | + "1. Check firewall rules to allow outbound HTTP/HTTPS traffic\n" |
| 157 | + "2. Verify DNS resolution: nslookup basemaps.cartocdn.com\n" |
| 158 | + "3. Test manual connection: curl http://a.basemaps.cartocdn.com/rastertiles/light_all/1/0/0.png\n" |
| 159 | + "4. Consider using a proxy server if outbound traffic must be restricted\n" |
| 160 | + ) |
| 161 | + |
| 162 | + if results["connectivity"] and not results["generation"]: |
| 163 | + logger.warning("=" * 60) |
| 164 | + logger.warning("MAP GENERATION ISSUE DETECTED") |
| 165 | + logger.warning("=" * 60) |
| 166 | + logger.warning( |
| 167 | + "Network connectivity works, but map generation failed.\n" |
| 168 | + "Check the error messages above for details.\n" |
| 169 | + "This may be a py-staticmaps configuration issue.\n" |
| 170 | + ) |
| 171 | + |
| 172 | + return all(results.values()) |
| 173 | + |
| 174 | + |
| 175 | +if __name__ == "__main__": |
| 176 | + success = main() |
| 177 | + sys.exit(0 if success else 1) |
| 178 | + |
0 commit comments